mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-20 04:38:00 +00:00
Merge remote-tracking branch 'upstream/master' into deck-importer-decks-file-format
This commit is contained in:
@@ -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<String> script) {
|
||||
Reader crr = new Reader();
|
||||
for(String line : script) {
|
||||
for (String line : script) {
|
||||
crr.parseLine(line);
|
||||
}
|
||||
return crr.getCard();
|
||||
|
||||
@@ -270,7 +270,6 @@ public final class CardRulesPredicates {
|
||||
return new PredicateSuperType(type, isEqual);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks for color.
|
||||
*
|
||||
|
||||
@@ -174,7 +174,7 @@ public final class PaperCard implements Comparable<IPaperCard>, 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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")) {
|
||||
|
||||
@@ -166,7 +166,6 @@ public class ChangeZoneAllEffect extends SpellAbilityEffect {
|
||||
Map<AbilityKey, Object> 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));
|
||||
|
||||
@@ -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<Player> tgtPlayers = getDefinedPlayersOrTargeted(sa);
|
||||
if( tgtPlayers.isEmpty() ) {
|
||||
if (tgtPlayers.isEmpty()) {
|
||||
tgtPlayers.add(sa.getActivatingPlayer());
|
||||
}
|
||||
|
||||
|
||||
@@ -1943,7 +1943,9 @@ public class Card extends GameEntity implements Comparable<Card>, 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<Card>, 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<Card>, 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);
|
||||
|
||||
|
||||
@@ -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")) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -926,8 +926,7 @@ public class CardProperty {
|
||||
final List<Card> cards = CardUtil.getThisTurnCast("Card", source, spellAbility);
|
||||
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;
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -89,7 +89,6 @@ public class TokenCreateTable extends ForwardingTable<Player, Card, Integer> {
|
||||
result += c.getValue();
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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."),
|
||||
|
||||
@@ -5,6 +5,7 @@ public enum AlternativeCost {
|
||||
Bestow,
|
||||
Cycling, // ActivatedAbility
|
||||
Dash,
|
||||
Disturb,
|
||||
Emerge,
|
||||
Escape,
|
||||
Evoke,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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")) {
|
||||
|
||||
@@ -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<DetailColors> 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,52 +167,102 @@ 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;
|
||||
}
|
||||
|
||||
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 type line
|
||||
drawTypeLine(g, card, state, canShow, headerColors, x, y, w, typeBoxHeight);
|
||||
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);
|
||||
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;
|
||||
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.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);
|
||||
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, MANA_SYMBOL_SIZE) + HEADER_PADDING;
|
||||
CardFaceSymbols.drawManaCost(g, mainManaCost, x + w - manaCostWidth, y + (h - MANA_SYMBOL_SIZE) / 2, MANA_SYMBOL_SIZE);
|
||||
manaCostWidth += CardFaceSymbols.getWidth(mainManaCost, manaSymbolSize) + HEADER_PADDING;
|
||||
CardFaceSymbols.drawManaCost(g, mainManaCost, x + w - manaCostWidth, y + (h - manaSymbolSize) / 2, manaSymbolSize);
|
||||
}
|
||||
|
||||
//draw name for card
|
||||
x += padding;
|
||||
w -= 2 * padding;
|
||||
if (!noText)
|
||||
g.drawText(CardTranslation.getTranslatedName(state.getName()), NAME_FONT, Color.BLACK, x, y, w - manaCostWidth - padding, h, false, Align.left, true);
|
||||
}
|
||||
|
||||
@@ -221,18 +282,106 @@ public class CardImageRenderer {
|
||||
};
|
||||
}
|
||||
|
||||
private static void drawArt(Graphics g, float x, float y, float w, float 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;
|
||||
}
|
||||
|
||||
private static void drawTypeLine(Graphics g, CardView card, CardStateView state, boolean canShow, Color[] colors, float x, float y, float w, float h) {
|
||||
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 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
|
||||
if (!noRarity) {
|
||||
float iconSize = h * 0.9f;
|
||||
float iconPadding = (h - iconSize) / 2;
|
||||
w -= iconSize + iconPadding * 2;
|
||||
@@ -250,8 +399,11 @@ public class CardImageRenderer {
|
||||
} 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;
|
||||
}
|
||||
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);
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
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":
|
||||
//draw watermark
|
||||
FSkinImage image = null;
|
||||
if (state.origCanProduceColoredMana() == 1 && !state.origProduceManaC()) {
|
||||
if (state.isPlains())
|
||||
image = FSkinImage.WATERMARK_W;
|
||||
break;
|
||||
case "Island":
|
||||
else if (state.isIsland())
|
||||
image = FSkinImage.WATERMARK_U;
|
||||
break;
|
||||
case "Swamp":
|
||||
else if (state.isSwamp())
|
||||
image = FSkinImage.WATERMARK_B;
|
||||
break;
|
||||
case "Mountain":
|
||||
else if (state.isMountain())
|
||||
image = FSkinImage.WATERMARK_R;
|
||||
break;
|
||||
case "Forest":
|
||||
else if (state.isForest())
|
||||
image = FSkinImage.WATERMARK_G;
|
||||
break;
|
||||
default:
|
||||
} else if (state.origProduceManaC()) {
|
||||
image = FSkinImage.WATERMARK_C;
|
||||
break;
|
||||
}
|
||||
if (image != null) {
|
||||
float iconSize = h * 0.75f;
|
||||
g.drawImage(image, x + (w - iconSize) / 2, y + (h - iconSize) / 2, iconSize, iconSize);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
boolean needTranslation = true;
|
||||
String text = "";
|
||||
if (card.isToken()) {
|
||||
if (card.getCloneOrigin() == null)
|
||||
needTranslation = false;
|
||||
}
|
||||
final String text = !card.isSplitCard() ?
|
||||
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 );
|
||||
if (StringUtils.isEmpty(text)) { return; }
|
||||
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<String> 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<DetailColors> 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:
|
||||
|
||||
@@ -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<String, FImageComplex> 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<String> 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,8 +327,7 @@ 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();
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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<T extends InventoryItem> extends ItemView<T> {
|
||||
} else {
|
||||
//commander bg
|
||||
g.drawImage(FSkin.getDeckbox().get(0), FSkin.getDeckbox().get(0), x, y, w, h, Color.GREEN, selected);
|
||||
|
||||
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);
|
||||
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<T extends InventoryItem> extends ItemView<T> {
|
||||
} 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
|
||||
|
||||
@@ -161,6 +161,10 @@ public class HomeScreen extends FScreen {
|
||||
return QuestCommander;
|
||||
}
|
||||
|
||||
public int getActiveButtonIndex() {
|
||||
return activeButtonIndex;
|
||||
}
|
||||
|
||||
public String getQuestWorld() {
|
||||
return QuestWorld;
|
||||
}
|
||||
|
||||
@@ -105,8 +105,11 @@ public class LoadGameMenu extends FPopupMenu {
|
||||
protected void buildMenu() {
|
||||
FScreen currentScreen = Forge.getCurrentScreen();
|
||||
for (LoadGameScreen lgs : LoadGameScreen.values()) {
|
||||
//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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -521,7 +521,7 @@ public class SettingsPage extends TabPage<SettingsScreen> {
|
||||
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);
|
||||
|
||||
@@ -393,7 +393,7 @@ public class FChoiceList<T> extends FList<T> 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<T> extends FList<T> 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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
7
forge-gui/res/cardsfolder/upcoming/avacyns_memorial.txt
Normal file
7
forge-gui/res/cardsfolder/upcoming/avacyns_memorial.txt
Normal file
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
8
forge-gui/res/cardsfolder/upcoming/candlelit_cavalry.txt
Normal file
8
forge-gui/res/cardsfolder/upcoming/candlelit_cavalry.txt
Normal file
@@ -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.
|
||||
10
forge-gui/res/cardsfolder/upcoming/candletrap.txt
Normal file
10
forge-gui/res/cardsfolder/upcoming/candletrap.txt
Normal file
@@ -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.
|
||||
10
forge-gui/res/cardsfolder/upcoming/curse_of_obsession.txt
Normal file
10
forge-gui/res/cardsfolder/upcoming/curse_of_obsession.txt
Normal file
@@ -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.
|
||||
5
forge-gui/res/cardsfolder/upcoming/defenestrate.txt
Normal file
5
forge-gui/res/cardsfolder/upcoming/defenestrate.txt
Normal file
@@ -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.
|
||||
9
forge-gui/res/cardsfolder/upcoming/deserted_beach.txt
Normal file
9
forge-gui/res/cardsfolder/upcoming/deserted_beach.txt
Normal file
@@ -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}.
|
||||
8
forge-gui/res/cardsfolder/upcoming/festival_crasher.txt
Normal file
8
forge-gui/res/cardsfolder/upcoming/festival_crasher.txt
Normal file
@@ -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.
|
||||
@@ -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.)
|
||||
@@ -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.)
|
||||
9
forge-gui/res/cardsfolder/upcoming/haunted_ridge.txt
Normal file
9
forge-gui/res/cardsfolder/upcoming/haunted_ridge.txt
Normal file
@@ -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}.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user