diff --git a/forge-core/src/main/java/forge/card/CardRules.java b/forge-core/src/main/java/forge/card/CardRules.java index ff8b6dd08fc..b981a8cc4ab 100644 --- a/forge-core/src/main/java/forge/card/CardRules.java +++ b/forge-core/src/main/java/forge/card/CardRules.java @@ -67,7 +67,7 @@ public final class CardRules implements ICardCharacteristics { } void reinitializeFromRules(CardRules newRules) { - if(!newRules.getName().equals(this.getName())) + if (!newRules.getName().equals(this.getName())) throw new UnsupportedOperationException("You cannot rename the card using the same CardRules object"); splitType = newRules.splitType; @@ -91,7 +91,7 @@ public final class CardRules implements ICardCharacteristics { } } int len = oracleText.length(); - for(int i = 0; i < len; i++) { + for (int i = 0; i < len; i++) { char c = oracleText.charAt(i); // This is to avoid needless allocations performed by toCharArray() switch(c) { case('('): isReminder = i > 0; break; // if oracle has only reminder, consider it valid rules (basic and true lands need this) @@ -99,7 +99,7 @@ public final class CardRules implements ICardCharacteristics { case('{'): isSymbol = true; break; case('}'): isSymbol = false; break; default: - if(isSymbol && !isReminder) { + if (isSymbol && !isReminder) { switch(c) { case('W'): res |= MagicColor.WHITE; break; case('U'): res |= MagicColor.BLUE; break; @@ -317,7 +317,7 @@ public final class CardRules implements ICardCharacteristics { /** Instantiates class, reads a card. For batch operations better create you own reader instance. */ public static CardRules fromScript(Iterable script) { Reader crr = new Reader(); - for(String line : script) { + for (String line : script) { crr.parseLine(line); } return crr.getCard(); diff --git a/forge-core/src/main/java/forge/card/CardRulesPredicates.java b/forge-core/src/main/java/forge/card/CardRulesPredicates.java index be11a1ee5d3..935c7d56aff 100644 --- a/forge-core/src/main/java/forge/card/CardRulesPredicates.java +++ b/forge-core/src/main/java/forge/card/CardRulesPredicates.java @@ -270,7 +270,6 @@ public final class CardRulesPredicates { return new PredicateSuperType(type, isEqual); } - /** * Checks for color. * diff --git a/forge-core/src/main/java/forge/item/PaperCard.java b/forge-core/src/main/java/forge/item/PaperCard.java index 21bcae19280..1ec639ba940 100644 --- a/forge-core/src/main/java/forge/item/PaperCard.java +++ b/forge-core/src/main/java/forge/item/PaperCard.java @@ -174,7 +174,7 @@ public final class PaperCard implements Comparable, InventoryItemFro artIndex = Math.max(artIndex0, IPaperCard.DEFAULT_ART_INDEX); foil = foil0; rarity = rarity0; - artist = (artist0 != null ? artist0 : IPaperCard.NO_ARTIST_NAME); + artist = (artist0 != null ? TextUtil.normalizeText(artist0) : IPaperCard.NO_ARTIST_NAME); collectorNumber = (collectorNumber0 != null) && (collectorNumber0.length() > 0) ? collectorNumber0 : IPaperCard.NO_COLLECTOR_NUMBER; } diff --git a/forge-core/src/main/java/forge/item/PaperToken.java b/forge-core/src/main/java/forge/item/PaperToken.java index 5467ca2cb1b..b240c4644b6 100644 --- a/forge-core/src/main/java/forge/item/PaperToken.java +++ b/forge-core/src/main/java/forge/item/PaperToken.java @@ -122,7 +122,7 @@ public class PaperToken implements InventoryItemFromSet, IPaperCard { String formatEdition = null == edition || CardEdition.UNKNOWN == edition ? "" : "_" + edition.getCode().toLowerCase(); this.imageFileName.add(String.format("%s%s", imageFileName, formatEdition)); - for(int idx = 2; idx <= this.artIndex; idx++) { + for (int idx = 2; idx <= this.artIndex; idx++) { this.imageFileName.add(String.format("%s%d%s", imageFileName, idx, formatEdition)); } } diff --git a/forge-core/src/main/java/forge/token/TokenDb.java b/forge-core/src/main/java/forge/token/TokenDb.java index 1175c231f4b..4e5876131af 100644 --- a/forge-core/src/main/java/forge/token/TokenDb.java +++ b/forge-core/src/main/java/forge/token/TokenDb.java @@ -40,7 +40,7 @@ public class TokenDb implements ITokenDatabase { } public void preloadTokens() { - for(CardEdition edition : this.editions) { + for (CardEdition edition : this.editions) { for (String name : edition.getTokens().keySet()) { try { getToken(name, edition.getCode()); diff --git a/forge-core/src/main/java/forge/util/TextUtil.java b/forge-core/src/main/java/forge/util/TextUtil.java index 33d4653c013..3ae45c32e70 100644 --- a/forge-core/src/main/java/forge/util/TextUtil.java +++ b/forge-core/src/main/java/forge/util/TextUtil.java @@ -1,11 +1,13 @@ package forge.util; +import java.text.Normalizer; import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Map.Entry; +import forge.item.IPaperCard; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; @@ -27,14 +29,19 @@ public class TextUtil { .put(10, "X").put(9, "IX") .put(5, "V").put(4, "IV").put(1, "I").build(); - public final static String toRoman(int number) { + public static String toRoman(int number) { if (number <= 0) { return ""; } int l = romanMap.floorKey(number); return romanMap.get(l) + toRoman(number-l); } + public static String normalizeText(String text) { + if (text == null) + return IPaperCard.NO_ARTIST_NAME; + return Normalizer.normalize(text, Normalizer.Form.NFD); + } /** * Safely converts an object to a String. * diff --git a/forge-game/src/main/java/forge/game/GameActionUtil.java b/forge-game/src/main/java/forge/game/GameActionUtil.java index 00065e4492e..091c9bf5b94 100644 --- a/forge-game/src/main/java/forge/game/GameActionUtil.java +++ b/forge-game/src/main/java/forge/game/GameActionUtil.java @@ -175,7 +175,29 @@ public final class GameActionUtil { for (final KeywordInterface inst : source.getKeywords()) { final String keyword = inst.getOriginal(); - if (keyword.startsWith("Escape")) { + if (keyword.startsWith("Disturb")) { + final String[] k = keyword.split(":"); + final Cost disturbCost = new Cost(k[1], true); + + final SpellAbility newSA = sa.copyWithManaCostReplaced(activator, disturbCost); + newSA.setActivatingPlayer(activator); + + newSA.putParam("PrecostDesc", "Disturb —"); + newSA.putParam("CostDesc", disturbCost.toString()); + + // makes new SpellDescription + final StringBuilder desc = new StringBuilder(); + desc.append(newSA.getCostDescription()); + desc.append("(").append(inst.getReminderText()).append(")"); + newSA.setDescription(desc.toString()); + newSA.putParam("AfterDescription", "(Disturbed)"); + + newSA.setAlternativeCost(AlternativeCost.Disturb); + newSA.getRestrictions().setZone(ZoneType.Graveyard); + newSA.setCardState(source.getAlternateState()); + + alternatives.add(newSA); + } else if (keyword.startsWith("Escape")) { final String[] k = keyword.split(":"); final Cost escapeCost = new Cost(k[1], true); @@ -211,6 +233,13 @@ public final class GameActionUtil { if (keyword.contains(":")) { final String[] k = keyword.split(":"); flashback.setPayCosts(new Cost(k[1], false)); + String extra = k.length > 2 ? k[2] : ""; + if (!extra.isEmpty()) { + String[] parts = extra.split("\\$"); + String key = parts[0]; + String value = parts[1]; + flashback.putParam(key, value); + } } alternatives.add(flashback); } else if (keyword.startsWith("Foretell")) { diff --git a/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneAllEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneAllEffect.java index b53383ed315..396a33268ee 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneAllEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneAllEffect.java @@ -166,7 +166,6 @@ public class ChangeZoneAllEffect extends SpellAbilityEffect { Map moveParams = Maps.newEnumMap(AbilityKey.class); if (destination == ZoneType.Battlefield) { - if (sa.hasAdditionalAbility("AnimateSubAbility")) { // need LKI before Animate does apply moveParams.put(AbilityKey.CardLKI, CardUtil.getLKICopy(c)); diff --git a/forge-game/src/main/java/forge/game/ability/effects/LifeGainEffect.java b/forge-game/src/main/java/forge/game/ability/effects/LifeGainEffect.java index 457a92f8b74..16a78793898 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/LifeGainEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/LifeGainEffect.java @@ -1,6 +1,5 @@ package forge.game.ability.effects; - import java.util.List; import forge.game.ability.AbilityUtils; @@ -40,7 +39,7 @@ public class LifeGainEffect extends SpellAbilityEffect { final int lifeAmount = AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("LifeAmount"), sa); List tgtPlayers = getDefinedPlayersOrTargeted(sa); - if( tgtPlayers.isEmpty() ) { + if (tgtPlayers.isEmpty()) { tgtPlayers.add(sa.getActivatingPlayer()); } diff --git a/forge-game/src/main/java/forge/game/card/Card.java b/forge-game/src/main/java/forge/game/card/Card.java index 6e67766d5a3..20800d449ab 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -1943,7 +1943,9 @@ public class Card extends GameEntity implements Comparable, IHasSVars { } else { sbLong.append(parts[0]).append(" ").append(ManaCostParser.parse(parts[1])).append("\r\n"); } - } else if (keyword.startsWith("Morph") || keyword.startsWith("Megamorph") || keyword.startsWith("Escape") || keyword.startsWith("Foretell:")) { + } else if (keyword.startsWith("Morph") || keyword.startsWith("Megamorph") + || keyword.startsWith("Escape") || keyword.startsWith("Foretell:") + || keyword.startsWith("Disturb")) { String[] k = keyword.split(":"); sbLong.append(k[0]); if (k.length > 1) { @@ -2033,7 +2035,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars { } else if (keyword.equals("Provoke") || keyword.equals("Ingest") || keyword.equals("Unleash") || keyword.equals("Soulbond") || keyword.equals("Partner") || keyword.equals("Retrace") || keyword.equals("Living Weapon") || keyword.equals("Myriad") || keyword.equals("Exploit") - || keyword.equals("Changeling") || keyword.equals("Delve") + || keyword.equals("Changeling") || keyword.equals("Delve") || keyword.equals("Decayed") || keyword.equals("Split second") || keyword.equals("Sunburst") || keyword.equals("Suspend") // for the ones without amount || keyword.equals("Foretell") // for the ones without cost @@ -2544,7 +2546,8 @@ public class Card extends GameEntity implements Comparable, IHasSVars { sbBefore.append("\r\n"); } else if (keyword.startsWith("Entwine") || keyword.startsWith("Madness") || keyword.startsWith("Miracle") || keyword.startsWith("Recover") - || keyword.startsWith("Escape") || keyword.startsWith("Foretell:")) { + || keyword.startsWith("Escape") || keyword.startsWith("Foretell:") + || keyword.startsWith("Disturb")) { final String[] k = keyword.split(":"); final Cost cost = new Cost(k[1], false); diff --git a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java index aa349a7d5aa..65af2511ad6 100644 --- a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java +++ b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java @@ -968,6 +968,26 @@ public class CardFactoryUtil { trigger.setOverridingAbility(AbilityFactory.getAbility(effect, card)); inst.addTrigger(trigger); + } else if (keyword.equals("Decayed")) { + final String attackTrig = "Mode$ Attacks | ValidCard$ Card.Self | Secondary$ True | TriggerDescription$ " + + "When a creature with decayed attacks, sacrifice it at end of combat."; + + final String delayTrigStg = "DB$ DelayedTrigger | Mode$ Phase | Phase$ EndCombat | ValidPlayer$ Player | " + + "TriggerDescription$ At end of combat, sacrifice CARDNAME."; + + final String trigSacStg = "DB$ SacrificeAll | Defined$ Self | Controller$ You"; + + SpellAbility delayTrigSA = AbilityFactory.getAbility(delayTrigStg, card); + + AbilitySub sacSA = (AbilitySub) AbilityFactory.getAbility(trigSacStg, card); + delayTrigSA.setAdditionalAbility("Execute", sacSA); + + final Trigger parsedTrigger = TriggerHandler.parseTrigger(attackTrig, card, intrinsic); + + delayTrigSA.setIntrinsic(intrinsic); + + parsedTrigger.setOverridingAbility(delayTrigSA); + inst.addTrigger(parsedTrigger); } else if (keyword.equals("Demonstrate")) { final String trigScript = "Mode$ SpellCast | ValidCard$ Card.Self | TriggerDescription$ Demonstrate (" + inst.getReminderText() + ")"; final String youCopyStr = "DB$ CopySpellAbility | Defined$ TriggeredSpellAbility | MayChooseTarget$ True | Optional$ True | RememberCopies$ True"; @@ -2150,16 +2170,20 @@ public class CardFactoryUtil { sb.append("Event$ Moved | ValidCard$ Card.Self | Origin$ Stack | ExcludeDestination$ Exile "); sb.append("| ValidStackSa$ Spell.Flashback | Description$ Flashback"); - if (keyword.contains(":")) { + if (keyword.contains(":")) { // K:Flashback:Cost:ExtraParam(Key$Value):ExtraDescription final String[] k = keyword.split(":"); final Cost cost = new Cost(k[1], false); - sb.append( cost.isOnlyManaCost() ? " " : "—"); + sb.append(cost.isOnlyManaCost() ? " " : "—"); sb.append(cost.toSimpleString()); if (!cost.isOnlyManaCost()) { sb.append("."); } + String extraDesc = k.length > 3 ? k[3] : ""; + if (!extraDesc.isEmpty()) { + sb.append(". ").append(extraDesc); + } } sb.append(" ("); @@ -3400,6 +3424,10 @@ public class CardFactoryUtil { } } else if (keyword.startsWith("Dash")) { effect = "Mode$ Continuous | Affected$ Card.Self+dashed | AddKeyword$ Haste"; + } else if (keyword.equals("Decayed")) { + effect = "Mode$ Continuous | Affected$ Card.Self | AddHiddenKeyword$ CARDNAME can't block. | " + + "Secondary$ True"; + svars.put("SacrificeEndCombat", "True"); } else if (keyword.equals("Defender")) { effect = "Mode$ CantAttack | ValidCard$ Card.Self | DefenderKeyword$ True | Secondary$ True"; } else if (keyword.equals("Devoid")) { diff --git a/forge-game/src/main/java/forge/game/card/CardProperty.java b/forge-game/src/main/java/forge/game/card/CardProperty.java index 43b6dcde919..6574fb42875 100644 --- a/forge-game/src/main/java/forge/game/card/CardProperty.java +++ b/forge-game/src/main/java/forge/game/card/CardProperty.java @@ -832,7 +832,7 @@ public class CardProperty { return Iterables.any(CardUtil.getThisTurnCast("Card", source, spellAbility), CardPredicates.sharesNameWith(card)); } else if (restriction.equals("MovedToGrave")) { if (!(spellAbility instanceof SpellAbility)) { - final SpellAbility root = ((SpellAbility)spellAbility).getRootAbility(); + final SpellAbility root = ((SpellAbility) spellAbility).getRootAbility(); if (root != null && (root.getPaidList("MovedToGrave") != null) && !root.getPaidList("MovedToGrave").isEmpty()) { final CardCollectionView cards = root.getPaidList("MovedToGrave"); @@ -855,7 +855,7 @@ public class CardProperty { if (!(spellAbility instanceof SpellAbility)) { System.out.println("Looking at TriggeredCard but no SA?"); } else { - Card triggeredCard = ((Card)((SpellAbility)spellAbility).getTriggeringObject(AbilityKey.Card)); + Card triggeredCard = ((Card) ((SpellAbility) spellAbility).getTriggeringObject(AbilityKey.Card)); if (triggeredCard != null && card.sharesNameWith(triggeredCard)) { return true; } @@ -924,10 +924,9 @@ public class CardProperty { } } else if (property.startsWith("SecondSpellCastThisTurn")) { final List cards = CardUtil.getThisTurnCast("Card", source, spellAbility); - if (cards.size() < 2) { + if (cards.size() < 2) { return false; - } - else if (cards.get(1) != card) { + } else if (cards.get(1) != card) { return false; } } else if (property.equals("ThisTurnCast")) { @@ -1232,8 +1231,7 @@ public class CardProperty { if (!cards.contains(card)) { return false; } - } - else if (property.startsWith("lowestCMC")) { + } else if (property.startsWith("lowestCMC")) { final CardCollectionView cards = game.getCardsIn(ZoneType.Battlefield); for (final Card crd : cards) { if (!crd.isLand() && !crd.isImmutable()) { @@ -1369,9 +1367,7 @@ public class CardProperty { if (!Expressions.compare(y, property, x)) { return false; } - } - - else if (property.startsWith("ManaCost")) { + } else if (property.startsWith("ManaCost")) { if (!card.getManaCost().getShortString().equals(property.substring(8))) { return false; } @@ -1412,7 +1408,7 @@ public class CardProperty { // These predicated refer to ongoing combat. If no combat happens, they'll return false (meaning not attacking/blocking ATM) else if (property.startsWith("attacking")) { if (null == combat) return false; - if (property.equals("attacking")) return card.isAttacking(); + if (property.equals("attacking")) return card.isAttacking(); if (property.equals("attackingYou")) return combat.isAttacking(card, sourceController); if (property.equals("attackingSame")) { final GameEntity attacked = combat.getDefenderByAttacker(source); @@ -1424,10 +1420,10 @@ public class CardProperty { GameEntity defender = combat.getDefenderByAttacker(card); if (defender instanceof Card) { // attack on a planeswalker that was removed from combat - if (!((Card)defender).isPlaneswalker()) { + if (!((Card) defender).isPlaneswalker()) { return false; } - defender = ((Card)defender).getController(); + defender = ((Card) defender).getController(); } if (!sourceController.equals(defender)) { return false; @@ -1471,12 +1467,17 @@ public class CardProperty { if (combat.isBlocking(card, c)) { return true; } - }; + } + ; return false; } } else if (property.startsWith("sharesBlockingAssignmentWith")) { - if (null == combat) { return false; } - if (null == combat.getAttackersBlockedBy(source) || null == combat.getAttackersBlockedBy(card)) { return false; } + if (null == combat) { + return false; + } + if (null == combat.getAttackersBlockedBy(source) || null == combat.getAttackersBlockedBy(card)) { + return false; + } CardCollection sourceBlocking = new CardCollection(combat.getAttackersBlockedBy(source)); CardCollection thisBlocking = new CardCollection(combat.getAttackersBlockedBy(card)); @@ -1503,16 +1504,17 @@ public class CardProperty { return false; } String valid = property.split(" ")[1]; - for(Card c : blocked) { + for (Card c : blocked) { if (c.isValid(valid, card.getController(), source, spellAbility)) { return true; } } - for(Card c : AbilityUtils.getDefinedCards(source, valid, spellAbility)) { + for (Card c : AbilityUtils.getDefinedCards(source, valid, spellAbility)) { if (blocked.contains(c)) { return true; } - }; + } + ; return false; } else if (property.startsWith("blockedByValidThisTurn ")) { CardCollectionView blocked = card.getBlockedByThisTurn(); @@ -1527,7 +1529,8 @@ public class CardProperty { if (blocked.contains(c)) { return true; } - }; + } + ; return false; } else if (property.startsWith("blockedBySourceThisTurn")) { return source.getBlockedByThisTurn().contains(card); diff --git a/forge-game/src/main/java/forge/game/card/CardView.java b/forge-game/src/main/java/forge/game/card/CardView.java index 0d59026e743..86121a87cc3 100644 --- a/forge-game/src/main/java/forge/game/card/CardView.java +++ b/forge-game/src/main/java/forge/game/card/CardView.java @@ -6,6 +6,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import forge.game.spellability.SpellAbility; import org.apache.commons.lang3.StringUtils; import com.google.common.base.Predicate; @@ -821,7 +822,7 @@ public class CardView extends GameEntityView { updateZoneText(c); updateDamage(c); - if (getBackup() == null && !c.isFaceDown() && c.hasBackSide()) { + if (getBackup() == null && !c.isFaceDown() && (c.hasBackSide()||c.isFlipCard()||c.isAdventureCard())) { set(TrackableProperty.PaperCardBackup, c.getPaperCard()); } @@ -1300,6 +1301,37 @@ public class CardView extends GameEntityView { public boolean hasLandwalk() { return get(TrackableProperty.HasLandwalk); } + public boolean hasHasAftermath() { + return get(TrackableProperty.HasAftermath); + } + + public boolean origProduceAnyMana() { + return get(TrackableProperty.OrigProduceAnyMana); + } + public boolean origProduceManaR() { + return get(TrackableProperty.OrigProduceManaR); + } + public boolean origProduceManaG() { + return get(TrackableProperty.OrigProduceManaG); + } + public boolean origProduceManaB() { + return get(TrackableProperty.OrigProduceManaB); + } + public boolean origProduceManaU() { + return get(TrackableProperty.OrigProduceManaU); + } + public boolean origProduceManaW() { + return get(TrackableProperty.OrigProduceManaW); + } + public boolean origProduceManaC() { + return get(TrackableProperty.OrigProduceManaC); + } + public int origCanProduceColoredMana() { + return get(TrackableProperty.CountOrigProduceColoredMana); + } + public int countBasicLandTypes() { + return get(TrackableProperty.CountBasicLandTypes); + } public String getAbilityText() { return get(TrackableProperty.AbilityText); @@ -1333,6 +1365,7 @@ public class CardView extends GameEntityView { set(TrackableProperty.HasInfect, c.hasKeyword(Keyword.INFECT, state)); set(TrackableProperty.HasStorm, c.hasKeyword(Keyword.STORM, state)); set(TrackableProperty.HasLandwalk, c.hasKeyword(Keyword.LANDWALK, state)); + set(TrackableProperty.HasAftermath, c.hasKeyword(Keyword.AFTERMATH, state)); updateAbilityText(c, state); //set protectionKey for Icons set(TrackableProperty.ProtectionKey, c.getProtectionKey()); @@ -1340,6 +1373,84 @@ public class CardView extends GameEntityView { set(TrackableProperty.HexproofKey, c.getHexproofKey()); //keywordkey set(TrackableProperty.KeywordKey, c.getKeywordKey()); + //update Trackable Mana Color for BG Colors + updateManaColorBG(state); + } + void updateManaColorBG(CardState state) { + boolean anyMana = false; + boolean rMana = false; + boolean gMana = false; + boolean bMana = false; + boolean uMana = false; + boolean wMana = false; + boolean cMana = false; + int count = 0; + int basicLandTypes = 0; + if (!state.getManaAbilities().isEmpty()) { + for (SpellAbility sa : state.getManaAbilities()) { + if (sa == null || sa.getManaPart() == null) + continue; + if (sa.getManaPart().isAnyMana()) { + anyMana = true; + } + switch (sa.getManaPart().getOrigProduced()) { + case "R": + if (!rMana) { + count += 1; + rMana = true; + } + break; + case "G": + if (!gMana) { + count += 1; + gMana = true; + } + break; + case "B": + if (!bMana) { + count += 1; + bMana = true; + } + break; + case "U": + if (!uMana) { + count += 1; + uMana = true; + } + break; + case "W": + if (!wMana) { + count += 1; + wMana = true; + } + break; + case "C": + if (!cMana) { + cMana = true; + } + break; + } + } + } + if (isForest()) + basicLandTypes += 1; + if (isMountain()) + basicLandTypes += 1; + if (isSwamp()) + basicLandTypes += 1; + if (isPlains()) + basicLandTypes += 1; + if (isIsland()) + basicLandTypes += 1; + set(TrackableProperty.CountBasicLandTypes, basicLandTypes); + set(TrackableProperty.OrigProduceManaR, rMana); + set(TrackableProperty.OrigProduceManaG, gMana); + set(TrackableProperty.OrigProduceManaB, bMana); + set(TrackableProperty.OrigProduceManaU, uMana); + set(TrackableProperty.OrigProduceManaW, wMana); + set(TrackableProperty.OrigProduceManaC, cMana); + set(TrackableProperty.CountOrigProduceColoredMana, count); + set(TrackableProperty.OrigProduceAnyMana, anyMana); } public boolean isBasicLand() { @@ -1375,6 +1486,12 @@ public class CardView extends GameEntityView { public boolean isIsland() { return getType().hasSubtype("Island"); } + public boolean isVehicle() { + return getType().hasSubtype("Vehicle"); + } + public boolean isArtifact() { + return getType().isArtifact(); + } } //special methods for updating card and player properties as needed and returning the new collection diff --git a/forge-game/src/main/java/forge/game/card/TokenCreateTable.java b/forge-game/src/main/java/forge/game/card/TokenCreateTable.java index a61586898f0..537e512a492 100644 --- a/forge-game/src/main/java/forge/game/card/TokenCreateTable.java +++ b/forge-game/src/main/java/forge/game/card/TokenCreateTable.java @@ -33,20 +33,20 @@ public class TokenCreateTable extends ForwardingTable { this.put(p, c, newValue); return newValue; } - + public int getFilterAmount(String validOwner, String validToken, final CardTraitBase ctb) { final Card host = ctb.getHostCard(); int result = 0; List filteredCards = null; List filteredPlayer = null; - + if (validOwner == null && validToken == null) { for (Integer i : values()) { result += i; } return result; } - + if (validOwner != null) { filteredPlayer = Lists.newArrayList(Iterables.filter(rowKeySet(), GameObjectPredicates.restriction(validOwner.split(","), host.getController(), host, ctb))); @@ -78,7 +78,7 @@ public class TokenCreateTable extends ForwardingTable { } return result; } - + for (Table.Cell c : this.cellSet()) { if (!filteredPlayer.contains(c.getRowKey())) { continue; @@ -89,7 +89,6 @@ public class TokenCreateTable extends ForwardingTable { result += c.getValue(); } - return result; } } diff --git a/forge-game/src/main/java/forge/game/card/token/TokenInfo.java b/forge-game/src/main/java/forge/game/card/token/TokenInfo.java index 82cd621cc94..a0df70f1261 100644 --- a/forge-game/src/main/java/forge/game/card/token/TokenInfo.java +++ b/forge-game/src/main/java/forge/game/card/token/TokenInfo.java @@ -152,7 +152,7 @@ public class TokenInfo { } static protected void protoTypeApplyTextChange(final Card result, final SpellAbility sa) { - // update Token with CardTextChanges + // update Token with CardTextChanges Map colorMap = sa.getChangedTextColors(); Map typeMap = sa.getChangedTextTypes(); if (!colorMap.isEmpty()) { diff --git a/forge-game/src/main/java/forge/game/keyword/Keyword.java b/forge-game/src/main/java/forge/game/keyword/Keyword.java index a31b1adbb0b..76803cf1b5e 100644 --- a/forge-game/src/main/java/forge/game/keyword/Keyword.java +++ b/forge-game/src/main/java/forge/game/keyword/Keyword.java @@ -46,12 +46,14 @@ public enum Keyword { CYCLING("Cycling", KeywordWithCost.class, false, "%s, Discard this card: Draw a card."), //Typecycling reminder text handled by Cycling class DASH("Dash", KeywordWithCost.class, false, "You may cast this spell for its dash cost. If you do, it gains haste, and it's returned from the battlefield to its owner's hand at the beginning of the next end step."), DEATHTOUCH("Deathtouch", SimpleKeyword.class, true, "Any amount of damage this deals to a creature is enough to destroy it."), + DECAYED("Decayed", SimpleKeyword.class, true, "This creature can't block. When it attacks, sacrifice it at end of combat."), DEFENDER("Defender", SimpleKeyword.class, true, "This creature can't attack."), DELVE("Delve", SimpleKeyword.class, true, "As an additional cost to cast this spell, you may exile any number of cards from your graveyard. Each card exiled this way reduces the cost to cast this spell by {1}."), DEMONSTRATE("Demonstrate", SimpleKeyword.class, false, "When you cast this spell, you may copy it. If you do, choose an opponent to also copy it. Players may choose new targets for their copies."), DETHRONE("Dethrone", SimpleKeyword.class, false, "Whenever this creature attacks the player with the most life or tied for the most life, put a +1/+1 counter on it."), DEVOUR("Devour", KeywordWithAmount.class, false, "As this creature enters the battlefield, you may sacrifice any number of creatures. This creature enters the battlefield with {%d:+1/+1 counter} on it for each creature sacrificed this way."), DEVOID("Devoid", SimpleKeyword.class, true, "This card has no color."), + DISTURB("Disturb", KeywordWithCost.class, false, "You may cast this card from your graveyard transformed for its disturb cost."), DOUBLE_STRIKE("Double Strike", SimpleKeyword.class, true, "This creature deals both first-strike and regular combat damage."), DREDGE("Dredge", KeywordWithAmount.class, false, "If you would draw a card, instead you may put exactly {%d:card} from the top of your library into your graveyard. If you do, return this card from your graveyard to your hand. Otherwise, draw a card."), ECHO("Echo", KeywordWithCost.class, false, "At the beginning of your upkeep, if this permanent came under your control since the beginning of your last upkeep, sacrifice it unless you pay %s."), @@ -77,7 +79,7 @@ public enum Keyword { FIRST_STRIKE("First Strike", SimpleKeyword.class, true, "This creature deals combat damage before creatures without first strike."), FLANKING("Flanking", SimpleKeyword.class, false, "Whenever this creature becomes blocked by a creature without flanking, the blocking creature gets -1/-1 until end of turn."), FLASH("Flash", SimpleKeyword.class, true, "You may cast this spell any time you could cast an instant."), - FLASHBACK("Flashback", KeywordWithCost.class, false, "You may cast this card from your graveyard by paying %s rather than paying its mana cost. If you do, exile it as it resolves."), + FLASHBACK("Flashback", KeywordWithCost.class, false, "You may cast this card from your graveyard for its flashback cost. Then exile it."), FLYING("Flying", SimpleKeyword.class, true, "This creature can't be blocked except by creatures with flying or reach."), FORETELL("Foretell", KeywordWithCost.class, false, "During your turn, you may pay {2} and exile this card from your hand face down. Cast it on a later turn for its foretell cost."), FORTIFY("Fortify", KeywordWithCost.class, false, "%s: Attach to target land you control. Fortify only as a sorcery."), diff --git a/forge-game/src/main/java/forge/game/spellability/AlternativeCost.java b/forge-game/src/main/java/forge/game/spellability/AlternativeCost.java index c3965363e72..6739271e7c2 100644 --- a/forge-game/src/main/java/forge/game/spellability/AlternativeCost.java +++ b/forge-game/src/main/java/forge/game/spellability/AlternativeCost.java @@ -5,6 +5,7 @@ public enum AlternativeCost { Bestow, Cycling, // ActivatedAbility Dash, + Disturb, Emerge, Escape, Evoke, diff --git a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java index 050942a1b30..5be31854b06 100644 --- a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java +++ b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java @@ -1370,6 +1370,10 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit return isAlternativeCost(AlternativeCost.Dash); } + public final boolean isDisturb() { + return isAlternativeCost(AlternativeCost.Disturb); + } + public final boolean isEscape() { return isAlternativeCost(AlternativeCost.Escape); } diff --git a/forge-game/src/main/java/forge/trackable/TrackableProperty.java b/forge-game/src/main/java/forge/trackable/TrackableProperty.java index e378e900ab9..09783eba9eb 100644 --- a/forge-game/src/main/java/forge/trackable/TrackableProperty.java +++ b/forge-game/src/main/java/forge/trackable/TrackableProperty.java @@ -111,6 +111,18 @@ public enum TrackableProperty { HasChangedColors(TrackableTypes.BooleanType), ChangedTypes(TrackableTypes.StringMapType), + //check produce mana for BG + OrigProduceManaR(TrackableTypes.BooleanType), + OrigProduceManaG(TrackableTypes.BooleanType), + OrigProduceManaB(TrackableTypes.BooleanType), + OrigProduceManaU(TrackableTypes.BooleanType), + OrigProduceManaW(TrackableTypes.BooleanType), + OrigProduceManaC(TrackableTypes.BooleanType), + OrigProduceAnyMana(TrackableTypes.BooleanType), + CountOrigProduceColoredMana(TrackableTypes.IntegerType), + //number of basic landtypes + CountBasicLandTypes(TrackableTypes.IntegerType), + KeywordKey(TrackableTypes.StringType), HasDeathtouch(TrackableTypes.BooleanType), HasDevoid(TrackableTypes.BooleanType), @@ -132,6 +144,7 @@ public enum TrackableProperty { HasTrample(TrackableTypes.BooleanType), HasVigilance(TrackableTypes.BooleanType), HasLandwalk(TrackableTypes.BooleanType), + HasAftermath(TrackableTypes.BooleanType), //protectionkey ProtectionKey(TrackableTypes.StringType), //hexproofkey diff --git a/forge-gui-mobile/src/forge/Graphics.java b/forge-gui-mobile/src/forge/Graphics.java index baae7551b1b..5b5b61019da 100644 --- a/forge-gui-mobile/src/forge/Graphics.java +++ b/forge-gui-mobile/src/forge/Graphics.java @@ -472,6 +472,12 @@ public class Graphics { batch.begin(); } + public void drawRectLines(float thickness, Color color, float x, float y, float w, float h) { + drawLine(thickness, color, x, y, x+w, y); + drawLine(thickness, color, x+thickness/2f, y+thickness/2f, x+thickness/2f, y+h-thickness/2f); + drawLine(thickness, color, x, y+h, x+w, y+h); + drawLine(thickness, color, x+w-thickness/2f, y+thickness/2f, x+w-thickness/2f, y+h-thickness/2f); + } public void fillRect(FSkinColor skinColor, float x, float y, float w, float h) { fillRect(skinColor.getColor(), x, y, w, h); @@ -850,6 +856,18 @@ public class Graphics { //use nifty trick with multiple text renders to draw outlined text public void drawOutlinedText(String text, FSkinFont skinFont, Color textColor, Color outlineColor, float x, float y, float w, float h, boolean wrap, int horzAlignment, boolean centerVertically) { + drawOutlinedText(text, skinFont, textColor, outlineColor, x, y, w, h, wrap, horzAlignment, centerVertically, false); + } + public void drawOutlinedText(String text, FSkinFont skinFont, Color textColor, Color outlineColor, float x, float y, float w, float h, boolean wrap, int horzAlignment, boolean centerVertically, boolean shadow) { + if (shadow) { + float oldAlpha = alphaComposite; + alphaComposite = 0.4f; + drawText(text, skinFont, outlineColor, x - 1.5f, y + 1.5f, w, h, wrap, horzAlignment, centerVertically); + drawText(text, skinFont, outlineColor, x + 1.5f, y + 1.5f, w, h, wrap, horzAlignment, centerVertically); + drawText(text, skinFont, outlineColor, x + 1.5f, y - 1.5f, w, h, wrap, horzAlignment, centerVertically); + drawText(text, skinFont, outlineColor, x - 1.5f, y - 1.5f, w, h, wrap, horzAlignment, centerVertically); + alphaComposite = oldAlpha; + } drawText(text, skinFont, outlineColor, x - 1, y, w, h, wrap, horzAlignment, centerVertically); drawText(text, skinFont, outlineColor, x, y - 1, w, h, wrap, horzAlignment, centerVertically); drawText(text, skinFont, outlineColor, x - 1, y - 1, w, h, wrap, horzAlignment, centerVertically); diff --git a/forge-gui-mobile/src/forge/assets/FBufferedImage.java b/forge-gui-mobile/src/forge/assets/FBufferedImage.java index dfc152ce99e..eaebff0e25b 100644 --- a/forge-gui-mobile/src/forge/assets/FBufferedImage.java +++ b/forge-gui-mobile/src/forge/assets/FBufferedImage.java @@ -4,6 +4,7 @@ import com.badlogic.gdx.Gdx; import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.graphics.Pixmap.Format; import com.badlogic.gdx.graphics.Texture; +import com.badlogic.gdx.graphics.g2d.TextureRegion; import com.badlogic.gdx.graphics.glutils.FrameBuffer; import com.badlogic.gdx.math.Matrix4; @@ -44,8 +45,30 @@ public abstract class FBufferedImage extends FImageComplex { return 0; } + @Override + public TextureRegion getTextureRegion() { + return new TextureRegion(checkFrameBuffer().getColorBufferTexture()); + } + @Override public Texture getTexture() { + return checkFrameBuffer().getColorBufferTexture(); + } + + public void clear() { + final FrameBuffer fb = frameBuffer; + if (fb != null) { + frameBuffer = null; + FThreads.invokeInEdtNowOrLater(new Runnable() { + @Override + public void run() { + fb.dispose(); //must be disposed on EDT thread + } + }); + } + } + + public FrameBuffer checkFrameBuffer() { if (frameBuffer == null) { Gdx.gl.glDisable(GL20.GL_SCISSOR_TEST); //prevent buffered image being clipped @@ -69,20 +92,7 @@ public abstract class FBufferedImage extends FImageComplex { Gdx.gl.glEnable(GL20.GL_SCISSOR_TEST); } - return frameBuffer.getColorBufferTexture(); - } - - public void clear() { - final FrameBuffer fb = frameBuffer; - if (fb != null) { - frameBuffer = null; - FThreads.invokeInEdtNowOrLater(new Runnable() { - @Override - public void run() { - fb.dispose(); //must be disposed on EDT thread - } - }); - } + return frameBuffer; } protected abstract void draw(Graphics g, float w, float h); diff --git a/forge-gui-mobile/src/forge/assets/FDelayLoadImage.java b/forge-gui-mobile/src/forge/assets/FDelayLoadImage.java index 36d87245f4f..10a339f59ba 100644 --- a/forge-gui-mobile/src/forge/assets/FDelayLoadImage.java +++ b/forge-gui-mobile/src/forge/assets/FDelayLoadImage.java @@ -3,6 +3,7 @@ package forge.assets; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.graphics.Texture; +import com.badlogic.gdx.graphics.g2d.TextureRegion; import forge.Graphics; //Special wrapper for a texture to be loaded later when it's needed @@ -32,6 +33,14 @@ public class FDelayLoadImage extends FImageComplex { return texture; } + @Override + public TextureRegion getTextureRegion() { + if (texture == null) { + texture = new Texture(Gdx.files.absolute(filename)); + } + return new TextureRegion(texture); + } + @Override public int getRegionX() { return 0; diff --git a/forge-gui-mobile/src/forge/assets/FImageComplex.java b/forge-gui-mobile/src/forge/assets/FImageComplex.java index 06d557424a5..c726f243093 100644 --- a/forge-gui-mobile/src/forge/assets/FImageComplex.java +++ b/forge-gui-mobile/src/forge/assets/FImageComplex.java @@ -1,9 +1,11 @@ package forge.assets; import com.badlogic.gdx.graphics.Texture; +import com.badlogic.gdx.graphics.g2d.TextureRegion; public abstract class FImageComplex implements FImage { public abstract Texture getTexture(); + public abstract TextureRegion getTextureRegion(); public abstract int getRegionX(); public abstract int getRegionY(); } diff --git a/forge-gui-mobile/src/forge/assets/FRotatedImage.java b/forge-gui-mobile/src/forge/assets/FRotatedImage.java index b0a8bdc3764..1022aaebd52 100644 --- a/forge-gui-mobile/src/forge/assets/FRotatedImage.java +++ b/forge-gui-mobile/src/forge/assets/FRotatedImage.java @@ -2,6 +2,7 @@ package forge.assets; import com.badlogic.gdx.graphics.Texture; +import com.badlogic.gdx.graphics.g2d.TextureRegion; import forge.Graphics; public class FRotatedImage extends FImageComplex { @@ -33,6 +34,11 @@ public class FRotatedImage extends FImageComplex { return texture; } + @Override + public TextureRegion getTextureRegion() { + return new TextureRegion(texture); + } + @Override public int getRegionX() { return srcX; diff --git a/forge-gui-mobile/src/forge/assets/FSkin.java b/forge-gui-mobile/src/forge/assets/FSkin.java index eb70ab5e237..dc5402b40e5 100644 --- a/forge-gui-mobile/src/forge/assets/FSkin.java +++ b/forge-gui-mobile/src/forge/assets/FSkin.java @@ -38,6 +38,7 @@ public class FSkin { private static String preferredName; private static boolean loaded = false; public static Texture hdLogo = null; + public static Texture overlay_alpha = null; public static void changeSkin(final String skinName) { final ForgePreferences prefs = FModel.getPreferences(); @@ -124,10 +125,27 @@ public class FSkin { FSkinTexture.BG_TEXTURE.load(); //load background texture early for splash screen + //load theme logo while changing skins + final FileHandle theme_logo = getSkinFile("hd_logo.png"); + if (theme_logo.exists()) { + Texture txOverlay = new Texture(theme_logo, true); + txOverlay.setFilter(Texture.TextureFilter.MipMapLinearLinear, Texture.TextureFilter.Linear); + hdLogo = txOverlay; + } else { + hdLogo = null; + } + final FileHandle duals_overlay = getDefaultSkinFile("overlay_alpha.png"); + if (duals_overlay.exists()) { + Texture txAlphaLines = new Texture(duals_overlay, true); + txAlphaLines.setFilter(Texture.TextureFilter.MipMapLinearLinear, Texture.TextureFilter.Linear); + overlay_alpha = txAlphaLines; + } else { + overlay_alpha = null; + } + if (splashScreen != null) { final FileHandle f = getSkinFile("bg_splash.png"); final FileHandle f2 = getSkinFile("bg_splash_hd.png"); //HD Splashscreen - final FileHandle f3 = getSkinFile("hd_logo.png"); if (!f.exists()) { if (!skinName.equals("default")) { @@ -149,14 +167,6 @@ public class FSkin { splashScreen.setBackground(new TextureRegion(txSplash, 0, 0, w, h - 100)); } - if (f3.exists()) { - Texture txOverlay = new Texture(f3, true); - txOverlay.setFilter(Texture.TextureFilter.MipMapLinearLinear, Texture.TextureFilter.Linear); - hdLogo = txOverlay; - } else { - hdLogo = null; - } - Pixmap pxSplash = new Pixmap(f); FProgressBar.BACK_COLOR = new Color(pxSplash.getPixel(25, h - 75)); FProgressBar.FORE_COLOR = new Color(pxSplash.getPixel(75, h - 75)); @@ -217,9 +227,12 @@ public class FSkin { final FileHandle f11 = getSkinFile(ForgeConstants.SPRITE_BUTTONS_FILE); final FileHandle f12 = getSkinFile(ForgeConstants.SPRITE_START_FILE); final FileHandle f13 = getDefaultSkinFile(ForgeConstants.SPRITE_DECKBOX_FILE); + + /*TODO Themeable final FileHandle f14 = getDefaultSkinFile(ForgeConstants.SPRITE_SETLOGO_FILE); final FileHandle f15 = getSkinFile(ForgeConstants.SPRITE_SETLOGO_FILE); final FileHandle f16 = getDefaultSkinFile(ForgeConstants.SPRITE_WATERMARK_FILE); + */ try { textures.put(f1.path(), new Texture(f1)); diff --git a/forge-gui-mobile/src/forge/assets/FSkinImage.java b/forge-gui-mobile/src/forge/assets/FSkinImage.java index af1258cd27b..e70cbd8bf5f 100644 --- a/forge-gui-mobile/src/forge/assets/FSkinImage.java +++ b/forge-gui-mobile/src/forge/assets/FSkinImage.java @@ -115,6 +115,28 @@ public enum FSkinImage implements FImage { WATERMARK_W (FSkinProp.IMG_WATERMARK_W, SourceFile.WATERMARKS), WATERMARK_C (FSkinProp.IMG_WATERMARK_C, SourceFile.WATERMARKS), + //CardBG + CARDBG_A (FSkinProp.IMG_CARDBG_A, SourceFile.CARDBG), + CARDBG_B (FSkinProp.IMG_CARDBG_B, SourceFile.CARDBG), + CARDBG_BG (FSkinProp.IMG_CARDBG_BG, SourceFile.CARDBG), + CARDBG_BR (FSkinProp.IMG_CARDBG_BR, SourceFile.CARDBG), + CARDBG_C (FSkinProp.IMG_CARDBG_C, SourceFile.CARDBG), + CARDBG_G (FSkinProp.IMG_CARDBG_G, SourceFile.CARDBG), + CARDBG_L (FSkinProp.IMG_CARDBG_L, SourceFile.CARDBG), + CARDBG_M (FSkinProp.IMG_CARDBG_M, SourceFile.CARDBG), + CARDBG_R (FSkinProp.IMG_CARDBG_R, SourceFile.CARDBG), + CARDBG_RG (FSkinProp.IMG_CARDBG_RG, SourceFile.CARDBG), + CARDBG_U (FSkinProp.IMG_CARDBG_U, SourceFile.CARDBG), + CARDBG_UB (FSkinProp.IMG_CARDBG_UB, SourceFile.CARDBG), + CARDBG_UG (FSkinProp.IMG_CARDBG_UG, SourceFile.CARDBG), + CARDBG_UR (FSkinProp.IMG_CARDBG_UR, SourceFile.CARDBG), + CARDBG_V (FSkinProp.IMG_CARDBG_V, SourceFile.CARDBG), + CARDBG_W (FSkinProp.IMG_CARDBG_W, SourceFile.CARDBG), + CARDBG_WB (FSkinProp.IMG_CARDBG_WB, SourceFile.CARDBG), + CARDBG_WG (FSkinProp.IMG_CARDBG_WG, SourceFile.CARDBG), + CARDBG_WR (FSkinProp.IMG_CARDBG_WR, SourceFile.CARDBG), + CARDBG_WU (FSkinProp.IMG_CARDBG_WU, SourceFile.CARDBG), + //Gameplay TAP (FSkinProp.IMG_TAP, SourceFile.MANAICONS), UNTAP (FSkinProp.IMG_UNTAP, SourceFile.MANAICONS), @@ -439,6 +461,7 @@ public enum FSkinImage implements FImage { MANAICONS(ForgeConstants.SPRITE_MANAICONS_FILE), SETLOGOS(ForgeConstants.SPRITE_SETLOGO_FILE), WATERMARKS(ForgeConstants.SPRITE_WATERMARK_FILE), + CARDBG(ForgeConstants.SPRITE_CARDBG_FILE), PLANAR_CONQUEST(ForgeConstants.SPRITE_PLANAR_CONQUEST_FILE); private final String filename; diff --git a/forge-gui-mobile/src/forge/assets/FTextureImage.java b/forge-gui-mobile/src/forge/assets/FTextureImage.java index a1316961b11..292d97c083e 100644 --- a/forge-gui-mobile/src/forge/assets/FTextureImage.java +++ b/forge-gui-mobile/src/forge/assets/FTextureImage.java @@ -2,6 +2,7 @@ package forge.assets; import com.badlogic.gdx.graphics.Texture; +import com.badlogic.gdx.graphics.g2d.TextureRegion; import forge.Graphics; public class FTextureImage extends FImageComplex { @@ -26,6 +27,11 @@ public class FTextureImage extends FImageComplex { return texture; } + @Override + public TextureRegion getTextureRegion() { + return new TextureRegion(texture); + } + @Override public int getRegionX() { return 0; diff --git a/forge-gui-mobile/src/forge/assets/FTextureRegionImage.java b/forge-gui-mobile/src/forge/assets/FTextureRegionImage.java index 1f5ed033593..dcd6c914b1b 100644 --- a/forge-gui-mobile/src/forge/assets/FTextureRegionImage.java +++ b/forge-gui-mobile/src/forge/assets/FTextureRegionImage.java @@ -27,6 +27,11 @@ public class FTextureRegionImage extends FImageComplex { return textureRegion.getTexture(); } + @Override + public TextureRegion getTextureRegion() { + return textureRegion; + } + @Override public int getRegionX() { return textureRegion.getRegionX(); diff --git a/forge-gui-mobile/src/forge/card/CardAvatarImage.java b/forge-gui-mobile/src/forge/card/CardAvatarImage.java index 6609ca06c35..fc2a843df55 100644 --- a/forge-gui-mobile/src/forge/card/CardAvatarImage.java +++ b/forge-gui-mobile/src/forge/card/CardAvatarImage.java @@ -32,7 +32,7 @@ public class CardAvatarImage implements FImage { @Override public void draw(Graphics g, float x, float y, float w, float h) { //force to get the avatar since the the cardartcache & loadingcache is always cleared on screen change or the battle bar will display black - image = CardRenderer.getCardArt(imageKey, false, false, false); + image = CardRenderer.getCardArt(imageKey, false, false, false, false, false, false, false, false, true); if (image == null) { return; //can't draw anything if can't be loaded yet } diff --git a/forge-gui-mobile/src/forge/card/CardImage.java b/forge-gui-mobile/src/forge/card/CardImage.java index 6d2e7be323e..fad064c4fb0 100644 --- a/forge-gui-mobile/src/forge/card/CardImage.java +++ b/forge-gui-mobile/src/forge/card/CardImage.java @@ -38,14 +38,14 @@ public class CardImage implements FImage { image = ImageCache.getImage(card); if (image == null) { if (!Forge.enableUIMask.equals("Off")) //render this if mask is still loading - CardImageRenderer.drawCardImage(g, CardView.getCardForUi(card), false, x, y, w, h, CardStackPosition.Top); + CardImageRenderer.drawCardImage(g, CardView.getCardForUi(card), false, x, y, w, h, CardStackPosition.Top, Forge.enableUIMask.equals("Art"), true); return; //can't draw anything if can't be loaded yet } } - if (image == ImageCache.defaultImage) { - CardImageRenderer.drawCardImage(g, CardView.getCardForUi(card), false, x, y, w, h, CardStackPosition.Top); + if (image == ImageCache.defaultImage || Forge.enableUIMask.equals("Art")) { + CardImageRenderer.drawCardImage(g, CardView.getCardForUi(card), false, x, y, w, h, CardStackPosition.Top, true, true); } else { if (Forge.enableUIMask.equals("Full")) { diff --git a/forge-gui-mobile/src/forge/card/CardImageRenderer.java b/forge-gui-mobile/src/forge/card/CardImageRenderer.java index ac665e22964..fcd3f8ba82a 100644 --- a/forge-gui-mobile/src/forge/card/CardImageRenderer.java +++ b/forge-gui-mobile/src/forge/card/CardImageRenderer.java @@ -6,6 +6,10 @@ import static forge.card.CardRenderer.isModernFrame; import java.util.ArrayList; import java.util.List; +import forge.ImageKeys; +import forge.assets.*; +import forge.item.PaperCard; +import forge.util.ImageUtil; import org.apache.commons.lang3.StringUtils; import com.badlogic.gdx.graphics.Color; @@ -15,15 +19,6 @@ import com.google.common.collect.ImmutableList; import forge.Forge; import forge.Graphics; -import forge.assets.FBufferedImage; -import forge.assets.FImage; -import forge.assets.FSkin; -import forge.assets.FSkinColor; -import forge.assets.FSkinFont; -import forge.assets.FSkinImage; -import forge.assets.FSkinTexture; -import forge.assets.ImageCache; -import forge.assets.TextRenderer; import forge.card.CardRenderer.CardStackPosition; import forge.card.mana.ManaCost; import forge.game.GameView; @@ -48,6 +43,10 @@ public class CardImageRenderer { private static float prevImageWidth, prevImageHeight; private static final float BLACK_BORDER_THICKNESS_RATIO = 0.021f; + private static Color fromDetailColor(DetailColors detailColor) { + return FSkinColor.fromRGB(detailColor.r, detailColor.g, detailColor.b); + } + public static void forceStaticFieldUpdate() { //force static fields to be updated the next time a card image is rendered prevImageWidth = 0; @@ -78,13 +77,17 @@ public class CardImageRenderer { public static void drawFaceDownCard(CardView card, Graphics g, float x, float y, float w, float h) { //try to draw the card sleeves first - if (FSkin.getSleeves().get(card.getOwner()) != null) - g.drawImage(FSkin.getSleeves().get(card.getOwner()), x, y, w, h); + FImage sleeves = MatchController.getPlayerSleeve(card.getOwner()); + if (sleeves != null) + g.drawImage(sleeves, x, y, w, h); else - drawArt(g, x, y, w, h); + drawArt(null, g, x, y, w, h, false, true); } - public static void drawCardImage(Graphics g, CardView card, boolean altState, float x, float y, float w, float h, CardStackPosition pos) { + public static void drawCardImage(Graphics g, CardView card, boolean altState, float x, float y, float w, float h, CardStackPosition pos, boolean useCardBGTexture, boolean showArtist) { + drawCardImage(g, card, altState, x, y, w, h, pos, useCardBGTexture, false, false, showArtist); + } + public static void drawCardImage(Graphics g, CardView card, boolean altState, float x, float y, float w, float h, CardStackPosition pos, boolean useCardBGTexture, boolean noText, boolean isChoiceList, boolean showArtist) { updateStaticFields(w, h); float blackBorderThickness = w * BLACK_BORDER_THICKNESS_RATIO; @@ -94,8 +97,16 @@ public class CardImageRenderer { w -= 2 * blackBorderThickness; h -= 2 * blackBorderThickness; - final CardStateView state = card.getState(altState); + CardStateView state = altState ? card.getAlternateState() : isChoiceList && card.isSplitCard() ? card.getLeftSplitState() : card.getCurrentState(); + final boolean isFaceDown = card.isFaceDown(); final boolean canShow = MatchController.instance.mayView(card); + //override + if (isFaceDown && altState && card.isSplitCard()) + state = card.getLeftSplitState(); + boolean isSaga = state.getType().hasSubtype("Saga"); + boolean isClass = state.getType().hasSubtype("Class"); + boolean isDungeon = state.getType().isDungeon(); + boolean drawDungeon = isDungeon && CardRenderer.getCardArt(card) != null; if (!canShow) { drawFaceDownCard(card, g, x, y, w, h); @@ -104,14 +115,12 @@ public class CardImageRenderer { //determine colors for borders final List borderColors; - final boolean isFaceDown = card.isFaceDown(); if (isFaceDown) { - borderColors = ImmutableList.of(DetailColors.FACE_DOWN); - } - else { + borderColors = !altState ? ImmutableList.of(DetailColors.FACE_DOWN) : !useCardBGTexture ? ImmutableList.of(DetailColors.FACE_DOWN) : CardDetailUtil.getBorderColors(state, canShow); + } else { borderColors = CardDetailUtil.getBorderColors(state, canShow); } - Color[] colors = fillColorBackground(g, borderColors, x, y, w, h); + Color[] colors = useCardBGTexture ? drawCardBackgroundTexture(state, g, borderColors, x, y, w, h) : fillColorBackground(g, borderColors, x, y, w, h); float artInset = blackBorderThickness * 0.5f; float outerBorderThickness = 2 * blackBorderThickness - artInset; @@ -122,7 +131,7 @@ public class CardImageRenderer { //draw header containing name and mana cost Color[] headerColors = FSkinColor.tintColors(Color.WHITE, colors, CardRenderer.NAME_BOX_TINT); - drawHeader(g, card, state, headerColors, x, y, w, headerHeight); + drawHeader(g, card, state, headerColors, x, y, w, headerHeight, isFaceDown && !altState, false); if (pos == CardStackPosition.BehindVert) { return; } //remaining rendering not needed if card is behind another card in a vertical stack boolean onTop = (pos == CardStackPosition.Top); @@ -134,14 +143,16 @@ public class CardImageRenderer { float typeBoxHeight = 2 * TYPE_FONT.getCapHeight(); float ptBoxHeight = 0; float textBoxHeight = h - headerHeight - artHeight - typeBoxHeight - outerBorderThickness - artInset; + if (state.isCreature() || state.isPlaneswalker() || state.getType().hasSubtype("Vehicle")) { - //if P/T box needed, make room for it ptBoxHeight = 2 * PT_FONT.getCapHeight(); - textBoxHeight -= ptBoxHeight; - } - else { - textBoxHeight -= 2 * artInset; } + //space for artist + textBoxHeight -= 2 * PT_FONT.getCapHeight(); + PaperCard paperCard = ImageUtil.getPaperCardFromImageKey(state.getImageKey()); + String artist = "WOTC"; + if (paperCard != null && !paperCard.getArtist().isEmpty()) + artist = paperCard.getArtist(); float minTextBoxHeight = 2 * headerHeight; if (textBoxHeight < minTextBoxHeight) { if (textBoxHeight < minTextBoxHeight) { @@ -156,53 +167,103 @@ public class CardImageRenderer { //draw art box with Forge icon if (artHeight > 0) { - drawArt(g, x + artInset, y, artWidth, artHeight); + if (isSaga) + drawArt(card, g, x + artInset+(artWidth/2), y, artWidth/2, artHeight+textBoxHeight, altState, isFaceDown); + else if (isClass) + drawArt(card, g, x + artInset, y, artWidth/2, artHeight+textBoxHeight, altState, isFaceDown); + else if (isDungeon) { + if (drawDungeon) { + drawArt(card, g, x + artInset, y, artWidth, artHeight+textBoxHeight, altState, isFaceDown); + y += textBoxHeight; + } + } + else + drawArt(card, g, x + artInset, y, artWidth, artHeight, altState, isFaceDown); y += artHeight; } - //draw type line - drawTypeLine(g, card, state, canShow, headerColors, x, y, w, typeBoxHeight); - y += typeBoxHeight; + if (isSaga) { + //draw text box + Color[] textBoxColors = FSkinColor.tintColors(Color.WHITE, colors, CardRenderer.TEXT_BOX_TINT); + drawTextBox(g, card, state, textBoxColors, x + artInset, y-artHeight, (w - 2 * artInset)/2, textBoxHeight+artHeight, onTop, useCardBGTexture, noText, altState, isFaceDown, canShow, isChoiceList); + y += textBoxHeight; - //draw text box - Color[] textBoxColors = FSkinColor.tintColors(Color.WHITE, colors, CardRenderer.TEXT_BOX_TINT); - drawTextBox(g, card, state, textBoxColors, x + artInset, y, w - 2 * artInset, textBoxHeight, onTop); - y += textBoxHeight; + //draw type line + drawTypeLine(g, state, canShow, headerColors, x, y, w, typeBoxHeight, noText, false, false); + y += typeBoxHeight; + } else if (isClass) { + //draw text box + Color[] textBoxColors = FSkinColor.tintColors(Color.WHITE, colors, CardRenderer.TEXT_BOX_TINT); + drawTextBox(g, card, state, textBoxColors, x + artInset+(artWidth/2), y-artHeight, (w - 2 * artInset)/2, textBoxHeight+artHeight, onTop, useCardBGTexture, noText, altState, isFaceDown, canShow, isChoiceList); + y += textBoxHeight; + + //draw type line + drawTypeLine(g, state, canShow, headerColors, x, y, w, typeBoxHeight, noText, false, false); + y += typeBoxHeight; + } else if (isDungeon) { + if (!drawDungeon) { + //draw textbox + Color[] textBoxColors = FSkinColor.tintColors(Color.WHITE, colors, CardRenderer.TEXT_BOX_TINT); + drawTextBox(g, card, state, textBoxColors, x + artInset, y-artHeight, (w - 2 * artInset), textBoxHeight+artHeight, onTop, useCardBGTexture, noText, altState, isFaceDown, canShow, isChoiceList); + y += textBoxHeight; + } + drawTypeLine(g, state, canShow, headerColors, x, y, w, typeBoxHeight, noText, false, false); + y += typeBoxHeight; + } else { + //draw type line + drawTypeLine(g, state, canShow, headerColors, x, y, w, typeBoxHeight, noText, false, false); + y += typeBoxHeight; + + //draw text box + Color[] textBoxColors = FSkinColor.tintColors(Color.WHITE, colors, CardRenderer.TEXT_BOX_TINT); + drawTextBox(g, card, state, textBoxColors, x + artInset, y, w - 2 * artInset, textBoxHeight, onTop, useCardBGTexture, noText, altState, isFaceDown, canShow, isChoiceList); + y += textBoxHeight; + } //draw P/T box if (onTop && ptBoxHeight > 0) { //only needed if on top since otherwise P/T will be hidden Color[] ptColors = FSkinColor.tintColors(Color.WHITE, colors, CardRenderer.PT_BOX_TINT); - drawPtBox(g, card, state, ptColors, x, y - 2 * artInset, w, ptBoxHeight); + drawPtBox(g, card, state, ptColors, x, y - 2 * artInset, w, ptBoxHeight, noText); } + //draw artist + if (showArtist) + g.drawOutlinedText(artist, TEXT_FONT, Color.WHITE, Color.DARK_GRAY, x+(TYPE_FONT.getCapHeight()/2), y+(TYPE_FONT.getCapHeight()/2), w, h, false, Align.left, false); } - private static void drawHeader(Graphics g, CardView card, CardStateView state, Color[] colors, float x, float y, float w, float h) { + private static void drawHeader(Graphics g, CardView card, CardStateView state, Color[] colors, float x, float y, float w, float h, boolean noText, boolean isAdventure) { + float oldAlpha = g.getfloatAlphaComposite(); + if (isAdventure) + g.setAlphaComposite(0.8f); fillColorBackground(g, colors, x, y, w, h); + g.setAlphaComposite(oldAlpha); g.drawRect(BORDER_THICKNESS, Color.BLACK, x, y, w, h); float padding = h / 8; - - //draw mana cost for card float manaCostWidth = 0; - ManaCost mainManaCost = state.getManaCost(); - if (card.isSplitCard() && card.getAlternateState() != null) { - //handle rendering both parts of split card - mainManaCost = card.getLeftSplitState().getManaCost(); - ManaCost otherManaCost = card.getAlternateState().getManaCost(); - manaCostWidth = CardFaceSymbols.getWidth(otherManaCost, MANA_SYMBOL_SIZE) + HEADER_PADDING; - CardFaceSymbols.drawManaCost(g, otherManaCost, x + w - manaCostWidth, y + (h - MANA_SYMBOL_SIZE) / 2, MANA_SYMBOL_SIZE); - //draw "//" between two parts of mana cost - manaCostWidth += NAME_FONT.getBounds("//").width + HEADER_PADDING; - g.drawText("//", NAME_FONT, Color.BLACK, x + w - manaCostWidth, y, w, h, false, Align.left, true); + float manaSymbolSize = isAdventure ? MANA_SYMBOL_SIZE * 0.75f : MANA_SYMBOL_SIZE; + if (!noText) { + //draw mana cost for card + ManaCost mainManaCost = state.getManaCost(); + if (card.isSplitCard() && card.getAlternateState() != null) { + //handle rendering both parts of split card + mainManaCost = card.getLeftSplitState().getManaCost(); + ManaCost otherManaCost = card.getRightSplitState().getManaCost(); + manaCostWidth = CardFaceSymbols.getWidth(otherManaCost, manaSymbolSize) + HEADER_PADDING; + CardFaceSymbols.drawManaCost(g, otherManaCost, x + w - manaCostWidth, y + (h - manaSymbolSize) / 2, manaSymbolSize); + //draw "//" between two parts of mana cost + manaCostWidth += NAME_FONT.getBounds("//").width + HEADER_PADDING; + g.drawText("//", NAME_FONT, Color.BLACK, x + w - manaCostWidth, y, w, h, false, Align.left, true); + } + manaCostWidth += CardFaceSymbols.getWidth(mainManaCost, manaSymbolSize) + HEADER_PADDING; + CardFaceSymbols.drawManaCost(g, mainManaCost, x + w - manaCostWidth, y + (h - manaSymbolSize) / 2, manaSymbolSize); } - manaCostWidth += CardFaceSymbols.getWidth(mainManaCost, MANA_SYMBOL_SIZE) + HEADER_PADDING; - CardFaceSymbols.drawManaCost(g, mainManaCost, x + w - manaCostWidth, y + (h - MANA_SYMBOL_SIZE) / 2, MANA_SYMBOL_SIZE); //draw name for card x += padding; w -= 2 * padding; - g.drawText(CardTranslation.getTranslatedName(state.getName()), NAME_FONT, Color.BLACK, x, y, w - manaCostWidth - padding, h, false, Align.left, true); + if (!noText) + g.drawText(CardTranslation.getTranslatedName(state.getName()), NAME_FONT, Color.BLACK, x, y, w - manaCostWidth - padding, h, false, Align.left, true); } public static final FBufferedImage forgeArt; @@ -221,37 +282,128 @@ public class CardImageRenderer { }; } - private static void drawArt(Graphics g, float x, float y, float w, float h) { - g.drawImage(forgeArt, x, y, w, h); + private static void drawArt(CardView cv, Graphics g, float x, float y, float w, float h, boolean altState, boolean isFaceDown) { + if (cv == null) { + if (isFaceDown) { + Texture cardBack = ImageCache.getImage(ImageKeys.getTokenKey(ImageKeys.HIDDEN_CARD), false); + if (cardBack != null) { + g.drawImage(cardBack, x, y, w, h); + return; + } + } + //fallback + g.drawImage(forgeArt, x, y, w, h); + g.drawRect(BORDER_THICKNESS, Color.BLACK, x, y, w, h); + return; + } + if (Forge.enableUIMask.equals("Art")) { + FImageComplex cardArt = CardRenderer.getCardArt(cv); + FImageComplex altArt = cardArt; + boolean isHidden = (cv.getCurrentState().getImageKey().equals(ImageKeys.getTokenKey(ImageKeys.HIDDEN_CARD)) + || cv.getCurrentState().getImageKey().equals(ImageKeys.getTokenKey(ImageKeys.FORETELL_IMAGE))); + if (cardArt != null) { + if (isHidden && !altState) { + g.drawImage(forgeArt, x, y, w, h); + } else if (cv.getCurrentState().getImageKey().equals(ImageKeys.getTokenKey(ImageKeys.MANIFEST_IMAGE)) && !altState) { + altArt = CardRenderer.getAlternateCardArt(ImageKeys.getTokenKey(ImageKeys.MANIFEST_IMAGE), false); + g.drawImage(altArt, x, y, w, h); + } else if (cv.getCurrentState().getImageKey().equals(ImageKeys.getTokenKey(ImageKeys.MORPH_IMAGE)) && !altState) { + altArt = CardRenderer.getAlternateCardArt(ImageKeys.getTokenKey(ImageKeys.MORPH_IMAGE), false); + g.drawImage(altArt, x, y, w, h); + } else { + if (cv.hasAlternateState()) { + if (altState) { + if (cv.getAlternateState().isPlaneswalker()) + altArt = CardRenderer.getAlternateCardArt(cv.getAlternateState().getImageKey(), cv.getAlternateState().isPlaneswalker()); + else { + altArt = CardRenderer.getCardArt(cv.getAlternateState().getImageKey(), cv.isSplitCard(), cv.getAlternateState().isPlane() || cv.getAlternateState().isPhenomenon(), cv.getText().contains("Aftermath"), + cv.getAlternateState().getType().hasSubtype("Saga"), cv.getAlternateState().getType().hasSubtype("Class"), cv.getAlternateState().getType().isDungeon(), cv.isFlipCard(), cv.getAlternateState().isPlaneswalker(), CardRenderer.isModernFrame(cv)); + } + } + } + if (cv.isSplitCard()) { + drawSplitCard(cv, altArt, g, x, y, w, h, altState, isFaceDown); + } else if (cv.isFlipCard()) { + drawFlipCard(isFaceDown ? altArt : cardArt, g, x, y, w, h, altState); + } else { + g.drawImage(altArt, x, y, w, h); + } + } + } else { + g.drawImage(forgeArt, x, y, w, h); + } + } else { + g.drawImage(forgeArt, x, y, w, h); + } g.drawRect(BORDER_THICKNESS, Color.BLACK, x, y, w, h); } - - private static void drawTypeLine(Graphics g, CardView card, CardStateView state, boolean canShow, Color[] colors, float x, float y, float w, float h) { + private static void drawSplitCard(CardView card, FImageComplex cardArt, Graphics g, float x, float y, float w, float h, boolean altState, boolean isFaceDown) { + CardView alt = card.getBackup(); + if (alt == null) + alt = card.getAlternateState().getCard(); + CardView cv = altState && isFaceDown ? alt : card; + boolean isAftermath = altState ? cv.getAlternateState().hasHasAftermath(): cv.getRightSplitState().hasHasAftermath(); + if (!isAftermath) { + CardEdition ed = FModel.getMagicDb().getEditions().get(cv.getCurrentState().getSetCode()); + boolean isOldFrame = ed != null && !ed.isModern(); + float modH = isOldFrame ? cardArt.getHeight()/12f : 0f; + float modW = !isOldFrame ? cardArt.getWidth()/12f : 0f; + float modW2 = !isOldFrame ? cardArt.getWidth()/6f : 0f; + float srcY = cardArt.getHeight() * 13f / 354f; + float srcHeight = cardArt.getHeight() * 190f / 354f; + float dh = srcHeight * (1 - cardArt.getWidth() / srcHeight / CardRenderer.CARD_ART_RATIO); + srcHeight -= dh; + srcY += dh / 2; + g.drawRotatedImage(cardArt.getTexture(), x, y, h+modH, w / 2, x + w / 2, y + w / 2, cardArt.getRegionX()+(int)modW, (int)srcY, (int)(cardArt.getWidth()-modW2), (int)srcHeight, -90); + g.drawRotatedImage(cardArt.getTexture(), x, y + w / 2, h+modH, w / 2, x + w / 2, y + w / 2, cardArt.getRegionX()+(int)modW, (int)cardArt.getHeight() - (int)(srcY + srcHeight), (int)(cardArt.getWidth()-modW2), (int)srcHeight, -90); + g.drawLine(BORDER_THICKNESS, Color.BLACK, x+w/2, y, x+w/2, y+h); + } else { + FImageComplex secondArt = CardRenderer.getAftermathSecondCardArt(cv.getCurrentState().getImageKey()); + g.drawRotatedImage(cardArt.getTexture(), x, y, w, h / 2, x + w, y + h / 2, cardArt.getRegionX(), cardArt.getRegionY(), (int)cardArt.getWidth(), (int)cardArt.getHeight() /2, 0); + g.drawRotatedImage(secondArt.getTexture(), x - h / 2 , y + h / 2, h /2, w, x, y + h / 2, secondArt.getRegionX(), secondArt.getRegionY(), (int)secondArt.getWidth(), (int)secondArt.getHeight(), 90); + g.drawLine(BORDER_THICKNESS, Color.BLACK, x, y+h/2, x+w, y+h/2); + } + } + private static void drawFlipCard(FImageComplex cardArt, Graphics g, float x, float y, float w, float h, boolean altState) { + if (altState) + g.drawRotatedImage(cardArt.getTextureRegion(), x, y, w, h, x + w / 2, y + h / 2, 180); + else + g.drawImage(cardArt, x, y, w, h); + } + private static void drawTypeLine(Graphics g, CardStateView state, boolean canShow, Color[] colors, float x, float y, float w, float h, boolean noText, boolean noRarity, boolean isAdventure) { + float oldAlpha = g.getfloatAlphaComposite(); + if (isAdventure) + g.setAlphaComposite(0.6f); fillColorBackground(g, colors, x, y, w, h); + g.setAlphaComposite(oldAlpha); g.drawRect(BORDER_THICKNESS, Color.BLACK, x, y, w, h); float padding = h / 8; //draw square icon for rarity - float iconSize = h * 0.9f; - float iconPadding = (h - iconSize) / 2; - w -= iconSize + iconPadding * 2; - //g.fillRect(CardRenderer.getRarityColor(state.getRarity()), x + w + iconPadding, y + (h - iconSize) / 2, iconSize, iconSize); - if (state.getRarity() == null) { - g.drawImage(FSkinImage.SET_SPECIAL, x + w + iconPadding, y + (h - iconSize) / 2, iconSize, iconSize); - } else if (state.getRarity() == CardRarity.Special ) { - g.drawImage(FSkinImage.SET_SPECIAL, x + w + iconPadding, y + (h - iconSize) / 2, iconSize, iconSize); - } else if (state.getRarity() == CardRarity.MythicRare) { - g.drawImage(FSkinImage.SET_MYTHIC, x + w + iconPadding, y + (h - iconSize) / 2, iconSize, iconSize); - } else if (state.getRarity() == CardRarity.Rare) { - g.drawImage(FSkinImage.SET_RARE, x + w + iconPadding, y + (h - iconSize) / 2, iconSize, iconSize); - } else if (state.getRarity() == CardRarity.Uncommon) { - g.drawImage(FSkinImage.SET_UNCOMMON, x + w + iconPadding, y + (h - iconSize) / 2, iconSize, iconSize); - } else { - g.drawImage(FSkinImage.SET_COMMON, x + w + iconPadding, y + (h - iconSize) / 2, iconSize, iconSize); + if (!noRarity) { + float iconSize = h * 0.9f; + float iconPadding = (h - iconSize) / 2; + w -= iconSize + iconPadding * 2; + //g.fillRect(CardRenderer.getRarityColor(state.getRarity()), x + w + iconPadding, y + (h - iconSize) / 2, iconSize, iconSize); + if (state.getRarity() == null) { + g.drawImage(FSkinImage.SET_SPECIAL, x + w + iconPadding, y + (h - iconSize) / 2, iconSize, iconSize); + } else if (state.getRarity() == CardRarity.Special ) { + g.drawImage(FSkinImage.SET_SPECIAL, x + w + iconPadding, y + (h - iconSize) / 2, iconSize, iconSize); + } else if (state.getRarity() == CardRarity.MythicRare) { + g.drawImage(FSkinImage.SET_MYTHIC, x + w + iconPadding, y + (h - iconSize) / 2, iconSize, iconSize); + } else if (state.getRarity() == CardRarity.Rare) { + g.drawImage(FSkinImage.SET_RARE, x + w + iconPadding, y + (h - iconSize) / 2, iconSize, iconSize); + } else if (state.getRarity() == CardRarity.Uncommon) { + g.drawImage(FSkinImage.SET_UNCOMMON, x + w + iconPadding, y + (h - iconSize) / 2, iconSize, iconSize); + } else { + g.drawImage(FSkinImage.SET_COMMON, x + w + iconPadding, y + (h - iconSize) / 2, iconSize, iconSize); + } } //draw type + if (noText) + return; x += padding; g.drawText(CardDetailUtil.formatCardType(state, canShow), TYPE_FONT, Color.BLACK, x, y, w, h, false, Align.left, true); } @@ -259,7 +411,28 @@ public class CardImageRenderer { //use text renderer to handle mana symbols and reminder text private static final TextRenderer cardTextRenderer = new TextRenderer(true); - private static void drawTextBox(Graphics g, CardView card, CardStateView state, Color[] colors, float x, float y, float w, float h, boolean onTop) { + private static void drawTextBox(Graphics g, CardView card, CardStateView state, Color[] colors, float x, float y, float w, float h, boolean onTop, boolean useCardBGTexture, boolean noText, boolean altstate, boolean isFacedown, boolean canShow, boolean isChoiceList) { + if (card.isAdventureCard()) { + if ((isFacedown && !altstate) || card.getZone() == ZoneType.Stack || isChoiceList || altstate) { + setTextBox(g, card, state, colors, x, y, w, h, onTop, useCardBGTexture, noText, 0f, 0f, false, altstate, isFacedown); + } else { + //left + //float headerHeight = Math.max(MANA_SYMBOL_SIZE + 2 * HEADER_PADDING, 2 * TYPE_FONT.getCapHeight()) + 2; + float typeBoxHeight = 2 * TYPE_FONT.getCapHeight(); + drawHeader(g, card, card.getState(true), colors, x, y, w - (w / 2), typeBoxHeight, noText, true); + drawTypeLine(g, card.getState(true), canShow, colors, x, y + typeBoxHeight, w - (w / 2), typeBoxHeight, noText, true, true); + float mod = (typeBoxHeight + typeBoxHeight); + setTextBox(g, card, state, colors, x, y + mod, w - (w / 2), h - mod, onTop, useCardBGTexture, noText, typeBoxHeight, typeBoxHeight, true, altstate, isFacedown); + //right + setTextBox(g, card, state, colors, x + w / 2, y, w - (w / 2), h, onTop, useCardBGTexture, noText, 0f, 0f, false, altstate, isFacedown); + } + } else { + setTextBox(g, card, state, colors, x, y, w, h, onTop, useCardBGTexture, noText, 0f, 0f, false, altstate, isFacedown); + } + } + private static void setTextBox(Graphics g, CardView card, CardStateView state, Color[] colors, float x, float y, float w, float h, boolean onTop, boolean useCardBGTexture, boolean noText, float adventureHeaderHeight, float adventureTypeHeight, boolean drawAdventure, boolean altstate, boolean isFaceDown) { + boolean fakeDuals = false; + //update land bg colors if (state.isLand()) { DetailColors modColors = DetailColors.WHITE; if (state.isBasicLand()) { @@ -273,53 +446,165 @@ public class CardImageRenderer { modColors = DetailColors.BLACK; else if (state.isPlains()) modColors = DetailColors.LAND; + } if (state.origCanProduceColoredMana() == 2) { + //dual colors + Color[] colorPairs = new Color[2]; + //init Color + colorPairs[0] = fromDetailColor(DetailColors.WHITE); + colorPairs[1] = fromDetailColor(DetailColors.WHITE); + //override + if (state.origProduceAnyMana()) { + colorPairs[0] = fromDetailColor(DetailColors.MULTICOLOR); + colorPairs[1] = fromDetailColor(DetailColors.MULTICOLOR); + } else { + fakeDuals = true; + if (state.origProduceManaW() && state.origProduceManaU()) { + colorPairs[0] = fromDetailColor(DetailColors.LAND); + colorPairs[1] = fromDetailColor(DetailColors.BLUE); + } else if (state.origProduceManaW() && state.origProduceManaB()) { + colorPairs[0] = fromDetailColor(DetailColors.LAND); + colorPairs[1] = fromDetailColor(DetailColors.BLACK); + } else if (state.origProduceManaW() && state.origProduceManaR()) { + colorPairs[0] = fromDetailColor(DetailColors.LAND); + colorPairs[1] = fromDetailColor(DetailColors.RED); + } else if (state.origProduceManaW() && state.origProduceManaG()) { + colorPairs[0] = fromDetailColor(DetailColors.LAND); + colorPairs[1] = fromDetailColor(DetailColors.GREEN); + } else if (state.origProduceManaU() && state.origProduceManaB()) { + colorPairs[0] = fromDetailColor(DetailColors.BLUE); + colorPairs[1] = fromDetailColor(DetailColors.BLACK); + } else if (state.origProduceManaU() && state.origProduceManaR()) { + colorPairs[0] = fromDetailColor(DetailColors.BLUE); + colorPairs[1] = fromDetailColor(DetailColors.RED); + } else if (state.origProduceManaU() && state.origProduceManaG()) { + colorPairs[0] = fromDetailColor(DetailColors.BLUE); + colorPairs[1] = fromDetailColor(DetailColors.GREEN); + } else if (state.origProduceManaB() && state.origProduceManaR()) { + colorPairs[0] = fromDetailColor(DetailColors.BLACK); + colorPairs[1] = fromDetailColor(DetailColors.RED); + } else if (state.origProduceManaB() && state.origProduceManaG()) { + colorPairs[0] = fromDetailColor(DetailColors.BLACK); + colorPairs[1] = fromDetailColor(DetailColors.GREEN); + } else if (state.origProduceManaR() && state.origProduceManaG()) { + colorPairs[0] = fromDetailColor(DetailColors.RED); + colorPairs[1] = fromDetailColor(DetailColors.GREEN); + } + } + colorPairs = FSkinColor.tintColors(Color.WHITE, colorPairs, 0.3f); + float oldAlpha = g.getfloatAlphaComposite(); + if (!useCardBGTexture) + fillColorBackground(g, colorPairs, x, y, w, h); + else { + g.setAlphaComposite(0.95f); + fillColorBackground(g, colorPairs, x, y, w, h); + if (fakeDuals && state.countBasicLandTypes() == 2) { + g.setAlphaComposite(0.1f); + drawAlphaLines(g, x, y, w, h); + } + g.setAlphaComposite(oldAlpha); + } + } else { + //override bg color + if (state.origCanProduceColoredMana() > 2 || state.origProduceAnyMana()) { + modColors = DetailColors.MULTICOLOR; + } else if (state.origCanProduceColoredMana() == 1) { + if (state.origProduceManaW()) + modColors = DetailColors.LAND; + else if (state.origProduceManaB()) + modColors = DetailColors.BLACK; + else if (state.origProduceManaG()) + modColors = DetailColors.GREEN; + else if (state.origProduceManaR()) + modColors = DetailColors.RED; + else if (state.origProduceManaU()) + modColors = DetailColors.BLUE; + } + Color bgColor = fromDetailColor(modColors); + bgColor = FSkinColor.tintColor(Color.WHITE, bgColor, CardRenderer.NAME_BOX_TINT); + float oldAlpha = g.getfloatAlphaComposite(); + if (!useCardBGTexture) + g.fillRect(bgColor, x, y, w, h); + else { + g.setAlphaComposite(0.95f); + g.fillRect(bgColor, x, y, w, h); + g.setAlphaComposite(oldAlpha); + } } - Color bgColor = FSkinColor.fromRGB(modColors.r, modColors.g, modColors.b); - bgColor = FSkinColor.tintColor(Color.WHITE, bgColor, CardRenderer.NAME_BOX_TINT); - g.fillRect(bgColor, x, y, w, h); } else { - fillColorBackground(g, colors, x, y, w, h); + float oldAlpha = g.getfloatAlphaComposite(); + if (!useCardBGTexture) + fillColorBackground(g, colors, x, y, w, h); + else { + g.setAlphaComposite(0.95f); + fillColorBackground(g, colors, x, y, w, h); + g.setAlphaComposite(oldAlpha); + } } g.drawRect(BORDER_THICKNESS, Color.BLACK, x, y, w, h); if (!onTop) { return; } //remaining rendering only needed if card on top if (state.isBasicLand()) { - //draw icons for basic lands - FSkinImage image; - switch (state.getName().replaceFirst("^Snow-Covered ", "")) { - case "Plains": - image = FSkinImage.WATERMARK_W; - break; - case "Island": - image = FSkinImage.WATERMARK_U; - break; - case "Swamp": - image = FSkinImage.WATERMARK_B; - break; - case "Mountain": - image = FSkinImage.WATERMARK_R; - break; - case "Forest": - image = FSkinImage.WATERMARK_G; - break; - default: + //draw watermark + FSkinImage image = null; + if (state.origCanProduceColoredMana() == 1 && !state.origProduceManaC()) { + if (state.isPlains()) + image = FSkinImage.WATERMARK_W; + else if (state.isIsland()) + image = FSkinImage.WATERMARK_U; + else if (state.isSwamp()) + image = FSkinImage.WATERMARK_B; + else if (state.isMountain()) + image = FSkinImage.WATERMARK_R; + else if (state.isForest()) + image = FSkinImage.WATERMARK_G; + } else if (state.origProduceManaC()) { image = FSkinImage.WATERMARK_C; - break; } - float iconSize = h * 0.75f; - g.drawImage(image, x + (w - iconSize) / 2, y + (h - iconSize) / 2, iconSize, iconSize); - } - else { + if (image != null) { + float iconSize = h * 0.75f; + g.drawImage(image, x + (w - iconSize) / 2, y + (h - iconSize) / 2, iconSize, iconSize); + } + } else { boolean needTranslation = true; + String text = ""; if (card.isToken()) { if (card.getCloneOrigin() == null) needTranslation = false; } - final String text = !card.isSplitCard() ? - card.getText(state, needTranslation ? CardTranslation.getTranslationTexts(state.getName(), "") : null) : - card.getText(state, needTranslation ? CardTranslation.getTranslationTexts(card.getLeftSplitState().getName(), card.getRightSplitState().getName()) : null ); - if (StringUtils.isEmpty(text)) { return; } + if (drawAdventure) { + // draw left textbox text + if (noText) + return; + if (card.isAdventureCard()) { + CardView cv = card.getBackup(); + if (cv == null || isFaceDown) + cv = card; + text = cv.getText(cv.getState(true), needTranslation ? CardTranslation.getTranslationTexts(cv.getName(), "") : null); + + } else { + text = !card.isSplitCard() ? + card.getText(state, needTranslation ? CardTranslation.getTranslationTexts(state.getName(), "") : null) : + card.getText(state, needTranslation ? CardTranslation.getTranslationTexts(card.getLeftSplitState().getName(), card.getRightSplitState().getName()) : null); + } + } else { + if (noText) + return; + if (card.isAdventureCard()) { + CardView cv = card.getBackup(); + if (cv == null || isFaceDown) + cv = card; + text = cv.getText(cv.getState(false), needTranslation ? CardTranslation.getTranslationTexts(cv.getName(), "") : null); + + } else { + text = !card.isSplitCard() ? + card.getText(state, needTranslation ? CardTranslation.getTranslationTexts(state.getName(), "") : null) : + card.getText(state, needTranslation ? CardTranslation.getTranslationTexts(card.getLeftSplitState().getName(), card.getRightSplitState().getName()) : null); + } + } + if (StringUtils.isEmpty(text)) { + return; + } float padding = TEXT_FONT.getCapHeight() * 0.75f; x += padding; @@ -329,8 +614,12 @@ public class CardImageRenderer { cardTextRenderer.drawText(g, text, TEXT_FONT, Color.BLACK, x, y, w, h, y, h, true, Align.left, true); } } - - private static void drawPtBox(Graphics g, CardView card, CardStateView state, Color[] colors, float x, float y, float w, float h) { + private static void drawAlphaLines(Graphics g, float x, float y, float w, float h) { + if (FSkin.overlay_alpha != null) { + g.drawImage(FSkin.overlay_alpha, x, y, w, h); + } + } + private static void drawPtBox(Graphics g, CardView card, CardStateView state, Color[] colors, float x, float y, float w, float h, boolean noText) { List pieces = new ArrayList<>(); if (state.isCreature()) { pieces.add(String.valueOf(state.getPower())); @@ -369,6 +658,8 @@ public class CardImageRenderer { fillColorBackground(g, colors, x, y, w, h); g.drawRect(BORDER_THICKNESS, Color.BLACK, x, y, w, h); + if (noText) + return; x += (boxWidth - totalPieceWidth) / 2; for (int i = 0; i < pieces.size(); i++) { g.drawText(pieces.get(i), PT_FONT, Color.BLACK, x, y, w, h, false, Align.left, true); @@ -399,8 +690,8 @@ public class CardImageRenderer { return; } - if (image == ImageCache.defaultImage) { //support drawing card image manually if card image not found - drawCardImage(g, card, altState, x, y, w, h, CardStackPosition.Top); + if (image == ImageCache.defaultImage || Forge.enableUIMask.equals("Art")) { //support drawing card image manually if card image not found + drawCardImage(g, card, altState, x, y, w, h, CardStackPosition.Top, true, true); } else { float radius = (h - w)/8; float wh_Adj = ForgeConstants.isGdxPortLandscape && isCurrentCard ? 1.38f:1.0f; @@ -517,11 +808,79 @@ public class CardImageRenderer { Color[] colors = new Color[backColors.size()]; for (int i = 0; i < colors.length; i++) { DetailColors dc = backColors.get(i); - colors[i] = FSkinColor.fromRGB(dc.r, dc.g, dc.b); + colors[i] = fromDetailColor(dc); } fillColorBackground(g, colors, x, y, w, h); return colors; } + public static Color[] drawCardBackgroundTexture(CardStateView state, Graphics g, List backColors, float x, float y, float w, float h) { + boolean isHybrid = state.getManaCost().hasHybrid(); + Color[] colors = new Color[backColors.size()]; + for (int i = 0; i < colors.length; i++) { + DetailColors dc = backColors.get(i); + colors[i] = fromDetailColor(dc); + } + switch (backColors.size()) { + case 1: + if (backColors.get(0) == DetailColors.FACE_DOWN) { + g.drawImage(FSkinImage.CARDBG_C, x, y, w,h); + } else if (backColors.get(0) == DetailColors.LAND) { + g.drawImage(FSkinImage.CARDBG_L, x, y, w,h); + }else if (backColors.get(0) == DetailColors.MULTICOLOR) { + g.drawImage(FSkinImage.CARDBG_M, x, y, w,h); + } else if (backColors.get(0) == DetailColors.COLORLESS) { + if (state.isVehicle()) + g.drawImage(FSkinImage.CARDBG_V, x, y, w,h); + else if (state.isArtifact()) + g.drawImage(FSkinImage.CARDBG_A, x, y, w,h); + else + g.drawImage(FSkinImage.CARDBG_C, x, y, w,h); + } else if (backColors.get(0) == DetailColors.GREEN) { + g.drawImage(FSkinImage.CARDBG_G, x, y, w,h); + } else if (backColors.get(0) == DetailColors.RED) { + g.drawImage(FSkinImage.CARDBG_R, x, y, w,h); + } else if (backColors.get(0) == DetailColors.BLACK) { + g.drawImage(FSkinImage.CARDBG_B, x, y, w,h); + } else if (backColors.get(0) == DetailColors.BLUE) { + g.drawImage(FSkinImage.CARDBG_U, x, y, w,h); + } else if (backColors.get(0) == DetailColors.WHITE) { + g.drawImage(FSkinImage.CARDBG_W, x, y, w,h); + } + break; + case 2: + if (!isHybrid) { + g.drawImage(FSkinImage.CARDBG_M, x, y, w, h); + } else if (backColors.contains(DetailColors.WHITE) && backColors.contains(DetailColors.BLUE)) { + g.drawImage(FSkinImage.CARDBG_WU, x, y, w, h); + } else if (backColors.contains(DetailColors.WHITE) && backColors.contains(DetailColors.BLACK)) { + g.drawImage(FSkinImage.CARDBG_WB, x, y, w, h); + } else if (backColors.contains(DetailColors.WHITE) && backColors.contains(DetailColors.RED)) { + g.drawImage(FSkinImage.CARDBG_WR, x, y, w, h); + } else if (backColors.contains(DetailColors.WHITE) && backColors.contains(DetailColors.GREEN)) { + g.drawImage(FSkinImage.CARDBG_WG, x, y, w, h); + } else if (backColors.contains(DetailColors.BLUE) && backColors.contains(DetailColors.BLACK)) { + g.drawImage(FSkinImage.CARDBG_UB, x, y, w, h); + } else if (backColors.contains(DetailColors.BLUE) && backColors.contains(DetailColors.RED)) { + g.drawImage(FSkinImage.CARDBG_UR, x, y, w, h); + } else if (backColors.contains(DetailColors.BLUE) && backColors.contains(DetailColors.GREEN)) { + g.drawImage(FSkinImage.CARDBG_UG, x, y, w, h); + } else if (backColors.contains(DetailColors.BLACK) && backColors.contains(DetailColors.RED)) { + g.drawImage(FSkinImage.CARDBG_BR, x, y, w, h); + } else if (backColors.contains(DetailColors.BLACK) && backColors.contains(DetailColors.GREEN)) { + g.drawImage(FSkinImage.CARDBG_BG, x, y, w, h); + } else if (backColors.contains(DetailColors.RED) && backColors.contains(DetailColors.GREEN)) { + g.drawImage(FSkinImage.CARDBG_RG, x, y, w, h); + } + break; + case 3: + g.drawImage(FSkinImage.CARDBG_M, x, y, w, h); + break; + default: + g.drawImage(FSkinImage.CARDBG_C, x, y, w,h); + break; + } + return colors; + } public static void fillColorBackground(Graphics g, Color[] colors, float x, float y, float w, float h) { switch (colors.length) { case 1: diff --git a/forge-gui-mobile/src/forge/card/CardRenderer.java b/forge-gui-mobile/src/forge/card/CardRenderer.java index fcd57e85d93..af5ad43624f 100644 --- a/forge-gui-mobile/src/forge/card/CardRenderer.java +++ b/forge-gui-mobile/src/forge/card/CardRenderer.java @@ -7,6 +7,9 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import forge.ImageKeys; +import forge.localinstance.properties.ForgeConstants; +import forge.util.FileUtil; import org.apache.commons.lang3.StringUtils; import com.badlogic.gdx.Gdx; @@ -195,6 +198,7 @@ public class CardRenderer { private static final Map cardArtCache = new HashMap<>(1024); public static final float CARD_ART_RATIO = 1.302f; public static final float CARD_ART_HEIGHT_PERCENTAGE = 0.43f; + private static List classicModuleCardtoCrop = FileUtil.readFile(ForgeConstants.CLASSIC_MODULE_CARD_TO_CROP_FILE); public static void clearcardArtCache(){ cardArtCache.clear(); @@ -207,16 +211,22 @@ public class CardRenderer { public static FImageComplex getCardArt(IPaperCard pc, boolean backFace) { CardType type = pc.getRules().getType(); - return getCardArt(pc.getImageKey(backFace), pc.getRules().getSplitType() == CardSplitType.Split, type.isPlane() || type.isPhenomenon(),pc.getRules().getOracleText().contains("Aftermath")); + return getCardArt(pc.getImageKey(backFace), pc.getRules().getSplitType() == CardSplitType.Split, + type.isPlane() || type.isPhenomenon(),pc.getRules().getOracleText().contains("Aftermath"), + type.hasSubtype("Saga"), type.hasSubtype("Class"), type.isDungeon(), CardSplitType.Flip.equals(pc.getRules().getSplitType()), + type.isPlaneswalker(), isModernFrame(pc)); } public static FImageComplex getCardArt(CardView card) { CardTypeView type = card.getCurrentState().getType(); - return getCardArt(card.getCurrentState().getImageKey(), card.isSplitCard(), type.isPlane() || type.isPhenomenon(),card.getText().contains("Aftermath")); + return getCardArt(card.getCurrentState().getImageKey(), card.isSplitCard(), type.isPlane() || type.isPhenomenon(), + card.getText().contains("Aftermath"), type.hasSubtype("Saga"), type.hasSubtype("Class"), type.isDungeon(), + card.isFlipCard(), type.isPlaneswalker(), isModernFrame(card)); } - public static FImageComplex getCardArt(String imageKey, boolean isSplitCard, boolean isHorizontalCard, boolean isAftermathCard) { + public static FImageComplex getCardArt(String imageKey, boolean isSplitCard, boolean isHorizontalCard, boolean isAftermathCard, boolean isSaga, boolean isClass, boolean isDungeon, boolean isFlipCard, boolean isPlanesWalker, boolean isModernFrame) { FImageComplex cardArt = cardArtCache.get(imageKey); + boolean isClassicModule = classicModuleCardtoCrop.contains(imageKey.substring(ImageKeys.CARD_PREFIX.length()).replace(".jpg","").replace(".png", "")); if (cardArt == null) { Texture image = new RendererCachedCardImage(imageKey, true).getImage(); if (image != null) { @@ -227,12 +237,41 @@ public class CardRenderer { float x, y; float w = image.getWidth(); float h = image.getHeight(); - if (isSplitCard && !isAftermathCard) { //allow rotated image for split cards + if (isClassicModule) { + x = w * 0.09f; + y = h * 0.2f; + w -= 2f * x; + h -= 3f * y; + }else if (isPlanesWalker) { + x = w * 0.09f; + y = h * 0.11f; + w -= 2f * x; + h -= 5.71f * y; + } else if (isFlipCard) { + x = w * 0.09f; + y = h * 0.32f; + w -= 2f * x; + h -= 2.1f * y; + } else if (isDungeon) { + x = w * 0.09f; + y = h * 0.1f; + w -= 2f * x; + h -= 2.2f * y; + } else if (isClass) { + x = w * 0.09f; + y = h * 0.11f; + w -= 1.1f * x + w / 2; + h -= 2.45f * y; + } else if (isSaga) { + x = (w * 0.1f) + (w * 0.8f / 2); + y = h * 0.11f; + w -= 1.16f * x; + h -= 2.45f * y; + } else if (isSplitCard && !isAftermathCard) { //allow rotated image for split cards x = w * 33f / 250f; y = 0; //delay adjusting y and h until drawn w *= 106f / 250f; - } - else if (isHorizontalCard) { //allow rotated image for horizontal cards + } else if (isHorizontalCard) { //allow rotated image for horizontal cards float artX = 40f, artY = 40f; float artW = 350f, artH = 156f; float srcW = 430f, srcH = 300f; @@ -241,8 +280,7 @@ public class CardRenderer { y = h * 40f / srcH; w *= artW / srcW; h *= artH / srcH; - } - else { //rotate art clockwise if its not the correct orientation + } else { //rotate art clockwise if its not the correct orientation x = w * artY / srcH; y = h * (srcW - artW - artX) / srcW; w *= artH / srcH; @@ -251,19 +289,18 @@ public class CardRenderer { cardArtCache.put(imageKey, cardArt); return cardArt; } - } - else { - x = w * 0.1f; - y = h * 0.11f; - w -= 2 * x; + } else { + //adjust smaller crop + x = isModernFrame ? w * 0.1f :w * 0.12f; + y = isModernFrame ? h * 0.12f : h * 0.11f; + w -= isModernFrame ? 2 * x : 2.1f * x; h *= CARD_ART_HEIGHT_PERCENTAGE; float ratioRatio = w / h / CARD_ART_RATIO; if (ratioRatio > 1) { //if too wide, shrink width float dw = w * (ratioRatio - 1); w -= dw; x += dw / 2; - } - else { //if too tall, shrink height + } else { //if too tall, shrink height float dh = h * (1 - ratioRatio); h -= dh; y += dh / 2; @@ -290,16 +327,15 @@ public class CardRenderer { if (image != null) { if (image == ImageCache.defaultImage) { cardArt = CardImageRenderer.forgeArt; - } - else { + } else { float x, y; float w = image.getWidth(); float h = image.getHeight(); //allow rotated image for split cards - x = w * 138f / 250f; - y = h * 210f / 370f; //delay adjusting y and h until drawn - w *= 68f / 250f; - h *= 128f / 370f; + x = w * 138f / 250f; + y = h * 210f / 370f; //delay adjusting y and h until drawn + w *= 68f / 250f; + h *= 128f / 370f; cardArt = new FTextureRegionImage(new TextureRegion(image, Math.round(x), Math.round(y), Math.round(w), Math.round(h))); @@ -310,6 +346,52 @@ public class CardRenderer { return cardArt; } + public static FImageComplex getAlternateCardArt(final String imageKey, boolean isPlanesWalker) { + FImageComplex cardArt = cardArtCache.get("Alternate_"+imageKey); + if (cardArt == null) { + Texture image = new CachedCardImage(imageKey) { + @Override + public void onImageFetched() { + ImageCache.clear(); + cardArtCache.remove("Alternate_" + imageKey); + } + }.getImage(); + if (image != null) { + if (image == ImageCache.defaultImage) { + cardArt = CardImageRenderer.forgeArt; + } else { + float x, y; + float w = image.getWidth(); + float h = image.getHeight(); + if (isPlanesWalker) { + x = w * 0.09f; + y = h * 0.11f; + w -= 2f * x; + h -= 5.71f * y; + } else { + x = w * 0.1f; + y = h * 0.11f; + w -= 2 * x; + h *= CARD_ART_HEIGHT_PERCENTAGE; + float ratioRatio = w / h / CARD_ART_RATIO; + if (ratioRatio > 1) { //if too wide, shrink width + float dw = w * (ratioRatio - 1); + w -= dw; + x += dw / 2; + } else { //if too tall, shrink height + float dh = h * (1 - ratioRatio); + h -= dh; + y += dh / 2; + } + } + cardArt = new FTextureRegionImage(new TextureRegion(image, Math.round(x), Math.round(y), Math.round(w), Math.round(h))); + } + cardArtCache.put("Alternate_"+imageKey, cardArt); + } + } + return cardArt; + } + public static void drawCardListItem(Graphics g, FSkinFont font, FSkinColor foreColor, CardView card, int count, String suffix, float x, float y, float w, float h, boolean compactMode) { final CardStateView state = card.getCurrentState(); if (card.getId() > 0) { @@ -457,8 +539,8 @@ public class CardRenderer { minusxy = 0.135f*radius; } if (image != null) { - if (image == ImageCache.defaultImage) { - CardImageRenderer.drawCardImage(g, CardView.getCardForUi(pc), false, x, y, w, h, pos); + if (image == ImageCache.defaultImage || Forge.enableUIMask.equals("Art")) { + CardImageRenderer.drawCardImage(g, CardView.getCardForUi(pc), false, x, y, w, h, pos, true, true); } else { if (Forge.enableUIMask.equals("Full")) { if (ImageCache.isBorderlessCardArt(image)) @@ -482,13 +564,13 @@ public class CardRenderer { } } else { //if card has invalid or no texture due to sudden changes in ImageCache, draw CardImageRenderer instead and wait for it to refresh automatically - CardImageRenderer.drawCardImage(g, CardView.getCardForUi(pc), false, x, y, w, h, pos); + CardImageRenderer.drawCardImage(g, CardView.getCardForUi(pc), false, x, y, w, h, pos, Forge.enableUIMask.equals("Art"), true); } } public static void drawCard(Graphics g, CardView card, float x, float y, float w, float h, CardStackPosition pos, boolean rotate) { - drawCard(g, card, x, y, w, h, pos, rotate, false); + drawCard(g, card, x, y, w, h, pos, rotate, false, false); } - public static void drawCard(Graphics g, CardView card, float x, float y, float w, float h, CardStackPosition pos, boolean rotate, boolean showAltState) { + public static void drawCard(Graphics g, CardView card, float x, float y, float w, float h, CardStackPosition pos, boolean rotate, boolean showAltState, boolean isChoiceList) { boolean canshow = MatchController.instance.mayView(card); boolean showsleeves = card.isFaceDown() && card.isInZone(EnumSet.of(ZoneType.Exile)); //fix facedown card image ie gonti lord of luxury Texture image = new RendererCachedCardImage(card, false).getImage( showAltState ? card.getAlternateState().getImageKey() : card.getCurrentState().getImageKey()); @@ -501,8 +583,12 @@ public class CardRenderer { minusxy = 0.135f*radius; } if (image != null) { - if (image == ImageCache.defaultImage) { - CardImageRenderer.drawCardImage(g, card, false, x, y, w, h, pos); + if (image == ImageCache.defaultImage || Forge.enableUIMask.equals("Art")) { + float oldAlpha = g.getfloatAlphaComposite(); + if (card.isPhasedOut()) + g.setAlphaComposite(0.2f); + CardImageRenderer.drawCardImage(g, card, showAltState, x, y, w, h, pos, true, false, isChoiceList, !showCardIdOverlay(card)); + g.setAlphaComposite(oldAlpha); } else if (showsleeves) { if (!card.isForeTold()) g.drawImage(sleeves, x, y, w, h); @@ -544,7 +630,11 @@ public class CardRenderer { drawFoilEffect(g, card, x, y, w, h, false); } else { //if card has invalid or no texture due to sudden changes in ImageCache, draw CardImageRenderer instead and wait for it to refresh automatically - CardImageRenderer.drawCardImage(g, card, false, x, y, w, h, pos); + float oldAlpha = g.getfloatAlphaComposite(); + if (card.isPhasedOut()) + g.setAlphaComposite(0.2f); + CardImageRenderer.drawCardImage(g, card, showAltState, x, y, w, h, pos, Forge.enableUIMask.equals("Art"), false, isChoiceList, !showCardIdOverlay(card)); + g.setAlphaComposite(oldAlpha); } } @@ -557,7 +647,7 @@ public class CardRenderer { boolean unselectable = !MatchController.instance.isSelectable(card) && MatchController.instance.isSelecting(); float cx, cy, cw, ch; cx = x; cy = y; cw = w; ch = h; - drawCard(g, card, x, y, w, h, pos, false, showAltState); + drawCard(g, card, x, y, w, h, pos, false, showAltState, isChoiceList); float padding = w * PADDING_MULTIPLIER; //adjust for card border x += padding; @@ -684,7 +774,7 @@ public class CardRenderer { multiplier = 0.150f; break; } - g.drawOutlinedText(CardTranslation.getTranslatedName(details.getName()), FSkinFont.forHeight(h * multiplier), Color.WHITE, Color.BLACK, x + padding -1f, y + padding, w - 2 * padding, h * 0.4f, true, Align.left, false); + g.drawOutlinedText(CardTranslation.getTranslatedName(details.getName()), FSkinFont.forHeight(h * multiplier), Color.WHITE, Color.BLACK, x + padding -1f, y + padding, w - 2 * padding, h * 0.4f, true, Align.left, false, true); } if (showCardManaCostOverlay(card)) { float manaSymbolSize = w / 4.5f; diff --git a/forge-gui-mobile/src/forge/card/CardZoom.java b/forge-gui-mobile/src/forge/card/CardZoom.java index d8b9d074caa..112747c94aa 100644 --- a/forge-gui-mobile/src/forge/card/CardZoom.java +++ b/forge-gui-mobile/src/forge/card/CardZoom.java @@ -181,7 +181,7 @@ public class CardZoom extends FOverlay { } if (flipIconBounds != null && flipIconBounds.contains(x, y)) { if (currentCard.isFaceDown() && currentCard.getBackup() != null) { - if (currentCard.getBackup().hasBackSide()) { + if (currentCard.getBackup().hasBackSide() || currentCard.getBackup().isFlipCard() || currentCard.getBackup().isAdventureCard()) { show(currentCard.getBackup()); return true; } diff --git a/forge-gui-mobile/src/forge/itemmanager/views/ImageView.java b/forge-gui-mobile/src/forge/itemmanager/views/ImageView.java index a27826a6d6c..d506e36fccf 100644 --- a/forge-gui-mobile/src/forge/itemmanager/views/ImageView.java +++ b/forge-gui-mobile/src/forge/itemmanager/views/ImageView.java @@ -16,6 +16,7 @@ import com.badlogic.gdx.utils.Align; import forge.Forge; import forge.Forge.KeyInputAdapter; import forge.Graphics; +import forge.ImageKeys; import forge.assets.FImage; import forge.assets.FImageComplex; import forge.assets.FSkin; @@ -24,17 +25,15 @@ import forge.assets.FSkinColor.Colors; import forge.assets.FSkinFont; import forge.assets.FSkinImage; import forge.assets.ImageCache; -import forge.card.CardFaceSymbols; -import forge.card.CardRenderer; +import forge.card.*; import forge.card.CardRenderer.CardStackPosition; -import forge.card.CardZoom; -import forge.card.ColorSet; import forge.deck.ArchetypeDeckGenerator; import forge.deck.CardThemedDeckGenerator; import forge.deck.CommanderDeckGenerator; import forge.deck.DeckProxy; import forge.deck.FDeckViewer; import forge.deck.io.DeckPreferences; +import forge.game.card.CardView; import forge.gamemodes.planarconquest.ConquestCommander; import forge.item.InventoryItem; import forge.item.PaperCard; @@ -54,6 +53,7 @@ import forge.toolbox.FEvent.FEventHandler; import forge.toolbox.FLabel; import forge.toolbox.FScrollPane; import forge.toolbox.FTextField; +import forge.util.ImageUtil; import forge.util.Localizer; import forge.util.TextUtil; import forge.util.Utils; @@ -1016,8 +1016,20 @@ public class ImageView extends ItemView { } else { //commander bg g.drawImage(FSkin.getDeckbox().get(0), FSkin.getDeckbox().get(0), x, y, w, h, Color.GREEN, selected); - TextureRegion tr = ImageCache.croppedBorderImage(dpImg); - g.drawImage(tr, x+(w-w*scale)/2, y+(h-h*scale)/1.5f, w*scale, h*scale); + + PaperCard paperCard = null; + String imageKey = item.getImageKey(false); + if (imageKey != null) { + if (imageKey.startsWith(ImageKeys.CARD_PREFIX)) + paperCard = ImageUtil.getPaperCardFromImageKey(imageKey); + } + if (paperCard != null && Forge.enableUIMask.equals("Art")) { + CardImageRenderer.drawCardImage(g, CardView.getCardForUi(paperCard), false, + x + (w - w * scale) / 2, y + (h - h * scale) / 1.5f, w * scale, h * scale, CardStackPosition.Top, true, false, false, true); + } else { + TextureRegion tr = ImageCache.croppedBorderImage(dpImg); + g.drawImage(tr, x + (w - w * scale) / 2, y + (h - h * scale) / 1.5f, w * scale, h * scale); + } } //fake labelname shadow g.drawText(item.getName(), GROUP_HEADER_FONT, Color.BLACK, (x + PADDING)-1f, (y + PADDING*2)+1f, w - 2 * PADDING, h - 2 * PADDING, true, Align.center, false); @@ -1026,8 +1038,8 @@ public class ImageView extends ItemView { } else { if (!dp.isGeneratedDeck()){ //If deck has Commander, use it as cardArt reference - String deckImageKey = dp.getDeck().getCommanders().isEmpty() ? dp.getHighestCMCCard().getImageKey(false) : dp.getDeck().getCommanders().get(0).getImageKey(false); - FImageComplex cardArt = CardRenderer.getCardArt(deckImageKey, false, false, false); + PaperCard paperCard = dp.getDeck().getCommanders().isEmpty() ? dp.getHighestCMCCard() : dp.getDeck().getCommanders().get(0); + FImageComplex cardArt = CardRenderer.getCardArt(paperCard); //draw the deckbox if (cardArt == null){ //draw generic box if null or still loading diff --git a/forge-gui-mobile/src/forge/screens/home/HomeScreen.java b/forge-gui-mobile/src/forge/screens/home/HomeScreen.java index c65782f334f..2e8c39f913b 100644 --- a/forge-gui-mobile/src/forge/screens/home/HomeScreen.java +++ b/forge-gui-mobile/src/forge/screens/home/HomeScreen.java @@ -161,6 +161,10 @@ public class HomeScreen extends FScreen { return QuestCommander; } + public int getActiveButtonIndex() { + return activeButtonIndex; + } + public String getQuestWorld() { return QuestWorld; } diff --git a/forge-gui-mobile/src/forge/screens/home/LoadGameMenu.java b/forge-gui-mobile/src/forge/screens/home/LoadGameMenu.java index 40082ecf6a4..e248e02fdf6 100644 --- a/forge-gui-mobile/src/forge/screens/home/LoadGameMenu.java +++ b/forge-gui-mobile/src/forge/screens/home/LoadGameMenu.java @@ -105,8 +105,11 @@ public class LoadGameMenu extends FPopupMenu { protected void buildMenu() { FScreen currentScreen = Forge.getCurrentScreen(); for (LoadGameScreen lgs : LoadGameScreen.values()) { - addItem(lgs.item); - lgs.item.setSelected(currentScreen == lgs.screen); + //fixes the overlapping menu items when the user suddenly switch from load game screen index to another screen + if (HomeScreen.instance.getActiveButtonIndex() == 1) { + addItem(lgs.item); + lgs.item.setSelected(currentScreen == lgs.screen); + } } } } diff --git a/forge-gui-mobile/src/forge/screens/settings/SettingsPage.java b/forge-gui-mobile/src/forge/screens/settings/SettingsPage.java index 477b2cd9d85..8bc21cf3f66 100644 --- a/forge-gui-mobile/src/forge/screens/settings/SettingsPage.java +++ b/forge-gui-mobile/src/forge/screens/settings/SettingsPage.java @@ -521,7 +521,7 @@ public class SettingsPage extends TabPage { lstSettings.addItem(new CustomSelectSetting(FPref.UI_ENABLE_BORDER_MASKING, localizer.getMessage("lblBorderMaskOption"), localizer.getMessage("nlBorderMaskOption"), - new String[]{"Off", "Crop", "Full"}) { + new String[]{"Off", "Crop", "Full", "Art"}) { @Override public void valueChanged(String newValue) { super.valueChanged(newValue); diff --git a/forge-gui-mobile/src/forge/toolbox/FChoiceList.java b/forge-gui-mobile/src/forge/toolbox/FChoiceList.java index 47d8edfd6d4..81fddb39214 100644 --- a/forge-gui-mobile/src/forge/toolbox/FChoiceList.java +++ b/forge-gui-mobile/src/forge/toolbox/FChoiceList.java @@ -393,7 +393,7 @@ public class FChoiceList extends FList implements ActivateHandler { boolean showAlt = false; if(cardView.hasAlternateState()){ if(cardView.hasBackSide()) - showAlt = value.contains(cardView.getBackSideName()); + showAlt = value.contains(cardView.getBackSideName()) || cardView.getAlternateState().getAbilityText().contains(value); else if (cardView.isAdventureCard()) showAlt = value.equals(cardView.getAlternateState().getAbilityText()); else if (cardView.isSplitCard()) { @@ -520,9 +520,12 @@ public class FChoiceList extends FList implements ActivateHandler { @Override public void drawValue(Graphics g, T value, FSkinFont font, FSkinColor foreColor, boolean pressed, float x, float y, float w, float h) { //should fix NPE ie Thief of Sanity, Gonti... etc - CardView cv = ((IHasCardView)value).getCardView().isFaceDown() && ((IHasCardView)value).getCardView().isInZone(EnumSet.of(ZoneType.Exile)) ? ((IHasCardView)value).getCardView().getBackup() : ((IHasCardView)value).getCardView(); - boolean showAlternate = showAlternate(cv, value.toString()); - CardRenderer.drawCardWithOverlays(g, cv, x, y, VStack.CARD_WIDTH, VStack.CARD_HEIGHT, CardStackPosition.Top, false, showAlternate, true); + CardView cv = ((IHasCardView)value).getCardView(); + if (cv != null) { + CardView render = cv.isFaceDown() && cv.isInZone(EnumSet.of(ZoneType.Exile)) ? cv.getBackup() : cv; + boolean showAlternate = showAlternate(render, value.toString()); + CardRenderer.drawCardWithOverlays(g, render, x, y, VStack.CARD_WIDTH, VStack.CARD_HEIGHT, CardStackPosition.Top, false, showAlternate, true); + } float dx = VStack.CARD_WIDTH + FList.PADDING; x += dx; diff --git a/forge-gui/res/cardsfolder/upcoming/baffling_defenses.txt b/forge-gui/res/cardsfolder/b/baffling_defenses.txt similarity index 100% rename from forge-gui/res/cardsfolder/upcoming/baffling_defenses.txt rename to forge-gui/res/cardsfolder/b/baffling_defenses.txt diff --git a/forge-gui/res/cardsfolder/upcoming/benalish_partisan.txt b/forge-gui/res/cardsfolder/b/benalish_partisan.txt similarity index 100% rename from forge-gui/res/cardsfolder/upcoming/benalish_partisan.txt rename to forge-gui/res/cardsfolder/b/benalish_partisan.txt diff --git a/forge-gui/res/cardsfolder/b/body_double.txt b/forge-gui/res/cardsfolder/b/body_double.txt index b0e5e8033bd..525c7aff7fa 100644 --- a/forge-gui/res/cardsfolder/b/body_double.txt +++ b/forge-gui/res/cardsfolder/b/body_double.txt @@ -3,6 +3,6 @@ ManaCost:4 U Types:Creature Shapeshifter PT:0/0 K:ETBReplacement:Copy:DBCopy:Optional -SVar:DBCopy:DB$ Clone | Choices$ Creature.Other | ChoiceZone$ Graveyard | SpellDescription$ You may have CARDNAME enter the battlefield as a copy of any artifact on the battlefield. +SVar:DBCopy:DB$ Clone | Choices$ Creature.Other | ChoiceZone$ Graveyard | SpellDescription$ You may have CARDNAME enter the battlefield as a copy of any creature card in a graveyard. SVar:Picture:http://www.wizards.com/global/images/magic/general/body_double.jpg Oracle:You may have Body Double enter the battlefield as a copy of any creature card in a graveyard. diff --git a/forge-gui/res/cardsfolder/upcoming/boneyard_aberration.txt b/forge-gui/res/cardsfolder/b/boneyard_aberration.txt similarity index 100% rename from forge-gui/res/cardsfolder/upcoming/boneyard_aberration.txt rename to forge-gui/res/cardsfolder/b/boneyard_aberration.txt diff --git a/forge-gui/res/cardsfolder/upcoming/bounty_of_the_deep.txt b/forge-gui/res/cardsfolder/b/bounty_of_the_deep.txt similarity index 100% rename from forge-gui/res/cardsfolder/upcoming/bounty_of_the_deep.txt rename to forge-gui/res/cardsfolder/b/bounty_of_the_deep.txt diff --git a/forge-gui/res/cardsfolder/b/brushland.txt b/forge-gui/res/cardsfolder/b/brushland.txt index 634ea28bee5..f69444e99bf 100644 --- a/forge-gui/res/cardsfolder/b/brushland.txt +++ b/forge-gui/res/cardsfolder/b/brushland.txt @@ -4,6 +4,6 @@ Types:Land A:AB$ Mana | Cost$ T | Produced$ C | SpellDescription$ Add {C}. A:AB$ Mana | Cost$ T | Produced$ W | SubAbility$ DBPain | SpellDescription$ Add {W}. CARDNAME deals 1 damage to you. A:AB$ Mana | Cost$ T | Produced$ G | SubAbility$ DBPain | SpellDescription$ Add {G}. CARDNAME deals 1 damage to you. -SVar:DBPain:DB$DealDamage | NumDmg$ 1 | Defined$ You +SVar:DBPain:DB$ DealDamage | NumDmg$ 1 | Defined$ You SVar:Picture:http://www.wizards.com/global/images/magic/general/brushland.jpg Oracle:{T}: Add {C}.\n{T}: Add {G} or {W}. Brushland deals 1 damage to you. diff --git a/forge-gui/res/cardsfolder/c/chrome_mox.txt b/forge-gui/res/cardsfolder/c/chrome_mox.txt index e301b8b767b..088c660ae2f 100644 --- a/forge-gui/res/cardsfolder/c/chrome_mox.txt +++ b/forge-gui/res/cardsfolder/c/chrome_mox.txt @@ -2,7 +2,7 @@ Name:Chrome Mox ManaCost:0 Types:Artifact T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Any | Destination$ Battlefield | OptionalDecider$ You | Execute$ TrigExile | TriggerDescription$ Imprint — When CARDNAME enters the battlefield, you may exile a nonartifact, nonland card from your hand. -SVar:TrigExile:DB$ChangeZone | Imprint$ True | Origin$ Hand | Destination$ Exile | ChangeType$ Card.nonArtifact+nonLand | ChangeNum$ 1 +SVar:TrigExile:DB$ ChangeZone | Imprint$ True | Origin$ Hand | Destination$ Exile | ChangeType$ Card.nonArtifact+nonLand | ChangeNum$ 1 A:AB$ ManaReflected | Cost$ T | Valid$ Defined.Imprinted | ColorOrType$ Color | ReflectProperty$ Is | SpellDescription$ Add one mana of any of the exiled card's colors. T:Mode$ ChangesZone | Origin$ Battlefield | ValidCard$ Card.Self | Destination$ Any | Execute$ DBCleanup | Static$ True T:Mode$ ChangesZone | Origin$ Exile | Destination$ Any | Static$ True | ValidCard$ Card.IsImprinted+ExiledWithSource | Execute$ DBForget diff --git a/forge-gui/res/cardsfolder/c/city_of_brass.txt b/forge-gui/res/cardsfolder/c/city_of_brass.txt index d98f99d64b0..a3b6851720f 100644 --- a/forge-gui/res/cardsfolder/c/city_of_brass.txt +++ b/forge-gui/res/cardsfolder/c/city_of_brass.txt @@ -3,6 +3,6 @@ ManaCost:no cost Types:Land A:AB$ Mana | Cost$ T | Produced$ Any | Amount$ 1 | SpellDescription$ Add one mana of any color. T:Mode$ Taps | ValidCard$ Card.Self | Execute$ TrigDamage | TriggerZones$ Battlefield | TriggerDescription$ Whenever CARDNAME becomes tapped, it deals 1 damage to you. -SVar:TrigDamage:DB$DealDamage | Defined$ You | NumDmg$ 1 +SVar:TrigDamage:DB$ DealDamage | Defined$ You | NumDmg$ 1 SVar:Picture:http://www.wizards.com/global/images/magic/general/city_of_brass.jpg Oracle:Whenever City of Brass becomes tapped, it deals 1 damage to you.\n{T}: Add one mana of any color. diff --git a/forge-gui/res/cardsfolder/c/cold_storage.txt b/forge-gui/res/cardsfolder/c/cold_storage.txt index 706870e99f0..1267fd23909 100644 --- a/forge-gui/res/cardsfolder/c/cold_storage.txt +++ b/forge-gui/res/cardsfolder/c/cold_storage.txt @@ -6,7 +6,7 @@ A:AB$ ChangeZoneAll | Cost$ Sac<1/CARDNAME> | ChangeType$ Card.Creature+IsRememb T:Mode$ ChangesZone | Origin$ Exile | Destination$ Any | Static$ True | ValidCard$ Card.IsRemembered+ExiledWithSource | Execute$ DBForget SVar:DBForget:DB$ Pump | ForgetObjects$ TriggeredCard T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | Static$ True | ValidCard$ Card.Self | Execute$ DBCleanup -SVar:DBCleanup:DB$Cleanup | ClearRemembered$ True +SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True SVar:NonStackingEffect:True AI:RemoveDeck:All SVar:Picture:http://www.wizards.com/global/images/magic/general/cold_storage.jpg diff --git a/forge-gui/res/cardsfolder/upcoming/danse_macabre.txt b/forge-gui/res/cardsfolder/d/danse_macabre.txt similarity index 100% rename from forge-gui/res/cardsfolder/upcoming/danse_macabre.txt rename to forge-gui/res/cardsfolder/d/danse_macabre.txt diff --git a/forge-gui/res/cardsfolder/upcoming/davriel_soul_broker.txt b/forge-gui/res/cardsfolder/d/davriel_soul_broker.txt similarity index 100% rename from forge-gui/res/cardsfolder/upcoming/davriel_soul_broker.txt rename to forge-gui/res/cardsfolder/d/davriel_soul_broker.txt diff --git a/forge-gui/res/cardsfolder/upcoming/davriels_withering.txt b/forge-gui/res/cardsfolder/d/davriels_withering.txt similarity index 100% rename from forge-gui/res/cardsfolder/upcoming/davriels_withering.txt rename to forge-gui/res/cardsfolder/d/davriels_withering.txt diff --git a/forge-gui/res/cardsfolder/upcoming/death_tyrant.txt b/forge-gui/res/cardsfolder/d/death_tyrant.txt similarity index 100% rename from forge-gui/res/cardsfolder/upcoming/death_tyrant.txt rename to forge-gui/res/cardsfolder/d/death_tyrant.txt diff --git a/forge-gui/res/cardsfolder/d/devour_intellect.txt b/forge-gui/res/cardsfolder/d/devour_intellect.txt index c1f0340f007..b6d5eab5db4 100644 --- a/forge-gui/res/cardsfolder/d/devour_intellect.txt +++ b/forge-gui/res/cardsfolder/d/devour_intellect.txt @@ -1,7 +1,7 @@ Name:Devour Intellect ManaCost:B Types:Sorcery -A:SP$ Pump | TgtPrompt$ Select target opponent | ValidTgt$ Opponent | SubAbility$ DBBranch +A:SP$ Pump | TgtPrompt$ Select target opponent | ValidTgts$ Opponent | SubAbility$ DBBranch SVar:DBBranch:DB$ Branch | BranchConditionSVar$ TreasureCheck | BranchConditionSVarCompare$ GE1 | FalseSubAbility$ DBDiscard | TrueSubAbility$ DBTreasureDiscard SVar:DBDiscard:DB$ Discard | Defined$ Targeted | NumCards$ 1 | Mode$ TgtChoose SVar:DBTreasureDiscard:DB$ Discard | Defined$ Targeted | NumCards$ 1 | Mode$ RevealYouChoose | DiscardValid$ Card.nonLand diff --git a/forge-gui/res/cardsfolder/upcoming/ethereal_grasp.txt b/forge-gui/res/cardsfolder/e/ethereal_grasp.txt similarity index 100% rename from forge-gui/res/cardsfolder/upcoming/ethereal_grasp.txt rename to forge-gui/res/cardsfolder/e/ethereal_grasp.txt diff --git a/forge-gui/res/cardsfolder/upcoming/faceless_agent.txt b/forge-gui/res/cardsfolder/f/faceless_agent.txt similarity index 100% rename from forge-gui/res/cardsfolder/upcoming/faceless_agent.txt rename to forge-gui/res/cardsfolder/f/faceless_agent.txt diff --git a/forge-gui/res/cardsfolder/upcoming/freyalise_skyshroud_partisan.txt b/forge-gui/res/cardsfolder/f/freyalise_skyshroud_partisan.txt similarity index 100% rename from forge-gui/res/cardsfolder/upcoming/freyalise_skyshroud_partisan.txt rename to forge-gui/res/cardsfolder/f/freyalise_skyshroud_partisan.txt diff --git a/forge-gui/res/cardsfolder/upcoming/grave_endeavor.txt b/forge-gui/res/cardsfolder/g/grave_endeavor.txt similarity index 100% rename from forge-gui/res/cardsfolder/upcoming/grave_endeavor.txt rename to forge-gui/res/cardsfolder/g/grave_endeavor.txt diff --git a/forge-gui/res/cardsfolder/upcoming/grim_hireling.txt b/forge-gui/res/cardsfolder/g/grim_hireling.txt similarity index 100% rename from forge-gui/res/cardsfolder/upcoming/grim_hireling.txt rename to forge-gui/res/cardsfolder/g/grim_hireling.txt diff --git a/forge-gui/res/cardsfolder/upcoming/hellish_rebuke.txt b/forge-gui/res/cardsfolder/h/hellish_rebuke.txt similarity index 100% rename from forge-gui/res/cardsfolder/upcoming/hellish_rebuke.txt rename to forge-gui/res/cardsfolder/h/hellish_rebuke.txt diff --git a/forge-gui/res/cardsfolder/upcoming/immovable_rod.txt b/forge-gui/res/cardsfolder/i/immovable_rod.txt similarity index 100% rename from forge-gui/res/cardsfolder/upcoming/immovable_rod.txt rename to forge-gui/res/cardsfolder/i/immovable_rod.txt diff --git a/forge-gui/res/cardsfolder/upcoming/indomitable_might.txt b/forge-gui/res/cardsfolder/i/indomitable_might.txt similarity index 100% rename from forge-gui/res/cardsfolder/upcoming/indomitable_might.txt rename to forge-gui/res/cardsfolder/i/indomitable_might.txt diff --git a/forge-gui/res/cardsfolder/k/kathril_aspect_warper.txt b/forge-gui/res/cardsfolder/k/kathril_aspect_warper.txt index 98fd1c2a4fb..a160256ea01 100755 --- a/forge-gui/res/cardsfolder/k/kathril_aspect_warper.txt +++ b/forge-gui/res/cardsfolder/k/kathril_aspect_warper.txt @@ -3,7 +3,7 @@ ManaCost:2 W B G Types:Legendary Creature Nightmare Insect PT:3/3 T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ PutKeywordCounter | TriggerDescription$ When CARDNAME enters the battlefield, put a flying counter on any creature you control if a creature card in your graveyard has flying. Repeat this process for first strike, double strike, deathtouch, hexproof, indestructible, lifelink, menace, reach, trample, and vigilance. Then put a +1/+1 counter on NICKNAME for each counter put on a creature this way. -SVar:PutKeywordCounter:DB$ PutCounter | Choices$ Creature.YouCtrl | ChoiceTitle$ Choose a creature | SharedKeywords$ Flying & First Strike & Double Strike & Deathtouch & Hexproof & Indestructible & Lifelink & Menace & Reach & Trample & Vigilance | SharedKeywordsZone$ Graveyard | SharedRestrictions$ Card.YouOwn | CounterNum$ 1 | RememberAmount$ True | SubAbility$ PutCounters +SVar:PutKeywordCounter:DB$ PutCounter | Choices$ Creature.YouCtrl | ChoiceTitle$ Choose a creature | SharedKeywords$ Flying & First Strike & Double Strike & Deathtouch & Hexproof & Indestructible & Lifelink & Menace & Reach & Trample & Vigilance | SharedKeywordsZone$ Graveyard | SharedRestrictions$ Creature.YouOwn | CounterNum$ 1 | RememberAmount$ True | SubAbility$ PutCounters SVar:PutCounters:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ X | SubAbility$ DBCleanup SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True SVar:X:Count$RememberedNumber diff --git a/forge-gui/res/cardsfolder/upcoming/kiora_the_tides_fury.txt b/forge-gui/res/cardsfolder/k/kiora_the_tides_fury.txt similarity index 100% rename from forge-gui/res/cardsfolder/upcoming/kiora_the_tides_fury.txt rename to forge-gui/res/cardsfolder/k/kiora_the_tides_fury.txt diff --git a/forge-gui/res/cardsfolder/upcoming/leonin_sanctifier.txt b/forge-gui/res/cardsfolder/l/leonin_sanctifier.txt similarity index 100% rename from forge-gui/res/cardsfolder/upcoming/leonin_sanctifier.txt rename to forge-gui/res/cardsfolder/l/leonin_sanctifier.txt diff --git a/forge-gui/res/cardsfolder/upcoming/longtusk_stalker.txt b/forge-gui/res/cardsfolder/l/longtusk_stalker.txt similarity index 100% rename from forge-gui/res/cardsfolder/upcoming/longtusk_stalker.txt rename to forge-gui/res/cardsfolder/l/longtusk_stalker.txt diff --git a/forge-gui/res/cardsfolder/upcoming/lorcan_warlock_collector.txt b/forge-gui/res/cardsfolder/l/lorcan_warlock_collector.txt similarity index 100% rename from forge-gui/res/cardsfolder/upcoming/lorcan_warlock_collector.txt rename to forge-gui/res/cardsfolder/l/lorcan_warlock_collector.txt diff --git a/forge-gui/res/cardsfolder/upcoming/lumbering_lightshield.txt b/forge-gui/res/cardsfolder/l/lumbering_lightshield.txt similarity index 100% rename from forge-gui/res/cardsfolder/upcoming/lumbering_lightshield.txt rename to forge-gui/res/cardsfolder/l/lumbering_lightshield.txt diff --git a/forge-gui/res/cardsfolder/upcoming/managorger_phoenix.txt b/forge-gui/res/cardsfolder/m/managorger_phoenix.txt similarity index 100% rename from forge-gui/res/cardsfolder/upcoming/managorger_phoenix.txt rename to forge-gui/res/cardsfolder/m/managorger_phoenix.txt diff --git a/forge-gui/res/cardsfolder/upcoming/manor_guardian.txt b/forge-gui/res/cardsfolder/m/manor_guardian.txt similarity index 100% rename from forge-gui/res/cardsfolder/upcoming/manor_guardian.txt rename to forge-gui/res/cardsfolder/m/manor_guardian.txt diff --git a/forge-gui/res/cardsfolder/upcoming/mantle_of_the_ancients.txt b/forge-gui/res/cardsfolder/m/mantle_of_the_ancients.txt similarity index 100% rename from forge-gui/res/cardsfolder/upcoming/mantle_of_the_ancients.txt rename to forge-gui/res/cardsfolder/m/mantle_of_the_ancients.txt diff --git a/forge-gui/res/cardsfolder/upcoming/mentor_of_evos_isle.txt b/forge-gui/res/cardsfolder/m/mentor_of_evos_isle.txt similarity index 100% rename from forge-gui/res/cardsfolder/upcoming/mentor_of_evos_isle.txt rename to forge-gui/res/cardsfolder/m/mentor_of_evos_isle.txt diff --git a/forge-gui/res/cardsfolder/upcoming/nihiloor.txt b/forge-gui/res/cardsfolder/n/nihiloor.txt similarity index 100% rename from forge-gui/res/cardsfolder/upcoming/nihiloor.txt rename to forge-gui/res/cardsfolder/n/nihiloor.txt diff --git a/forge-gui/res/cardsfolder/upcoming/phantom_steed.txt b/forge-gui/res/cardsfolder/p/phantom_steed.txt similarity index 100% rename from forge-gui/res/cardsfolder/upcoming/phantom_steed.txt rename to forge-gui/res/cardsfolder/p/phantom_steed.txt diff --git a/forge-gui/res/cardsfolder/upcoming/plaguecrafters_familiar.txt b/forge-gui/res/cardsfolder/p/plaguecrafters_familiar.txt similarity index 100% rename from forge-gui/res/cardsfolder/upcoming/plaguecrafters_familiar.txt rename to forge-gui/res/cardsfolder/p/plaguecrafters_familiar.txt diff --git a/forge-gui/res/cardsfolder/upcoming/pool_of_vigorous_growth.txt b/forge-gui/res/cardsfolder/p/pool_of_vigorous_growth.txt similarity index 100% rename from forge-gui/res/cardsfolder/upcoming/pool_of_vigorous_growth.txt rename to forge-gui/res/cardsfolder/p/pool_of_vigorous_growth.txt diff --git a/forge-gui/res/cardsfolder/p/promise_of_tomorrow.txt b/forge-gui/res/cardsfolder/p/promise_of_tomorrow.txt index 1a614184f1b..79f55a9408c 100755 --- a/forge-gui/res/cardsfolder/p/promise_of_tomorrow.txt +++ b/forge-gui/res/cardsfolder/p/promise_of_tomorrow.txt @@ -2,7 +2,7 @@ Name:Promise of Tomorrow ManaCost:2 W Types:Enchantment T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Creature.YouCtrl | TriggerZones$ Battlefield | Execute$ TrigChange | TriggerDescription$ Whenever a creature you control dies, exile it. -SVar:TrigChange:DB$ ChangeZone | Defined$ TriggeredCard | Origin$ Graveyard | Destination$ Exile +SVar:TrigChange:DB$ ChangeZone | Defined$ TriggeredNewCardLKICopy | Origin$ Graveyard | Destination$ Exile T:Mode$ Phase | Phase$ End of Turn | TriggerZones$ Battlefield | IsPresent$ Creature.YouCtrl | PresentCompare$ EQ0 | Execute$ TrigSac | TriggerDescription$ At the beginning of each end step, if you control no creatures, sacrifice CARDNAME and return all cards exiled with it to the battlefield under your control. SVar:TrigSac:DB$ Sacrifice | Defined$ Self | SubAbility$ DBChangeZoneAll SVar:DBChangeZoneAll:DB$ ChangeZoneAll | Origin$ Exile | Destination$ Battlefield | ChangeType$ Card.ExiledWithSource diff --git a/forge-gui/res/cardsfolder/upcoming/reckless_ringleader.txt b/forge-gui/res/cardsfolder/r/reckless_ringleader.txt similarity index 100% rename from forge-gui/res/cardsfolder/upcoming/reckless_ringleader.txt rename to forge-gui/res/cardsfolder/r/reckless_ringleader.txt diff --git a/forge-gui/res/cardsfolder/upcoming/rod_of_absorption.txt b/forge-gui/res/cardsfolder/r/rod_of_absorption.txt similarity index 100% rename from forge-gui/res/cardsfolder/upcoming/rod_of_absorption.txt rename to forge-gui/res/cardsfolder/r/rod_of_absorption.txt diff --git a/forge-gui/res/cardsfolder/upcoming/sarkhan_wanderer_to_shiv.txt b/forge-gui/res/cardsfolder/s/sarkhan_wanderer_to_shiv.txt similarity index 100% rename from forge-gui/res/cardsfolder/upcoming/sarkhan_wanderer_to_shiv.txt rename to forge-gui/res/cardsfolder/s/sarkhan_wanderer_to_shiv.txt diff --git a/forge-gui/res/cardsfolder/upcoming/sarkhans_scorn.txt b/forge-gui/res/cardsfolder/s/sarkhans_scorn.txt similarity index 100% rename from forge-gui/res/cardsfolder/upcoming/sarkhans_scorn.txt rename to forge-gui/res/cardsfolder/s/sarkhans_scorn.txt diff --git a/forge-gui/res/cardsfolder/upcoming/scion_of_shiv.txt b/forge-gui/res/cardsfolder/s/scion_of_shiv.txt similarity index 100% rename from forge-gui/res/cardsfolder/upcoming/scion_of_shiv.txt rename to forge-gui/res/cardsfolder/s/scion_of_shiv.txt diff --git a/forge-gui/res/cardsfolder/upcoming/share_the_spoils.txt b/forge-gui/res/cardsfolder/s/share_the_spoils.txt similarity index 100% rename from forge-gui/res/cardsfolder/upcoming/share_the_spoils.txt rename to forge-gui/res/cardsfolder/s/share_the_spoils.txt diff --git a/forge-gui/res/cardsfolder/upcoming/shoreline_scout.txt b/forge-gui/res/cardsfolder/s/shoreline_scout.txt similarity index 100% rename from forge-gui/res/cardsfolder/upcoming/shoreline_scout.txt rename to forge-gui/res/cardsfolder/s/shoreline_scout.txt diff --git a/forge-gui/res/cardsfolder/upcoming/skyshroud_ambush.txt b/forge-gui/res/cardsfolder/s/skyshroud_ambush.txt similarity index 100% rename from forge-gui/res/cardsfolder/upcoming/skyshroud_ambush.txt rename to forge-gui/res/cardsfolder/s/skyshroud_ambush.txt diff --git a/forge-gui/res/cardsfolder/s/skyshroud_forest.txt b/forge-gui/res/cardsfolder/s/skyshroud_forest.txt index 6a7b52c22e8..f7d693eda75 100644 --- a/forge-gui/res/cardsfolder/s/skyshroud_forest.txt +++ b/forge-gui/res/cardsfolder/s/skyshroud_forest.txt @@ -5,6 +5,6 @@ A:AB$ Mana | Cost$ T | Produced$ C | SpellDescription$ Add {C}. A:AB$ Mana | Cost$ T | Produced$ U | SubAbility$ DBPain | SpellDescription$ Add {U}. CARDNAME deals 1 damage to you. A:AB$ Mana | Cost$ T | Produced$ G | SubAbility$ DBPain | SpellDescription$ Add {G}. CARDNAME deals 1 damage to you. K:CARDNAME enters the battlefield tapped. -SVar:DBPain:DB$DealDamage | NumDmg$ 1 | Defined$ You +SVar:DBPain:DB$ DealDamage | NumDmg$ 1 | Defined$ You SVar:Picture:http://www.wizards.com/global/images/magic/general/skyshroud_forest.jpg Oracle:Skyshroud Forest enters the battlefield tapped.\n{T}: Add {C}.\n{T}: Add {G} or {U}. Skyshroud Forest deals 1 damage to you. diff --git a/forge-gui/res/cardsfolder/upcoming/skyshroud_lookout.txt b/forge-gui/res/cardsfolder/s/skyshroud_lookout.txt similarity index 100% rename from forge-gui/res/cardsfolder/upcoming/skyshroud_lookout.txt rename to forge-gui/res/cardsfolder/s/skyshroud_lookout.txt diff --git a/forge-gui/res/cardsfolder/upcoming/subversive_acolyte.txt b/forge-gui/res/cardsfolder/s/subversive_acolyte.txt similarity index 100% rename from forge-gui/res/cardsfolder/upcoming/subversive_acolyte.txt rename to forge-gui/res/cardsfolder/s/subversive_acolyte.txt diff --git a/forge-gui/res/cardsfolder/upcoming/teyo_aegis_adept.txt b/forge-gui/res/cardsfolder/t/teyo_aegis_adept.txt similarity index 100% rename from forge-gui/res/cardsfolder/upcoming/teyo_aegis_adept.txt rename to forge-gui/res/cardsfolder/t/teyo_aegis_adept.txt diff --git a/forge-gui/res/cardsfolder/upcoming/thorough_investigation.txt b/forge-gui/res/cardsfolder/t/thorough_investigation.txt similarity index 100% rename from forge-gui/res/cardsfolder/upcoming/thorough_investigation.txt rename to forge-gui/res/cardsfolder/t/thorough_investigation.txt diff --git a/forge-gui/res/cardsfolder/upcoming/tome_of_the_infinite.txt b/forge-gui/res/cardsfolder/t/tome_of_the_infinite.txt similarity index 100% rename from forge-gui/res/cardsfolder/upcoming/tome_of_the_infinite.txt rename to forge-gui/res/cardsfolder/t/tome_of_the_infinite.txt diff --git a/forge-gui/res/cardsfolder/upcoming/avacyns_memorial.txt b/forge-gui/res/cardsfolder/upcoming/avacyns_memorial.txt new file mode 100644 index 00000000000..9b5f43b9ec2 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/avacyns_memorial.txt @@ -0,0 +1,7 @@ +Name:Avacyn's Memorial +ManaCost:5 W W W +Types:Legendary Artifact +K:Indestructible +S:Mode$ Continuous | Affected$ Permanent.Other+YouCtrl+Legendary | AddKeyword$ Indestructible | Description$ Other legendary permanents you control have indestructible. +DeckNeeds:Type$Legendary +Oracle:Indestructible\nOther legendary permanents you control have indestructible. diff --git a/forge-gui/res/cardsfolder/upcoming/baithook_angler_hook_haunt_drifter.txt b/forge-gui/res/cardsfolder/upcoming/baithook_angler_hook_haunt_drifter.txt new file mode 100644 index 00000000000..ee761c99313 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/baithook_angler_hook_haunt_drifter.txt @@ -0,0 +1,19 @@ +Name:Baithook Angler +ManaCost:1 U +Types:Creature Human Peasant +PT:2/1 +K:Disturb:1 U +AlternateMode:DoubleFaced +Oracle:Disturb {1}{U} (You may cast this card from your graveyard transformed for its disturb cost.) + +ALTERNATE + +Name:Hook-Haunt Drifter +ManaCost:no cost +Types:Creature Spirit +Colors:blue +PT:1/2 +K:Flying +R:Event$ Moved | ValidCard$ Card.Self | Destination$ Graveyard | ReplaceWith$ Exile | Description$ If CARDNAME would be put into a graveyard from anywhere, exile it instead. +SVar:Exile:DB$ ChangeZone | Hidden$ True | Origin$ All | Destination$ Exile | Defined$ ReplacedCard +Oracle:Flying\nIf Hook-Haunt Drifter would be put into a graveyard from anywhere, exile it instead. diff --git a/forge-gui/res/cardsfolder/upcoming/beloved_beggar_generous_soul.txt b/forge-gui/res/cardsfolder/upcoming/beloved_beggar_generous_soul.txt new file mode 100644 index 00000000000..8119a4e8cab --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/beloved_beggar_generous_soul.txt @@ -0,0 +1,20 @@ +Name:Beloved Beggar +ManaCost:1 W +Types:Creature Human Peasant +PT:0/4 +K:Disturb:4 W W +AlternateMode:DoubleFaced +Oracle:Disturb {4}{W}{W} (You may cast this card from your graveyard transformed for its disturb cost.) + +ALTERNATE + +Name:Generous Soul +ManaCost:no cost +Types:Creature Spirit +Colors:white +PT:4/4 +K:Flying +K:Vigilance +R:Event$ Moved | ValidCard$ Card.Self | Destination$ Graveyard | ReplaceWith$ Exile | Description$ If CARDNAME would be put into a graveyard from anywhere, exile it instead. +SVar:Exile:DB$ ChangeZone | Hidden$ True | Origin$ All | Destination$ Exile | Defined$ ReplacedCard +Oracle:Flying, vigilance\nIf Generous Soul would be put into a graveyard from anywhere, exile it instead. diff --git a/forge-gui/res/cardsfolder/upcoming/candlelit_cavalry.txt b/forge-gui/res/cardsfolder/upcoming/candlelit_cavalry.txt new file mode 100644 index 00000000000..b9ff6e67850 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/candlelit_cavalry.txt @@ -0,0 +1,8 @@ +Name:Candlelit Cavalry +ManaCost:4 G +Types:Creature Human Knight +PT:5/5 +T:Mode$ Phase | Phase$ BeginCombat | ValidPlayer$ You | TriggerZones$ Battlefield | CheckSVar$ X | SVarCompare$ GE3 | Execute$ TrigPump | TriggerDescription$ Coven – At the beginning of combat on your turn, if you control three or more creatures with different powers, CARDNAME gains trample until end of turn. +SVar:TrigPump:DB$ Pump | Defined$ Self | KW$ Trample +SVar:X:Count$DifferentPower_Creature.YouCtrl +Oracle:Coven — At the beginning of combat on your turn, if you control three or more creatures with different powers, Candlelit Cavalry gains trample until end of turn. diff --git a/forge-gui/res/cardsfolder/upcoming/candletrap.txt b/forge-gui/res/cardsfolder/upcoming/candletrap.txt new file mode 100644 index 00000000000..529dae71f57 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/candletrap.txt @@ -0,0 +1,10 @@ +Name:Candletrap +ManaCost:W +Types:Enchantment Aura +K:Enchant creature +A:SP$ Attach | Cost$ W | ValidTgts$ Creature | AILogic$ Curse +S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddKeyword$ Defender | Description$ Enchanted creature has defender. +R:Event$ DamageDone | Prevent$ True | IsCombat$ True | ValidSource$ Creature.EnchantedBy | Description$ Prevent all combat damage that would be dealt by enchanted creature. +A:AB$ ChangeZone | Cost$ 2 W Sac<1/CARDNAME> | Defined$ Enchanted | CheckSVar$ X | SVarCompare$ GE3 | Origin$ Battlefield | Destination$ Exile | PrecostDesc$ Coven — | SpellDescription$ Exile enchanted creature. Activate only if you control three or more creatures with different powers. +SVar:X:Count$DifferentPower_Creature.YouCtrl +Oracle:Enchant creature\nEnchanted creature has defender.\nPrevent all combat damage that would be dealt by enchanted creature.\nCoven — {2}{W}, Sacrifice Candletrap: Exile enchanted creature. Activate only if you control three or more creatures with different powers. diff --git a/forge-gui/res/cardsfolder/upcoming/curse_of_obsession.txt b/forge-gui/res/cardsfolder/upcoming/curse_of_obsession.txt new file mode 100644 index 00000000000..1d62167727c --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/curse_of_obsession.txt @@ -0,0 +1,10 @@ +Name:Curse of Obsession +ManaCost:4 R +Types:Enchantment Aura Curse +K:Enchant player +A:SP$ Attach | ValidTgts$ Player | AILogic$ Curse +T:Mode$ Phase | Phase$ Draw | ValidPlayer$ Player.EnchantedBy | TriggerZones$ Battlefield | Execute$ TrigDraw | TriggerDescription$ At the beginning of enchanted player's draw step, that player draws two additional cards. +SVar:TrigDraw:DB$ Draw | Defined$ TriggeredPlayer | NumCards$ 2 +T:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ Player.EnchantedBy | TriggerZones$ Battlefield | Execute$ TrigDiscard | TriggerDescription$ At the beginning of enchanted player's end step, that player discards their hand. +SVar:TrigDiscard:DB$ Discard | Defined$ TriggeredPlayer | Mode$ Hand +Oracle:Enchant player\nAt the beginning of enchanted player's draw step, that player draws two additional cards.\nAt the beginning of enchanted player's end step, that player discards their hand. diff --git a/forge-gui/res/cardsfolder/upcoming/defenestrate.txt b/forge-gui/res/cardsfolder/upcoming/defenestrate.txt new file mode 100644 index 00000000000..a9613a1014d --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/defenestrate.txt @@ -0,0 +1,5 @@ +Name:Defenestrate +ManaCost:2 B +Types:Instant +A:SP$ Destroy | Cost$ 2 B | ValidTgts$ Creature.withoutFlying | TgtPrompt$ Select target creature without flying | SpellDescription$ Destroy target creature without flying. +Oracle:Destroy target creature without flying. diff --git a/forge-gui/res/cardsfolder/upcoming/deserted_beach.txt b/forge-gui/res/cardsfolder/upcoming/deserted_beach.txt new file mode 100644 index 00000000000..46e2097c763 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/deserted_beach.txt @@ -0,0 +1,9 @@ +Name:Deserted Beach +ManaCost:no cost +Types:Land +A:AB$ Mana | Cost$ T | Produced$ W | SpellDescription$ Add {W}. +A:AB$ Mana | Cost$ T | Produced$ U | SpellDescription$ Add {U}. +K:ETBReplacement:Other:LandTapped +SVar:LandTapped:DB$ Tap | Defined$ Self | ETB$ True | ConditionCheckSVar$ ETBCheckSVar2 | ConditionSVarCompare$ LT2 | SpellDescription$ CARDNAME enters the battlefield tapped unless you control two or more other lands. +SVar:ETBCheckSVar2:Count$LastStateBattlefield Land.YouCtrl +Oracle:Deserted Beach enters the battlefield tapped unless you control two or more other lands.\n{T}: Add {W} or {U}. diff --git a/forge-gui/res/cardsfolder/upcoming/festival_crasher.txt b/forge-gui/res/cardsfolder/upcoming/festival_crasher.txt new file mode 100644 index 00000000000..ca123d0a13c --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/festival_crasher.txt @@ -0,0 +1,8 @@ +Name:Festival Crasher +ManaCost:1 R +Types:Creature Devil +PT:1/3 +T:Mode$ SpellCast | ValidCard$ Instant,Sorcery | ValidActivatingPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigPump | TriggerDescription$ Whenever you cast an instant or sorcery spell, CARDNAME gets +2/+0 until end of turn. +SVar:TrigPump:DB$ Pump | Defined$ Self | NumAtt$ 2 +DeckHints:Type$Instant|Sorcery +Oracle:Whenever you cast an instant or sorcery spell, Festival Crasher gets +2/+0 until end of turn. diff --git a/forge-gui/res/cardsfolder/upcoming/galvanic_iteration.txt b/forge-gui/res/cardsfolder/upcoming/galvanic_iteration.txt new file mode 100644 index 00000000000..c16eac9843c --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/galvanic_iteration.txt @@ -0,0 +1,8 @@ +Name:Galvanic Iteration +ManaCost:U R +Types:Instant +K:Flashback:1 U R +A:SP$ DelayedTrigger | Cost$ U R | AILogic$ SpellCopy | Execute$ EffTrigCopy | ThisTurn$ True | Mode$ SpellCast | ValidCard$ Instant,Sorcery | ValidActivatingPlayer$ You | SpellDescription$ When you cast your next instant or sorcery spell this turn, copy that spell. You may choose new targets for the copy. +SVar:EffTrigCopy:DB$ CopySpellAbility | Defined$ TriggeredSpellAbility | MayChooseTarget$ True +SVar:AIPriorityModifier:9 +Oracle:When you cast your next instant or sorcery spell this turn, copy that spell. You may choose new targets for the copy.\nFlashback {1}{U}{R} (You may cast this card from your graveyard for its flashback cost. Then exile it.) diff --git a/forge-gui/res/cardsfolder/upcoming/gisa_glorious_resurrector.txt b/forge-gui/res/cardsfolder/upcoming/gisa_glorious_resurrector.txt new file mode 100644 index 00000000000..0ad42785d43 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/gisa_glorious_resurrector.txt @@ -0,0 +1,11 @@ +Name:Gisa, Glorious Resurrector +ManaCost:2 B B +Types:Legendary Creature Human Wizard +PT:4/4 +R:Event$ Moved | ActiveZones$ Battlefield | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Creature.OppCtrl | ReplaceWith$ Exile | Description$ If a creature an opponent controls would die, exile it instead. +SVar:Exile:DB$ ChangeZone | Hidden$ True | Origin$ All | Destination$ Exile | Defined$ ReplacedCard +T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Battlefield | IsPresent$ Card.ExiledWithSource | PresentZone$ Exile | Execute$ TrigChange | TriggerDescription$ At the beginning of your upkeep, put all creature cards exiled with CARDNAME onto the battlefield under your control. They gain decayed. (A creature with decayed can't block. When it attacks, sacrifice it at end of combat.) +SVar:TrigChange:DB$ ChangeZoneAll | ChangeType$ Card.ExiledWithSource | Origin$ Exile | Destination$ Battlefield | GainControl$ True | RememberChanged$ True | SubAbility$ DBPump +SVar:DBPump:DB$ PumpAll | ValidCards$ Card.IsRemembered | KW$ Decayed | Duration$ Permanent | SubAbility$ DBCleanup +SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True +Oracle:If a creature an opponent controls would die, exile it instead.\nAt the beginning of your upkeep, put all creature cards exiled with Gisa, Glorious Resurrector onto the battlefield under your control. They gain decayed. (A creature with decayed can't block. When it attacks, sacrifice it at end of combat.) diff --git a/forge-gui/res/cardsfolder/upcoming/haunted_ridge.txt b/forge-gui/res/cardsfolder/upcoming/haunted_ridge.txt new file mode 100644 index 00000000000..710fe7eb9f6 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/haunted_ridge.txt @@ -0,0 +1,9 @@ +Name:Haunted Ridge +ManaCost:no cost +Types:Land +A:AB$ Mana | Cost$ T | Produced$ B | SpellDescription$ Add {B}. +A:AB$ Mana | Cost$ T | Produced$ R | SpellDescription$ Add {R}. +K:ETBReplacement:Other:LandTapped +SVar:LandTapped:DB$ Tap | Defined$ Self | ETB$ True | ConditionCheckSVar$ ETBCheckSVar2 | ConditionSVarCompare$ LT2 | SpellDescription$ CARDNAME enters the battlefield tapped unless you control two or more other lands. +SVar:ETBCheckSVar2:Count$LastStateBattlefield Land.YouCtrl +Oracle:Haunted Ridge enters the battlefield tapped unless you control two or more other lands.\n{T}: Add {B} or {R}. diff --git a/forge-gui/res/cardsfolder/upcoming/howl_of_the_hunt.txt b/forge-gui/res/cardsfolder/upcoming/howl_of_the_hunt.txt new file mode 100644 index 00000000000..f58b40a48d3 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/howl_of_the_hunt.txt @@ -0,0 +1,11 @@ +Name:Howl of the Hunt +ManaCost:2 G +Types:Enchantment Aura +K:Flash +K:Enchant creature +A:SP$ Attach | ValidTgts$ Creature | AILogic$ Pump +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | IsPresent$ Creature.EnchantedBy+Wolf,Creature.EnchantedBy+Werewolf | PresentCompare$ EQ1 | Execute$ TrigUntap | TriggerDescription$ When CARDNAME enters the battlefield, if enchanted creature is a Wolf or Werewolf, untap that creature. +SVar:TrigUntap:DB$ Untap | Defined$ Enchanted +S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddPower$ 2 | AddToughness$ 2 | AddKeyword$ Vigilance | Description$ Enchanted creature gets +2/+2 and has vigilance. +DeckHints:Type$Wolf|Werewolf +Oracle:Flash\nEnchant creature\nWhen Howl of the Hunt enters the battlefield, if enchanted creature is a Wolf or Werewolf, untap that creature.\nEnchanted creature gets +2/+2 and has vigilance. diff --git a/forge-gui/res/cardsfolder/upcoming/jadar_ghoulcaller_of_nephalia.txt b/forge-gui/res/cardsfolder/upcoming/jadar_ghoulcaller_of_nephalia.txt new file mode 100644 index 00000000000..e41309aa18a --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/jadar_ghoulcaller_of_nephalia.txt @@ -0,0 +1,9 @@ +Name:Jadar, Ghoulcaller of Nephalia +ManaCost:1 B +Types:Legendary Creature Human Wizard +PT:1/1 +T:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ You | TriggerZones$ Battlefield | IsPresent$ Creature.YouCtrl+withDecayed | PresentCompare$ EQ0 | Execute$ TrigToken | TriggerDescription$ At the beginning of your end step, if you control no creatures with decayed, create a 2/2 black Zombie creature token with decayed. (It can't block. When it attacks, sacrifice it at end of combat.) +SVar:TrigToken:DB$ Token | TokenScript$ b_2_2_zombie_decayed +DeckHas:Ability$Token +DeckHints:Type$Zombie +Oracle:At the beginning of your end step, if you control no creatures with decayed, create a 2/2 black Zombie creature token with decayed. (It can't block. When it attacks, sacrifice it at end of combat.) diff --git a/forge-gui/res/cardsfolder/upcoming/might_of_the_old_ways.txt b/forge-gui/res/cardsfolder/upcoming/might_of_the_old_ways.txt new file mode 100644 index 00000000000..cd17e0daa72 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/might_of_the_old_ways.txt @@ -0,0 +1,7 @@ +Name:Might of the Old Ways +ManaCost:1 G +Types:Instant +A:SP$ Pump | ValidTgts$ Creature | TgtPrompt$ Select target creature | NumAtt$ 2 | NumDef$ 2 | SubAbility$ DBDraw | SpellDescription$ Target creature gets +2/+2 until end of turn. +SVar:DBDraw:DB$ Draw | Defined$ You | NumCards$ 1 | ConditionCheckSVar$ X | ConditionSVarCompare$ GE3 | StackDescription$ SpellDescription | SpellDescription$ Coven — Then if you control three or more creatures with different powers, draw a card. +SVar:X:Count$DifferentPower_Creature.YouCtrl +Oracle:Target creature gets +2/+2 until end of turn.\nCoven — Then if you control three or more creatures with different powers, draw a card. diff --git a/forge-gui/res/cardsfolder/upcoming/overgrown_farmland.txt b/forge-gui/res/cardsfolder/upcoming/overgrown_farmland.txt new file mode 100644 index 00000000000..b217c1121c4 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/overgrown_farmland.txt @@ -0,0 +1,9 @@ +Name:Overgrown Farmland +ManaCost:no cost +Types:Land +A:AB$ Mana | Cost$ T | Produced$ G | SpellDescription$ Add {G}. +A:AB$ Mana | Cost$ T | Produced$ W | SpellDescription$ Add {W}. +K:ETBReplacement:Other:LandTapped +SVar:LandTapped:DB$ Tap | Defined$ Self | ETB$ True | ConditionCheckSVar$ ETBCheckSVar2 | ConditionSVarCompare$ LT2 | SpellDescription$ CARDNAME enters the battlefield tapped unless you control two or more other lands. +SVar:ETBCheckSVar2:Count$LastStateBattlefield Land.YouCtrl +Oracle:Overgrown Farmland enters the battlefield tapped unless you control two or more other lands.\n{T}: Add {G} or {W}. diff --git a/forge-gui/res/cardsfolder/upcoming/pestilent_wolf.txt b/forge-gui/res/cardsfolder/upcoming/pestilent_wolf.txt new file mode 100644 index 00000000000..2a6aca9d2ce --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/pestilent_wolf.txt @@ -0,0 +1,6 @@ +Name:Pestilent Wolf +ManaCost:1 G +Types:Creature Wolf +PT:2/2 +A:AB$ Pump | Cost$ 2 G | Defined$ Self | KW$ Deathtouch | SpellDescription$ CARDNAME gains deathtouch until end of turn. +Oracle:{2}{G}: Pestilent Wolf gains deathtouch until end of turn. diff --git a/forge-gui/res/cardsfolder/upcoming/poppet_stitcher_poppet_factory.txt b/forge-gui/res/cardsfolder/upcoming/poppet_stitcher_poppet_factory.txt new file mode 100644 index 00000000000..e4083276b4d --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/poppet_stitcher_poppet_factory.txt @@ -0,0 +1,23 @@ +Name:Poppet Stitcher +ManaCost:2 U +Types:Creature Human Wizard +PT:2/3 +T:Mode$ SpellCast | ValidCard$ Instant,Sorcery | ValidActivatingPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigToken | TriggerDescription$ Whenever you cast an instant or sorcery spell, create a 2/2 black Zombie creature token with decayed. (It can’t block. When it attacks, sacrifice it at end of combat.) +SVar:TrigToken:DB$ Token | TokenScript$ b_2_2_zombie_decayed | TokenOwner$ You +T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | IsPresent$ Creature.token+YouCtrl | PresentCompare$ GE3 | TriggerZones$ Battlefield | Execute$ TrigTransform | OptionalDecider$ You | TriggerDescription$ At the beginning of your upkeep, if you control three or more creature tokens, you may transform CARDNAME. +SVar:TrigTransform:DB$ SetState | Defined$ Self | Mode$ Transform +AlternateMode:DoubleFaced +DeckNeeds:Type$Instant|Sorcery +DeckHas:Ability$Token +Oracle:Whenever you cast an instant or sorcery spell, create a 2/2 black Zombie creature token with decayed. (It can’t block. When it attacks, sacrifice it at end of combat.)\nAt the beginning of your upkeep, if you control three or more creature tokens, you may transform Poppet Sticher. + +ALTERNATE + +Name:Poppet Factory +ManaCost:no cost +Types:Artifact +Colors:blue +S:Mode$ Continuous | Affected$ Creature.token+YouCtrl | SetPower$ 3 | SetToughness$ 3 | RemoveAllAbilities$ True | Description$ Creature tokens you control lose all abilities and have base power and toughness 3/3. +T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigTransform | OptionalDecider$ You | TriggerDescription$ At the beginning of your upkeep, you may transform CARDNAME. +SVar:TrigTransform:DB$ SetState | Defined$ Self | Mode$ Transform +Oracle:Creature tokens you control lose all abilities and have base power and toughness 3/3.\nAt the beginning of your upkeep, you may transform Poppet Factory. diff --git a/forge-gui/res/cardsfolder/upcoming/rockfall_vale.txt b/forge-gui/res/cardsfolder/upcoming/rockfall_vale.txt new file mode 100644 index 00000000000..782d12362b0 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/rockfall_vale.txt @@ -0,0 +1,9 @@ +Name:Rockfall Vale +ManaCost:no cost +Types:Land +A:AB$ Mana | Cost$ T | Produced$ R | SpellDescription$ Add {R}. +A:AB$ Mana | Cost$ T | Produced$ G | SpellDescription$ Add {G}. +K:ETBReplacement:Other:LandTapped +SVar:LandTapped:DB$ Tap | Defined$ Self | ETB$ True | ConditionCheckSVar$ ETBCheckSVar2 | ConditionSVarCompare$ LT2 | SpellDescription$ CARDNAME enters the battlefield tapped unless you control two or more other lands. +SVar:ETBCheckSVar2:Count$LastStateBattlefield Land.YouCtrl +Oracle:Rockfall Vale enters the battlefield tapped unless you control two or more other lands.\n{T}: Add {R} or {G}. diff --git a/forge-gui/res/cardsfolder/upcoming/saryth_the_vipers_fang.txt b/forge-gui/res/cardsfolder/upcoming/saryth_the_vipers_fang.txt new file mode 100644 index 00000000000..c45a1c34181 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/saryth_the_vipers_fang.txt @@ -0,0 +1,8 @@ +Name:Saryth, the Viper's Fang +ManaCost:2 G G +Types:Legendary Creature Human Warlock +PT:3/4 +S:Mode$ Continuous | Affected$ Creature.tapped+YouCtrl+Other | AddKeyword$ Deathtouch | Description$ Other tapped creatures you control have deathtouch. +S:Mode$ Continuous | Affected$ Creature.untapped+YouCtrl+Other | AddKeyword$ Hexproof | Description$ Other untapped creatures you control have hexproof. +A:AB$ Untap | Cost$ 1 T | ValidTgts$ Creature.Other+YouCtrl,Land.Other+YouCtrl | TgtPrompt$ Select another target creature or land you control | SpellDescription$ Untap another target creature or land you control. +Oracle:Other tapped creatures you control have deathtouch.\nOther untapped creatures you control have hexproof.\n{1}, {T}: Untap another target creature or land you control. diff --git a/forge-gui/res/cardsfolder/upcoming/secrets_of_the_key.txt b/forge-gui/res/cardsfolder/upcoming/secrets_of_the_key.txt new file mode 100644 index 00000000000..40e26432924 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/secrets_of_the_key.txt @@ -0,0 +1,8 @@ +Name:Secrets of the Key +ManaCost:U +Types:Instant +K:Flashback:3 U +A:SP$ Investigate | Cost$ U | Num$ X | SpellDescription$ Investigate. If this spell was cast from a graveyard, investigate twice instead. (To investigate, create a colorless Clue artifact token with "{2}, Sacrifice this artifact: Draw a card.") +SVar:X:Count$wasCastFromGraveyard.2.1 +DeckHas:Ability$Investigate & Ability$Token +Oracle:Investigate. If this spell was cast from a graveyard, investigate twice instead. (To investigate, create a colorless Clue artifact token with "{2}, Sacrifice this artifact: Draw a card.")\nFlashback {3}{U} diff --git a/forge-gui/res/cardsfolder/upcoming/shipwreck_marsh.txt b/forge-gui/res/cardsfolder/upcoming/shipwreck_marsh.txt new file mode 100644 index 00000000000..d9753fd51f7 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/shipwreck_marsh.txt @@ -0,0 +1,9 @@ +Name:Shipwreck Marsh +ManaCost:no cost +Types:Land +A:AB$ Mana | Cost$ T | Produced$ U | SpellDescription$ Add {U}. +A:AB$ Mana | Cost$ T | Produced$ B | SpellDescription$ Add {B}. +K:ETBReplacement:Other:LandTapped +SVar:LandTapped:DB$ Tap | Defined$ Self | ETB$ True | ConditionCheckSVar$ ETBCheckSVar2 | ConditionSVarCompare$ LT2 | SpellDescription$ CARDNAME enters the battlefield tapped unless you control two or more other lands. +SVar:ETBCheckSVar2:Count$LastStateBattlefield Land.YouCtrl +Oracle:Shipwreck Marsh enters the battlefield tapped unless you control two or more other lands.\n{T}: Add {U} or {B}. diff --git a/forge-gui/res/cardsfolder/upcoming/sigarda_champion_of_light.txt b/forge-gui/res/cardsfolder/upcoming/sigarda_champion_of_light.txt new file mode 100644 index 00000000000..a5e30299231 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/sigarda_champion_of_light.txt @@ -0,0 +1,12 @@ +Name:Sigarda, Champion of Light +ManaCost:1 G W W +Types:Legendary Creature Angel +PT:4/4 +K:Flying +K:Trample +S:Mode$ Continuous | Affected$ Creature.Human+YouCtrl | AddPower$ 1 | AddToughness$ 1 | Description$ Humans you control get +1/+1. +T:Mode$ Attacks | ValidCard$ Card.Self | CheckSVar$ X | SVarCompare$ GE3 | Execute$ TrigDig | TriggerDescription$ Coven - Whenever Sigarda attacks, if you control three or more creatures with different powers, look at the top five cards of your library. You may reveal a Human creature card from among them and put it into your hand. Put the rest on the bottom of your library in a random order. +SVar:TrigDig:DB$ Dig | DigNum$ 5 | ChangeNum$ 1 | Optional$ True | ChangeValid$ Creature.Human +SVar:X:Count$DifferentPower_Creature.YouCtrl +DeckNeeds:Type$Human +Oracle:Flying, trample\nHumans you control get +1/+1\nCoven - Whenever Sigarda attacks, if you control three or more creatures with different powers, look at the top five cards of your library. You may reveal a Human creature card from among them and put it into your hand. Put the rest on the bottom of your library in a random order. diff --git a/forge-gui/res/cardsfolder/upcoming/snarling_wolf.txt b/forge-gui/res/cardsfolder/upcoming/snarling_wolf.txt new file mode 100644 index 00000000000..d00029effba --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/snarling_wolf.txt @@ -0,0 +1,6 @@ +Name:Snarling Wolf +ManaCost:G +Types:Creature Wolf +PT:1/1 +A:AB$ Pump | Cost$ 1 G | Defined$ Self | NumAtt$ 2 | NumDef$ 2 | ActivationLimit$ 1 | SpellDescription$ CARDNAME gets +2/+2 until end of turn. Activate only once each turn. +Oracle:{1}{G}: Snarling Wolf gets +2/+2 until end of turn. Activate only once each turn. diff --git a/forge-gui/res/cardsfolder/upcoming/visions_of_dominance.txt b/forge-gui/res/cardsfolder/upcoming/visions_of_dominance.txt new file mode 100644 index 00000000000..a12c291705f --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/visions_of_dominance.txt @@ -0,0 +1,9 @@ +Name:Visions of Dominance +ManaCost:2 G +Types:Sorcery +A:SP$ PutCounter | ValidTgts$ Creature | TgtPrompt$ Select target creature | CounterType$ P1P1 | SubAbility$ DBPump | StackDescription$ Put a +1/+1 counter on {c:Targeted}, then double the number of +1/+1 counters on it. | SpellDescription$ Put a +1/+1 counter on target creature, then double the number of +1/+1 counters on it. +SVar:DBPump:DB$ MultiplyCounter | Defined$ Targeted | CounterType$ P1P1 | StackDescription$ None +K:Flashback:8 G G:ReduceCost$X:This spell costs {X} less to cast this way, where X is the greatest mana value of a commander you own on the battlefield or in the command zone. +SVar:X:Count$HighestCMC_Card.IsCommander+YouOwn+inZoneBattlefield,Card.IsCommander+YouOwn+inZoneCommand +DeckHas:Ability$Counters +Oracle:Put a +1/+1 counter on target creature, then double the number of +1/+1 counters on it.\nFlashback {8}{G}{G}. This spell costs {X} less to cast this way, where X is the greatest mana value of a commander you own on the battlefield or in the command zone. (You may cast this card from your graveyard for its flashback cost. Then exile it.) diff --git a/forge-gui/res/cardsfolder/upcoming/visions_of_dread.txt b/forge-gui/res/cardsfolder/upcoming/visions_of_dread.txt new file mode 100644 index 00000000000..9993a928797 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/visions_of_dread.txt @@ -0,0 +1,9 @@ +Name:Visions of Dread +ManaCost:2 B +Types:Sorcery +A:SP$ ChooseCard | ValidTgts$ Opponent | TgtPrompt$ Select target opponent | Choices$ Creature.TargetedPlayerOwn | ChoiceZone$ Graveyard | AILogic$ WorstCard | Amount$ 1 | SubAbility$ DBChangeZone | Mandatory$ True | StackDescription$ SpellDescription | SpellDescription$ Target opponent puts a creature card of their choice from their graveyard onto the battlefield under your control. +SVar:DBChangeZone:DB$ ChangeZone | Defined$ ChosenCard | Origin$ Graveyard | GainControl$ True | Destination$ Battlefield | StackDescription$ None +K:Flashback:8 B B:ReduceCost$X:This spell costs {X} less to cast this way, where X is the greatest mana value of a commander you own on the battlefield or in the command zone. +SVar:X:Count$HighestCMC_Card.IsCommander+YouOwn+inZoneBattlefield,Card.IsCommander+YouOwn+inZoneCommand +DeckHas:Ability$Graveyard +Oracle:Target opponent puts a creature card of their choice from their graveyard onto the battlefield under your control.\nFlashback {8}{B}{B}. This spell costs {X} less to cast this way, where X is the greatest mana value of a commander you own on the battlefield or in the command zone. (You may cast this card from your graveyard for its flashback cost. Then exile it.) diff --git a/forge-gui/res/cardsfolder/upcoming/visions_of_duplicity.txt b/forge-gui/res/cardsfolder/upcoming/visions_of_duplicity.txt new file mode 100644 index 00000000000..eed1d63f2c8 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/visions_of_duplicity.txt @@ -0,0 +1,9 @@ +Name:Visions of Duplicity +ManaCost:2 U +Types:Sorcery +A:SP$ ExchangeControl | ValidTgts$ Creature.YouDontCtrl | TgtPrompt$ Select target creature you don't control | TargetMin$ 2 | TargetMax$ 2 | TargetUnique$ True | StackDescription$ SpellDescription | SpellDescription$ Exchange control of two target creatures you don't control. +SVar:DBChangeZone:DB$ ChangeZone | Defined$ ChosenCard | Origin$ Graveyard | GainControl$ True | Destination$ Battlefield +K:Flashback:8 U U:ReduceCost$X:This spell costs {X} less to cast this way, where X is the greatest mana value of a commander you own on the battlefield or in the command zone. +SVar:X:Count$HighestCMC_Card.IsCommander+YouOwn+inZoneBattlefield,Card.IsCommander+YouOwn+inZoneCommand +AI:RemoveDeck:All +Oracle:Exchange control of two target creatures you don't control.\nFlashback {8}{U}{U}. This spell costs {X} less to cast this way, where X is the greatest mana value of a commander you own on the battlefield or in the command zone. (You may cast this card from your graveyard for its flashback cost. Then exile it.) diff --git a/forge-gui/res/cardsfolder/upcoming/visions_of_glory.txt b/forge-gui/res/cardsfolder/upcoming/visions_of_glory.txt new file mode 100644 index 00000000000..d40d6e1ef6c --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/visions_of_glory.txt @@ -0,0 +1,9 @@ +Name:Visions of Glory +ManaCost:4 W +Types:Sorcery +A:SP$ Token | TokenScript$ w_1_1_human | TokenAmount$ Y | SpellDescription$ Create a 1/1 white Human creature token for each creature you control. +SVar:Y:Count$TypeYouCtrl.Creature +K:Flashback:8 W W:ReduceCost$X:This spell costs {X} less to cast this way, where X is the greatest mana value of a commander you own on the battlefield or in the command zone. +SVar:X:Count$HighestCMC_Card.IsCommander+YouOwn+inZoneBattlefield,Card.IsCommander+YouOwn+inZoneCommand +DeckHas:Ability$Token +Oracle:Create a 1/1 white Human creature token for each creature you control.\nFlashback {8}{W}{W}. This spell costs {X} less to cast this way, where X is the greatest mana value of a commander you own on the battlefield or in the command zone. (You may cast this card from your graveyard for its flashback cost. Then exile it.) diff --git a/forge-gui/res/cardsfolder/upcoming/visions_of_ruin.txt b/forge-gui/res/cardsfolder/upcoming/visions_of_ruin.txt new file mode 100644 index 00000000000..180dbf4f43d --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/visions_of_ruin.txt @@ -0,0 +1,12 @@ +Name:Visions of Ruin +ManaCost:3 R +Types:Sorcery +A:SP$ Sacrifice | Defined$ Player.Opponent | SacValid$ Artifact | SacMessage$ artifact | SubAbility$ DBToken | RememberSacrificed$ True | StackDescription$ SpellDescription | SpellDescription$ Each opponent sacrifices an artifact. +SVar:DBToken:DB$ Token | TokenAmount$ Y | TokenScript$ c_a_treasure_sac | SubAbility$ DBCleanup | SpellDescription$ For each artifact sacrificed this way, you create a Treasure token. +SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True +SVar:Y:Count$RememberedSize +K:Flashback:8 R R:ReduceCost$X:This spell costs {X} less to cast this way, where X is the greatest mana value of a commander you own on the battlefield or in the command zone. +SVar:X:Count$HighestCMC_Card.IsCommander+YouOwn+inZoneBattlefield,Card.IsCommander+YouOwn+inZoneCommand +DeckHas:Ability$Token & Ability$Sacrifice +AI:RemoveDeck:Random +Oracle:Each opponent sacrifices an artifact. For each artifact sacrificed this way, you create a Treasure token.\nFlashback {8}{R}{R}. This spell costs {X} less to cast this way, where X is the greatest mana value of a commander you own on the battlefield or in the command zone. (You may cast this card from your graveyard for its flashback cost. Then exile it.) diff --git a/forge-gui/res/cardsfolder/upcoming/wilhelt_the_rotcleaver.txt b/forge-gui/res/cardsfolder/upcoming/wilhelt_the_rotcleaver.txt new file mode 100644 index 00000000000..b6a22e56d08 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/wilhelt_the_rotcleaver.txt @@ -0,0 +1,11 @@ +Name:Wilhelt, the Rotcleaver +ManaCost:2 U B +Types:Legendary Creature Zombie Warrior +PT:3/3 +T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Zombie.Other+YouCtrl+withoutDecayed | TriggerZones$ Battlefield | Execute$ TrigToken | TriggerDescription$ Whenever another Zombie you control dies, if it didn't have decayed, create a 2/2 black Zombie creature token with decayed. (It can't block. When it attacks, sacrifice it at end of combat.) +SVar:TrigToken:DB$ Token | TokenScript$ b_2_2_zombie_decayed +T:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigDraw | TriggerDescription$ At the beginning of your end step, you may sacrifice a Zombie. If you do, draw a card. +SVar:TrigDraw:AB$ Draw | Cost$ Sac<1/Zombie> | NumCards$ 1 +DeckNeeds:Type$Zombie +DeckHas:Ability$Token & Ability$Sacrifice +Oracle:Whenever another Zombie you control dies, if it didn't have decayed, create a 2/2 black Zombie creature token with decayed. (It can't block. When it attacks, sacrifice it at end of combat.)\nAt the beginning of your end step, you may sacrifice a Zombie. If you do, draw a card. diff --git a/forge-gui/res/cardsfolder/upcoming/vengeful_ancestor.txt b/forge-gui/res/cardsfolder/v/vengeful_ancestor.txt similarity index 100% rename from forge-gui/res/cardsfolder/upcoming/vengeful_ancestor.txt rename to forge-gui/res/cardsfolder/v/vengeful_ancestor.txt diff --git a/forge-gui/res/cardsfolder/upcoming/veteran_charger.txt b/forge-gui/res/cardsfolder/v/veteran_charger.txt similarity index 100% rename from forge-gui/res/cardsfolder/upcoming/veteran_charger.txt rename to forge-gui/res/cardsfolder/v/veteran_charger.txt diff --git a/forge-gui/res/cardsfolder/upcoming/wand_of_orcus.txt b/forge-gui/res/cardsfolder/w/wand_of_orcus.txt similarity index 100% rename from forge-gui/res/cardsfolder/upcoming/wand_of_orcus.txt rename to forge-gui/res/cardsfolder/w/wand_of_orcus.txt diff --git a/forge-gui/res/cardsfolder/upcoming/winged_boots.txt b/forge-gui/res/cardsfolder/w/winged_boots.txt similarity index 100% rename from forge-gui/res/cardsfolder/upcoming/winged_boots.txt rename to forge-gui/res/cardsfolder/w/winged_boots.txt diff --git a/forge-gui/res/cardsfolder/upcoming/wingsteed_trainer.txt b/forge-gui/res/cardsfolder/w/wingsteed_trainer.txt similarity index 100% rename from forge-gui/res/cardsfolder/upcoming/wingsteed_trainer.txt rename to forge-gui/res/cardsfolder/w/wingsteed_trainer.txt diff --git a/forge-gui/res/editions/Innistrad Midnight Hunt Commander.txt b/forge-gui/res/editions/Innistrad Midnight Hunt Commander.txt new file mode 100644 index 00000000000..1fe00e32f80 --- /dev/null +++ b/forge-gui/res/editions/Innistrad Midnight Hunt Commander.txt @@ -0,0 +1,18 @@ +[metadata] +Code=MIC +Date=2021-09-24 +Name=Innistrad: Midnight Hunt Commander +Type=Commander +ScryfallCode=MIC + +[cards] +1 M Leinore, Autumn Sovereign @Fariba Khamseh +2 R Wilhelt, the Rotcleaver @Chris Rallis +31 M Avacyn's Memorial @Kasia 'Kafis' Zielińska +32 R Visions of Glory @Alexander Mokhov +33 R Visions of Duplicity @Alexander Mokhov +34 R Visions of Dread @Andrew Mar +35 R Curse of Obsession @Irvin Rodriguez +36 R Visions of Ruin @Andrew Mar +37 R Visions of Dominance @Andrew Mar +40 R Wilhelt, the Rotcleaver @Chris Rallis diff --git a/forge-gui/res/editions/Innistrad Midnight Hunt.txt b/forge-gui/res/editions/Innistrad Midnight Hunt.txt index c04b68ef900..c9caba9f062 100644 --- a/forge-gui/res/editions/Innistrad Midnight Hunt.txt +++ b/forge-gui/res/editions/Innistrad Midnight Hunt.txt @@ -10,13 +10,50 @@ Booster=10 Common, 3 Uncommon, 1 RareMythic, 1 BasicLand Prerelease=6 Boosters, 1 RareMythic+ [cards] +3 U Beloved Beggar @Francisco Miyara +7 R Brutal Cathar @Karl Kopinski +9 C Candletrap @Manuel Castañón +17 M Enduring Angel @Irina Nordsol +40 C Unruly Mob @Ryan Pancoast +42 C Baithook Angler @Uriah Voth 44 C Consider @Zezhou Chen +65 U Ominous Roost @Josu Hernaiz +73 C Secrets of the Key @Alix Branwyn +79 C Stormrider Spirit @Lake Hurwitz 81 R Triskaidekaphile @Slawomir Maniak +84 C Arrogant Outlaw @Aurore Folny 91 R Champion of the Perished @Kekai Kotaki +95 C Defenestrate @Darek Zabrocki +101 U Foul Play @Campbell White 107 U Infernal Grasp @Naomi Baker +108 R Jadar, Ghoulcaller of Nephalia @Yongjae Choi +130 C Brimstone Vandal @Andreas Zafiratos +134 R Curse of Shaken Faith +138 C Famished Foragers @Lorenzo Mastroianni +140 C Festival Crasher @Milivoj Ćeran +142 R Giestflame Reservoir @Anna Christenson +146 R Light Up the Night @Wei Wei 154 U Play with Fire @Svetlin Velinov +163 C Tavern Ruffian @Zoltan Boros +164 C Thermo-Alchemist @Raymond Swanland +165 U Village Watch @Nestor Ossandon Leal +175 C Candlelit Cavalry @Viko Menezes +180 C Dawnhart Rejuvenator @Darren Tan +188 C Howl of the Hunt @Josu Hernaiz +189 C Might of the Old Ways @Zezhou Chen +192 C Pestilent Wolf @Oriana Menendez +199 C Snarling Wolf @Ilse Gort 208 M Wrenn and Seven @Heonhwa Choe +211 M Arlinn, the Pack's Hope @Anna Steinbauer +224 R Galvanic Iteration @Johann Bodin 229 U Join the Dance @Raoul Vitale +240 M Sigarda, Champion of Light @Howard Lyon +246 R Tovolar, Dire Overlord @Chris Rahn +260 R Deserted Beach @Jonas De Ro +263 R Haunted Ridge @Jonas De Ro +265 R Overgrown Farmland @Jonas De Ro +266 R Rockfall Vale @Muhammad Firdaus +267 R Shipwreck Marsh @Jonas De Ro 268 L Plains @Alayna Danner 269 L Plains @Dan Mumford 270 L Island @Evan Cagle @@ -28,6 +65,24 @@ Prerelease=6 Boosters, 1 RareMythic+ 276 L Forest @Alayna Danner 277 L Forest @Dan Mumford 278 M Wrenn and Seven @Bram Sels +279 M Arlinn, the Pack's Hope @Eric Deschamps +281 R Deserted Beach @Piotr Dura +282 R Haunted Ridge @Piotr Dura +283 R Overgrown Farmland @Donato Giancola +284 R Rockfall Vale @Piotr Dura +285 R Shipwreck Marsh @Steven Belledin +296 C Tavern Ruffian @Michael Walsh +297 U Village Watch @Andrea De Dominicis +304 R Saryth, the Viper's Fang +307 M Arlinn, the Pack's Hope @Emma Rios +311 R Tovolar, Dire Overlord @Michael Walsh +315 R Jadar, Ghoulcaller of Nephalia @DZO +323 M Sigarda, Champion of Light @N.C. Winters +355 R Giestflame Reservoir @Anna Christenson +356 R Light Up the Night @Wei Wei +368 R Can't Stay Away @Milivoj Ćeran +371 R Galvanic Iteration @Johann Bodin +374 R Rite of Harmony 380 L Plains @Andreas Rocha 381 L Island @Andreas Rocha 382 L Swamp @Kasia 'Kafis' Zielińska diff --git a/forge-gui/res/editions/Judge Gift Cards 2021.txt b/forge-gui/res/editions/Judge Gift Cards 2021.txt index 4431aa0944b..7b25b619221 100644 --- a/forge-gui/res/editions/Judge Gift Cards 2021.txt +++ b/forge-gui/res/editions/Judge Gift Cards 2021.txt @@ -7,8 +7,12 @@ ScryfallCode=PJ21 [cards] 1 M Morophon, the Boundless @Svetlin Velinov +2 M K'rrik, Son of Yawgmoth @Daniel Ljunggren +3 M Edgar Markov @Martina Fackova +4 M Ezuri, Claw of Progress @Livia Prima 5 M The Gitrog Monster @Nils Hamm 6 R Grand Arbiter Augustin IV @Matt Stewart 7 M Karlov of the Ghost Council @Wisnu Tan +8 M Mizzix of the Izmagnus @Zoltan Boros 9 M Nicol Bolas, the Ravager @Lie Setiawan 10 M Zacama, Primal Calamity @Yigit Koroglu diff --git a/forge-gui/res/languages/es-ES.properties b/forge-gui/res/languages/es-ES.properties index 0faf914d5b6..0a180301925 100644 --- a/forge-gui/res/languages/es-ES.properties +++ b/forge-gui/res/languages/es-ES.properties @@ -120,15 +120,15 @@ cbpCounterDisplayLocation=Ubicación del contador cbpGraveyardOrdering=Permitir ordenar cartas puestas en el cementerio lblAltLifeDisplay=Diseño alternativo de jugador (Modo apaisado) nlAltLifeDisplay=Permite un diseño alternativo para mostrar los contadores de vida, veneno, energía y experiencia. -lblAltZoneTabs=Alternate Player Zone Layout (Landscape Mode) -nlAltZoneTabs=Enables alternate layout for displaying Player Hand, Graveyard, Library and Exile zones. -lblPreferredArt=Preferencia de arte de tarjeta -nlPreferredArt=Especifica qué arte debe seleccionarse para las tarjetas cuando no se especifica ninguna edición. -lblPrefArtExpansionOnly=Prefiera el arte de la tarjeta de los conjuntos básicos, de expansión y de reimpresión +lblAltZoneTabs=Diseño alternativo de la zona del jugador (Modo apaisado) +nlAltZoneTabs=Permite un diseño alternativo para mostrar las zonas de la mano del jugador, el cementerio, la biblioteca y el exilio. +lblPreferredArt=Preferencia de arte de carta +nlPreferredArt=Especifica qué arte debe seleccionarse para las cartas cuando no se especifica ninguna edición. +lblPrefArtExpansionOnly=Prefiere el arte de la carta de los conjuntos básicos, de expansión y de reimpresión nlPrefArtExpansionOnly=Siempre que sea posible, prefiera las artes de las cartas recopiladas de los conjuntos básicos, de expansión y de reimpresión (por ejemplo, sin promociones, sin Online ediciones). -lblSmartCardArtOpt=Habilitar la selección inteligente para el arte de la tarjeta en los mazos -nlSmartCardArtOpt=Si está habilitado, Card Art in Decks se optimizará automáticamente para que coincida con marcos de tarjetas, ediciones y múltiples ilustraciones artísticas. -nlSmartCardArtOptNote=NOTA: Esta opción solo afectará a las tarjetas que no tengan una edición especificada. Las demás tarjetas se mantendrán sin cambios. (Advertencia: experimental) +lblSmartCardArtOpt=Habilitar la selección inteligente para el arte de la carta en los mazos +nlSmartCardArtOpt=Si está habilitado, Card Art in Decks se optimizará automáticamente para que coincida con marcos de cartas, ediciones y múltiples ilustraciones artísticas. +nlSmartCardArtOptNote=NOTA: Esta opción solo afectará a las cartas que no tengan una edición especificada. Las demás cartas se mantendrán sin cambios. (Advertencia: experimental) latestArtOpt=Ilustración más reciente originalArtOpt=Ilustración original Troubleshooting=Solución de problemas @@ -176,7 +176,7 @@ nlLoadCardsLazily=Si está activado, Forge cargará los scripts de las cartas se nlLoadHistoricFormats=Si está activado, Forge cargará todas los formatos históricos, esto puede demorar un poco más en cargarse en el inicio. GraphicOptions=Opciones gráficas nlDefaultFontSize=El tamaño de fuente predeterminado dentro de la interfaz de usuario. Todos los elementos de fuente se escalan en relación a esto. (Necesita reinicio) -nlCardArtFormat=The format of card art images. (Full: image of entire card. Crop: only the art part) +nlCardArtFormat=El formato de las imágenes del arte de la carta. (Full: imagen de toda la carta. Crop: sólo la parte del arte) cbpMulliganRule=Regla de mulligan nlImageFetcher=Permite la descarga al vuelo de las imágenes de cartas que falten. nlDisplayFoil=Mostrar las cartas foil con un capa sobre la carta que da efecto foil @@ -187,10 +187,10 @@ nlLargeCardViewers=Hace que todos los visores de cartas en el programa sean much nlSmallDeckViewer=Establece la ventana del visor de mazo para que sea de 800x600 en lugar de una proporción del tamaño de la pantalla. nlRandomArtInPools=Genera cartas con arte al azar en pools generados de cartas de modo limitado. nlUiForTouchScreen=Aumenta algunos elementos de la interfaz de usuario para proporcionar una mejor experiencia en dispositivos de pantalla táctil. (Necesita reinicio) -nlCompactPrompt=Oculte el encabezado y use una fuente más pequeña en el panel de solicitud para hacerlo más compacto. +nlCompactPrompt=Oculta el encabezado y usa una fuente más pequeña en el panel de solicitud para hacerlo más compacto. nlHideReminderText=Ocultar el texto del recordatorio en el panel de Detalle de la carta -nlCardTextUseSansSerif=Render card images by using Sans-serif font for card text. (Requires restart) -nlCardTextHideReminder=When render card images, skip rendering reminder text. +nlCardTextUseSansSerif=Renderiza las imágenes de las cartas utilizando la fuente Sans-serif para el texto de las cartas. (Requiere reiniciar) +nlCardTextHideReminder=Cuando se renderizan las imágenes de las cartas, se omite la renderización del texto de recordatorio. nlOpenPacksIndiv=Al abrir packs de cartas (Fat Packs) y cajas de sobres, los sobres se abrirán y se mostrarán de uno en uno. nlTokensInSeparateRow=Muestra las fichas en una fila separada en el campo de batalla debajo de las criaturas que no son fichas. nlStackCreatures=Apila criaturas idénticas en el campo de batalla, como tierras, artefactos y encantamientos. @@ -208,8 +208,8 @@ nlSrOptimize=Establecer varias opciones para que Forge funcione mejor con los le KeyboardShortcuts=Atajos de teclado cbpLandPlayed=Notificación al entrar una tierra en juego nlpLandPlayed=Elige cuando quieras recibir notificaciones visuales de una tierra que entra en el campo de batalla: Nunca, siempre o sólo para las tierras que entran en el campo de batalla por una acción de un jugador IA. -cbpSwitchStates=Switch card states -nlSwitchStates=Display alternate state for every second copy in deck viewers. +cbpSwitchStates=Alterna el estado de cartas +nlSwitchStates=Mostrar el estado alternativo de cada segunda copia en los visores de mazos. #VSubmenuAchievements.java lblAchievements=Trofeos #VSubmenuDownloaders.java @@ -304,7 +304,7 @@ lblGetaNewRandomName=Obtener un nuevo nombre al azar lblArchenemy=Archienemigo lblHeroes=Heroes lblRemove=Quitar -ttlblAvatar=L-click: Seleccionar avatar. R-clic: aleatorizar avatar. +ttlblAvatar=Click izdo: Seleccionar avatar. Click dcho: aleatorizar avatar. lblReady=Listo lblKick=Quitar lblReallyKick=¿Quitar a %s? @@ -368,21 +368,21 @@ lblReset=Reset lblAuto=Auto #VAssignDamage.java lbLAssignDamageDealtBy=Asignar daño hecho por %s -lblLClickDamageMessage=Clic izquierdo: Asigna 1 daño. (Clic izquierdo + Control): Asigna el daño restante a letal -lblRClickDamageMessage=Clic derecho: Desasignar 1 daño. (Clic derecho + Control): Desasignar todo el daño. +lblLClickDamageMessage=Clic izdo: Asigna 1 daño. (Clic izdo + Control): Asigna el daño restante a letal +lblRClickDamageMessage=Clic dcho: Desasignar 1 daño. (Clic dcho + Control): Desasignar todo el daño. lblTotalDamageText=Puntos de daño disponibles: Desconocido lblAssignRemainingText=Distribuye los puntos de daño restantes entre las entidades letalmente heridas. lblLethal=Letal lblAvailableDamagePoints=Puntos de daño disponibles #VAssignGenericAmount.java # The {0} below should be amount label (like "shield" or "damage"), and {1} will be the name of the effect source -lbLAssignAmountForEffect=Assign {0} by {1} -lblLClickAmountMessage=Left click: Assign 1 {0}. (Left Click + Control): Assign maximum {0} points. -lblRClickAmountMessage=Right click: Unassign 1 {0}. (Right Click + Control): Unassign all {0} points. -lblTotalAmountText=Available {0} points: Unknown -lblAvailableAmount=Available {0} points -lblMax=Max -lblShield=shield +lbLAssignAmountForEffect=Asignado {0} por {1} +lblLClickAmountMessage=Click izdo: Asigna 1 {0}. (Click izdo + Control): Asigna máximo {0} puntos. +lblRClickAmountMessage=Click dcho: Desasigna 1 {0}. (Click dcho + Control): Desasigna todos los {0} puntos. +lblTotalAmountText={0} puntos disponibles: Desconocido +lblAvailableAmount={0} puntos disponibles +lblMax=Máx +lblShield=escudo #KeyboardShortcuts.java lblSHORTCUT_SHOWSTACK=Partida: mostrar panel de pila lblSHORTCUT_SHOWCOMBAT=Partida: mostrar panel de combate @@ -1050,7 +1050,7 @@ nlEnableNonLegalCards=Habilita cartas no legales como Un-Sets y cartas para test lblAllowCustomCardsInDecks=Habilitar cartas personalizadas nlAllowCustomCardsInDecks=Habilita el uso de cartas personalizadas en mazos para jugar. (Requiere reinicio) lblDisableCardImages=Desactivar imágenes de cartas -nlDisableCardImages=Cuando está habilitado, Forge no mostrará imágenes de tarjetas. +nlDisableCardImages=Cuando está habilitado, Forge no mostrará imágenes de cartas. lblExperimentalNetworkCompatibility=Compatibilidad de red experimental nlExperimentalNetworkCompatibility=Forge cambia a un flujo de red compatible. (Si no estás seguro, deshabilita esta opción) lblDisposeTextures=Desechar texturas @@ -1776,8 +1776,8 @@ lblTopBidWithValueLife=puja más alta con {0} de vida #BondEffect.java lblSelectACardPair=Selecciona una carta para emparejarla con #CamouflageEffect.java -lblChooseBlockerForAttacker=Choose a creature to block {0} -lblChooseBlockersForPile=Choose creatures to put in pile {0} (can be empty) +lblChooseBlockerForAttacker=Elige una criatura para bloquear a {0} +lblChooseBlockersForPile=Elige criaturas para poner en la pila {0} (puede estar vacía) #ChangeCombatantsEffect.java lblChooseDefenderToAttackWithCard=Elige con qué defensor atacar con {0} #ChangeTargetsEffect.java @@ -2004,7 +2004,7 @@ lblRepeatAddCard=Repetir añadir última carta lblRemoveFromGame=Quitar carta del juego lblRiggedRoll=Rollo Plano montado lblWalkTo=Planeswalk a -lblAskAI=Ask AI for suggestion +lblAskAI=Pide sugerencia a la IA #PhaseType.java lblUntapStep=Enderezar lblUpkeepStep=Mantenimiento @@ -2697,4 +2697,4 @@ lblWildOpponentNumberError=Los oponentes salvajes sólo pueden ser de 0 a 3 #GauntletWinLose.java lblGauntletProgress=Progreso del desafío #SpellAbility.java -lblInvalidTargetSpecification=Not all target requirements are met. +lblInvalidTargetSpecification=No se cumplen todos los requisitos de objetivos. diff --git a/forge-gui/res/lists/classicModuleCardtoCrop.txt b/forge-gui/res/lists/classicModuleCardtoCrop.txt new file mode 100644 index 00000000000..b8f95f92c77 --- /dev/null +++ b/forge-gui/res/lists/classicModuleCardtoCrop.txt @@ -0,0 +1,9 @@ +Treasure Vault|AFR|2 +Temple of the Dragon Queen|AFR|2 +Lair of the Hydra|AFR|2 +Hive of the Eye Tyrant|AFR|2 +Hall of Storm Giants|AFR|2 +Evolving Wilds|AFR|2 +Dungeon Descent|AFR|2 +Den of the Bugbear|AFR|2 +Cave of the Frost Dragon|AFR|2 \ No newline at end of file diff --git a/forge-gui/res/quest/commanderprecons/Built from Scratch (2018).dck b/forge-gui/res/quest/commanderprecons/Built from Scratch (2018).dck index 1005f239486..c9178a3bebd 100644 --- a/forge-gui/res/quest/commanderprecons/Built from Scratch (2018).dck +++ b/forge-gui/res/quest/commanderprecons/Built from Scratch (2018).dck @@ -32,7 +32,7 @@ Name=Built from Scratch (2018) 1 Great Furnace|CM2 1 Hoard-Smelter Dragon|CM2 1 Ichor Wellspring|CM2 -#1 Impact Resonance|CM2 +1 Impact Resonance|CM2 1 Incite Rebellion|CM2 1 Ingot Chewer|CM2 1 Jalum Tome|CM2 diff --git a/forge-gui/res/quest/commanderprecons/Built from Scratch.dck b/forge-gui/res/quest/commanderprecons/Built from Scratch.dck index f247e6da845..455cc8b5ced 100644 --- a/forge-gui/res/quest/commanderprecons/Built from Scratch.dck +++ b/forge-gui/res/quest/commanderprecons/Built from Scratch.dck @@ -32,7 +32,7 @@ Name=Built from Scratch 1 Great Furnace|C14 1 Hoard-Smelter Dragon|C14 1 Ichor Wellspring|C14 -#1 Impact Resonance|C14 +1 Impact Resonance|C14 1 Incite Rebellion|C14 1 Ingot Chewer|C14 1 Jalum Tome|C14 diff --git a/forge-gui/res/quest/commanderprecons/Entropic Uprising.dck b/forge-gui/res/quest/commanderprecons/Entropic Uprising.dck index 1aa1d82891b..daa28afdf00 100644 --- a/forge-gui/res/quest/commanderprecons/Entropic Uprising.dck +++ b/forge-gui/res/quest/commanderprecons/Entropic Uprising.dck @@ -83,6 +83,6 @@ Name=Entropic Uprising 1 Curtains' Call|C16 1 Parting Thoughts|C16 1 Grave Upheaval|C16 -#1 Cruel Entertainment|C16 +1 Cruel Entertainment|C16 1 Treacherous Terrain|C16 1 Ash Barrens|C16 diff --git a/forge-gui/res/quest/commanderprecons/Feline Ferocity.dck b/forge-gui/res/quest/commanderprecons/Feline Ferocity.dck index 55b220d0d43..ede58f094b2 100644 --- a/forge-gui/res/quest/commanderprecons/Feline Ferocity.dck +++ b/forge-gui/res/quest/commanderprecons/Feline Ferocity.dck @@ -77,7 +77,7 @@ Name=Feline Ferocity 1 Soul's Majesty|C17 1 Spirit of the Hearth|C17 1 Staff of Nin|C17 -#1 Stalking Leonin|C17 +1 Stalking Leonin|C17 1 Stirring Wildwood|C17 1 Sunspear Shikari|C17 1 Swiftfoot Boots|C17 diff --git a/forge-gui/res/skins/default/overlay_alpha.png b/forge-gui/res/skins/default/overlay_alpha.png new file mode 100644 index 00000000000..d22f00e91a2 Binary files /dev/null and b/forge-gui/res/skins/default/overlay_alpha.png differ diff --git a/forge-gui/res/skins/default/sprite_cardbg.png b/forge-gui/res/skins/default/sprite_cardbg.png new file mode 100644 index 00000000000..344a531202b Binary files /dev/null and b/forge-gui/res/skins/default/sprite_cardbg.png differ diff --git a/forge-gui/res/tokenscripts/b_2_2_zombie_decayed.txt b/forge-gui/res/tokenscripts/b_2_2_zombie_decayed.txt new file mode 100644 index 00000000000..095b3b04cf1 --- /dev/null +++ b/forge-gui/res/tokenscripts/b_2_2_zombie_decayed.txt @@ -0,0 +1,7 @@ +Name:Zombie +ManaCost:no cost +Types:Creature Zombie +Colors:black +PT:2/2 +K:Decayed +Oracle:Decayed diff --git a/forge-gui/src/main/java/forge/deck/DeckProxy.java b/forge-gui/src/main/java/forge/deck/DeckProxy.java index 1892e7b7566..fd06cc92019 100644 --- a/forge-gui/src/main/java/forge/deck/DeckProxy.java +++ b/forge-gui/src/main/java/forge/deck/DeckProxy.java @@ -256,7 +256,8 @@ public class DeckProxy implements InventoryItem { for (final Entry pc : getDeck().getAllCardsInASinglePool()) { if (pc.getKey().getRules().getManaCost() != null) { - if (pc.getKey().getRules().getSplitType() != CardSplitType.Split) + if (pc.getKey().getRules().getType().hasSubtype("Saga") || pc.getKey().getRules().getType().hasSubtype("Class") || CardSplitType.Split.equals(pc.getKey().getRules().getSplitType())) + continue; keyCMC.put(pc.getKey(),pc.getKey().getRules().getManaCost().getCMC()); } } diff --git a/forge-gui/src/main/java/forge/localinstance/properties/ForgeConstants.java b/forge-gui/src/main/java/forge/localinstance/properties/ForgeConstants.java index e1ea5dce308..313d27841b5 100644 --- a/forge-gui/src/main/java/forge/localinstance/properties/ForgeConstants.java +++ b/forge-gui/src/main/java/forge/localinstance/properties/ForgeConstants.java @@ -52,6 +52,7 @@ public final class ForgeConstants { public static final String NET_DECKS_OATHBREAKER_LIST_FILE = LISTS_DIR + "net-decks-oathbreaker.txt"; public static final String NET_DECKS_TINYLEADERS_LIST_FILE = LISTS_DIR + "net-decks-tinyleaders.txt"; public static final String BORDERLESS_CARD_LIST_FILE = LISTS_DIR + "borderlessCardList.txt"; + public static final String CLASSIC_MODULE_CARD_TO_CROP_FILE = LISTS_DIR + "classicModuleCardtoCrop.txt"; public static final String SKINS_LIST_FILE = LISTS_DIR + "skinsList.txt"; public static final String CJK_FONTS_LIST_FILE = LISTS_DIR + "font-list.txt"; public static final String NET_ARCHIVE_STANDARD_DECKS_LIST_FILE = LISTS_DIR + "net-decks-archive-standard.txt"; @@ -122,6 +123,7 @@ public final class ForgeConstants { public static final String SPRITE_PLANAR_CONQUEST_FILE = "sprite_planar_conquest.png"; public static final String SPRITE_SETLOGO_FILE = "sprite_setlogo.png"; public static final String SPRITE_WATERMARK_FILE = "sprite_watermark.png"; + public static final String SPRITE_CARDBG_FILE = "sprite_cardbg.png"; public static final String FONT_FILE = "font1.ttf"; public static final String SPLASH_BG_FILE = "bg_splash.png"; public static final String MATCH_BG_FILE = "bg_match.jpg"; diff --git a/forge-gui/src/main/java/forge/localinstance/skin/FSkinProp.java b/forge-gui/src/main/java/forge/localinstance/skin/FSkinProp.java index eef03fe9fcd..779eb4c2a42 100644 --- a/forge-gui/src/main/java/forge/localinstance/skin/FSkinProp.java +++ b/forge-gui/src/main/java/forge/localinstance/skin/FSkinProp.java @@ -410,6 +410,28 @@ public enum FSkinProp { IMG_WATERMARK_W (new int[] {2, 1006, 500, 500}, PropType.WATERMARKS), IMG_WATERMARK_C (new int[] {504, 1006, 500, 500}, PropType.WATERMARKS), + //FOR CARDBG + IMG_CARDBG_A (new int[] {2, 2, 339, 496}, PropType.CARDBG), + IMG_CARDBG_B (new int[] {343, 2, 339, 496}, PropType.CARDBG), + IMG_CARDBG_BG (new int[] {684, 2, 339, 496}, PropType.CARDBG), + IMG_CARDBG_BR (new int[] {1025, 2, 339, 496}, PropType.CARDBG), + IMG_CARDBG_C (new int[] {1366, 2, 339, 496}, PropType.CARDBG), + IMG_CARDBG_G (new int[] {2, 500, 339, 496}, PropType.CARDBG), + IMG_CARDBG_L (new int[] {343, 500, 339, 496}, PropType.CARDBG), + IMG_CARDBG_M (new int[] {684, 500, 339, 496}, PropType.CARDBG), + IMG_CARDBG_R (new int[] {1025, 500, 339, 496}, PropType.CARDBG), + IMG_CARDBG_RG (new int[] {1366, 500, 339, 496}, PropType.CARDBG), + IMG_CARDBG_U (new int[] {2, 998, 339, 496}, PropType.CARDBG), + IMG_CARDBG_UB (new int[] {343, 998, 339, 496}, PropType.CARDBG), + IMG_CARDBG_UG (new int[] {684, 998, 339, 496}, PropType.CARDBG), + IMG_CARDBG_UR (new int[] {1025, 998, 339, 496}, PropType.CARDBG), + IMG_CARDBG_V (new int[] {1366, 998, 339, 496}, PropType.CARDBG), + IMG_CARDBG_W (new int[] {2, 1496, 339, 496}, PropType.CARDBG), + IMG_CARDBG_WB (new int[] {343, 1496, 339, 496}, PropType.CARDBG), + IMG_CARDBG_WG (new int[] {684, 1496, 339, 496}, PropType.CARDBG), + IMG_CARDBG_WR (new int[] {1025, 1496, 339, 496}, PropType.CARDBG), + IMG_CARDBG_WU (new int[] {1366, 1496, 339, 496}, PropType.CARDBG), + IMG_FAV1 (new int[] {0, 0, 100, 100}, PropType.FAVICON), IMG_FAV2 (new int[] {100, 0, 100, 100}, PropType.FAVICON), IMG_FAV3 (new int[] {200, 0, 100, 100}, PropType.FAVICON), @@ -514,6 +536,7 @@ public enum FSkinProp { DECKBOX, SETLOGO, WATERMARKS, + CARDBG, FAVICON } }