Compare commits

...

25 Commits

Author SHA1 Message Date
GitHub Actions
a29da54b2e [maven-release-plugin] prepare release forge-2.0.02 2025-02-15 20:50:03 +00:00
Chris H
ec63b230c8 Temporarily remove flatten library for release 2025-02-15 12:34:35 -05:00
Jetz
e880a83df2 Let SVars in functional variants overwrite original script 2025-02-15 12:34:00 -05:00
Jetz
80cc7218a3 URLs for token image keys for speed and max_speed 2025-02-15 12:34:00 -05:00
Renato Filipe Vidal Santos
4809fb858a Update gale_conduit_of_the_arcane.txt 2025-02-15 12:19:58 -05:00
Jetz
3af62888dc Remove wake lock on desktop 2025-02-15 07:50:06 +01:00
Jetz
79e1d0a0f0 Count destroyed attractions for "number of attractions you've visited this turn" 2025-02-15 07:50:06 +01:00
Jetz
c447dfc888 Support "Affinitycycling" and "Affinity for Affinity" 2025-02-15 07:50:06 +01:00
Jetz
8149966915 Fix Tyrranax Rex importing 2025-02-15 07:50:06 +01:00
Hans Mackowiak
472f9481e8 Update radiant_lotus.txt 2025-02-15 07:49:21 +01:00
Hans Mackowiak
2209ce3cee Update sanguine_soothsayer.txt
fix mana cost
2025-02-14 14:57:40 +01:00
Hans Mackowiak
258c89e65d Update CounterEffect.java (#7014) 2025-02-14 09:06:25 +01:00
tool4ever
11913085ef Misc cleanup (#7009) 2025-02-13 15:51:19 +01:00
Chris H
53fca12a57 Fix Radiant lotus 2025-02-12 21:14:01 -05:00
Chris H
8e8a795f19 Migrate upcoming 2025-02-12 20:34:55 -05:00
Renato Filipe Vidal Santos
a4b27321ac Oracle update: During your turn (#7005) 2025-02-12 10:55:57 +01:00
Renato Filipe Vidal Santos
ead83d932f Oracle update: Affinity (#7001) 2025-02-12 10:34:50 +01:00
tool4ever
900bd4327d Update pride_of_the_road.txt 2025-02-12 10:21:39 +01:00
Paul Hammerton
1a2bb054f4 Merge pull request #7000 from paulsnoops/fix-sld
Edition updates: SLD
2025-02-11 19:09:54 +00:00
Paul Hammerton
f908df46c8 Edition updates: SLD 2025-02-11 19:07:12 +00:00
Paul Hammerton
f8c97842c4 Merge pull request #6999 from paulsnoops/edition-updates
Edition updates: SLD
2025-02-11 17:08:59 +00:00
Paul Hammerton
c324b45025 Edition updates: SLD 2025-02-11 17:02:00 +00:00
tool4ever
5061ceda0e Clean up (#6995) 2025-02-10 21:43:04 +00:00
Chris H
d1e677eb4f Update rangers_refueler.txt 2025-02-10 17:37:26 +01:00
Jetz72
493a8f351b Add Speed Tracker to Command Zone (#6982)
* Add command zone effect displaying speed

* Remove enum counter type for speed.

* Make Start Your Engines an SBA.

* LifeLost -> LifeLostAll per speed rules.

* Use same game event for all speed changes.

* Fix keyword not appearing in detail text

* Cleanup extra createSpeedEffect.

* Add support for arbitrary overlay text. Remove fake counters.

* Text styling.

* Remove extra SBA check.

* Remove speed from PlayerView; localization support.

---------

Co-authored-by: Jetz <Jetz722@gmail.com>
2025-02-10 07:43:48 +03:00
431 changed files with 600 additions and 596 deletions

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>forge</artifactId>
<groupId>forge</groupId>
<version>${revision}</version>
<version>2.0.02</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -6,7 +6,7 @@
<parent>
<artifactId>forge</artifactId>
<groupId>forge</groupId>
<version>${revision}</version>
<version>2.0.02</version>
</parent>
<artifactId>forge-ai</artifactId>

View File

@@ -22,7 +22,6 @@ import forge.game.player.Player;
import forge.game.player.PlayerActionConfirmMode;
import forge.game.spellability.AbilitySub;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.SpellAbilityStackInstance;
import forge.game.spellability.TargetRestrictions;
import forge.game.staticability.StaticAbilityMustTarget;
import forge.game.zone.ZoneType;
@@ -138,8 +137,6 @@ public class ChangeZoneAi extends SpellAbilityAi {
if (aiLogic != null) {
if (aiLogic.equals("Always")) {
return true;
} else if (aiLogic.startsWith("ExileSpell")) {
return doExileSpellLogic(aiPlayer, sa);
} else if (aiLogic.startsWith("SacAndUpgrade")) { // Birthing Pod, Natural Order, etc.
return doSacAndUpgradeLogic(aiPlayer, sa);
} else if (aiLogic.startsWith("SacAndRetFromGrave")) { // Recurring Nightmare, etc.
@@ -878,6 +875,10 @@ public class ChangeZoneAi extends SpellAbilityAi {
origin.addAll(ZoneType.listValueOf(sa.getParam("TgtZone")));
}
if (origin.contains(ZoneType.Stack) && doExileSpellLogic(ai, sa, mandatory)) {
return true;
}
final ZoneType destination = ZoneType.smartValueOf(sa.getParam("Destination"));
final Game game = ai.getGame();
@@ -902,7 +903,6 @@ public class ChangeZoneAi extends SpellAbilityAi {
}
CardCollection list = CardLists.getTargetableCards(game.getCardsIn(origin), sa);
// Filter AI-specific targets if provided
list = ComputerUtil.filterAITgts(sa, ai, list, true);
if (sa.hasParam("AITgtsOnlyBetterThanSelf")) {
list = CardLists.filter(list, card -> ComputerUtilCard.evaluateCreature(card) > ComputerUtilCard.evaluateCreature(source) + 30);
@@ -2061,33 +2061,26 @@ public class ChangeZoneAi extends SpellAbilityAi {
}
}
private boolean doExileSpellLogic(final Player aiPlayer, final SpellAbility sa) {
String aiLogic = sa.getParamOrDefault("AILogic", "");
SpellAbilityStackInstance top = aiPlayer.getGame().getStack().peek();
List<ApiType> dangerousApi = Arrays.asList(ApiType.DealDamage, ApiType.DamageAll, ApiType.Destroy, ApiType.DestroyAll, ApiType.Sacrifice, ApiType.SacrificeAll);
int manaCost = 0;
int minCost = 0;
if (aiLogic.contains(".")) {
minCost = Integer.parseInt(aiLogic.substring(aiLogic.indexOf(".") + 1));
private static boolean doExileSpellLogic(final Player ai, final SpellAbility sa, final boolean mandatory) {
List<ApiType> dangerousApi = null;
CardCollection spells = new CardCollection(ai.getGame().getStackZone().getCards());
Collections.reverse(spells);
if (!mandatory && !spells.isEmpty()) {
spells = spells.subList(0, 1);
spells = ComputerUtil.filterAITgts(sa, ai, spells, true);
dangerousApi = Arrays.asList(ApiType.DealDamage, ApiType.DamageAll, ApiType.Destroy, ApiType.DestroyAll, ApiType.Sacrifice, ApiType.SacrificeAll);
}
if (top != null) {
SpellAbility topSA = top.getSpellAbility();
if (topSA != null) {
if (topSA.getPayCosts().hasManaCost()) {
manaCost = topSA.getPayCosts().getTotalMana().getCMC();
}
if ((manaCost >= minCost || dangerousApi.contains(topSA.getApi()))
&& topSA.getActivatingPlayer().isOpponentOf(aiPlayer)
&& sa.canTargetSpellAbility(topSA)) {
for (Card c : spells) {
SpellAbility topSA = ai.getGame().getStack().getSpellMatchingHost(c);
if (topSA != null && (dangerousApi == null ||
(dangerousApi.contains(topSA.getApi()) && topSA.getActivatingPlayer().isOpponentOf(ai)))
&& sa.canTarget(topSA)) {
sa.resetTargets();
sa.getTargets().add(topSA);
return sa.isTargetNumberValid();
}
}
}
return false;
}

View File

@@ -6,7 +6,7 @@
<parent>
<artifactId>forge</artifactId>
<groupId>forge</groupId>
<version>${revision}</version>
<version>2.0.02</version>
</parent>
<artifactId>forge-core</artifactId>

View File

@@ -222,7 +222,7 @@ final class CardFace implements ICardFace, Cloneable {
else variant.replacements.addAll(0, this.replacements);
if(variant.variables == null) variant.variables = this.variables;
else variant.variables.putAll(this.variables);
else this.variables.forEach((k, v) -> variant.variables.putIfAbsent(k, v));
if(variant.nonAbilityText == null) variant.nonAbilityText = this.nonAbilityText;
if(variant.draftActions == null) variant.draftActions = this.draftActions;

View File

@@ -472,7 +472,8 @@ public class DeckRecognizer {
"side", "sideboard", "sb",
"main", "card", "mainboard",
"avatar", "commander", "schemes",
"conspiracy", "planes", "deck", "dungeon"};
"conspiracy", "planes", "deck", "dungeon",
"attractions", "contraptions"};
private static CharSequence[] allCardTypes(){
List<String> cardTypesList = new ArrayList<>();
@@ -670,6 +671,9 @@ public class DeckRecognizer {
// ok so the card has been found - let's see if there's any restriction on the set
return checkAndSetCardToken(pc, edition, cardCount, deckSecFromCardLine,
currentDeckSection, true);
// On the off chance we accidentally interpreted part of the card's name as a set code, e.g. "Tyrranax Rex"
if (data.isMTGCard(cardName + " " + setCode))
continue;
// UNKNOWN card as in the Counterspell|FEM case
return Token.UnknownCard(cardName, setCode, cardCount);
}

View File

@@ -1,13 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>forge</artifactId>
<groupId>forge</groupId>
<version>${revision}</version>
<version>2.0.02</version>
</parent>
<artifactId>forge-game</artifactId>

View File

@@ -705,7 +705,7 @@ public class GameAction {
StaticAbility stAb = eff.addStaticAbility(AbilityUtils.getSVar(cause, cause.getParam("StaticEffect")));
stAb.setActiveZone(EnumSet.of(ZoneType.Command));
// needed for ETB lookahead like Bronzehide Lion
stAb.putParam("AffectedZone", "Battlefield,Hand,Graveyard,Exile,Stack,Library,Command");
stAb.putParam("AffectedZone", "All");
SpellAbilityEffect.addForgetOnMovedTrigger(eff, "Battlefield");
game.getAction().moveToCommand(eff, cause);
}

View File

@@ -3704,6 +3704,10 @@ public class AbilityUtils {
return doXMath(amount, m, source, ctb);
}
if (value.equals("AttractionsVisitedThisTurn")) {
return doXMath(player.getAttractionsVisitedThisTurn(), m, source, ctb);
}
if (value.startsWith("PlaneswalkedToThisTurn")) {
int found = 0;
String name = value.split(" ")[1];

View File

@@ -107,11 +107,9 @@ public class ChangeZoneAllEffect extends SpellAbilityEffect {
final Zone originZone = game.getZoneOf(c);
// Fizzle spells so that they are removed from stack (e.g. Summary Dismissal)
if (sa.hasParam("Fizzle")) {
if (originZone.is(ZoneType.Exile) || originZone.is(ZoneType.Hand) || originZone.is(ZoneType.Stack)) {
if (originZone.is(ZoneType.Stack)) {
game.getStack().remove(c);
}
}
if (remLKI) {
source.addRemembered(CardCopyService.getLKICopy(c));

View File

@@ -558,22 +558,15 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
continue;
}
if (originZone.is(ZoneType.Stack)) {
game.getStack().remove(gameCard);
}
Card movedCard = null;
Map<AbilityKey, Object> moveParams = AbilityKey.newMap();
AbilityKey.addCardZoneTableParams(moveParams, triggerList);
if (destination.equals(ZoneType.Library)) {
// If a card is moved to library from the stack, remove its spells from the stack
if (sa.hasParam("Fizzle")) {
// TODO only AI still targets as card, try to remove it
if (gameCard.isInZone(ZoneType.Exile) || gameCard.isInZone(ZoneType.Hand) || gameCard.isInZone(ZoneType.Stack)) {
// This only fizzles spells, not anything else.
game.getStack().remove(gameCard);
}
}
movedCard = game.getAction().moveToLibrary(gameCard, libraryPosition, sa, moveParams);
} else if (destination.equals(ZoneType.Battlefield)) {
if (destination.equals(ZoneType.Battlefield)) {
moveParams.put(AbilityKey.SimultaneousETB, tgtCards);
if (sa.isReplacementAbility()) {
ReplacementEffect re = sa.getReplacementEffect();
@@ -725,15 +718,6 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
commandCards.add(movedCard); //add to list to reveal the commandzone cards
}
// If a card is Exiled from the stack, remove its spells from the stack
if (sa.hasParam("Fizzle")) {
if (gameCard.isInZone(ZoneType.Exile) || gameCard.isInZone(ZoneType.Hand)
|| gameCard.isInZone(ZoneType.Stack) || gameCard.isInZone(ZoneType.Command)) {
// This only fizzles spells, not anything else.
game.getStack().remove(gameCard);
}
}
if (sa.hasParam("WithCountersType")) {
CounterType cType = CounterType.getType(sa.getParam("WithCountersType"));
int cAmount = AbilityUtils.calculateAmount(hostCard, sa.getParamOrDefault("WithCountersAmount", "1"), sa);

View File

@@ -257,7 +257,7 @@ public class CounterEffect extends SpellAbilityEffect {
params.put(AbilityKey.StackSa, tgtSA);
String destination = srcSA.hasParam("Destination") ? srcSA.getParam("Destination") : tgtSA.isAftermath() ? "Exile" : "Graveyard";
String destination = srcSA.getParamOrDefault("Destination", "Graveyard");
if (srcSA.hasParam("DestinationChoice")) { //Hinder
List<String> pos = Arrays.asList(srcSA.getParam("DestinationChoice").split(","));
destination = srcSA.getActivatingPlayer().getController().chooseSomeType(Localizer.getInstance().getMessage("lblRemoveDestination"), tgtSA, pos);

View File

@@ -32,6 +32,7 @@ public class ManaEffect extends SpellAbilityEffect {
@Override
public void resolve(SpellAbility sa) {
final Card card = sa.getHostCard();
final Game game = card.getGame();
final AbilityManaPart abMana = sa.getManaPart();
final List<Player> tgtPlayers = getDefinedPlayersOrTargeted(sa);
final Player activator = sa.getActivatingPlayer();
@@ -39,10 +40,7 @@ public class ManaEffect extends SpellAbilityEffect {
// Spells are not undoable
sa.setUndoable(sa.isAbility() && sa.isUndoable() && tgtPlayers.size() < 2 && !sa.hasParam("ActivationLimit"));
final boolean optional = sa.hasParam("Optional");
final Game game = activator.getGame();
if (optional && !activator.getController().confirmAction(sa, null, Localizer.getInstance().getMessage("lblDoYouWantAddMana"), null)) {
if (sa.hasParam("Optional") && !activator.getController().confirmAction(sa, null, Localizer.getInstance().getMessage("lblDoYouWantAddMana"), null)) {
return;
}

View File

@@ -340,6 +340,8 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
protected boolean renderForUi = true;
private final CardView view;
private String overlayText = null;
private SpellAbility[] basicLandAbilities = new SpellAbility[MagicColor.WUBRG.length];
private int planeswalkerAbilityActivated;
@@ -6826,6 +6828,17 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
return getType().hasStringType("Class");
}
/**
* Displays a string as an overlay on top of this card (similar to the way counter text is shown).
*/
public void setOverlayText(String overlayText) {
this.overlayText = overlayText;
view.updateMarkerText(this);
}
public String getOverlayText() {
return this.overlayText;
}
public final void animateBestow() {
animateBestow(true);
}

View File

@@ -214,8 +214,8 @@ public class CardCopyService {
if (cachedCard != null) {
return cachedCard;
}
String msg = "CardUtil:getLKICopy copy object";
String msg = "CardUtil:getLKICopy copy object";
Breadcrumb bread = new Breadcrumb(msg);
bread.setData("Card", copyFrom.getName());
bread.setData("CardState", copyFrom.getCurrentStateName().toString());
@@ -304,22 +304,20 @@ public class CardCopyService {
newCopy.setCounters(Maps.newHashMap(copyFrom.getCounters()));
newCopy.setColor(copyFrom.getColor().getColor());
newCopy.setPhasedOut(copyFrom.getPhasedOut());
newCopy.setTapped(copyFrom.isTapped());
newCopy.setTributed(copyFrom.isTributed());
newCopy.setMonstrous(copyFrom.isMonstrous());
newCopy.setRenowned(copyFrom.isRenowned());
newCopy.setSolved(copyFrom.isSolved());
newCopy.setSaddled(copyFrom.isSaddled());
newCopy.setPromisedGift(copyFrom.getPromisedGift());
newCopy.setSaddled(copyFrom.isSaddled());
if (newCopy.isSaddled()) newCopy.setSaddledByThisTurn(copyFrom.getSaddledByThisTurn());
if (copyFrom.isSuspected()) {
newCopy.setSuspectedEffect(getLKICopy(copyFrom.getSuspectedEffect(), cachedMap));
}
newCopy.setColor(copyFrom.getColor().getColor());
newCopy.setPhasedOut(copyFrom.getPhasedOut());
newCopy.setTapped(copyFrom.isTapped());
newCopy.setDamageHistory(copyFrom.getDamageHistory());
newCopy.setDamageReceivedThisTurn(copyFrom.getDamageReceivedThisTurn());
newCopy.setExcessDamageReceivedThisTurn(copyFrom.getExcessDamageThisTurn());
@@ -354,8 +352,6 @@ public class CardCopyService {
}
newCopy.setChosenEvenOdd(copyFrom.getChosenEvenOdd());
//newCopy.getEtbCounters().putAll(copyFrom.getEtbCounters());
newCopy.setUnearthed(copyFrom.isUnearthed());
newCopy.copyFrom(copyFrom);

View File

@@ -3764,6 +3764,8 @@ public class CardFactoryUtil {
desc = "Basic land";
} else if (type.equals("Land.Artifact")) {
desc = "Artifact land";
} else if (type.startsWith("Card.with")) {
desc = type.substring(9);
}
sb.append(" Discard<1/CARDNAME> | ActivationZone$ Hand | PrecostDesc$ ").append(desc).append("cycling ");
@@ -3784,17 +3786,20 @@ public class CardFactoryUtil {
if (keyword.startsWith("Affinity")) {
final String[] k = keyword.split(":");
final String t = k[1];
String d = "";
if (k.length > 2) {
final StringBuilder s = new StringBuilder();
s.append(k[2]).append("s");
d = s.toString();
}
String desc = "Artifact".equals(t) ? "artifacts" : CardType.getPluralType(t);
if (!d.isEmpty()) {
desc = d;
String desc;
if(k.length > 2) {
String typeText = k[2];
if(typeText.contains(" with "))
desc = typeText.substring(typeText.indexOf(" with ") + 6);
else
desc = typeText + "s";
}
else if ("Artifact".equals(t))
desc = "artifacts";
else
desc = CardType.getPluralType(t);
StringBuilder sb = new StringBuilder();
sb.append("Mode$ ReduceCost | ValidCard$ Card.Self | Type$ Spell | Amount$ AffinityX | EffectZone$ All");
sb.append("| Description$ Affinity for ").append(desc);

View File

@@ -491,6 +491,7 @@ public class CardView extends GameEntityView {
}
void updateCurrentRoom(Card c) {
set(TrackableProperty.CurrentRoom, c.getCurrentRoom());
updateMarkerText(c);
}
public int getIntensity() {
@@ -514,6 +515,7 @@ public class CardView extends GameEntityView {
}
void updateClassLevel(Card c) {
set(TrackableProperty.ClassLevel, c.getClassLevel());
updateMarkerText(c);
}
public int getRingLevel() {
@@ -525,6 +527,41 @@ public class CardView extends GameEntityView {
set(TrackableProperty.RingLevel, p.getNumRingTemptedYou());
}
public String getOverlayText() {
return get(TrackableProperty.OverlayText);
}
public List<String> getMarkerText() {
return get(TrackableProperty.MarkerText);
}
void updateMarkerText(Card c) {
List<String> markerItems = new ArrayList<>();
if(c.getCurrentRoom() != null && !c.getCurrentRoom().isEmpty()) {
markerItems.add("In Room:");
markerItems.add(c.getCurrentRoom());
}
if(c.isClassCard() && c.isInZone(ZoneType.Battlefield)) {
markerItems.add("CL:" + c.getClassLevel());
}
if(getRingLevel() > 0) {
markerItems.add("RL:" + getRingLevel());
}
if(StringUtils.isNotEmpty(c.getOverlayText())) {
set(TrackableProperty.OverlayText, c.getOverlayText());
markerItems.add(c.getOverlayText());
}
else {
//Overlay text is any custom string. It gets mixed in with the other marker lines, but it also needs its
//own property so that it can display in the card detail text.
set(TrackableProperty.OverlayText, null);
}
if(markerItems.isEmpty())
set(TrackableProperty.MarkerText, null);
else
set(TrackableProperty.MarkerText, markerItems);
}
private String getRemembered() {
return get(TrackableProperty.Remembered);
}
@@ -974,6 +1011,7 @@ public class CardView extends GameEntityView {
updateDamage(c);
updateSpecialize(c);
updateRingLevel(c);
updateMarkerText(c);
if (c.getIntensity(false) > 0) {
updateIntensity(c);

View File

@@ -95,8 +95,6 @@ public enum CounterEnumType {
CORRUPTION("CRPTN", 210, 121, 210),
CRANK("CRANK!", 181, 148, 15),
CROAK("CROAK", 155, 255, 5),
CREDIT("CRDIT", 188, 197, 234),

View File

@@ -26,6 +26,8 @@ public class KeywordWithCostAndType extends KeywordInstance<KeywordWithCostAndTy
n[i] = "basic land";
} else if (n[i].equals("Land.Artifact")) {
n[i] = "artifact land";
} else if (n[i].startsWith("Card.with")) {
n[i] = n[i].substring(9);
}
}

View File

@@ -112,6 +112,7 @@ public class Player extends GameEntity implements Comparable<Player> {
private int expentThisTurn;
private int numLibrarySearchedOwn; //The number of times this player has searched his library
private int venturedThisTurn;
private int attractionsVisitedThisTurn;
private int descended;
private int numRingTemptedYou;
private int devotionMod;
@@ -189,7 +190,6 @@ public class Player extends GameEntity implements Comparable<Player> {
private final Map<Card, Integer> commanderCast = Maps.newHashMap();
private final Map<Card, Integer> commanderDamage = Maps.newHashMap();
private DetachedCardEffect commanderEffect = null;
private DetachedCardEffect speedEffect;
private Card monarchEffect;
private Card initiativeEffect;
@@ -197,6 +197,7 @@ public class Player extends GameEntity implements Comparable<Player> {
private Card contraptionSprocketEffect;
private Card radiationEffect;
private Card keywordEffect;
private Card speedEffect;
private Map<Long, Integer> additionalVotes = Maps.newHashMap();
private Map<Long, Integer> additionalOptionalVotes = Maps.newHashMap();
@@ -237,10 +238,6 @@ public class Player extends GameEntity implements Comparable<Player> {
return achievementTracker;
}
public final PlayerOutcome getOutcome() {
return stats.getOutcome();
}
private String chooseName(String originalName) {
String nameCandidate = originalName;
for (int i = 2; i <= 8; i++) { // several tries, not matter how many
@@ -1333,6 +1330,25 @@ public class Player extends GameEntity implements Comparable<Player> {
return drawn;
}
public final void resetNumDrawnThisDrawStep() {
numDrawnThisDrawStep = 0;
}
public final void resetNumDrawnThisTurn() {
numDrawnThisTurn = 0;
view.updateNumDrawnThisTurn(this);
}
public final int getNumDrawnThisTurn() {
return numDrawnThisTurn;
}
public final int getNumDrawnLastTurn() {
return numDrawnLastTurn;
}
public final int numDrawnThisDrawStep() {
return numDrawnThisDrawStep;
}
/**
* Returns PlayerZone corresponding to the given zone of game.
*/
@@ -1360,7 +1376,6 @@ public class Player extends GameEntity implements Comparable<Player> {
}
}
public final CardCollectionView getCardsIn(final ZoneType zoneType) {
return getCardsIn(zoneType, true);
}
@@ -1448,35 +1463,12 @@ public class Player extends GameEntity implements Comparable<Player> {
return CardCollection.combine(getCardsIn(Player.ALL_ZONES), getCardsIn(ZoneType.Stack), inboundTokens);
}
public final void resetNumDrawnThisDrawStep() {
numDrawnThisDrawStep = 0;
}
public final void resetNumDrawnThisTurn() {
numDrawnThisTurn = 0;
view.updateNumDrawnThisTurn(this);
}
public final int getNumDrawnThisTurn() {
return numDrawnThisTurn;
}
public final int getNumDrawnLastTurn() {
return numDrawnLastTurn;
}
public final int numDrawnThisDrawStep() {
return numDrawnThisDrawStep;
}
public final void resetNumRollsThisTurn() {
numRollsThisTurn = 0;
}
public final int getNumRollsThisTurn() {
return numRollsThisTurn;
}
public void roll() {
numRollsThisTurn++;
}
@@ -1954,37 +1946,23 @@ public class Player extends GameEntity implements Comparable<Player> {
completedDungeons.clear();
}
public final int getNumRingTemptedYou() {
return numRingTemptedYou;
}
public final void incrementRingTemptedYou() {
numRingTemptedYou++;
}
public final void setNumRingTemptedYou(int value) {
numRingTemptedYou = value;
}
public final void resetRingTemptedYou() {
numRingTemptedYou = 0;
}
public final int getSpeed() {
return speed;
}
public final void increaseSpeed() {
if (speedEffect == null) createSpeedEffect();
if (!maxSpeed()) { // can't increase past 4
int old = speed;
speed++;
view.updateSpeed(this);
getGame().fireEvent(new GameEventSpeedChanged(this, old, speed)); //play sound effect
updateSpeedEffect();
}
}
public final void decreaseSpeed() {
if (speed > 1) { // can't decrease speed below 1
int old = speed;
speed--;
view.updateSpeed(this);
game.fireEvent(new GameEventSpeedChanged(this, old, speed));
updateSpeedEffect();
}
}
public final boolean noSpeed() {
@@ -1995,26 +1973,62 @@ public class Player extends GameEntity implements Comparable<Player> {
}
public final void setSpeed(int i) { //just used for copy/save
speed = i;
if (speed > 0) view.updateSpeed(this);
if(this.speedEffect != null)
updateSpeedEffect();
}
public final void createSpeedEffect() {
final PlayerZone com = getZone(ZoneType.Command);
DetachedCardEffect eff = new DetachedCardEffect(this, "Speed Effect");
if(this.speedEffect != null || this.noSpeed())
return;
speedEffect = new Card(game.nextCardId(), null, game);
speedEffect.setOwner(this);
speedEffect.setGamePieceType(GamePieceType.EFFECT);
speedEffect.addAlternateState(CardStateName.Flipped, false);
CardState speedFront = speedEffect.getState(CardStateName.Original);
CardState speedBack = speedEffect.getState(CardStateName.Flipped);
speedFront.setImageKey("t:speed");
speedFront.setName("Start Your Engines!");
speedBack.setImageKey("t:max_speed");
speedBack.setName("Max Speed!");
String label = Localizer.getInstance().getMessage("lblSpeed", this.speed);
speedEffect.setOverlayText(label);
// 702.179d There is an inherent triggered ability associated with a player having 1 or more speed. This ability has no source and is controlled by that player.
// That ability is “Whenever one or more opponents lose life during your turn, if your speed is less than 4, your speed increases by 1. This ability triggers only once each turn.”
String trigger = "Mode$ LifeLostAll | ValidPlayer$ Opponent | TriggerZones$ Command | ActivationLimit$ 1 | " +
"PlayerTurn$ True | CheckSVar$ Count$YourSpeed | SVarCompare$ LT4 | "
+ "TriggerDescription$ Whenever one or more opponents lose life during your turn, if your speed is less than 4, your speed increases by 1. This ability triggers only once each turn.";
String speedUp = "DB$ ChangeSpeed";
Trigger lifeLostTrigger = TriggerHandler.parseTrigger(trigger, eff, true);
lifeLostTrigger.setOverridingAbility(AbilityFactory.getAbility(speedUp, eff));
eff.addTrigger(lifeLostTrigger);
this.speedEffect = eff;
com.add(eff);
}
Trigger lifeLostTrigger = TriggerHandler.parseTrigger(trigger, speedEffect, true);
lifeLostTrigger.setOverridingAbility(AbilityFactory.getAbility(speedUp, speedEffect));
speedFront.addTrigger(lifeLostTrigger);
public final List<Card> getPlaneswalkedToThisTurn() {
return planeswalkedToThisTurn;
speedEffect.updateStateForView();
if(this.maxSpeed())
speedEffect.setState(CardStateName.Flipped, true);
final PlayerZone com = getZone(ZoneType.Command);
com.add(speedEffect);
this.updateZoneForView(com);
}
protected void updateSpeedEffect() {
if(this.speedEffect == null) {
if(this.noSpeed())
return;
createSpeedEffect();
}
Localizer localizer = Localizer.getInstance();
String label = this.maxSpeed() ? localizer.getMessage("lblMaxSpeed") : localizer.getMessage("lblSpeed", this.speed);
speedEffect.setOverlayText(label);
if(maxSpeed() && speedEffect.getCurrentStateName() == CardStateName.Original)
speedEffect.setState(CardStateName.Flipped, true);
else if(!maxSpeed() && speedEffect.getCurrentStateName() == CardStateName.Flipped)
speedEffect.setState(CardStateName.Original, true);
}
public final void altWinBySpellEffect(final String sourceName) {
@@ -2477,6 +2491,9 @@ public class Player extends GameEntity implements Comparable<Player> {
return game.getMatch().getPlayers().get(game.getRegisteredPlayers().indexOf(this));
}
public final PlayerOutcome getOutcome() {
return stats.getOutcome();
}
private void setOutcome(PlayerOutcome outcome) {
stats.setOutcome(outcome);
}
@@ -2573,6 +2590,7 @@ public class Player extends GameEntity implements Comparable<Player> {
setCommitedCrimeThisTurn(0);
diceRollsThisTurn = Lists.newArrayList();
setExpentThisTurn(0);
attractionsVisitedThisTurn = 0;
damageReceivedThisTurn.clear();
planeswalkedToThisTurn.clear();
@@ -2705,6 +2723,10 @@ public class Player extends GameEntity implements Comparable<Player> {
return !isInGame();
}
public final List<Card> getPlaneswalkedToThisTurn() {
return planeswalkedToThisTurn;
}
/**
* Takes the top plane of the planar deck and put it face up in the command zone.
* Then runs triggers.
@@ -3391,6 +3413,19 @@ public class Player extends GameEntity implements Comparable<Player> {
getTheRing().updateStateForView();
}
public final int getNumRingTemptedYou() {
return numRingTemptedYou;
}
public final void incrementRingTemptedYou() {
numRingTemptedYou++;
}
public final void setNumRingTemptedYou(int value) {
numRingTemptedYou = value;
}
public final void resetRingTemptedYou() {
numRingTemptedYou = 0;
}
public void changeOwnership(Card card) {
// If lost then gained, just clear out of lost.
// If gained then lost, just clear out of gained.
@@ -3615,6 +3650,26 @@ public class Player extends GameEntity implements Comparable<Player> {
return radiationEffect != null;
}
public Card getKeywordCard() {
if (keywordEffect != null) {
return keywordEffect;
}
final PlayerZone com = getZone(ZoneType.Command);
keywordEffect = new Card(game.nextCardId(), null, game);
keywordEffect.setGamePieceType(GamePieceType.EFFECT);
keywordEffect.setOwner(this);
keywordEffect.setName("Keyword Effects");
keywordEffect.setImageKey(ImageKeys.HIDDEN_CARD);
keywordEffect.updateStateForView();
com.add(keywordEffect);
this.updateZoneForView(com);
return keywordEffect;
}
public void updateKeywordCardAbilityText() {
if (getKeywordCard() == null)
return;
@@ -3739,27 +3794,6 @@ public class Player extends GameEntity implements Comparable<Player> {
|| !hasKeyword("Spells and abilities you control can't cause you to search your library.");
}
public Card getKeywordCard() {
if (keywordEffect != null) {
return keywordEffect;
}
final PlayerZone com = getZone(ZoneType.Command);
keywordEffect = new Card(game.nextCardId(), null, game);
keywordEffect.setGamePieceType(GamePieceType.EFFECT);
keywordEffect.setOwner(this);
keywordEffect.setName("Keyword Effects");
keywordEffect.setImageKey(ImageKeys.HIDDEN_CARD);
keywordEffect.updateStateForView();
com.add(keywordEffect);
this.updateZoneForView(com);
return keywordEffect;
}
public void addAdditionalVote(long timestamp, int value) {
additionalVotes.put(timestamp, value);
getView().updateAdditionalVote(this);
@@ -3950,7 +3984,6 @@ public class Player extends GameEntity implements Comparable<Player> {
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromPlayer(this);
game.getTriggerHandler().runTrigger(TriggerType.CommitCrime, runParams, false);
}
public int getCommittedCrimeThisTurn() {
@@ -3977,12 +4010,17 @@ public class Player extends GameEntity implements Comparable<Player> {
public void visitAttractions(int light) {
CardCollection attractions = CardLists.filter(getCardsIn(ZoneType.Battlefield), CardPredicates.isAttractionWithLight(light));
for (Card c : attractions) {
if(!c.wasVisitedThisTurn())
this.attractionsVisitedThisTurn++;
c.visitAttraction(this);
}
}
public void rollToVisitAttractions() {
this.visitAttractions(RollDiceEffect.rollDiceForPlayerToVisitAttractions(this));
}
public int getAttractionsVisitedThisTurn() {
return this.attractionsVisitedThisTurn;
}
public int getCrankCounter() {
return this.crankCounter;
@@ -3990,8 +4028,8 @@ public class Player extends GameEntity implements Comparable<Player> {
public void setCrankCounter(int counters) {
this.crankCounter = counters;
if (this.contraptionSprocketEffect != null) {
Map<CounterType, Integer> counterMap = Map.of(CounterType.get(CounterEnumType.CRANK), this.crankCounter);
contraptionSprocketEffect.setCounters(counterMap);
String label = Localizer.getInstance().getMessage("lblCrank", this.crankCounter);
contraptionSprocketEffect.setOverlayText(label);
}
else if (this.getCardsIn(ZoneType.Battlefield).anyMatch(CardPredicates.CONTRAPTIONS)) {
this.createContraptionSprockets();
@@ -4017,12 +4055,8 @@ public class Player extends GameEntity implements Comparable<Player> {
contraptionSprocketEffect.setName("Contraption Sprockets");
contraptionSprocketEffect.setGamePieceType(GamePieceType.EFFECT);
//Add "counters" on the effect to represent the current CRANK counter position.
//This and some other un-cards could benefit from a distinct system for positional counters or markers,
//see for instance Baron von Count or B-I-N-G-O. For now this is sufficient to display the current sprocket
//on the counter overlay, and I don't think any existing effect will notice it.
Map<CounterType, Integer> counterMap = Map.of(CounterType.get(CounterEnumType.CRANK), this.crankCounter);
contraptionSprocketEffect.setCounters(counterMap);
String label = Localizer.getInstance().getMessage("lblCrank", this.crankCounter);
contraptionSprocketEffect.setOverlayText(label);
contraptionSprocketEffect.setText("At the beginning of your upkeep, if you control a Contraption, move the CRANK! counter to the next sprocket and crank any number of that sprocket's Contraptions.");
contraptionSprocketEffect.updateStateForView();

View File

@@ -329,13 +329,6 @@ public class PlayerView extends GameEntityView {
set(TrackableProperty.ControlVotes, val);
}
public int getSpeed() {
return get(TrackableProperty.Speed);
}
void updateSpeed(Player p) {
set(TrackableProperty.Speed, p.getSpeed());
}
public int getAdditionalVillainousChoices() {
return get(TrackableProperty.AdditionalVillainousChoices);
}
@@ -604,10 +597,6 @@ public class PlayerView extends GameEntityView {
}
details.add(Localizer.getInstance().getMessage("lblExtraTurnCountHas", String.valueOf(getExtraTurnCount())));
if (getSpeed() > 0) {
details.add(Localizer.getInstance().getMessage("lblSpeed", String.valueOf(getSpeed())));
}
final String keywords = Lang.joinHomogenous(getDisplayableKeywords());
if (!keywords.isEmpty()) {
details.add(keywords);

View File

@@ -344,12 +344,10 @@ public class StaticAbility extends CardTraitBase implements IIdentifiable, Clone
if (zone == null || !this.validHostZones.contains(zone.getZoneType())) {
return false;
}
} else {
if (!getHostCard().isInPlay()) { // default
} else if (!getHostCard().isInPlay()) { // default
return false;
}
}
}
String condition = getParam("Condition");
if (null != condition) {

View File

@@ -917,12 +917,11 @@ public final class StaticAbilityContinuous {
private static ColorSet getColorsFromParam(StaticAbility stAb, final String colors) {
final Card hostCard = stAb.getHostCard();
ColorSet addColors;
ColorSet addColors = null;
if (colors.equals("ChosenColor")) {
if (hostCard.hasChosenColor()) {
addColors = ColorSet.fromNames(hostCard.getChosenColors());
}
return null;
} else if (colors.equals("All")) {
addColors = ColorSet.ALL_COLORS;
} else {

View File

@@ -384,17 +384,15 @@ public class TriggerHandler {
return false; // It's not the right phase to go off.
}
if (TriggerType.Always.equals(regtrig.getMode())) {
if (game.getStack().hasStateTrigger(regtrig.getId())) {
return false; // State triggers that are already on the stack
// don't trigger again.
}
}
if (regtrig.isSuppressed()) {
return false; // Trigger removed by effect
}
if (TriggerType.Always.equals(regtrig.getMode()) && game.getStack().hasStateTrigger(regtrig.getId())) {
return false; // State triggers that are already on the stack
// don't trigger again.
}
// do not check delayed
if (regtrig.getSpawningAbility() == null && !regtrig.zonesCheck(game.getZoneOf(regtrig.getHostCard()))) {
return false; // Host card isn't where it needs to be.
@@ -415,6 +413,10 @@ public class TriggerHandler {
return false; // Not the right mode.
}
if (regtrig.isSuppressed()) {
return false; // Trigger removed by effect
}
/* this trigger can only be activated once per turn, verify it hasn't already run */
if (!regtrig.checkActivationLimit()) {
return false;
@@ -431,21 +433,17 @@ public class TriggerHandler {
if (!regtrig.performTest(runParams)) {
return false; // Test failed.
}
if (regtrig.isSuppressed()) {
return false; // Trigger removed by effect
}
if (TriggerType.Always.equals(regtrig.getMode())) {
if (game.getStack().hasStateTrigger(regtrig.getId())) {
if (TriggerType.Always.equals(regtrig.getMode()) && game.getStack().hasStateTrigger(regtrig.getId())) {
return false; // State triggers that are already on the stack
// don't trigger again.
}
}
// check if any static abilities are disabling the trigger (Torpor Orb and the like)
if (!regtrig.isStatic() && StaticAbilityDisableTriggers.disabled(game, regtrig, runParams)) {
return false;
}
return true;
}

View File

@@ -85,6 +85,8 @@ public enum TrackableProperty {
RingLevel(TrackableTypes.IntegerType),
CurrentRoom(TrackableTypes.StringType),
Intensity(TrackableTypes.IntegerType),
OverlayText(TrackableTypes.StringType),
MarkerText(TrackableTypes.StringListType),
Remembered(TrackableTypes.StringType),
NamedCard(TrackableTypes.StringListType),
PlayerMayLook(TrackableTypes.PlayerViewCollectionType, FreezeMode.IgnoresFreeze),
@@ -217,7 +219,6 @@ public enum TrackableProperty {
CommanderCast(TrackableTypes.IntegerMapType),
CommanderDamage(TrackableTypes.IntegerMapType),
MindSlaveMaster(TrackableTypes.PlayerViewType),
Speed(TrackableTypes.IntegerType),
Ante(TrackableTypes.CardViewCollectionType, FreezeMode.IgnoresFreeze),
Battlefield(TrackableTypes.CardViewCollectionType, FreezeMode.IgnoresFreeze), //zones can't respect freeze, otherwise cards that die from state based effects won't have that reflected in the UI

View File

@@ -23,7 +23,7 @@
<parent>
<artifactId>forge</artifactId>
<groupId>forge</groupId>
<version>${revision}</version>
<version>2.0.02</version>
</parent>
<artifactId>forge-gui-android</artifactId>
@@ -66,7 +66,7 @@
<configuration>
<!-- generate versionName from revision property to snapshot-version property -->
<name>snapshot-version</name>
<value>${revision}</value>
<value>2.0.02-SNAPSHOT</value>
<regex>-SNAPSHOT</regex>
<replacement>-SNAPSHOT-${month.date}</replacement>
<failIfNoMatch>false</failIfNoMatch>

View File

@@ -1,11 +1,10 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>forge</artifactId>
<groupId>forge</groupId>
<version>${revision}</version>
<version>2.0.02</version>
</parent>
<artifactId>forge-gui-desktop</artifactId>
@@ -50,7 +49,7 @@
<configuration>
<!-- generate versionName from revision property to snapshot-version property -->
<name>snapshot-version</name>
<value>${revision}</value>
<value>2.0.02-SNAPSHOT</value>
<regex>-SNAPSHOT</regex>
<replacement>-SNAPSHOT-${month.date}</replacement>
<failIfNoMatch>false</failIfNoMatch>
@@ -489,31 +488,17 @@
<mkdir dir="${project.build.directory}/${project.build.finalName}-osx" />
<copy todir="${project.build.directory}/${project.build.finalName}-osx">
<fileset dir="${basedir}/../forge-gui/" includes="LICENSE.txt" />
<fileset dir="${basedir}/../forge-gui/release-files/"
includes="CHANGES.txt"/>
<fileset dir="${basedir}/../forge-gui/release-files/"
includes="CONTRIBUTORS.txt"/>
<fileset dir="${basedir}/../forge-gui/release-files/"
includes="ISSUES.txt"/>
<fileset dir="${basedir}/../forge-gui/release-files/"
includes="INSTALLATION.txt"/>
<fileset dir="${basedir}/../forge-gui/release-files/"
includes="GAMEPAD_README.txt"/>
<fileset dir="${basedir}/../forge-gui/release-files/" includes="CHANGES.txt" />
<fileset dir="${basedir}/../forge-gui/release-files/" includes="CONTRIBUTORS.txt" />
<fileset dir="${basedir}/../forge-gui/release-files/" includes="ISSUES.txt" />
<fileset dir="${basedir}/../forge-gui/release-files/" includes="INSTALLATION.txt" />
<fileset dir="${basedir}/../forge-gui/release-files/" includes="GAMEPAD_README.txt" />
<fileset dir="${basedir}/../forge-gui/" includes="MANUAL.txt" />
<fileset dir="${basedir}/" includes="sentry.properties" />
</copy>
<taskdef name="bundleapp"
classpath="${basedir}/../forge-gui/${configSourceDirectory}/appbundler-1.0-custom.jar"
classname="com.oracle.appbundler.AppBundlerTask"/>
<bundleapp
outputdirectory="${project.build.directory}/${project.build.finalName}-osx"
name="${project.name}" displayname="${project.name}"
shortversion="${project.version}" identifier="forge.view.Main"
icon="${basedir}/${configSourceDirectory}/Forge.icns"
applicationCategory="public.app-category.games"
mainclassname="forge.view.Main">
<classpath
file="${project.build.directory}/${project.build.finalName}-jar-with-dependencies.jar"/>
<taskdef name="bundleapp" classpath="${basedir}/../forge-gui/${configSourceDirectory}/appbundler-1.0-custom.jar" classname="com.oracle.appbundler.AppBundlerTask" />
<bundleapp outputdirectory="${project.build.directory}/${project.build.finalName}-osx" name="${project.name}" displayname="${project.name}" shortversion="${project.version}" identifier="forge.view.Main" icon="${basedir}/${configSourceDirectory}/Forge.icns" applicationCategory="public.app-category.games" mainclassname="forge.view.Main">
<classpath file="${project.build.directory}/${project.build.finalName}-jar-with-dependencies.jar" />
<classpath file="${basedir}/../forge-gui/forge.profile.properties.example" />
<option value="-Dapple.laf.useScreenMenuBar=true" />
<option value="-Dcom.apple.macos.use-file-dialog-packages=true" />
@@ -527,19 +512,12 @@
<fileset dir="${basedir}/../forge-gui/res" excludes="**/cardsfolder/**" />
</copy>
<mkdir dir="${project.build.directory}/${project.build.finalName}-osx/Forge.app/Contents/Resources/res/cardsfolder" />
<zip destfile="${project.build.directory}/${project.build.finalName}-osx/Forge.app/Contents/Resources/res/cardsfolder/cardsfolder.zip"
basedir="${basedir}/../forge-gui/res/cardsfolder" level="1"/>
<symlink
link="${project.build.directory}/${project.build.finalName}-osx/Applications"
resource="/Applications"/>
<exec executable="${basedir}/../forge-gui/${configSourceDirectory}/create-dmg"
failonerror="false">
<zip destfile="${project.build.directory}/${project.build.finalName}-osx/Forge.app/Contents/Resources/res/cardsfolder/cardsfolder.zip" basedir="${basedir}/../forge-gui/res/cardsfolder" level="1" />
<symlink link="${project.build.directory}/${project.build.finalName}-osx/Applications" resource="/Applications" />
<exec executable="${basedir}/../forge-gui/${configSourceDirectory}/create-dmg" failonerror="false">
<arg line="--volname ${project.name}-${project.version} --background ${basedir}/../forge-gui/${configSourceDirectory}/backgroundImage.jpg --window-size 700 419 --icon-size 64 --icon ${forge.file.name} 141 283 --icon ${applications.file.name} 452 283 --icon ${changes.file.name} 645 80 --icon ${license.file.name} 645 200 --icon ${readme.file.name} 645 320 ${project.build.directory}/${project.build.finalName}.dmg ${project.build.directory}/${project.build.finalName}-osx" />
</exec>
<tar basedir="${project.build.directory}"
includes="${project.build.finalName}.dmg"
destfile="${project.build.directory}/${project.build.finalName}-osx.tar.bz2"
compression="bzip2"/>
<tar basedir="${project.build.directory}" includes="${project.build.finalName}.dmg" destfile="${project.build.directory}/${project.build.finalName}-osx.tar.bz2" compression="bzip2" />
<!--<symlink link="${project.build.directory}/${project.build.finalName}-osx/Applications" action="delete" /> -->
<exec executable="rm" failonerror="false">
<arg line="-f ${project.build.directory}/${project.build.finalName}-osx/Applications" />
@@ -629,18 +607,9 @@
<fileset dir="${basedir}/../forge-gui/" includes="MANUAL.txt" />
<fileset dir="${basedir}/" includes="sentry.properties" />
</copy>
<taskdef name="bundleapp"
classpath="${basedir}/../forge-gui/${configSourceDirectory}/appbundler-1.0-custom.jar"
classname="com.oracle.appbundler.AppBundlerTask"/>
<bundleapp
outputdirectory="${project.build.directory}/${project.build.finalName}-osx"
name="${project.name}" displayname="${project.name}"
shortversion="${project.version}" identifier="forge.view.Main"
icon="${basedir}/${configSourceDirectory}/Forge.icns"
applicationCategory="public.app-category.games"
mainclassname="forge.view.Main">
<classpath
file="${project.build.directory}/${project.build.finalName}-jar-with-dependencies.jar"/>
<taskdef name="bundleapp" classpath="${basedir}/../forge-gui/${configSourceDirectory}/appbundler-1.0-custom.jar" classname="com.oracle.appbundler.AppBundlerTask" />
<bundleapp outputdirectory="${project.build.directory}/${project.build.finalName}-osx" name="${project.name}" displayname="${project.name}" shortversion="${project.version}" identifier="forge.view.Main" icon="${basedir}/${configSourceDirectory}/Forge.icns" applicationCategory="public.app-category.games" mainclassname="forge.view.Main">
<classpath file="${project.build.directory}/${project.build.finalName}-jar-with-dependencies.jar" />
<classpath file="${basedir}/../forge-gui/forge.profile.properties.example" />
<option value="-Dapple.laf.useScreenMenuBar=true" />
<option value="-Dcom.apple.macos.use-file-dialog-packages=true" />
@@ -654,19 +623,12 @@
<fileset dir="${basedir}/../forge-gui/res" excludes="**/cardsfolder/**" />
</copy>
<mkdir dir="${project.build.directory}/${project.build.finalName}-osx/Forge.app/Contents/Resources/res/cardsfolder" />
<zip destfile="${project.build.directory}/${project.build.finalName}-osx/Forge.app/Contents/Resources/res/cardsfolder/cardsfolder.zip"
basedir="${basedir}/../forge-gui/res/cardsfolder" level="1"/>
<symlink
link="${project.build.directory}/${project.build.finalName}-osx/Applications"
resource="/Applications"/>
<exec executable="${basedir}/../forge-gui/${configSourceDirectory}/create-dmg"
failonerror="false">
<zip destfile="${project.build.directory}/${project.build.finalName}-osx/Forge.app/Contents/Resources/res/cardsfolder/cardsfolder.zip" basedir="${basedir}/../forge-gui/res/cardsfolder" level="1" />
<symlink link="${project.build.directory}/${project.build.finalName}-osx/Applications" resource="/Applications" />
<exec executable="${basedir}/../forge-gui/${configSourceDirectory}/create-dmg" failonerror="false">
<arg line="--volname ${project.name}-${project.version} --background ${basedir}/../forge-gui/${configSourceDirectory}/backgroundImage.jpg --window-size 700 419 --icon-size 64 --icon ${forge.file.name} 141 283 --icon ${applications.file.name} 452 283 --icon ${changes.file.name} 645 80 --icon ${license.file.name} 645 200 --icon ${readme.file.name} 645 320 ${project.build.directory}/${project.build.finalName}.dmg ${project.build.directory}/${project.build.finalName}-osx" />
</exec>
<tar basedir="${project.build.directory}"
includes="${project.build.finalName}.dmg"
destfile="${project.build.directory}/${project.build.finalName}-osx.tar.bz2"
compression="bzip2"/>
<tar basedir="${project.build.directory}" includes="${project.build.finalName}.dmg" destfile="${project.build.directory}/${project.build.finalName}-osx.tar.bz2" compression="bzip2" />
<!--<symlink link="${project.build.directory}/${project.build.finalName}-osx/Applications" action="delete" /> -->
<exec executable="rm" failonerror="false">
<arg line="-f ${project.build.directory}/${project.build.finalName}-osx/Applications" />

View File

@@ -533,11 +533,8 @@ public class CardPanel extends SkinnedPanel implements CardContainer, IDisposabl
}
if (card.getCurrentRoom() != null && !card.getCurrentRoom().isEmpty()) {
List<String> markers = new ArrayList<>();
markers.add("In Room:");
markers.add(card.getCurrentRoom());
drawMarkersTabs(g, markers);
if(card.getMarkerText() != null) {
drawMarkersTabs(g, card.getMarkerText());
}
final int combatXSymbols = (cardXOffset + (cardWidth / 4)) - 16;

View File

@@ -11,7 +11,7 @@
<parent>
<artifactId>forge</artifactId>
<groupId>forge</groupId>
<version>${revision}</version>
<version>2.0.02</version>
</parent>
<artifactId>forge-gui-ios</artifactId>
@@ -35,7 +35,7 @@
<filtering>true</filtering>
</resource>
</resources>
<finalName>forge-ios-${revision}</finalName>
<finalName>forge-ios-2.0.02-SNAPSHOT</finalName>
</build>
<dependencies>

View File

@@ -9,7 +9,7 @@
<parent>
<artifactId>forge</artifactId>
<groupId>forge</groupId>
<version>${revision}</version>
<version>2.0.02</version>
</parent>
<artifactId>forge-gui-mobile-dev</artifactId>
@@ -45,7 +45,7 @@
<configuration>
<!-- generate versionName from revision property to snapshot-version property -->
<name>snapshot-version</name>
<value>${revision}</value>
<value>2.0.02-SNAPSHOT</value>
<regex>-SNAPSHOT</regex>
<replacement>-SNAPSHOT-${month.date}</replacement>
<failIfNoMatch>false</failIfNoMatch>

View File

@@ -4,7 +4,7 @@
<parent>
<artifactId>forge</artifactId>
<groupId>forge</groupId>
<version>${revision}</version>
<version>2.0.02</version>
</parent>
<artifactId>forge-gui-mobile</artifactId>

View File

@@ -781,22 +781,11 @@ public class CardRenderer {
}
if (card.getCurrentRoom() != null && !card.getCurrentRoom().isEmpty()) {
List<String> markers = new ArrayList<>();
markers.add("In Room:");
markers.add(card.getCurrentRoom());
if(card.getMarkerText() != null) {
List<String> markers = card.getMarkerText();
if(markers.size() > 1) //Use smaller text for multi-line strings.
drawMarkersTabs(markers, g, x, y, w, h, false);
}
//Class level
if (card.getCurrentState().getType().hasStringType("Class") && ZoneType.Battlefield.equals(card.getZone())) {
List<String> markers = new ArrayList<>();
markers.add("CL:" + card.getClassLevel());
drawMarkersTabs(markers, g, x, y - markersHeight, w, h, true);
}
//Ring level
if (card.getRingLevel() > 0) {
List<String> markers = new ArrayList<>();
markers.add("RL:" + card.getRingLevel());
else
drawMarkersTabs(markers, g, x, y - markersHeight, w, h, true);
}
@@ -1528,7 +1517,7 @@ public class CardRenderer {
int pageSize = 128;
//only generate images for characters that could be used by Forge
String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890/-+:'";
String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890/-+:'!—";
final PixmapPacker packer = new PixmapPacker(pageSize, pageSize, Pixmap.Format.RGBA8888, 2, false);
final FreeTypeFontParameter parameter = new FreeTypeFontParameter();

View File

@@ -4,7 +4,7 @@
<parent>
<artifactId>forge</artifactId>
<groupId>forge</groupId>
<version>${revision}</version>
<version>2.0.02</version>
</parent>
<artifactId>forge-gui</artifactId>

View File

@@ -1,6 +1,6 @@
Name:Aether Gust
ManaCost:1 U
Types:Instant
A:SP$ ChangeZone | ValidTgts$ Card.inZoneStack+Red,Card.inZoneStack+Green,Permanent.Red,Permanent.Green | TgtZone$ Battlefield,Stack | TgtPrompt$ Select target spell or permanent that's red or green | AlternativeDecider$ TargetedOwner | Origin$ Battlefield,Stack | Fizzle$ True | Destination$ Library | DestinationAlternative$ Library | LibraryPositionAlternative$ -1 | StackDescription$ {c:TargetedOwner} puts {c:Targeted} on the top or bottom of their library. | SpellDescription$ Choose target spell or permanent that's red or green. Its owner puts it on the top or bottom of their library.
A:SP$ ChangeZone | ValidTgts$ Card.inZoneStack+Red,Card.inZoneStack+Green,Permanent.Red,Permanent.Green | TgtZone$ Battlefield,Stack | TgtPrompt$ Select target spell or permanent that's red or green | AlternativeDecider$ TargetedOwner | Origin$ Battlefield,Stack | Destination$ Library | DestinationAlternative$ Library | LibraryPositionAlternative$ -1 | StackDescription$ {c:TargetedOwner} puts {c:Targeted} on the top or bottom of their library. | SpellDescription$ Choose target spell or permanent that's red or green. Its owner puts it on the top or bottom of their library.
AI:RemoveDeck:Random
Oracle:Choose target spell or permanent that's red or green. Its owner puts it on the top or bottom of their library.

View File

@@ -2,8 +2,8 @@ Name:Ahn-Crop Invader
ManaCost:2 R
Types:Creature Zombie Minotaur Warrior
PT:2/2
S:Mode$ Continuous | Affected$ Card.Self | AddKeyword$ First Strike | Condition$ PlayerTurn | Description$ As long as it's your turn, CARDNAME has first strike.
S:Mode$ Continuous | Affected$ Card.Self | AddKeyword$ First Strike | Condition$ PlayerTurn | Description$ During your turn, CARDNAME has first strike.
A:AB$ Pump | Cost$ 1 Sac<1/Creature.Other/another creature> | Defined$ Self | NumAtt$ +2 | AILogic$ Aristocrat | SpellDescription$ CARDNAME gets +2/+0 until end of turn.
SVar:AIPreference:SacCost$Creature.Other
DeckHas:Ability$Sacrifice
Oracle:As long as it's your turn, Ahn-Crop Invader has first strike.\n{1}, Sacrifice another creature: Ahn-Crop Invader gets +2/+0 until end of turn.
Oracle:During your turn, Ahn-Crop Invader has first strike.\n{1}, Sacrifice another creature: Ahn-Crop Invader gets +2/+0 until end of turn.

View File

@@ -2,7 +2,7 @@ Name:Anara, Wolvid Familiar
ManaCost:3 G
Types:Legendary Creature Wolf Beast
PT:4/4
S:Mode$ Continuous | Affected$ Permanent.IsCommander+YouCtrl | AddKeyword$ Indestructible | Condition$ PlayerTurn | Description$ As long as it's your turn, commanders you control have indestructible. (Effects that say "destroy" don't destroy them. A creature with indestructible can't be destroyed by damage.)
S:Mode$ Continuous | Affected$ Permanent.IsCommander+YouCtrl | AddKeyword$ Indestructible | Condition$ PlayerTurn | Description$ During your turn, commanders you control have indestructible. (Effects that say "destroy" don't destroy them. A creature with indestructible can't be destroyed by damage.)
K:Partner
AI:RemoveDeck:NonCommander
Oracle:As long as it's your turn, commanders you control have indestructible. (Effects that say "destroy" don't destroy them. A creature with indestructible can't be destroyed by damage.)\nPartner (You can have two commanders if both have partner.)
Oracle:During your turn, commanders you control have indestructible. (Effects that say "destroy" don't destroy them. A creature with indestructible can't be destroyed by damage.)\nPartner (You can have two commanders if both have partner.)

View File

@@ -2,8 +2,7 @@ Name:Angelic Observer
ManaCost:5 W
Types:Creature Angel Advisor
PT:3/3
K:Affinity:Citizen
K:Flying
S:Mode$ ReduceCost | ValidCard$ Card.Self | Type$ Spell | Amount$ X | EffectZone$ All | Description$ This spell costs {1} less to cast for each Citizen you control.
SVar:X:Count$TypeYouCtrl.Citizen
DeckHints:Type$Citizen
Oracle:This spell costs {1} less to cast for each Citizen you control.\nFlying
Oracle:Affinity for Citizens (This spell costs {1} less to cast for each Citizen you control.)\nFlying

View File

@@ -3,9 +3,9 @@ ManaCost:2 W W
Types:Creature Human
PT:2+*/2+*
K:Trample
S:Mode$ Continuous | CharacteristicDefining$ True | SetPower$ X | SetToughness$ X | Condition$ PlayerTurn | Description$ As long as it's your turn, CARDNAME's power and toughness are each equal to 2 plus the number of Swamps your opponents control. As long as it's not your turn, CARDNAME's power and toughness are each 2.
S:Mode$ Continuous | CharacteristicDefining$ True | SetPower$ X | SetToughness$ X | Condition$ PlayerTurn | Description$ During your turn, CARDNAME's power and toughness are each equal to 2 plus the number of Swamps your opponents control. During turns other than yours, CARDNAME's power and toughness are each 2.
SVar:X:Count$Valid Swamp.OppCtrl/Plus.2
S:Mode$ Continuous | CharacteristicDefining$ True | SetPower$ 2 | SetToughness$ 2 | Condition$ NotPlayerTurn
AI:RemoveDeck:Random
DeckHints:Name$Urborg, Tomb of Yawgmoth & Keyword$Swampwalk
Oracle:Trample\nAs long as it's your turn, Angry Mob's power and toughness are each equal to 2 plus the number of Swamps your opponents control. As long as it's not your turn, Angry Mob's power and toughness are each 2.
Oracle:Trample\nDuring your turn, Angry Mob's power and toughness are each equal to 2 plus the number of Swamps your opponents control. During turns other than yours, Angry Mob's power and toughness are each 2.

View File

@@ -2,7 +2,6 @@ Name:Argivian Phalanx
ManaCost:5 W
Types:Creature Human Kor Soldier
PT:4/4
K:Affinity:Creature
K:Vigilance
S:Mode$ ReduceCost | ValidCard$ Card.Self | Type$ Spell | Amount$ X | EffectZone$ All | Description$ This spell costs {1} less to cast for each creature you control.
SVar:X:Count$Valid Creature.YouCtrl
Oracle:This spell costs {1} less to cast for each creature you control.\nVigilance
Oracle:Affinity for creatures (This spell costs {1} less to cast for each creature you control.)\nVigilance

View File

@@ -5,7 +5,7 @@ K:Flash
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigExile | TriggerDescription$ When CARDNAME enters, exile target spell.
S:Mode$ CantBeCast | ValidCard$ Card.nonLand+sharesNameWith Remembered.ExiledWithSource | Caster$ Opponent | Description$ Your opponents can't cast spells with the same name as the card exiled by CARDNAME.
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.Self | Execute$ TrigBounce | TriggerDescription$ When CARDNAME leaves the battlefield, return the exiled card to its owner's hand.
SVar:TrigExile:DB$ ChangeZone | TargetType$ Spell | ValidTgts$ Card | TgtZone$ Stack | Origin$ Stack | Fizzle$ True | Destination$ Exile | IsCurse$ True | TgtPrompt$ Choose target spell | RememberChanged$ True
SVar:TrigExile:DB$ ChangeZone | TargetType$ Spell | ValidTgts$ Card | TgtZone$ Stack | Origin$ Stack | Destination$ Exile | IsCurse$ True | TgtPrompt$ Choose target spell | RememberChanged$ True
SVar:TrigBounce:DB$ ChangeZone | Origin$ Exile | Destination$ Hand | Defined$ Remembered | SubAbility$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
Oracle:Flash\nWhen Ashiok's Erasure enters, exile target spell.\nYour opponents can't cast spells with the same name as the exiled card.\nWhen Ashiok's Erasure leaves the battlefield, return the exiled card to its owner's hand.

View File

@@ -1,9 +1,9 @@
Name:At Knifepoint
ManaCost:1 B R
Types:Enchantment
S:Mode$ Continuous | Affected$ Creature.Outlaw+YouCtrl | AddKeyword$ First Strike | Condition$ PlayerTurn | Description$ As long as it's your turn, outlaws you control have first strike. (Assassins, Mercenaries, Pirates, Rogues, and Warlocks are outlaws.)
S:Mode$ Continuous | Affected$ Creature.Outlaw+YouCtrl | AddKeyword$ First Strike | Condition$ PlayerTurn | Description$ During your turn, outlaws you control have first strike. (Assassins, Mercenaries, Pirates, Rogues, and Warlocks are outlaws.)
T:Mode$ CommitCrime | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigToken | ActivationLimit$ 1 | TriggerDescription$ Whenever you commit a crime, create a 1/1 red Mercenary creature token with "{T}: Target creature you control gets +1/+0 until end of turn. Activate only as a sorcery." This ability triggers only once each turn.
SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ r_1_1_mercenary_tappump | TokenOwner$ You
DeckHas:Ability$Token & Type$Mercenary
DeckHints:Type$Assassin|Mercenary|Pirate|Rogue|Warlock
Oracle:As long as it's your turn, outlaws you control have first strike. (Assassins, Mercenaries, Pirates, Rogues, and Warlocks are outlaws.)\nWhenever you commit a crime, create a 1/1 red Mercenary creature token with "{T}: Target creature you control gets +1/+0 until end of turn. Activate only as a sorcery." This ability triggers only once each turn.
Oracle:During your turn, outlaws you control have first strike. (Assassins, Mercenaries, Pirates, Rogues, and Warlocks are outlaws.)\nWhenever you commit a crime, create a 1/1 red Mercenary creature token with "{T}: Target creature you control gets +1/+0 until end of turn. Activate only as a sorcery." This ability triggers only once each turn.

View File

@@ -5,7 +5,7 @@ PT:2/2
K:Flash
K:Flying
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigExile | TriggerDescription$ When CARDNAME enters, exile target spell. It becomes plotted. (Its owner may cast it as a sorcery on a later turn without paying its mana cost.)
SVar:TrigExile:DB$ ChangeZone | TargetType$ Spell | ValidTgts$ Card | TgtZone$ Stack | Origin$ Stack | Fizzle$ True | Mandatory$ True | Destination$ Exile | IsCurse$ True | TgtPrompt$ Choose target spell | RememberChanged$ True | SubAbility$ DBPlot
SVar:TrigExile:DB$ ChangeZone | TargetType$ Spell | ValidTgts$ Card | TgtZone$ Stack | Origin$ Stack | Destination$ Exile | IsCurse$ True | TgtPrompt$ Choose target spell | RememberChanged$ True | SubAbility$ DBPlot
SVar:DBPlot:DB$ AlterAttribute | Defined$ Remembered | Attributes$ Plotted | SubAbility$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
S:Mode$ RaiseCost | Activator$ Opponent | ValidCard$ Card.wasCastFromGraveyard,Card.wasCastFromExile | Type$ Spell | Amount$ 2 | Description$ Spells your opponents cast from graveyards or from exile cost {2} more to cast.

View File

@@ -3,6 +3,6 @@ ManaCost:3 R W
Types:Legendary Creature Human Assassin
PT:3/4
K:Double Strike
S:Mode$ Continuous | Affected$ Creature.Historic+Other+YouCtrl | AddKeyword$ Double Strike | Condition$ PlayerTurn | Description$ As long as it's your turn, other historic creatures you control have double strike.
S:Mode$ Continuous | Affected$ Creature.Historic+Other+YouCtrl | AddKeyword$ Double Strike | Condition$ PlayerTurn | Description$ During your turn, other historic creatures you control have double strike.
K:Disguise:1 R W
Oracle:Double strike\nAs long as it's your turn, other historic creatures you control have double strike.\nDisguise {1}{R}{W} (You may cast this card face down for {3} as a 2/2 creature with ward {2}. Turn it face up any time for its disguise cost.)
Oracle:Double strike\nDuring your turn, other historic creatures you control have double strike.\nDisguise {1}{R}{W} (You may cast this card face down for {3} as a 2/2 creature with ward {2}. Turn it face up any time for its disguise cost.)

View File

@@ -2,7 +2,7 @@ Name:Bedrock Tortoise
ManaCost:3 G
Types:Creature Turtle
PT:0/6
S:Mode$ Continuous | Affected$ Creature.YouCtrl | AddKeyword$ Hexproof | Condition$ PlayerTurn | Description$ As long as it's your turn, creatures you control have hexproof.
S:Mode$ Continuous | Affected$ Creature.YouCtrl | AddKeyword$ Hexproof | Condition$ PlayerTurn | Description$ During your turn, creatures you control have hexproof.
S:Mode$ CombatDamageToughness | ValidCard$ Creature.powerLTtoughness+YouCtrl | Description$ Each creature you control with toughness greater than its power assigns combat damage equal to its toughness rather than its power.
DeckHints:Type$Turtle|Spider|Treefolk|Crab|Wall
Oracle:As long as it's your turn, creatures you control have hexproof.\nEach creature you control with toughness greater than its power assigns combat damage equal to its toughness rather than its power.
Oracle:During your turn, creatures you control have hexproof.\nEach creature you control with toughness greater than its power assigns combat damage equal to its toughness rather than its power.

View File

@@ -1,12 +1,12 @@
Name:Bilbo's Ring
ManaCost:3
Types:Legendary Artifact Equipment
S:Mode$ Continuous | Affected$ Creature.EquippedBy | AddKeyword$ Hexproof | Condition$ PlayerTurn | Description$ As long as it's your turn, equipped creature has hexproof and can't be blocked.
S:Mode$ CantBlockBy | ValidAttacker$ Creature.EquippedBy | Secondary$ True | Condition$ PlayerTurn | Description$ As long as it's your turn, equipped creature has hexproof and can't be blocked.
S:Mode$ Continuous | Affected$ Creature.EquippedBy | AddKeyword$ Hexproof | Condition$ PlayerTurn | Description$ During your turn, equipped creature has hexproof and can't be blocked.
S:Mode$ CantBlockBy | ValidAttacker$ Creature.EquippedBy | Secondary$ True | Condition$ PlayerTurn | Description$ During your turn, equipped creature has hexproof and can't be blocked.
T:Mode$ Attacks | ValidCard$ Card.EquippedBy | Alone$ True | Execute$ TrigDraw | TriggerDescription$ Whenever equipped creature attacks alone, you draw a card and you lose 1 life.
SVar:TrigDraw:DB$ Draw | SubAbility$ DBLoseLife
SVar:DBLoseLife:DB$ LoseLife | LifeAmount$ 1
K:Equip:4
K:Equip:1:Halfling.YouCtrl:Halfling you control
DeckHints:Type$Halfling
Oracle:As long as it's your turn, equipped creature has hexproof and can't be blocked.\nWhenever equipped creature attacks alone, you draw a card and you lose 1 life.\nEquip Halfling {1} ({1}: Attach to target Halfling you control. Equip only as a sorcery.)\nEquip {4} ({4}: Attach to target creature you control. Equip only as a sorcery.)
Oracle:During your turn, equipped creature has hexproof and can't be blocked.\nWhenever equipped creature attacks alone, you draw a card and you lose 1 life.\nEquip Halfling {1} ({1}: Attach to target Halfling you control. Equip only as a sorcery.)\nEquip {4} ({4}: Attach to target creature you control. Equip only as a sorcery.)

View File

@@ -2,5 +2,5 @@ Name:Black Hole
ManaCost:3 B
Types:Sorcery
A:SP$ Destroy | ValidTgts$ Creature | TgtPrompt$ Select target creature and up to X other target creatures, where X is the number of Attractions you're visited this turn | TargetMin$ 1 | TargetMax$ X | SpellDescription$ Destroy target creature and up to X other target creatures, where X is the number of Attractions you've visited this turn.
SVar:X:Count$Valid Attraction.VisitedThisTurn/Plus.1
SVar:X:PlayerCountPropertyYou$AttractionsVisitedThisTurn/Plus.1
Oracle:Destroy target creature and up to X other target creatures, where X is the number of Attractions you've visited this turn.

View File

@@ -2,6 +2,6 @@ Name:Blood Burglar
ManaCost:1 B
Types:Creature Vampire Rogue
PT:2/2
S:Mode$ Continuous | Affected$ Card.Self | AddKeyword$ Lifelink | Condition$ PlayerTurn | Description$ As long as it's your turn, CARDNAME has lifelink.
S:Mode$ Continuous | Affected$ Card.Self | AddKeyword$ Lifelink | Condition$ PlayerTurn | Description$ During your turn, CARDNAME has lifelink.
DeckHas:Ability$LifeGain
Oracle:As long as it's your turn, Blood Burglar has lifelink. (Damage dealt by this creature also causes you to gain that much life.)
Oracle:During your turn, Blood Burglar has lifelink. (Damage dealt by this creature also causes you to gain that much life.)

View File

@@ -2,7 +2,6 @@ Name:Brine Giant
ManaCost:6 U
Types:Creature Giant
PT:5/6
S:Mode$ ReduceCost | ValidCard$ Card.Self | Type$ Spell | Amount$ X | EffectZone$ All | Description$ This spell costs {1} less to cast for each enchantment you control.
SVar:X:Count$Valid Enchantment.YouCtrl
K:Affinity:Enchantment
DeckHints:Type$Enchantment
Oracle:This spell costs {1} less to cast for each enchantment you control.
Oracle:Affinity for enchantments (This spell costs {1} less to cast for each enchantment you control.)

View File

@@ -3,6 +3,6 @@ ManaCost:2 U R
Types:Instant
K:Devoid
A:SP$ Charm | MinCharmNum$ 1 | CharmNum$ 2 | Choices$ DBReturn,DBDmg
SVar:DBReturn:DB$ ChangeZone | ValidTgts$ Creature,Card.inZoneStack | TgtZone$ Stack,Battlefield | Origin$ Battlefield,Stack | Fizzle$ True | Destination$ Hand | SpellDescription$ Return target spell or creature to its owner's hand.
SVar:DBReturn:DB$ ChangeZone | ValidTgts$ Creature,Card.inZoneStack | TgtZone$ Stack,Battlefield | Origin$ Battlefield,Stack | Destination$ Hand | SpellDescription$ Return target spell or creature to its owner's hand.
SVar:DBDmg:DB$ DealDamage | ValidTgts$ Creature,Planeswalker | TgtPrompt$ Select target creature or planeswalker | NumDmg$ 2 | ReplaceDyingDefined$ Targeted | SpellDescription$ CARDNAME deals 2 damage to target creature or planeswalker. If that creature or planeswalker would die this turn, exile it instead.
Oracle:Devoid (This card has no color.)\nChoose one or both —\n• Return target spell or creature to its owner's hand.\n• Brutal Expulsion deals 2 damage to target creature or planeswalker. If that creature or planeswalker would die this turn, exile it instead.

View File

@@ -2,7 +2,7 @@ Name:Cait, Cage Brawler
ManaCost:R G
Types:Legendary Creature Human Warrior
PT:1/1
S:Mode$ Continuous | Affected$ Card.Self | AddKeyword$ Indestructible | Condition$ PlayerTurn | Description$ As long as it's your turn, CARDNAME has indestructible.
S:Mode$ Continuous | Affected$ Card.Self | AddKeyword$ Indestructible | Condition$ PlayerTurn | Description$ During your turn, CARDNAME has indestructible.
T:Mode$ Attacks | ValidCard$ Card.Self | TriggerZones$ Battlefield | Execute$ TrigDraw | TriggerDescription$ Whenever NICKNAME attacks, you and defending player each draw a card, then discard a card. Put two +1/+1 counters on NICKNAME if you discarded the card with the highest mana value among those cards or tied for highest.
SVar:TrigDraw:DB$ Draw | Defined$ You & TriggeredDefendingPlayer | NumCards$ 1 | SubAbility$ DBDiscard
SVar:DBDiscard:DB$ Discard | Mode$ TgtChoose | Defined$ TriggeredDefendingPlayerAndYou | RememberDiscarded$ True | SubAbility$ DBPutCounter
@@ -10,4 +10,4 @@ SVar:DBPutCounter:DB$ PutCounter | ConditionDefined$ Remembered | ConditionPrese
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
SVar:HasAttackEffect:True
DeckHas:Ability$Discard|Counters
Oracle:As long as it's your turn, Cait, Cage Brawler has indestructible.\nWhenever Cait attacks, you and defending player each draw a card, then discard a card. Put two +1/+1 counters on Cait if you discarded the card with the highest mana value among those cards or tied for highest.
Oracle:During your turn, Cait, Cage Brawler has indestructible.\nWhenever Cait attacks, you and defending player each draw a card, then discard a card. Put two +1/+1 counters on Cait if you discarded the card with the highest mana value among those cards or tied for highest.

View File

@@ -2,5 +2,5 @@ Name:Circle of the Moon Druid
ManaCost:2 G
Types:Creature Human Elf Druid
PT:2/4
S:Mode$ Continuous | Affected$ Card.Self | Condition$ PlayerTurn | EffectZone$ Battlefield | SetPower$ 4 | SetToughness$ 2 | AddType$ Creature & Bear | RemoveCreatureTypes$ True | Description$ Bear Form — As long as it's your turn, CARDNAME is a Bear with base power and toughness 4/2. (It loses all other creature types.)
Oracle:Bear Form — As long as it's your turn, Circle of the Moon Druid is a Bear with base power and toughness 4/2. (It loses all other creature types.)
S:Mode$ Continuous | Affected$ Card.Self | Condition$ PlayerTurn | EffectZone$ Battlefield | SetPower$ 4 | SetToughness$ 2 | AddType$ Creature & Bear | RemoveCreatureTypes$ True | Description$ Bear Form — During your turn, CARDNAME is a Bear with base power and toughness 4/2. (It loses all other creature types.)
Oracle:Bear Form — During your turn, Circle of the Moon Druid is a Bear with base power and toughness 4/2. (It loses all other creature types.)

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