This commit is contained in:
Alessandro Coli
2020-03-12 21:07:17 +01:00
17 changed files with 173 additions and 43 deletions

View File

@@ -113,7 +113,11 @@ public final class ImageKeys {
}
//try fullborder...
if (filename.contains(".full")) {
file = findFile(dir, TextUtil.fastReplace(filename, ".full", ".fullborder"));
String fullborderFile = TextUtil.fastReplace(filename, ".full", ".fullborder");
file = findFile(dir, fullborderFile);
if (file != null) { return file; }
// if there's an art variant try without it
file = findFile(dir, TextUtil.fastReplace(fullborderFile, "1.fullborder", ".fullborder"));
if (file != null) { return file; }
}
//if an image, like phenomenon or planes is missing .full in their filenames but you have an existing images that have .full/.fullborder

View File

@@ -222,7 +222,12 @@ public final class CardRules implements ICardCharacteristics {
public boolean canBeBrawlCommander() {
CardType type = mainPart.getType();
return (type.isLegendary() && type.isCreature()) || type.isPlaneswalker();
return type.isLegendary() && (type.isCreature() || type.isPlaneswalker());
}
public boolean canBeTinyLeadersCommander() {
CardType type = mainPart.getType();
return type.isLegendary() && (type.isCreature() || type.isPlaneswalker());
}
public String getMeldWith() {

View File

@@ -594,8 +594,10 @@ public final class CardRulesPredicates {
public static final Predicate<CardRules> IS_VANGUARD = CardRulesPredicates.coreType(true, CardType.CoreType.Vanguard);
public static final Predicate<CardRules> IS_CONSPIRACY = CardRulesPredicates.coreType(true, CardType.CoreType.Conspiracy);
public static final Predicate<CardRules> IS_NON_LAND = CardRulesPredicates.coreType(false, CardType.CoreType.Land);
public static final Predicate<CardRules> CAN_BE_BRAWL_COMMANDER = Predicates.or(Presets.IS_PLANESWALKER,
Predicates.and(Presets.IS_CREATURE, Presets.IS_LEGENDARY));
public static final Predicate<CardRules> CAN_BE_BRAWL_COMMANDER = Predicates.and(Presets.IS_LEGENDARY,
Predicates.or(Presets.IS_CREATURE, Presets.IS_PLANESWALKER));
public static final Predicate<CardRules> CAN_BE_TINY_LEADERS_COMMANDER = Predicates.and(Presets.IS_LEGENDARY,
Predicates.or(Presets.IS_CREATURE, Presets.IS_PLANESWALKER));
/** The Constant IS_NON_CREATURE_SPELL. **/
public static final Predicate<CardRules> IS_NON_CREATURE_SPELL = com.google.common.base.Predicates

View File

@@ -463,6 +463,9 @@ public enum DeckFormat {
if (this.equals(DeckFormat.Brawl)) {
return rules.canBeBrawlCommander();
}
if (this.equals(DeckFormat.TinyLeaders)) {
return rules.canBeTinyLeadersCommander();
}
return rules.canBeCommander();
}

View File

@@ -22,8 +22,10 @@ import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import forge.card.MagicColor;
import forge.card.mana.ManaCost;
import forge.card.mana.ManaCostParser;
import forge.game.ability.AbilityFactory;
import forge.game.ability.AbilityUtils;
import forge.game.ability.ApiType;
import forge.game.card.*;
@@ -32,9 +34,15 @@ import forge.game.cost.Cost;
import forge.game.keyword.KeywordInterface;
import forge.game.player.Player;
import forge.game.player.PlayerController;
import forge.game.replacement.ReplacementEffect;
import forge.game.replacement.ReplacementHandler;
import forge.game.replacement.ReplacementLayer;
import forge.game.spellability.*;
import forge.game.trigger.Trigger;
import forge.game.trigger.TriggerHandler;
import forge.game.trigger.TriggerType;
import forge.game.zone.ZoneType;
import forge.util.Lang;
import forge.util.TextUtil;
import org.apache.commons.lang3.StringUtils;
@@ -363,10 +371,11 @@ public final class GameActionUtil {
}
SpellAbility result = null;
final Card host = sa.getHostCard();
final Game game = host.getGame();
final Player activator = sa.getActivatingPlayer();
final PlayerController pc = activator.getController();
host.getGame().getAction().checkStaticAbilities(false);
game.getAction().checkStaticAbilities(false);
boolean reset = false;
@@ -429,7 +438,60 @@ public final class GameActionUtil {
int v = pc.chooseNumberForKeywordCost(sa, cost, ki, str, Integer.MAX_VALUE);
if (v > 0) {
host.addReplacementEffect(CardFactoryUtil.makeEtbCounter("etbCounter:P1P1:" + v, host, false));
final Card eff = new Card(game.nextCardId(), game);
eff.setTimestamp(game.getNextTimestamp());
eff.setName(c.getName() + "'s Effect");
eff.addType("Effect");
eff.setToken(true); // Set token to true, so when leaving play it gets nuked
eff.setOwner(activator);
eff.setImageKey(c.getImageKey());
eff.setColor(MagicColor.COLORLESS);
eff.setImmutable(true);
// try to get the SpellAbility from the mana ability
//eff.setEffectSource((SpellAbility)null);
eff.addRemembered(host);
String abStr = "DB$ PutCounter | Defined$ ReplacedCard | CounterType$ P1P1 | ETB$ True | CounterNum$ " + v;
SpellAbility saAb = AbilityFactory.getAbility(abStr, c);
CardFactoryUtil.setupETBReplacementAbility(saAb);
String desc = "It enters the battlefield with ";
desc += Lang.nounWithNumeral(v, CounterType.P1P1.getName() + " counter");
desc += " on it.";
String repeffstr = "Event$ Moved | ValidCard$ Card.IsRemembered | Destination$ Battlefield | Description$ " + desc;
ReplacementEffect re = ReplacementHandler.parseReplacement(repeffstr, eff, true);
re.setLayer(ReplacementLayer.Other);
re.setOverridingAbility(saAb);
eff.addReplacementEffect(re);
// Forgot Trigger
String trig = "Mode$ ChangesZone | ValidCard$ Card.IsRemembered | Origin$ Stack | Destination$ Any | TriggerZones$ Command | Static$ True";
String forgetEffect = "DB$ Pump | ForgetObjects$ TriggeredCard";
String exileEffect = "DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile"
+ " | ConditionDefined$ Remembered | ConditionPresent$ Card | ConditionCompare$ EQ0";
SpellAbility saForget = AbilityFactory.getAbility(forgetEffect, eff);
AbilitySub saExile = (AbilitySub) AbilityFactory.getAbility(exileEffect, eff);
saForget.setSubAbility(saExile);
final Trigger parsedTrigger = TriggerHandler.parseTrigger(trig, eff, true);
parsedTrigger.setOverridingAbility(saForget);
eff.addTrigger(parsedTrigger);
eff.updateStateForView();
// TODO: Add targeting to the effect so it knows who it's dealing with
game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);
game.getAction().moveTo(ZoneType.Command, eff, null);
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
if (result == null) {
result = sa.copy();
}

View File

@@ -254,7 +254,7 @@ public class ManaPool extends ManaConversionMatrix implements Iterable<Mana> {
}
}
if (mana.addsCounters(sa)) {
mana.getManaAbility().createETBCounters(host);
mana.getManaAbility().createETBCounters(host, this.owner);
}
if (mana.triggersWhenSpent()) {
mana.getManaAbility().addTriggersWhenSpent(sa, host);

View File

@@ -6,23 +6,24 @@
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package forge.game.spellability;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import forge.card.ColorSet;
import forge.card.MagicColor;
import forge.card.mana.ManaAtom;
import forge.game.Game;
import forge.game.ability.AbilityFactory;
import forge.game.ability.AbilityKey;
import forge.game.card.Card;
@@ -35,6 +36,8 @@ import forge.game.replacement.*;
import forge.game.trigger.Trigger;
import forge.game.trigger.TriggerHandler;
import forge.game.trigger.TriggerType;
import forge.game.zone.ZoneType;
import forge.util.Lang;
import forge.util.TextUtil;
import org.apache.commons.lang3.StringUtils;
@@ -47,7 +50,7 @@ import java.util.regex.Pattern;
* <p>
* Abstract AbilityMana class.
* </p>
*
*
* @author Forge
* @version $Id$
*/
@@ -79,7 +82,7 @@ public class AbilityManaPart implements java.io.Serializable {
* <p>
* Constructor for AbilityMana.
* </p>
*
*
* @param sourceCard
* a {@link forge.game.card.Card} object.
*/
@@ -112,7 +115,7 @@ public class AbilityManaPart implements java.io.Serializable {
* <p>
* produceMana.
* </p>
*
*
* @param produced
* a {@link java.lang.String} object.
* @param player
@@ -170,7 +173,7 @@ public class AbilityManaPart implements java.io.Serializable {
* cannotCounterPaidWith.
* </p>
* @param saBeingPaid
*
*
* @return a {@link java.lang.String} object.
*/
public boolean cannotCounterPaidWith(SpellAbility saBeingPaid) {
@@ -187,7 +190,7 @@ public class AbilityManaPart implements java.io.Serializable {
* addKeywords.
* </p>
* @param saBeingPaid
*
*
* @return a {@link java.lang.String} object.
*/
public boolean addKeywords(SpellAbility saBeingPaid) {
@@ -206,7 +209,7 @@ public class AbilityManaPart implements java.io.Serializable {
* <p>
* getKeywords.
* </p>
*
*
* @return a {@link java.lang.String} object.
*/
public String getKeywords() {
@@ -218,7 +221,7 @@ public class AbilityManaPart implements java.io.Serializable {
* addsCounters.
* </p>
* @param saBeingPaid
*
*
* @return a {@link java.lang.String} object.
*/
public boolean addsCounters(SpellAbility saBeingPaid) {
@@ -228,10 +231,26 @@ public class AbilityManaPart implements java.io.Serializable {
/**
* createETBCounters
*/
public void createETBCounters(Card c) {
public void createETBCounters(Card c, Player controller) {
String[] parse = this.addsCounters.split("_");
// Convert random SVars if there are other cards with this effect
if (c.isValid(parse[0], c.getController(), c, null)) {
final Game game = this.sourceCard.getGame();
final Card eff = new Card(game.nextCardId(), game);
eff.setTimestamp(game.getNextTimestamp());
eff.setName(sourceCard.getName() + "'s Effect");
eff.addType("Effect");
eff.setToken(true); // Set token to true, so when leaving play it gets nuked
eff.setOwner(controller);
eff.setImageKey(sourceCard.getImageKey());
eff.setColor(MagicColor.COLORLESS);
eff.setImmutable(true);
// try to get the SpellAbility from the mana ability
//eff.setEffectSource((SpellAbility)null);
eff.addRemembered(c);
String abStr = "DB$ PutCounter | Defined$ ReplacedCard | CounterType$ " + parse[1]
+ " | ETB$ True | CounterNum$ " + parse[2];
@@ -241,15 +260,37 @@ public class AbilityManaPart implements java.io.Serializable {
}
CardFactoryUtil.setupETBReplacementAbility(sa);
String repeffstr = "Event$ Moved | ValidCard$ Card.Self | Destination$ Battlefield "
+ " | Secondary$ True | Description$ CARDNAME"
+ " enters the battlefield with " + CounterType.valueOf(parse[1]).getName() + " counters.";
String desc = "It enters the battlefield with ";
desc += Lang.nounWithNumeral(parse[2], CounterType.valueOf(parse[1]).getName() + " counter");
desc += " on it.";
ReplacementEffect re = ReplacementHandler.parseReplacement(repeffstr, c, false);
String repeffstr = "Event$ Moved | ValidCard$ Card.IsRemembered | Destination$ Battlefield | Description$ " + desc;
ReplacementEffect re = ReplacementHandler.parseReplacement(repeffstr, eff, true);
re.setLayer(ReplacementLayer.Other);
re.setOverridingAbility(sa);
c.addChangedCardTraits(null, null, null, ImmutableList.of(re), null, false, false, false, sa.getHostCard().getGame().getNextTimestamp());
eff.addReplacementEffect(re);
// Forgot Trigger
String trig = "Mode$ ChangesZone | ValidCard$ Card.IsRemembered | Origin$ Stack | Destination$ Any | TriggerZones$ Command | Static$ True";
String forgetEffect = "DB$ Pump | ForgetObjects$ TriggeredCard";
String exileEffect = "DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile"
+ " | ConditionDefined$ Remembered | ConditionPresent$ Card | ConditionCompare$ EQ0";
SpellAbility saForget = AbilityFactory.getAbility(forgetEffect, eff);
AbilitySub saExile = (AbilitySub) AbilityFactory.getAbility(exileEffect, eff);
saForget.setSubAbility(saExile);
final Trigger parsedTrigger = TriggerHandler.parseTrigger(trig, eff, true);
parsedTrigger.setOverridingAbility(saForget);
eff.addTrigger(parsedTrigger);
eff.updateStateForView();
// TODO: Add targeting to the effect so it knows who it's dealing with
game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);
game.getAction().moveTo(ZoneType.Command, eff, null);
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
}
}
@@ -270,7 +311,7 @@ public class AbilityManaPart implements java.io.Serializable {
* <p>
* getManaRestrictions.
* </p>
*
*
* @return a {@link java.lang.String} object.
*/
public String getManaRestrictions() {
@@ -281,7 +322,7 @@ public class AbilityManaPart implements java.io.Serializable {
* <p>
* meetsManaRestrictions.
* </p>
*
*
* @param sa
* a {@link forge.game.spellability.SpellAbility} object.
* @return a boolean.
@@ -297,7 +338,7 @@ public class AbilityManaPart implements java.io.Serializable {
if (restriction.equals("nonSpell")) {
return !sa.isSpell();
}
if (restriction.equals("CumulativeUpkeep")) {
if (sa.isCumulativeupkeep()) {
return true;
@@ -350,7 +391,7 @@ public class AbilityManaPart implements java.io.Serializable {
* <p>
* mana.
* </p>
*
*
* @return a {@link java.lang.String} object.
*/
public final String mana() {
@@ -439,7 +480,7 @@ public class AbilityManaPart implements java.io.Serializable {
* <p>
* canProduce.
* </p>
*
*
* @param s
* a {@link java.lang.String} object.
* @return a boolean.
@@ -469,7 +510,7 @@ public class AbilityManaPart implements java.io.Serializable {
* <p>
* isBasic.
* </p>
*
*
* @return a boolean.
*/
public final boolean isBasic() {
@@ -542,7 +583,7 @@ public class AbilityManaPart implements java.io.Serializable {
public Card getSourceCard() {
return sourceCard;
}
public void setSourceCard(final Card host) {
sourceCard = host;
}

View File

@@ -103,7 +103,7 @@ public final class CEditorConstructed extends CDeckEditor<Deck> {
case TinyLeaders:
allSections.add(DeckSection.Commander);
commanderFilter = CardRulesPredicates.Presets.CAN_BE_COMMANDER;
commanderFilter = CardRulesPredicates.Presets.CAN_BE_TINY_LEADERS_COMMANDER;
commanderPool = ItemPool.createFrom(FModel.getMagicDb().getCommonCards().getAllCards(Predicates.compose(commanderFilter, PaperCard.FN_GET_RULES)), PaperCard.class);
normalPool = ItemPool.createFrom(FModel.getMagicDb().getCommonCards().getAllCards(), PaperCard.class);

View File

@@ -856,6 +856,9 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
case Brawl:
isLegalCommander = card.getRules().canBeBrawlCommander();
break;
case TinyLeaders:
isLegalCommander = card.getRules().canBeTinyLeadersCommander();
break;
case Oathbreaker:
isLegalCommander = card.getRules().canBeOathbreaker();
captionSuffix = localizer.getMessage("lblOathbreaker");

View File

@@ -84,4 +84,4 @@ Modern Horizons, 3/6/WAR, MH1
Core Set 2020, 3/6/M20, M20
Throne of Eldraine, 3/6/ELD, ELD
Theros Beyond Death, 3/6/THB, THB
Mystery Booster, 3/6/MB1, MB1
Mystery Booster, 3/6/THB, MB1

View File

@@ -1,7 +1,7 @@
Name:Ghastly Demise
ManaCost:B
Types:Instant
A:SP$ Destroy | Cost$ B | ValidTgts$ Creature.nonBlack+toughnessLEX | TgtPrompt$ Select target nonblack creature with toughness less than or equal to the number of cards in your graveyard. | References$ X | SpellDescription$ Destroy target nonblack creature if its toughness is less than or equal to the number of cards in your graveyard.
A:SP$ Destroy | Cost$ B | ValidTgts$ Creature.nonBlack | TgtPrompt$ Select target nonblack creature | ConditionCheckSVar$ Y | ConditionSVarCompare$ LEX | References$ X,Y | StackDescription$ SpellDescription | SpellDescription$ Destroy target nonblack creature if its toughness is less than or equal to the number of cards in your graveyard.
SVar:Y:Targeted$CardToughness
SVar:X:Count$InYourYard
SVar:Picture:http://www.wizards.com/global/images/magic/general/ghastly_demise.jpg
Oracle:Destroy target nonblack creature if its toughness is less than or equal to the number of cards in your graveyard.

View File

@@ -1,9 +1,10 @@
Name:Nissa's Pilgrimage
ManaCost:2 G
Types:Sorcery
A:SP$ ChangeZone | Cost$ 2 G | Origin$ Library | Destination$ Battlefield | Tapped$ True | ChangeType$ Land.Basic+Forest | ChangeNum$ 1 | SubAbility$ DBChangeZone1 | NoShuffle$ True | SpellDescription$ Search your library for up to two basic Forest cards, reveal those cards, and put one onto the battlefield tapped and the rest into your hand. Then shuffle your library. Spell mastery — If there are two or more instant or sorcery cards in your graveyard, search your library for up to three basic Forest cards instead of two.
SVar:DBChangeZone1:DB$ChangeZone | Origin$ Library | Destination$ Hand | SubAbility$ DBChangeZone2 | ChangeType$ Land.Basic+Forest | ChangeNum$ 1 | ConditionCheckSVar$ X | ConditionSVarCompare$ LT2 | References$ X
SVar:DBChangeZone2:DB$ChangeZone | Origin$ Library | Destination$ Hand | ChangeType$ Land.Basic+Forest | ChangeNum$ 2 | ConditionCheckSVar$ X | ConditionSVarCompare$ GE2 | References$ X
SVar:X:Count$ValidGraveyard Instant.YouOwn,Sorcery.YouOwn
SVar:Picture:http://www.wizards.com/global/images/magic/general/nissas_pilgrimage.jpg
A:SP$ ChangeZone | Cost$ 2 G | Origin$ Library | Destination$ Library | ChangeType$ Land.Basic+Forest | ChangeNum$ X | References$ X,Y | RememberChanged$ True | SubAbility$ DBBattlefield | Shuffle$ False | StackDescription$ SpellDescription | SpellDescription$ Search your library for up to two basic Forest cards, reveal those cards, and put one onto the battlefield tapped and the rest into your hand. Then shuffle your library. Spell mastery — If there are two or more instant or sorcery cards in your graveyard, search your library for up to three basic Forest cards instead of two.
SVar:DBBattlefield:DB$ ChangeZone | Origin$ Library | Destination$ Battlefield | Tapped$ True | SubAbility$ DBHand | ChangeType$ Card.IsRemembered | ChangeNum$ 1 | Mandatory$ True | NoLooking$ True | SelectPrompt$ Select a card to go to the battlefield | Shuffle$ False | StackDescription$ None
SVar:DBHand:DB$ ChangeZone | Origin$ Library | Destination$ Hand | Defined$ Remembered | NoLooking$ True | StackDescription$ None | SubAbility$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
SVar:X:Count$Compare Y GE2.3.2
SVar:Y:Count$ValidGraveyard Instant.YouOwn,Sorcery.YouOwn
Oracle:Search your library for up to two basic Forest cards, reveal those cards, and put one onto the battlefield tapped and the rest into your hand. Then shuffle your library.\nSpell mastery — If there are two or more instant or sorcery cards in your graveyard, search your library for up to three basic Forest cards instead of two.

View File

@@ -3,7 +3,7 @@ ManaCost:1 W
Types:Enchantment Saga
K:Saga:3:TrigChange,TrigToken,TrigGainLife
SVar:TrigChange:DB$ ChangeZone | Origin$ Library | Destination$ Hand | ChangeType$ Land.Plains+Basic | ChangeNum$ 1 | SpellDescription$ Search your library for a basic Plains card, reveal it, put it into your hand, then shuffle your library.
SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ c_0_4_wall_defender | TokenOwner$ You | LegacyImage$ c 0 4 wall defender thb | SpellDescription$ Create a 0/4 colorless Wall artifact creature token with defender.
SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ c_0_4_a_wall_defender | TokenOwner$ You | LegacyImage$ c 0 4 wall defender thb | SpellDescription$ Create a 0/4 colorless Wall artifact creature token with defender.
SVar:TrigGainLife:DB$ GainLife | Defined$ You | LifeAmount$ 2 | SpellDescription$ You gain 2 life.
DeckHas:Ability$LifeGain & Ability$Token
Oracle:(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)\nI - Search your library for a basic Plains card, reveal it, put it into your hand, then shuffle your library.\nII - Create a 0/4 colorless Wall artifact creature token with defender.\nIII - You gain 2 life.

View File

@@ -377,7 +377,7 @@ Prerelease=6 Boosters, 1 RareMythic+
[tokens]
b_2_2_zombie
c_0_4_wall_defender
c_0_4_a_wall_defender
g_1_2_spider_reach
g_2_2_wolf
r_x_1_elemental_trample_haste

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -1,6 +1,6 @@
Name:Wall
ManaCost:no cost
Types:Creature Wall
Types:Artifact Creature Wall
PT:0/4
K:Defender
Oracle:Defender

View File

@@ -6,6 +6,8 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import forge.FThreads;
import forge.ImageKeys;
@@ -50,7 +52,14 @@ public abstract class ImageFetcher {
setDownload.append(ImageUtil.getDownloadUrl(paperCard, backFace));
downloadUrls.add(setDownload.toString());
int artIndex = Integer.parseInt(imageKey.split("\\|")[2]);
int artIndex = 1;
final Pattern pattern = Pattern.compile(
"^.:([^|]*\\|){2}(\\d+).*$"
);
Matcher matcher = pattern.matcher(imageKey);
if (matcher.matches()) {
artIndex = Integer.parseInt(matcher.group(2));
}
final StaticData data = StaticData.instance();
final String cardNum = data.getCommonCards().getCardCollectorNumber(paperCard.getName(), paperCard.getEdition(), artIndex);
if (cardNum != null) {