Merge remote-tracking branch 'remotes/core/master'

This commit is contained in:
Anthony Calosa
2019-09-12 20:38:53 +08:00
94 changed files with 51764 additions and 444 deletions

View File

@@ -51,10 +51,10 @@ public class Localizer {
formatter.setLocale(locale);
String formattedMessage = "CHAR ENCODING ERROR";
//Support non-English-standard characters
formattedMessage = new String(formatter.format(messageArguments).getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
//Support non-English-standard characters
formattedMessage = new String(formatter.format(messageArguments).getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
return formattedMessage;
return formattedMessage;
}
@@ -112,7 +112,7 @@ public class Localizer {
public static class Language {
public String languageName;
public String langaugeID;
public String languageID;
}
}

View File

@@ -27,6 +27,7 @@ import com.google.common.eventbus.EventBus;
import forge.card.CardRarity;
import forge.card.CardStateName;
import forge.card.CardType.Supertype;
import forge.game.ability.AbilityKey;
import forge.game.card.*;
import forge.game.combat.Combat;
import forge.game.event.Event;
@@ -713,9 +714,9 @@ public class Game {
ingamePlayers.remove(p);
lostPlayers.add(p);
final Map<String, Object> runParams = new TreeMap<>();
runParams.put("Player", p);
getTriggerHandler().runTriggerOld(TriggerType.LosesGame, runParams, false);
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
runParams.put(AbilityKey.Player, p);
getTriggerHandler().runTrigger(TriggerType.LosesGame, runParams, false);
}
/**

View File

@@ -23,6 +23,7 @@ import forge.GameCommand;
import forge.StaticData;
import forge.card.CardStateName;
import forge.game.ability.AbilityFactory;
import forge.game.ability.AbilityKey;
import forge.game.ability.AbilityUtils;
import forge.game.ability.ApiType;
import forge.game.ability.effects.AttachEffect;
@@ -79,7 +80,7 @@ public class GameAction {
return changeZone(zoneFrom, zoneTo, c, position, cause, null);
}
public Card changeZone(final Zone zoneFrom, Zone zoneTo, final Card c, Integer position, SpellAbility cause, Map<String, Object> params) {
private Card changeZone(final Zone zoneFrom, Zone zoneTo, final Card c, Integer position, SpellAbility cause, Map<AbilityKey, Object> params) {
if (c.isCopiedSpell() || (c.isImmutable() && zoneTo.is(ZoneType.Exile))) {
// Remove Effect from command immediately, this is essential when some replacement
// effects happen during the resolving of a spellability ("the next time ..." effect)
@@ -284,19 +285,19 @@ public class GameAction {
copied.getOwner().addInboundToken(copied);
}
Map<String, Object> repParams = Maps.newHashMap();
repParams.put("Event", "Moved");
repParams.put("Affected", copied);
repParams.put("CardLKI", lastKnownInfo);
repParams.put("Cause", cause);
repParams.put("Origin", zoneFrom != null ? zoneFrom.getZoneType() : null);
repParams.put("Destination", zoneTo.getZoneType());
Map<AbilityKey, Object> repParams = AbilityKey.newMap();
repParams.put(AbilityKey.Event, "Moved");
repParams.put(AbilityKey.Affected, copied);
repParams.put(AbilityKey.CardLKI, lastKnownInfo);
repParams.put(AbilityKey.Cause, cause);
repParams.put(AbilityKey.Origin, zoneFrom != null ? zoneFrom.getZoneType() : null);
repParams.put(AbilityKey.Destination, zoneTo.getZoneType());
if (params != null) {
repParams.putAll(params);
}
ReplacementResult repres = game.getReplacementHandler().run(repParams);
ReplacementResult repres = game.getReplacementHandler().run(EnumMapUtil.toStringMap(repParams));
if (repres != ReplacementResult.NotReplaced) {
// reset failed manifested Cards back to original
if (c.isManifested()) {
@@ -399,27 +400,25 @@ public class GameAction {
// play the change zone sound
game.fireEvent(new GameEventCardChangeZone(c, zoneFrom, zoneTo));
final Map<String, Object> runParams = Maps.newHashMap();
runParams.put("Card", lastKnownInfo);
runParams.put("Cause", cause);
runParams.put("Origin", zoneFrom != null ? zoneFrom.getZoneType().name() : null);
runParams.put("Destination", zoneTo.getZoneType().name());
runParams.put("SpellAbilityStackInstance", game.stack.peek());
runParams.put("IndividualCostPaymentInstance", game.costPaymentStack.peek());
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromCard(lastKnownInfo);
runParams.put(AbilityKey.Cause, cause);
runParams.put(AbilityKey.Origin, zoneFrom != null ? zoneFrom.getZoneType().name() : null);
runParams.put(AbilityKey.Destination, zoneTo.getZoneType().name());
runParams.put(AbilityKey.SpellAbilityStackInstance, game.stack.peek());
runParams.put(AbilityKey.IndividualCostPaymentInstance, game.costPaymentStack.peek());
if (params != null) {
runParams.putAll(params);
}
game.getTriggerHandler().runTriggerOld(TriggerType.ChangesZone, runParams, true);
game.getTriggerHandler().runTrigger(TriggerType.ChangesZone, runParams, true);
if (zoneFrom != null && zoneFrom.is(ZoneType.Battlefield) && !zoneFrom.getPlayer().equals(zoneTo.getPlayer())) {
final Map<String, Object> runParams2 = Maps.newHashMap();
runParams2.put("Card", lastKnownInfo);
runParams2.put("OriginalController", zoneFrom.getPlayer());
final Map<AbilityKey, Object> runParams2 = AbilityKey.mapFromCard(lastKnownInfo);
runParams2.put(AbilityKey.OriginalController, zoneFrom.getPlayer());
if(params != null) {
runParams2.putAll(params);
}
game.getTriggerHandler().runTriggerOld(TriggerType.ChangesController, runParams2, false);
game.getTriggerHandler().runTrigger(TriggerType.ChangesController, runParams2, false);
}
// AllZone.getStack().chooseOrderOfSimultaneousStackEntryAll();
@@ -529,17 +528,17 @@ public class GameAction {
return moveTo(zoneTo, c, cause, null);
}
public final Card moveTo(final Zone zoneTo, Card c, SpellAbility cause, Map<String, Object> params) {
// FThreads.assertExecutedByEdt(false); // This code must never be executed from EDT,
// use FThreads.invokeInNewThread to run code in a pooled thread
return moveTo(zoneTo, c, null, cause, params);
}
public final Card moveTo(final Zone zoneTo, Card c, Integer position, SpellAbility cause) {
return moveTo(zoneTo, c, position, cause, null);
}
public final Card moveTo(final Zone zoneTo, Card c, Integer position, SpellAbility cause, Map<String, Object> params) {
private Card moveTo(final Zone zoneTo, Card c, SpellAbility cause, Map<AbilityKey, Object> params) {
// FThreads.assertExecutedByEdt(false); // This code must never be executed from EDT,
// use FThreads.invokeInNewThread to run code in a pooled thread
return moveTo(zoneTo, c, null, cause, params);
}
private Card moveTo(final Zone zoneTo, Card c, Integer position, SpellAbility cause, Map<AbilityKey, Object> params) {
// Ideally move to should never be called without a prevZone
// Remove card from Current Zone, if it has one
final Zone zoneFrom = game.getZoneOf(c);
@@ -599,10 +598,9 @@ public class GameAction {
c.setTurnInZone(tiz);
c.setCameUnderControlSinceLastUpkeep(true);
final Map<String, Object> runParams = Maps.newHashMap();
runParams.put("Card", c);
runParams.put("OriginalController", original);
game.getTriggerHandler().runTriggerOld(TriggerType.ChangesController, runParams, false);
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromCard(c);
runParams.put(AbilityKey.OriginalController, original);
game.getTriggerHandler().runTrigger(TriggerType.ChangesController, runParams, false);
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
for (Player p : game.getPlayers()) {
@@ -612,17 +610,14 @@ public class GameAction {
}
public final Card moveToStack(final Card c, SpellAbility cause) {
return moveToStack(c, cause, null);
}
public final Card moveToStack(final Card c, SpellAbility cause, Map<String, Object> params) {
final Zone stack = game.getStackZone();
return moveTo(stack, c, cause, params);
return moveTo(stack, c, cause);
}
public final Card moveToGraveyard(final Card c, SpellAbility cause) {
return moveToGraveyard(c, cause, null);
}
public final Card moveToGraveyard(final Card c, SpellAbility cause, Map<String, Object> params) {
public final Card moveToGraveyard(final Card c, SpellAbility cause, Map<AbilityKey, Object> params) {
final PlayerZone grave = c.getOwner().getZone(ZoneType.Graveyard);
// must put card in OWNER's graveyard not controller's
return moveTo(grave, c, cause, params);
@@ -632,25 +627,21 @@ public class GameAction {
return moveToHand(c, cause, null);
}
public final Card moveToHand(final Card c, SpellAbility cause, Map<String, Object> params) {
public final Card moveToHand(final Card c, SpellAbility cause, Map<AbilityKey, Object> params) {
final PlayerZone hand = c.getOwner().getZone(ZoneType.Hand);
return moveTo(hand, c, cause, params);
}
public final Card moveToPlay(final Card c, SpellAbility cause) {
return moveToPlay(c, cause, null);
}
public final Card moveToPlay(final Card c, SpellAbility cause, Map<String, Object> params) {
final PlayerZone play = c.getController().getZone(ZoneType.Battlefield);
return moveTo(play, c, cause, params);
return moveTo(play, c, cause, null);
}
public final Card moveToPlay(final Card c, final Player p, SpellAbility cause) {
return moveToPlay(c, p, cause, null);
}
public final Card moveToPlay(final Card c, final Player p, SpellAbility cause, Map<String, Object> params) {
public final Card moveToPlay(final Card c, final Player p, SpellAbility cause, Map<AbilityKey, Object> params) {
// move to a specific player's Battlefield
final PlayerZone play = p.getZone(ZoneType.Battlefield);
return moveTo(play, c, cause, params);
@@ -660,7 +651,7 @@ public class GameAction {
return moveToBottomOfLibrary(c, cause, null);
}
public final Card moveToBottomOfLibrary(final Card c, SpellAbility cause, Map<String, Object> params) {
public final Card moveToBottomOfLibrary(final Card c, SpellAbility cause, Map<AbilityKey, Object> params) {
return moveToLibrary(c, -1, cause, params);
}
@@ -668,7 +659,7 @@ public class GameAction {
return moveToLibrary(c, cause, null);
}
public final Card moveToLibrary(final Card c, SpellAbility cause, Map<String, Object> params) {
public final Card moveToLibrary(final Card c, SpellAbility cause, Map<AbilityKey, Object> params) {
return moveToLibrary(c, 0, cause, params);
}
@@ -676,7 +667,7 @@ public class GameAction {
return moveToLibrary(c, libPosition, cause, null);
}
public final Card moveToLibrary(Card c, int libPosition, SpellAbility cause, Map<String, Object> params) {
public final Card moveToLibrary(Card c, int libPosition, SpellAbility cause, Map<AbilityKey, Object> params) {
final PlayerZone library = c.getOwner().getZone(ZoneType.Library);
if (libPosition == -1 || libPosition > library.size()) {
libPosition = library.size();
@@ -685,21 +676,17 @@ public class GameAction {
}
public final Card moveToVariantDeck(Card c, ZoneType zone, int deckPosition, SpellAbility cause) {
return moveToVariantDeck(c, zone, deckPosition, cause, null);
}
public final Card moveToVariantDeck(Card c, ZoneType zone, int deckPosition, SpellAbility cause, Map<String, Object> params) {
final PlayerZone deck = c.getOwner().getZone(zone);
if (deckPosition == -1 || deckPosition > deck.size()) {
deckPosition = deck.size();
}
return changeZone(game.getZoneOf(c), deck, c, deckPosition, cause, params);
return changeZone(game.getZoneOf(c), deck, c, deckPosition, cause);
}
public final Card exile(final Card c, SpellAbility cause) {
return exile(c, cause, null);
}
public final Card exile(final Card c, SpellAbility cause, Map<String, Object> params) {
public final Card exile(final Card c, SpellAbility cause, Map<AbilityKey, Object> params) {
if (game.isCardExiled(c)) {
return c;
}
@@ -709,44 +696,35 @@ public class GameAction {
final Card copied = moveTo(removed, c, cause, params);
// Run triggers
final Map<String, Object> runParams = Maps.newHashMap();
runParams.put("Card", c);
runParams.put("Cause", cause);
runParams.put("Origin", origin.getZoneType().name());
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromCard(c);
runParams.put(AbilityKey.Cause, cause);
runParams.put(AbilityKey.Origin, origin.getZoneType().name());
if (params != null) {
runParams.putAll(params);
}
game.getTriggerHandler().runTriggerOld(TriggerType.Exiled, runParams, false);
game.getTriggerHandler().runTrigger(TriggerType.Exiled, runParams, false);
return copied;
}
public final Card moveTo(final ZoneType name, final Card c, SpellAbility cause) {
return moveTo(name, c, cause, null);
}
public final Card moveTo(final ZoneType name, final Card c, SpellAbility cause, Map<String, Object> params) {
return moveTo(name, c, 0, cause, params);
return moveTo(name, c, 0, cause);
}
public final Card moveTo(final ZoneType name, final Card c, final int libPosition, SpellAbility cause) {
return moveTo(name, c, libPosition, cause, null);
}
public final Card moveTo(final ZoneType name, final Card c, final int libPosition, SpellAbility cause, Map<String, Object> params) {
// Call specific functions to set PlayerZone, then move onto moveTo
switch(name) {
case Hand: return moveToHand(c, cause, params);
case Library: return moveToLibrary(c, libPosition, cause, params);
case Battlefield: return moveToPlay(c, cause, params);
case Graveyard: return moveToGraveyard(c, cause, params);
case Exile: return exile(c, cause, params);
case Stack: return moveToStack(c, cause, params);
case PlanarDeck: return moveToVariantDeck(c, ZoneType.PlanarDeck, libPosition, cause, params);
case SchemeDeck: return moveToVariantDeck(c, ZoneType.SchemeDeck, libPosition, cause, params);
case Hand: return moveToHand(c, cause);
case Library: return moveToLibrary(c, libPosition, cause);
case Battlefield: return moveToPlay(c, cause);
case Graveyard: return moveToGraveyard(c, cause);
case Exile: return exile(c, cause);
case Stack: return moveToStack(c, cause);
case PlanarDeck: return moveToVariantDeck(c, ZoneType.PlanarDeck, libPosition, cause);
case SchemeDeck: return moveToVariantDeck(c, ZoneType.SchemeDeck, libPosition, cause);
default: // sideboard will also get there
return moveTo(c.getOwner().getZone(name), c, cause, params);
return moveTo(c.getOwner().getZone(name), c, cause);
}
}
@@ -912,10 +890,10 @@ public class GameAction {
// preList means that this is run by a pre Check with LKI objects
// in that case Always trigger should not Run
if (preList.isEmpty()) {
final Map<String, Object> runParams = Maps.newHashMap();
game.getTriggerHandler().runTriggerOld(TriggerType.Always, runParams, false);
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
game.getTriggerHandler().runTrigger(TriggerType.Always, runParams, false);
game.getTriggerHandler().runTriggerOld(TriggerType.Immediate, runParams, false);
game.getTriggerHandler().runTrigger(TriggerType.Immediate, runParams, false);
}
// Update P/T and type in the view only once after all the cards have been processed, to avoid flickering
@@ -1434,10 +1412,9 @@ public class GameAction {
game.fireEvent(new GameEventCardDestroyed());
// Run triggers
final Map<String, Object> runParams = Maps.newHashMap();
runParams.put("Card", c);
runParams.put("Causer", activator);
game.getTriggerHandler().runTriggerOld(TriggerType.Destroyed, runParams, false);
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromCard(c);
runParams.put(AbilityKey.Causer, activator);
game.getTriggerHandler().runTrigger(TriggerType.Destroyed, runParams, false);
final Card sacrificed = sacrificeDestroy(c, sa, table);
return sacrificed != null;
@@ -1614,8 +1591,7 @@ public class GameAction {
checkStateEffects(true); // why?
// Run Trigger beginning of the game
final Map<String, Object> runParams = Maps.newHashMap();
game.getTriggerHandler().runTriggerOld(TriggerType.NewGame, runParams, true);
game.getTriggerHandler().runTrigger(TriggerType.NewGame, AbilityKey.newMap(), true);
//</THIS CODE WILL WORK WITH PHASE = NULL>
@@ -1778,9 +1754,9 @@ public class GameAction {
game.setMonarch(p);
// Run triggers
final Map<String, Object> runParams = Maps.newHashMap();
runParams.put("Player", p);
game.getTriggerHandler().runTriggerOld(TriggerType.BecomeMonarch, runParams, false);
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
runParams.put(AbilityKey.Player, p);
game.getTriggerHandler().runTrigger(TriggerType.BecomeMonarch, runParams, false);
}
// Make scry an action function so that it can be used for mulligans (with a null cause)
@@ -1840,9 +1816,9 @@ public class GameAction {
if (cause != null) {
// set up triggers (but not actually do them until later)
final Map<String, Object> runParams = Maps.newHashMap();
runParams.put("Player", p);
game.getTriggerHandler().runTriggerOld(TriggerType.Scry, runParams, false);
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
runParams.put(AbilityKey.Player, p);
game.getTriggerHandler().runTrigger(TriggerType.Scry, runParams, false);
}
}
}

View File

@@ -17,6 +17,7 @@
*/
package forge.game;
import forge.game.ability.AbilityKey;
import forge.game.card.Card;
import forge.game.card.CardCollection;
import forge.game.card.CardCollectionView;
@@ -214,13 +215,13 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
int prevent = damage - restDamage;
preventMap.put(source, this, damage - restDamage);
final Map<String, Object> runParams = Maps.newHashMap();
runParams.put("DamageTarget", this);
runParams.put("DamageAmount", prevent);
runParams.put("DamageSource", source);
runParams.put("IsCombatDamage", isCombat);
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
runParams.put(AbilityKey.DamageTarget, this);
runParams.put(AbilityKey.DamageAmount, prevent);
runParams.put(AbilityKey.DamageSource, source);
runParams.put(AbilityKey.IsCombatDamage, isCombat);
getGame().getTriggerHandler().runTriggerOld(TriggerType.DamagePrevented, runParams, false);
getGame().getTriggerHandler().runTrigger(TriggerType.DamagePrevented, runParams, false);
}
return restDamage;

View File

@@ -7,6 +7,7 @@ import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Maps;
import com.google.common.collect.Table;
import forge.game.ability.AbilityKey;
import forge.game.card.Card;
import forge.game.card.CounterType;
import forge.game.spellability.SpellAbility;
@@ -47,9 +48,9 @@ public class GameEntityCounterTable extends ForwardingTable<GameEntity, CounterT
public void triggerCountersPutAll(final Game game) {
if (!isEmpty()) {
final Map<String, Object> runParams = Maps.newHashMap();
runParams.put("Objects", this);
game.getTriggerHandler().runTriggerOld(TriggerType.CounterAddedAll, runParams, false);
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
runParams.put(AbilityKey.Objects, this);
game.getTriggerHandler().runTrigger(TriggerType.CounterAddedAll, runParams, false);
}
}
}

View File

@@ -1,9 +1,10 @@
package forge.game;
import forge.game.ability.AbilityKey;
import forge.game.player.Player;
import forge.game.trigger.TriggerType;
import java.util.HashMap;
import java.util.Map;
import com.google.common.collect.ImmutableList;
@@ -36,10 +37,10 @@ public enum PlanarDice {
trigRes = Chaos;
}
HashMap<String,Object> runParams = new HashMap<>();
runParams.put("Player", roller);
runParams.put("Result", trigRes);
roller.getGame().getTriggerHandler().runTriggerOld(TriggerType.PlanarDice, runParams,false);
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
runParams.put(AbilityKey.Player, roller);
runParams.put(AbilityKey.Result, trigRes);
roller.getGame().getTriggerHandler().runTrigger(TriggerType.PlanarDice, runParams,false);
return res;

View File

@@ -1,12 +1,52 @@
package forge.game.ability;
import java.util.EnumMap;
import java.util.Map;
/**
* Keys for Ability parameter maps.
*/
public enum AbilityKey {
AbilityMana("AbilityMana"),
Affected("Affected"),
Attacker("Attacker"),
Attackers("Attackers"),
AttackingPlayer("AttackingPlayer"),
AttackedTarget("AttackedTarget");
AttackedTarget("AttackedTarget"),
Blocker("Blocker"),
Blockers("Blockers"),
Card("Card"),
CardLKI("CardLKI"),
Cause("Cause"),
Causer("Causer"),
Championed("Championed"),
CounteredSA("CounteredSA"),
DamageAmount("DamageAmount"),
DamageSource("DamageSource"),
DamageTarget("DamageTarget"),
Defender("Defender"),
DefendingPlayer("DefendingPlayer"),
Destination("Destination"),
Event("Event"),
Fighter("Fighter"),
Fizzle("Fizzle"),
IsCombatDamage("IsCombatDamage"),
Player("Player"),
IndividualCostPaymentInstance("IndividualCostPaymentInstance"),
MonstrosityAmount("MonstrosityAmount"),
NumBlockers("NumBlockers"),
Objects("Objects"),
Origin("Origin"),
OriginalController("OriginalController"),
Produced("Produced"),
Result("Result"),
Scheme("Scheme"),
SpellAbilityStackInstance("SpellAbilityStackInstance"),
StackSa("StackSa"),
StackSi("StackSi"),
Target("Target"),
Won("Won");
private String key;
@@ -18,4 +58,15 @@ public enum AbilityKey {
public String toString() {
return key;
}
public static <V> EnumMap<AbilityKey, V> newMap() {
return new EnumMap<>(AbilityKey.class);
}
public static Map<AbilityKey, Object> mapFromCard(forge.game.card.Card card) {
final Map<AbilityKey, Object> runParams = newMap();
runParams.put(Card, card);
return runParams;
}
}

View File

@@ -1,7 +1,7 @@
package forge.game.ability.effects;
import com.google.common.collect.Maps;
import forge.game.Game;
import forge.game.ability.AbilityKey;
import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card;
import forge.game.player.Player;
@@ -40,9 +40,9 @@ public class AbandonEffect extends SpellAbilityEffect {
controller.getZone(ZoneType.SchemeDeck).add(source);
// Run triggers
final Map<String, Object> runParams = Maps.newHashMap();
runParams.put("Scheme", source);
game.getTriggerHandler().runTriggerOld(TriggerType.Abandoned, runParams, false);
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
runParams.put(AbilityKey.Scheme, source);
game.getTriggerHandler().runTrigger(TriggerType.Abandoned, runParams, false);
}
}

View File

@@ -1,6 +1,7 @@
package forge.game.ability.effects;
import forge.game.Game;
import forge.game.ability.AbilityKey;
import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card;
import forge.game.event.GameEventCombatChanged;
@@ -11,7 +12,6 @@ import forge.game.trigger.TriggerType;
import org.apache.commons.lang3.StringUtils;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import java.util.List;
import java.util.Map;
@@ -40,13 +40,13 @@ public class BecomesBlockedEffect extends SpellAbilityEffect {
game.getCombat().setBlocked(c, true);
if (!c.getDamageHistory().getCreatureGotBlockedThisCombat()) {
isCombatChanged = true;
final Map<String, Object> runParams = Maps.newHashMap();
runParams.put("Attacker", c);
runParams.put("Blockers", Lists.<Card>newArrayList());
runParams.put("NumBlockers", 0);
runParams.put("Defender", game.getCombat().getDefenderByAttacker(c));
runParams.put("DefendingPlayer", game.getCombat().getDefenderPlayerByAttacker(c));
game.getTriggerHandler().runTriggerOld(TriggerType.AttackerBlocked, runParams, false);
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
runParams.put(AbilityKey.Attacker, c);
runParams.put(AbilityKey.Blockers, Lists.<Card>newArrayList());
runParams.put(AbilityKey.NumBlockers, 0);
runParams.put(AbilityKey.Defender, game.getCombat().getDefenderByAttacker(c));
runParams.put(AbilityKey.DefendingPlayer, game.getCombat().getDefenderPlayerByAttacker(c));
game.getTriggerHandler().runTrigger(TriggerType.AttackerBlocked, runParams, false);
}
}
}

View File

@@ -1,6 +1,6 @@
package forge.game.ability.effects;
import com.google.common.collect.Maps;
import forge.game.ability.AbilityKey;
import forge.game.ability.AbilityUtils;
import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card;
@@ -53,26 +53,28 @@ public class BlockEffect extends SpellAbilityEffect {
blocker.addBlockedThisTurn(attacker);
attacker.addBlockedByThisTurn(blocker);
Map<String, Object> runParams = Maps.newHashMap();
runParams.put("Attacker", attacker);
runParams.put("Blocker", blocker);
game.getTriggerHandler().runTriggerOld(TriggerType.AttackerBlockedByCreature, runParams, false);
{
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
runParams.put(AbilityKey.Attacker, attacker);
runParams.put(AbilityKey.Blocker, blocker);
game.getTriggerHandler().runTrigger(TriggerType.AttackerBlockedByCreature, runParams, false);
}
runParams = Maps.newHashMap();
runParams.put("Blocker", blocker);
runParams.put("Attackers", attacker);
game.getTriggerHandler().runTriggerOld(TriggerType.Blocks, runParams, false);
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
runParams.put(AbilityKey.Blocker, blocker);
runParams.put(AbilityKey.Attackers, attacker);
game.getTriggerHandler().runTrigger(TriggerType.Blocks, runParams, false);
}
attacker.getDamageHistory().setCreatureGotBlockedThisCombat(true);
if (!wasBlocked) {
final Map<String, Object> runParams = Maps.newHashMap();
runParams.put("Attacker", attacker);
runParams.put("Blockers", blockers);
runParams.put("NumBlockers", blockers.size());
runParams.put("Defender", combat.getDefenderByAttacker(attacker));
runParams.put("DefendingPlayer", combat.getDefenderPlayerByAttacker(attacker));
game.getTriggerHandler().runTriggerOld(TriggerType.AttackerBlocked, runParams, false);
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
runParams.put(AbilityKey.Attacker, attacker);
runParams.put(AbilityKey.Blockers, blockers);
runParams.put(AbilityKey.NumBlockers, blockers.size());
runParams.put(AbilityKey.Defender, combat.getDefenderByAttacker(attacker));
runParams.put(AbilityKey.DefendingPlayer, combat.getDefenderPlayerByAttacker(attacker));
game.getTriggerHandler().runTrigger(TriggerType.AttackerBlocked, runParams, false);
combat.orderBlockersForDamageAssignment(attacker, new CardCollection(blockers));
}

View File

@@ -1,9 +1,9 @@
package forge.game.ability.effects;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import forge.game.Game;
import forge.game.GameActionUtil;
import forge.game.ability.AbilityKey;
import forge.game.ability.AbilityUtils;
import forge.game.ability.SpellAbilityEffect;
import forge.game.card.*;
@@ -79,10 +79,10 @@ public class ChangeZoneAllEffect extends SpellAbilityEffect {
if (!libCards.isEmpty()) {
sa.getActivatingPlayer().getController().reveal(libCards, ZoneType.Library, libCards.get(0).getOwner());
}
final Map<String, Object> runParams = Maps.newHashMap();
runParams.put("Player", sa.getActivatingPlayer());
runParams.put("Target", tgtPlayers);
game.getTriggerHandler().runTriggerOld(TriggerType.SearchedLibrary, runParams, false);
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
runParams.put(AbilityKey.Player, sa.getActivatingPlayer());
runParams.put(AbilityKey.Target, tgtPlayers);
game.getTriggerHandler().runTrigger(TriggerType.SearchedLibrary, runParams, false);
}
if (origin.contains(ZoneType.Hand) && sa.hasParam("Search")) {
CardCollection handCards = CardLists.filterControlledBy(CardLists.getValidCards(cards, "Card.inZoneHand", sa.getActivatingPlayer(), source),

View File

@@ -4,12 +4,12 @@ import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import forge.GameCommand;
import forge.card.CardStateName;
import forge.game.Game;
import forge.game.GameEntity;
import forge.game.GameObject;
import forge.game.ability.AbilityKey;
import forge.game.ability.AbilityUtils;
import forge.game.ability.SpellAbilityEffect;
import forge.game.card.*;
@@ -470,7 +470,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
}
}
movedCard = game.getAction().moveToLibrary(tgtC, libraryPosition, sa, null);
movedCard = game.getAction().moveToLibrary(tgtC, libraryPosition, sa);
} else {
if (destination.equals(ZoneType.Battlefield)) {
@@ -534,7 +534,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
}
movedCard = game.getAction().moveTo(
tgtC.getController().getZone(destination), tgtC, sa, null);
tgtC.getController().getZone(destination), tgtC, sa);
if (sa.hasParam("Unearth")) {
movedCard.setUnearthed(true);
movedCard.addChangedCardKeywords(Lists.newArrayList("Haste"), null, false, false,
@@ -591,7 +591,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
}
tgtC.setExiledWith(host);
}
movedCard = game.getAction().moveTo(destination, tgtC, sa, null);
movedCard = game.getAction().moveTo(destination, tgtC, sa);
// If a card is Exiled from the stack, remove its spells from the stack
if (sa.hasParam("Fizzle")) {
if (tgtC.isInZone(ZoneType.Exile) || tgtC.isInZone(ZoneType.Hand)
@@ -830,10 +830,10 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
}
}
}
final Map<String, Object> runParams = Maps.newHashMap();
runParams.put("Player", decider);
runParams.put("Target", Lists.newArrayList(player));
decider.getGame().getTriggerHandler().runTriggerOld(TriggerType.SearchedLibrary, runParams, false);
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
runParams.put(AbilityKey.Player, decider);
runParams.put(AbilityKey.Target, Lists.newArrayList(player));
decider.getGame().getTriggerHandler().runTrigger(TriggerType.SearchedLibrary, runParams, false);
}
if (!defined && changeType != null) {
@@ -967,7 +967,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
Card movedCard = null;
final Zone originZone = game.getZoneOf(c);
if (destination.equals(ZoneType.Library)) {
movedCard = game.getAction().moveToLibrary(c, libraryPos, sa, null);
movedCard = game.getAction().moveToLibrary(c, libraryPos, sa);
}
else if (destination.equals(ZoneType.Battlefield)) {
if (sa.hasParam("Tapped")) {
@@ -1096,7 +1096,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
c.addFaceupCommand(unanimate);
}
}
movedCard = game.getAction().moveTo(c.getController().getZone(destination), c, sa, null);
movedCard = game.getAction().moveTo(c.getController().getZone(destination), c, sa);
if (sa.hasParam("Tapped")) {
movedCard.setTapped(true);
}
@@ -1108,7 +1108,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
movedCard.setTimestamp(ts);
}
else if (destination.equals(ZoneType.Exile)) {
movedCard = game.getAction().exile(c, sa, null);
movedCard = game.getAction().exile(c, sa);
if (!c.isToken()) {
Card host = sa.getOriginalHost();
if (host == null) {
@@ -1121,7 +1121,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
}
}
else {
movedCard = game.getAction().moveTo(destination, c, sa, null);
movedCard = game.getAction().moveTo(destination, c, sa);
}
movedCards.add(movedCard);
@@ -1131,10 +1131,9 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
}
if (champion) {
final Map<String, Object> runParams = Maps.newHashMap();
runParams.put("Card", source);
runParams.put("Championed", c);
game.getTriggerHandler().runTriggerOld(TriggerType.Championed, runParams, false);
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromCard(source);
runParams.put(AbilityKey.Championed, c);
game.getTriggerHandler().runTrigger(TriggerType.Championed, runParams, false);
}
if (remember) {
@@ -1199,9 +1198,9 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
final Zone originZone = tgtHost.getZone();
game.getStack().remove(si);
Map<String,Object> params = Maps.newHashMap();
params.put("StackSa", tgtSA);
params.put("StackSi", si);
Map<AbilityKey,Object> params = AbilityKey.newMap();
params.put(AbilityKey.StackSa, tgtSA);
params.put(AbilityKey.StackSi, si);
Card movedCard = null;
if (srcSA.hasParam("Destination")) {

View File

@@ -8,6 +8,7 @@ import forge.game.card.Card;
import forge.game.player.Player;
import forge.game.spellability.AbilitySub;
import forge.game.spellability.SpellAbility;
import forge.util.Aggregates;
import forge.util.Lang;
import forge.util.collect.FCollection;
@@ -57,6 +58,7 @@ public class CharmEffect extends SpellAbilityEffect {
int num = Integer.parseInt(sa.getParamOrDefault("CharmNum", "1"));
int min = Integer.parseInt(sa.getParamOrDefault("MinCharmNum", String.valueOf(num)));
boolean repeat = sa.hasParam("CanRepeatModes");
boolean random = sa.hasParam("Random");
boolean oppChooses = "Opponent".equals(sa.getParam("Chooser"));
List<AbilitySub> list = CharmEffect.makePossibleOptions(sa);
@@ -73,6 +75,9 @@ public class CharmEffect extends SpellAbilityEffect {
sb.append(Lang.getNumeral(min)).append(" or ").append(list.size() == 2 ? "both" : "more");
}
if (random) {
sb.append("at random.");
}
if (repeat) {
sb.append(". You may choose the same mode more than once.");
}
@@ -93,6 +98,7 @@ public class CharmEffect extends SpellAbilityEffect {
int num = Integer.parseInt(sa.getParamOrDefault("CharmNum", "1"));
int min = Integer.parseInt(sa.getParamOrDefault("MinCharmNum", String.valueOf(num)));
boolean repeat = sa.hasParam("CanRepeatModes");
boolean random = sa.hasParam("Random");
boolean oppChooses = "Opponent".equals(sa.getParam("Chooser"));
List<AbilitySub> list = CharmEffect.makePossibleOptions(sa);
@@ -116,6 +122,9 @@ public class CharmEffect extends SpellAbilityEffect {
}
}
if (random) {
sb.append("at random.");
}
if (repeat) {
sb.append(". You may choose the same mode more than once.");
}
@@ -165,6 +174,11 @@ public class CharmEffect extends SpellAbilityEffect {
: Integer.parseInt(sa.getParamOrDefault("CharmNum", "1"));
final int min = sa.hasParam("MinCharmNum") ? Integer.parseInt(sa.getParam("MinCharmNum")) : num;
if (sa.hasParam("Random")) {
chainAbilities(sa, Aggregates.random(makePossibleOptions(sa), num));
return true;
}
Card source = sa.getHostCard();
Player activator = sa.getActivatingPlayer();
Player chooser = sa.getActivatingPlayer();

View File

@@ -2,6 +2,7 @@ package forge.game.ability.effects;
import forge.game.GameAction;
import forge.game.GameLogEntryType;
import forge.game.ability.AbilityKey;
import forge.game.ability.AbilityUtils;
import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card;
@@ -12,7 +13,7 @@ import forge.game.trigger.TriggerType;
import forge.game.zone.PlayerZone;
import forge.game.zone.ZoneType;
import java.util.HashMap;
import java.util.Map;
public class ClashEffect extends SpellAbilityEffect {
@@ -32,8 +33,8 @@ public class ClashEffect extends SpellAbilityEffect {
final boolean victory = clashWithOpponent(sa);
// Run triggers
final HashMap<String, Object> runParams = new HashMap<>();
runParams.put("Player", sa.getHostCard().getController());
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
runParams.put(AbilityKey.Player, sa.getHostCard().getController());
if (victory) {
@@ -42,18 +43,18 @@ public class ClashEffect extends SpellAbilityEffect {
AbilityUtils.resolve(sub);
}
runParams.put("Won", "True");
runParams.put(AbilityKey.Won, "True");
} else {
AbilitySub sub = sa.getAdditionalAbility("OtherwiseSubAbility");
if (sub != null) {
AbilityUtils.resolve(sub);
}
runParams.put("Won", "False");
runParams.put(AbilityKey.Won, "False");
}
sa.getHostCard().getGame().getTriggerHandler().runTriggerOld(TriggerType.Clashed, runParams, false);
sa.getHostCard().getGame().getTriggerHandler().runTrigger(TriggerType.Clashed, runParams, false);
}
/**

View File

@@ -181,7 +181,7 @@ public class CopyPermanentEffect extends SpellAbilityEffect {
// Temporarily register triggers of an object created with CopyPermanent
//game.getTriggerHandler().registerActiveTrigger(copy, false);
final Card copyInPlay = game.getAction().moveToPlay(t, sa, null);
final Card copyInPlay = game.getAction().moveToPlay(t, sa);
if (copyInPlay.getZone() != null) {
triggerList.put(ZoneType.None, copyInPlay.getZone().getZoneType(), copyInPlay);

View File

@@ -2,6 +2,7 @@ package forge.game.ability.effects;
import forge.game.Game;
import forge.game.GameLogEntryType;
import forge.game.ability.AbilityKey;
import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card;
import forge.game.card.CardFactoryUtil;
@@ -168,9 +169,9 @@ public class CounterEffect extends SpellAbilityEffect {
tgtSA.getHostCard().unanimateBestow(true);
}
Map<String, Object> params = Maps.newHashMap();
params.put("StackSa", tgtSA);
params.put("StackSi", si);
Map<AbilityKey, Object> params = AbilityKey.newMap();
params.put(AbilityKey.StackSa, tgtSA);
params.put(AbilityKey.StackSi, si);
String destination = srcSA.hasParam("Destination") ? srcSA.getParam("Destination") : tgtSA.isAftermath() ? "Exile" : "Graveyard";
if (srcSA.hasParam("DestinationChoice")) {//Hinder
@@ -208,12 +209,11 @@ public class CounterEffect extends SpellAbilityEffect {
+ srcSA.getHostCard().getName());
}
// Run triggers
final Map<String, Object> runParams = Maps.newHashMap();
runParams.put("Player", tgtSA.getActivatingPlayer());
runParams.put("Card", tgtSA.getHostCard());
runParams.put("Cause", srcSA.getHostCard());
runParams.put("CounteredSA", tgtSA);
game.getTriggerHandler().runTriggerOld(TriggerType.Countered, runParams, false);
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromCard(tgtSA.getHostCard());
runParams.put(AbilityKey.Player, tgtSA.getActivatingPlayer());
runParams.put(AbilityKey.Cause, srcSA.getHostCard());
runParams.put(AbilityKey.CounteredSA, tgtSA);
game.getTriggerHandler().runTrigger(TriggerType.Countered, runParams, false);
if (!tgtSA.isAbility()) {

View File

@@ -8,6 +8,7 @@ import forge.game.Game;
import forge.game.GameEntity;
import forge.game.GameEntityCounterTable;
import forge.game.GameObject;
import forge.game.ability.AbilityKey;
import forge.game.ability.AbilityUtils;
import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card;
@@ -189,11 +190,10 @@ public class CountersPutEffect extends SpellAbilityEffect {
}
if (obj instanceof Card) {
Card tgtCard = gameCard;
counterAmount = sa.usesTargeting() && sa.hasParam("DividedAsYouChoose") ? sa.getTargetRestrictions().getDividedValue(tgtCard) : counterAmount;
if (!sa.usesTargeting() || tgtCard.canBeTargetedBy(sa)) {
counterAmount = sa.usesTargeting() && sa.hasParam("DividedAsYouChoose") ? sa.getTargetRestrictions().getDividedValue(gameCard) : counterAmount;
if (!sa.usesTargeting() || gameCard.canBeTargetedBy(sa)) {
if (max != -1) {
counterAmount = Math.max(Math.min(max - tgtCard.getCounters(counterType), counterAmount), 0);
counterAmount = Math.max(Math.min(max - gameCard.getCounters(counterType), counterAmount), 0);
}
if (sa.hasParam("UpTo")) {
Map<String, Object> params = Maps.newHashMap();
@@ -204,15 +204,15 @@ public class CountersPutEffect extends SpellAbilityEffect {
// Adapt need extra logic
if (sa.hasParam("Adapt")) {
if (!(tgtCard.getCounters(CounterType.P1P1) == 0
|| tgtCard.hasKeyword("CARDNAME adapts as though it had no +1/+1 counters"))) {
if (!(gameCard.getCounters(CounterType.P1P1) == 0
|| gameCard.hasKeyword("CARDNAME adapts as though it had no +1/+1 counters"))) {
continue;
}
}
if (sa.hasParam("Tribute")) {
// make a copy to check if it would be on the battlefield
Card noTributeLKI = CardUtil.getLKICopy(tgtCard);
Card noTributeLKI = CardUtil.getLKICopy(gameCard);
// this check needs to check if this card would be on the battlefield
noTributeLKI.setLastKnownZone(activator.getZone(ZoneType.Battlefield));
@@ -235,65 +235,58 @@ public class CountersPutEffect extends SpellAbilityEffect {
continue;
}
String message = "Do you want to put " + counterAmount + " +1/+1 counters on " + tgtCard + " ?";
String message = "Do you want to put " + counterAmount + " +1/+1 counters on " + gameCard + " ?";
Player chooser = pc.chooseSingleEntityForEffect(activator.getOpponents(), sa, "Choose an opponent");
if (chooser.getController().confirmAction(sa, PlayerActionConfirmMode.Tribute, message)) {
tgtCard.setTributed(true);
gameCard.setTributed(true);
} else {
continue;
}
}
if (rememberCards) {
card.addRemembered(tgtCard);
card.addRemembered(gameCard);
}
final Zone zone = tgtCard.getGame().getZoneOf(tgtCard);
final Zone zone = gameCard.getGame().getZoneOf(gameCard);
if (zone == null || zone.is(ZoneType.Battlefield) || zone.is(ZoneType.Stack)) {
if (etbcounter) {
tgtCard.addEtbCounter(counterType, counterAmount, placer);
gameCard.addEtbCounter(counterType, counterAmount, placer);
} else {
tgtCard.addCounter(counterType, counterAmount, placer, true, table);
gameCard.addCounter(counterType, counterAmount, placer, true, table);
}
if (remember) {
final int value = tgtCard.getTotalCountersToAdd();
tgtCard.addCountersAddedBy(card, counterType, value);
final int value = gameCard.getTotalCountersToAdd();
gameCard.addCountersAddedBy(card, counterType, value);
}
if (sa.hasParam("Evolve")) {
final Map<String, Object> runParams = Maps.newHashMap();
runParams.put("Card", tgtCard);
game.getTriggerHandler().runTriggerOld(TriggerType.Evolved, runParams, false);
game.getTriggerHandler().runTrigger(TriggerType.Evolved, AbilityKey.mapFromCard(gameCard), false);
}
if (sa.hasParam("Monstrosity")) {
tgtCard.setMonstrous(true);
final Map<String, Object> runParams = Maps.newHashMap();
runParams.put("Card", tgtCard);
runParams.put("MonstrosityAmount", counterAmount);
game.getTriggerHandler().runTriggerOld(TriggerType.BecomeMonstrous, runParams, false);
gameCard.setMonstrous(true);
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromCard(gameCard);
runParams.put(AbilityKey.MonstrosityAmount, counterAmount);
game.getTriggerHandler().runTrigger(TriggerType.BecomeMonstrous, runParams, false);
}
if (sa.hasParam("Renown")) {
tgtCard.setRenowned(true);
final Map<String, Object> runParams = Maps.newHashMap();
runParams.put("Card", tgtCard);
game.getTriggerHandler().runTriggerOld(TriggerType.BecomeRenowned, runParams, false);
gameCard.setRenowned(true);
game.getTriggerHandler().runTrigger(TriggerType.BecomeRenowned, AbilityKey.mapFromCard(gameCard), false);
}
if (sa.hasParam("Adapt")) {
// need to remove special keyword
tgtCard.removeHiddenExtrinsicKeyword("CARDNAME adapts as though it had no +1/+1 counters");
final Map<String, Object> runParams = Maps.newHashMap();
runParams.put("Card", tgtCard);
game.getTriggerHandler().runTriggerOld(TriggerType.Adapt, runParams, false);
gameCard.removeHiddenExtrinsicKeyword("CARDNAME adapts as though it had no +1/+1 counters");
game.getTriggerHandler().runTrigger(TriggerType.Adapt, AbilityKey.mapFromCard(gameCard), false);
}
} else {
// adding counters to something like re-suspend cards
// etbcounter should apply multiplier
if (etbcounter) {
tgtCard.addEtbCounter(counterType, counterAmount, placer);
gameCard.addEtbCounter(counterType, counterAmount, placer);
} else {
tgtCard.addCounter(counterType, counterAmount, placer, false, table);
gameCard.addCounter(counterType, counterAmount, placer, false, table);
}
}
game.updateLastStateForCard(tgtCard);
game.updateLastStateForCard(gameCard);
}
} else if (obj instanceof Player) {
// Add Counters to players!

View File

@@ -1,9 +1,9 @@
package forge.game.ability.effects;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import forge.game.Game;
import forge.game.GameEntityCounterTable;
import forge.game.ability.AbilityKey;
import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card;
import forge.game.card.CardCollection;
@@ -16,7 +16,6 @@ import forge.game.zone.ZoneType;
import forge.util.Lang;
import java.util.List;
import java.util.Map;
public class ExploreEffect extends SpellAbilityEffect {
@@ -83,9 +82,7 @@ public class ExploreEffect extends SpellAbilityEffect {
}
// a creature does explore even if it isn't on the battlefield anymore
final Map<String, Object> runParams = Maps.newHashMap();
runParams.put("Card", c);
game.getTriggerHandler().runTriggerOld(TriggerType.Explores, runParams, false);
game.getTriggerHandler().runTrigger(TriggerType.Explores, AbilityKey.mapFromCard(c), false);
}
table.triggerCountersPutAll(game);
}

View File

@@ -1,10 +1,10 @@
package forge.game.ability.effects;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import forge.game.Game;
import forge.game.GameEntityCounterTable;
import forge.game.ability.AbilityKey;
import forge.game.ability.AbilityUtils;
import forge.game.card.Card;
import forge.game.card.CardDamageMap;
@@ -59,9 +59,9 @@ public class FightEffect extends DamageBaseEffect {
dealDamage(sa, fighters.get(0), fighters.get(1));
for (Card c : fighters) {
final Map<String, Object> runParams = Maps.newHashMap();
runParams.put("Fighter", c);
game.getTriggerHandler().runTriggerOld(TriggerType.Fight, runParams, false);
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
runParams.put(AbilityKey.Fighter, c);
game.getTriggerHandler().runTrigger(TriggerType.Fight, runParams, false);
}
}

View File

@@ -1,7 +1,7 @@
package forge.game.ability.effects;
import com.google.common.collect.Maps;
import forge.game.GameObject;
import forge.game.ability.AbilityKey;
import forge.game.ability.AbilityUtils;
import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card;
@@ -209,10 +209,10 @@ public class FlipCoinEffect extends SpellAbilityEffect {
caller.getGame().getAction().nofityOfValue(sa, caller, wonFlip ? "win" : "lose", null);
// Run triggers
Map<String,Object> runParams = Maps.newHashMap();
runParams.put("Player", caller);
runParams.put("Result", Boolean.valueOf(wonFlip));
caller.getGame().getTriggerHandler().runTriggerOld(TriggerType.FlippedCoin, runParams, false);
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
runParams.put(AbilityKey.Player, caller);
runParams.put(AbilityKey.Result, wonFlip);
caller.getGame().getTriggerHandler().runTrigger(TriggerType.FlippedCoin, runParams, false);
} while (sa.hasParam("FlipUntilYouLose") && wonFlip);
if (sa.hasParam("FlipUntilYouLose") && sa.hasAdditionalAbility("LoseSubAbility")) {

View File

@@ -4,12 +4,12 @@ import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import forge.StaticData;
import forge.card.CardRulesPredicates;
import forge.card.ColorSet;
import forge.card.MagicColor;
import forge.game.Game;
import forge.game.ability.AbilityKey;
import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card;
import forge.game.card.CardFactory;
@@ -23,7 +23,6 @@ import forge.item.PaperCard;
import forge.util.Aggregates;
import java.util.List;
import java.util.Map;
public class PlayLandVariantEffect extends SpellAbilityEffect {
@@ -80,9 +79,7 @@ public class PlayLandVariantEffect extends SpellAbilityEffect {
game.fireEvent(new GameEventLandPlayed(activator, source));
// Run triggers
final Map<String, Object> runParams = Maps.newHashMap();
runParams.put("Card", source);
game.getTriggerHandler().runTriggerOld(TriggerType.LandPlayed, runParams, false);
game.getTriggerHandler().runTrigger(TriggerType.LandPlayed, AbilityKey.mapFromCard(source), false);
game.getStack().unfreezeStack();
activator.addLandPlayedThisTurn();
}

View File

@@ -536,27 +536,50 @@ public class CardView extends GameEntityView {
}
public String getText() {
return getText(getCurrentState());
return getText(getCurrentState(), null);
}
public String getText(CardStateView state) {
public String getText(CardStateView state, HashMap<String, String> translationsText) {
final StringBuilder sb = new StringBuilder();
//final boolean isSplitCard = (state.getState() == CardStateName.LeftSplit);
String tname = "", toracle = "", taltname = "", taltoracle = "";
// If we have translations, use them
if (translationsText != null) {
tname = translationsText.get("name");
taltname = translationsText.get("altname");
// Don't translate oracles if the card is a cloned one
if (((String) get(TrackableProperty.Cloner)).isEmpty()) {
toracle = translationsText.get("oracle");
taltoracle = translationsText.get("altoracle");
}
}
tname = tname.isEmpty() ? state.getName() : tname;
toracle = toracle.isEmpty() ? state.getOracleText() : toracle;
if (isSplitCard()) {
taltname = getAlternateState().getName();
taltoracle = getAlternateState().getOracleText();
}
if (getId() < 0) {
if (isSplitCard()) {
sb.append("(").append(state.getName()).append(") ");
sb.append(state.getOracleText());
sb.append("(").append(tname).append(") ");
sb.append(toracle);
sb.append("\r\n\r\n");
sb.append("(").append(getAlternateState().getName()).append(") ");
sb.append(getAlternateState().getOracleText());
sb.append("(").append(taltname).append(") ");
sb.append(taltoracle);
return sb.toString().trim();
} else {
return state.getOracleText();
return toracle;
}
}
final String rulesText = state.getRulesText();
if (!rulesText.isEmpty()) {
if (!toracle.isEmpty()) {
sb.append(toracle).append("\r\n\r\n");
} else if (!rulesText.isEmpty()) {
sb.append(rulesText).append("\r\n\r\n");
}
if (isCommander()) {
@@ -565,6 +588,7 @@ public class CardView extends GameEntityView {
}
if (isSplitCard() && !isFaceDown()) {
// TODO: Translation?
CardStateView view = state.getState() == CardStateName.LeftSplit ? state : getAlternateState();
if (getZone() != ZoneType.Stack) {
sb.append("(");
@@ -573,12 +597,12 @@ public class CardView extends GameEntityView {
}
sb.append(view.getAbilityText());
} else {
sb.append(state.getAbilityText());
if (toracle.isEmpty()) sb.append(state.getAbilityText());
}
if (isSplitCard() && !isFaceDown() && getZone() != ZoneType.Stack) {
//ensure ability text for right half of split card is included unless spell is on stack
sb.append("\r\n\r\n").append("(").append(getAlternateState().getName()).append(") ").append(getAlternateState().getOracleText());
sb.append("\r\n\r\n").append("(").append(taltname).append(") ").append(taltoracle);
}
String nonAbilityText = get(TrackableProperty.NonAbilityText);

View File

@@ -223,7 +223,7 @@ public class CostAdjustment {
cardsToDelveOut.add(c);
} else if (!test) {
sa.getHostCard().addDelved(c);
final Card d = game.getAction().exile(c, null, null);
final Card d = game.getAction().exile(c, null);
table.put(ZoneType.Graveyard, d.getZone().getZoneType(), d);
}
}

View File

@@ -30,7 +30,7 @@ public abstract class AbstractMulligan {
CardCollection toMulligan = new CardCollection(player.getCardsIn(ZoneType.Hand));
revealPreMulligan(toMulligan);
for (final Card c : toMulligan) {
player.getGame().getAction().moveToLibrary(c, null, null);
player.getGame().getAction().moveToLibrary(c, null);
}
try {
Thread.sleep(100); //delay for a tiny bit to give UI a chance catch up

View File

@@ -242,7 +242,7 @@ public class Player extends GameEntity implements Comparable<Player> {
game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);
activeScheme = getZone(ZoneType.SchemeDeck).get(0);
// gameAction moveTo ?
game.getAction().moveTo(ZoneType.Command, activeScheme, null, null);
game.getAction().moveTo(ZoneType.Command, activeScheme, null);
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
// Run triggers
@@ -1308,7 +1308,7 @@ public class Player extends GameEntity implements Comparable<Player> {
if (toGrave != null) {
for(Card c : toGrave) {
getGame().getAction().moveToGraveyard(c, cause, null);
getGame().getAction().moveToGraveyard(c, cause);
numToGrave++;
}
}
@@ -1316,7 +1316,7 @@ public class Player extends GameEntity implements Comparable<Player> {
if (toTop != null) {
Collections.reverse(toTop); // the last card in list will become topmost in library, have to revert thus.
for(Card c : toTop) {
getGame().getAction().moveToLibrary(c, cause, null);
getGame().getAction().moveToLibrary(c, cause);
numToTop++;
}
}
@@ -1399,7 +1399,7 @@ public class Player extends GameEntity implements Comparable<Player> {
}
}
c = game.getAction().moveToHand(c, null, null);
c = game.getAction().moveToHand(c, null);
drawn.add(c);
if (topCardRevealed) {
@@ -1574,16 +1574,16 @@ public class Player extends GameEntity implements Comparable<Player> {
sb.append(this).append(" discards ").append(c);
final Card newCard;
if (discardToTopOfLibrary) {
newCard = game.getAction().moveToLibrary(c, 0, sa, null);
newCard = game.getAction().moveToLibrary(c, 0, sa);
sb.append(" to the library");
// Play the Discard sound
}
else if (discardMadness) {
newCard = game.getAction().exile(c, sa, null);
newCard = game.getAction().exile(c, sa);
sb.append(" with Madness");
}
else {
newCard = game.getAction().moveToGraveyard(c, sa, null);
newCard = game.getAction().moveToGraveyard(c, sa);
// Play the Discard sound
}
if (table != null) {
@@ -1651,7 +1651,7 @@ public class Player extends GameEntity implements Comparable<Player> {
for (Card m : milledView) {
final ZoneType origin = m.getZone().getZoneType();
final Card d = game.getAction().moveTo(destination, m, sa, null);
final Card d = game.getAction().moveTo(destination, m, sa);
table.put(origin, d.getZone().getZoneType(), d);
}
@@ -1711,7 +1711,7 @@ public class Player extends GameEntity implements Comparable<Player> {
if (land.isFaceDown()) {
land.turnFaceUp();
}
game.getAction().moveTo(getZone(ZoneType.Battlefield), land, null, new HashMap<>());
game.getAction().moveTo(getZone(ZoneType.Battlefield), land, null);
// play a sound
game.fireEvent(new GameEventLandPlayed(this, land));
@@ -2512,7 +2512,7 @@ public class Player extends GameEntity implements Comparable<Player> {
game.getView().updatePlanarPlayer(getView());
for (Card c : currentPlanes) {
game.getAction().moveTo(ZoneType.Command,c, null, null);
game.getAction().moveTo(ZoneType.Command,c, null);
//getZone(ZoneType.PlanarDeck).remove(c);
//getZone(ZoneType.Command).add(c);
}
@@ -2542,7 +2542,7 @@ public class Player extends GameEntity implements Comparable<Player> {
//game.getZoneOf(plane).remove(plane);
plane.clearControllers();
//getZone(ZoneType.PlanarDeck).add(plane);
game.getAction().moveTo(ZoneType.PlanarDeck, plane,-1, null, null);
game.getAction().moveTo(ZoneType.PlanarDeck, plane,-1, null);
}
currentPlanes.clear();

View File

@@ -23,6 +23,7 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import forge.card.mana.ManaAtom;
import forge.game.ability.AbilityKey;
import forge.game.trigger.Trigger;
import forge.game.trigger.TriggerHandler;
import forge.util.TextUtil;
@@ -166,13 +167,11 @@ public class AbilityManaPart implements java.io.Serializable {
manaPool.add(this.lastManaProduced);
// Run triggers
final Map<String, Object> runParams = Maps.newHashMap();
runParams.put("Card", source);
runParams.put("Player", player);
runParams.put("AbilityMana", sa);
runParams.put("Produced", afterReplace);
player.getGame().getTriggerHandler().runTriggerOld(TriggerType.TapsForMana, runParams, false);
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromCard(source);
runParams.put(AbilityKey.Player, player);
runParams.put(AbilityKey.AbilityMana, sa);
runParams.put(AbilityKey.Produced, afterReplace);
player.getGame().getTriggerHandler().runTrigger(TriggerType.TapsForMana, runParams, false);
if (source.isLand()) {
player.setTappedLandForManaThisTurn(true);
}

View File

@@ -327,6 +327,7 @@ public class TriggerHandler {
// The plan is to slowly refactor any usages of `runTriggerOld` to use `runTrigger`. Then we can just inline
// `runTriggerOld` into `runTrigger` and change the code inside to just always use a `Map<TriggerKey, Object>`.
// The reason we can't just call them both `runTrigger` is because we get a `same erasure` compile error if we do.
@Deprecated
public final void runTriggerOld(final TriggerType mode, final Map<String, Object> runParams, boolean holdTrigger) {
if (suppressedModes.contains(mode)) {
return;

View File

@@ -36,6 +36,7 @@ import forge.GameCommand;
import forge.game.Game;
import forge.game.GameLogEntryType;
import forge.game.GameObject;
import forge.game.ability.AbilityKey;
import forge.game.ability.AbilityUtils;
import forge.game.ability.ApiType;
import forge.game.card.Card;
@@ -136,7 +137,7 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
// on the stack zone, move there
if (ability.isSpell()) {
if (!source.isCopiedSpell() && !source.isInZone(ZoneType.Stack)) {
ability.setHostCard(game.getAction().moveToStack(source, ability, null));
ability.setHostCard(game.getAction().moveToStack(source, ability));
}
}
@@ -531,10 +532,10 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
else if ((source.isInstant() || source.isSorcery() || fizzle) &&
source.isInZone(ZoneType.Stack)) {
// If Spell and still on the Stack then let it goto the graveyard or replace its own movement
Map<String, Object> params = Maps.newHashMap();
params.put("StackSa", sa);
params.put("StackSi", si);
params.put("Fizzle", fizzle);
Map<AbilityKey, Object> params = AbilityKey.newMap();
params.put(AbilityKey.StackSa, sa);
params.put(AbilityKey.StackSi, si);
params.put(AbilityKey.Fizzle, fizzle);
game.getAction().moveToGraveyard(source, null, params);
}
}

View File

@@ -355,7 +355,21 @@ public enum CSubmenuPreferences implements ICDoc {
}
private void initializeDefaultLanguageComboBox() {
final String [] choices = {"en-US", "es-ES", "de-DE"};
final File lang_root = new File(ForgeConstants.LANG_DIR);
final File[] files = lang_root.listFiles();
final List<String> allLanguages = new ArrayList<>();
for ( File file : files ) {
if ( !file.isFile() ) {
continue;
}
String languageName = file.getName();
if (!languageName.endsWith(".properties")) {
continue;
}
allLanguages.add(languageName.replace(".properties", ""));
}
final String [] choices = new String[ allLanguages.size() ];
allLanguages.toArray( choices );
final FPref userSetting = FPref.UI_LANGUAGE;
final FComboBoxPanel<String> panel = this.view.getCbpDefaultLanguageComboBoxPanel();
final FComboBox<String> comboBox = createComboBox(choices, userSetting);

View File

@@ -21,6 +21,7 @@ import forge.CachedCardImage;
import forge.FThreads;
import forge.StaticData;
import forge.card.CardEdition;
import forge.card.CardTranslation;
import forge.card.mana.ManaCost;
import forge.game.card.Card;
import forge.game.card.CardView;
@@ -690,7 +691,7 @@ public class CardPanel extends SkinnedPanel implements CardContainer, IDisposabl
}
// Card name overlay
titleText.setText(card.getCurrentState().getName());
titleText.setText(CardTranslation.getTranslatedName(card.getCurrentState().getName()));
final int damage = card.getDamage();
damageText.setText(damage > 0 ? "\u00BB " + damage + " \u00AB" : "");

View File

@@ -2,6 +2,7 @@ package forge.gamesimulationtests.util;
import forge.deck.Deck;
import forge.game.*;
import forge.game.ability.AbilityKey;
import forge.game.card.Card;
import forge.game.event.GameEventGameFinished;
import forge.game.player.Player;
@@ -137,8 +138,7 @@ public class GameWrapper {
//game.getAction().startGame( null ) determines starting player, draws starting hands, handles mulligans, and initiates the first turn
//skip drawing initial hand and mulliganing
game.setAge( GameStage.Play );
final HashMap<String, Object> runParams = new HashMap<>();
game.getTriggerHandler().runTriggerOld( TriggerType.NewGame, runParams, false );
game.getTriggerHandler().runTrigger(TriggerType.NewGame, AbilityKey.newMap(), false);
//first player in the list starts, no coin toss etc
game.getPhaseHandler().startFirstTurn( game.getPlayers().get( 0 ) );

View File

@@ -10,6 +10,7 @@ import forge.assets.AssetsDownloader;
import forge.assets.FSkin;
import forge.assets.FSkinFont;
import forge.assets.ImageCache;
import forge.card.CardTranslation;
import forge.error.BugReporter;
import forge.error.ExceptionHandler;
import forge.interfaces.IDeviceAdapter;
@@ -108,6 +109,9 @@ public class Forge implements ApplicationListener {
splashScreen.getProgressBar().setDescription("Loading fonts...");
FSkinFont.preloadAll();
splashScreen.getProgressBar().setDescription("Loading card translations...");
CardTranslation.preloadTranslation(prefs.getPref(FPref.UI_LANGUAGE));
splashScreen.getProgressBar().setDescription("Finishing startup...");
Gdx.app.postRunnable(new Runnable() {

View File

@@ -352,30 +352,115 @@ public class FSkinFont {
//only generate images for characters that could be used by Forge
String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890\"!?'.,;:()[]{}<>|/@\\^$-%+=#_&*\u2014\u2022";
chars += "ÁÉÍÓÚáéíóúÀÈÌÒÙàèìòùÑñÄËÏÖÜäëïöüẞß";
//generate from zh-CN.properties
chars += "“”、一三上下不与专且东两个中临为主么义之乐也了争事二于五些交产"
+ "亮人什仅从他付代以们件价任优会传伤但位低住体何作你使例供侧侵便"
+ "保信倍候值偏做停偿像允元充先克入全关其具兼兽内再冒决况准减凑几"
+ "出击分切列则创初删利到制刷前剪副力功加动助励势包化匹区十协单卖"
+ "博卡印即原去参双发取受变叠口句只召可台史右号司合同名后向吗否含"
+ "启告员周命和咒品哪唤售商喜器回因团困围图在地场均坊坏坟型域基堆"
+ "塞境墓增声处备复多大天太失头夹奇奖套好如始子字存它完宏官定宝实"
+ "宠客害家容对导将小少尝就局层屏展属巅左差已市布带帮常幕并幻序库"
+ "应底店度建开异弊式引张弧强当录形征很得御徽必志快忽态性总恢息您"
+ "悬情惊惰想意慢戏成我或战户所手才打执找把抓抗折报拉拟拥择括持指"
+ "按挑挡挥损换据捷排探接控掷描提摩摸撤播操擎支收改攻放故效数文斗"
+ "斯新方旅无日旧时明易星是显景暂暴更替最有服期未本术机权束来杯松"
+ "板构果柄染查标栏样格框档检植模次止正此死殊段每比池没法注洗洛活"
+ "测消涵混添清渐温渲游源滤灵灼点烁烧然片版牌物特状独率王玩环现珍"
+ "理瓦生用由画界留略登白百的盒盖盘目直相省看着瞄瞬知破确磨示神票"
+ "禁离种秘积称移稀程空窗立竞章第等筑筛简管箭类精系素索紧红级线组"
+ "细终经结给络统继绩续维绿缓编缩缺网置者而联肥背胜能脚自至致舒航"
+ "般艇色节英范莫获菜蓝薪藏行衍衡补表被西要覆观规视览觉角解触言誓"
+ "警计认让记许论设证评试诗诡该详语误说请读调谋谜象败费资赌赛赢起"
+ "距跳踢转轮轻载较辑输辨边过运近返还这进远连述退适选逐递速造道那"
+ "部都配醒释里重量金钮铁银销锁锋错锦键长门闪闭间队防阴阶阻限除险"
+ "隆随隐障难雄集雪需静非靠面音页顶项顺须顿预题颜风飞首验骰高魔鹏"
+ "黑默鼠!(),:?";
//generate from zh-CN.properties,and cardnames-zh-CN.txt
//forge generate 3000+ characters cache need Take some time(MIN_FONT_SIZE - MAX_FONT_SIZE all size)
//maybe using libgdx-hiero generate font cache is better
chars += "·âéöû—“”•−●、。「」『』一丁七万三!(),/:;?~鼹鼻齐齑"
+ "上下不与丑专且世丘业丛东丝两严丧个中丰临丸丹为丽举乃久么义"
+ "之乌乍乐乔乖乘乙九也乡书乱乳乾了予争事二于云互五井亘亚些亡"
+ "交亥亦产享京亮亲亵人亿什仁仅仆仇今介仍从仑仓仕他仗付仙代令"
+ "以仪们仰仲件价任份仿伊伍伏伐休众优伙会伟传伤伦伪伯伴伶伺似"
+ "伽但位低住佐佑体何余佚佛作你佣佩佳使例侍侏供依侠侣侦侧侬侮"
+ "侯侵便促俄俊俐俑俘保信修俯俸個倍倒候借倡倦倨倪债值倾假偏做"
+ "停偶偷偿傀傍储催傲像僧僭僵僻儒儡儿兀允元充兆先光克免兔兕党"
+ "入全八公六兰共关兴兵其具典兹养兼兽内册再冒冕写军农冠冢冥冬"
+ "冰冲决况冶冷冻净准凋凌减凑凛凝几凡凤凭凯凰凶出击凿刀刃分切"
+ "刈刍刑划列则刚创初删判利别刮到制刷刹刺刻刽剂剃削剌前剎剑剖"
+ "剜剥剧剩剪副割剽劈力劝办功加务劣动助努劫励劲劳势勃勇勉勋勒"
+ "勘募勤勾包匍匐匕化北匙匠匪匹区医匿十千升午半华协卑卒卓单卖"
+ "南博卜占卡卢卦卫印危即却卵卷卸厂厄厅历厉压厚原厢厥厦厨去参"
+ "叉及友双反发叔取受变叙叛叠口古句另叨只叫召叮可台史右叶号司"
+ "叹吁吃各合吉吊同名后吏吐向吓吕吗君吞吟否含听吮启吱吸吹吻吼"
+ "呆告呕员周味呼命咆和咏咒咕咬咯咳咽哀品哈响哑哗哥哨哩哪哭哮"
+ "哲哺唐唤售唯唱啃啄商啜啪啮啸喀喂善喉喊喋喘喙喜喝喧喷嗅嗔嗜"
+ "嗡嗣嗫嘉嘎嘘嘲嘴嘶噜噤器噬嚎嚼囊囚四回因团囤园困围固国图圆"
+ "圈團土圣在地场圾均坊坍坎坏坐坑块坚坛坝坞坟坠坤坦坪坷垂垃型"
+ "垒垛垠垢垣垦埃埋城域培基堂堆堕堡堤堪堰塌塑塔塘塞填境墓墙增"
+ "墟墨壁壅壕壤士壬壮声壳壶处备复夏外多夜够大天太夫央失头夷夸"
+ "夹夺奇奈奉奋奎契奔奖套奢奥女奴她好如妃妄妆妇妈妖妙妥妪妮妲"
+ "妹姆姊始姓姜姥姬姿威娃娅娜婆婉婪婶媒嫁嫩嬉子孑孔孕字存孚孢"
+ "季孤学孪孳孵孽宁它宅宇守安完宏宗官宙定宜宝实宠审客宣室宪宫"
+ "宰害宴家容宾宿寂寄密寇富寒寓寝察寡寨寰寸对寺寻导封射将尉尊"
+ "小少尔尖尘尚尝尤尬就尸尹尺尼尽尾局层居屈屋屏屑展属屠履屯山"
+ "屹岁岑岔岖岗岚岛岩岱岳岸峡峭峰峻崇崎崔崖崩崽嵌巅巍川巡巢工"
+ "左巧巨巫差己已巳巴巷币市布帅帆师希帕帖帘帜帝带席帮帷常帽幅"
+ "幔幕干平年并幸幻幼幽广庄庆庇床序库应底店庙府庞废度座庭庶廉"
+ "廊延建开异弃弄弊式弑弓引弗弘弟张弥弦弧弩弯弱張弹强归当录彗"
+ "形彩彰影役彻彼往征径待很徊律後徒徕得徘徙從御復循微徵德徽心"
+ "必忆忌忍忒志忘忠忧快忱念忽忾忿怀态怒怖思急性怨怪怯总恍恐恒"
+ "恕恢恣恨恩恫息恰恳恶恸恼悉悍悔悖悟患悦您悬悯悲悼情惊惑惘惚"
+ "惠惧惨惩惫惰想惹愁愈愎意愚感愣愤愧愿慈慌慎慑慕慢慧慨慰慷憎"
+ "憩懦戈戏成我戒戕或战戟截戮戳戴户戾房所扁扇扈手才扎扑扒打托"
+ "扣执扩扫扬扭扮扯扰找承技抄抉把抑抓投抖抗折抚抛抢护报披抱抵"
+ "抹押抽拂拆拉拍拒拓拔拖拘招拜拟拣拥拦拧拨择括拯拱拳拷拼拽拾"
+ "拿持挂指按挑挖挚挟挠挡挣挥挪挫振挺挽捆捉捍捕捞损换捣捧据捷"
+ "捻掀授掉掌掐排掘掠探接控推掩措掮掳掷揍描提插握揭援揽搁搅搏"
+ "搐搜搞搬搭携摄摆摇摘摧摩摸摹撒撕撞撤撬播撵撼擅操擎擒擞擦攀"
+ "攫支收改攻放政故效敌敏救敕教敞敢散敦敬数敲整文斐斑斓斗斤斥"
+ "斧斩断斯新方施旁旅旋族旗无既日旧旨早旭时旷旸旺昂昆昌明昏易"
+ "昔昙星映春昨昭是昵昼显晃晋晓晕晖晚晨普景晰晴晶晷智暂暗暮暴"
+ "曙曜曝曦曲曳更曼曾替最月有服朗望朝期木未末本札术朵机朽杀杂"
+ "权杉李村杖杜束条来杨杯杰松板极构析林枚果枝枢枪枭枯架枷柄柏"
+ "某染柜查柩柯柱柳栅标栈栋栏树栓栖栗株样核根格栽桂框案桌桎桑"
+ "桓桠档桥桨桩桶梁梅梓梢梣梦梧梨梭梯械检棄棍棒棕棘棚森棱棺椁"
+ "植椎椒椽楂楔楚楣楼概榄榆榔榨榴槌槛模横樱樵橇橡橫檀檐次欢欣"
+ "欧欲欺歇歌止正此步武歪死歼殁殆殇殉殊残殍殒殓殖殡殴段殷殿毁"
+ "毅母每毒比毕毛毡氅氏民氓气氤氦氧氲水永汀汁求汇汉汐汗汛池污"
+ "汤汨汪汰汲汹汽沃沈沉沌沐沙沟没沥沦沮河沸油治沼沾沿泄泉泊法"
+ "泛泞泡波泣泥注泪泯泰泽洁洋洒洗洛洞津洪洲活洼派流浅浆浇浊测"
+ "济浑浓浚浩浪浮浴海浸涅消涉涌涎涛涟涡涤润涨涩液涵淋淘淤淬深"
+ "混淹添清渊渎渐渔渗渝渠渡渣渥温港渲渴游湍湖湛湮湾湿溃溅源溜"
+ "溢溪溯溶溺滋滑滓滔滚滞满滤滥滨滴漂漏演漠漩漫潘潜潭潮澄澈澹"
+ "激濑濒瀑瀚灌火灭灯灰灵灼灾灿炉炎炙炫炬炭炮炸点炼炽烁烂烈烙"
+ "烛烟烤烦烧烫烬热烽焉焊焚焦焰然煌煎煞煤照煮煽熄熊熏熔熟熠熵"
+ "燃燎燕燧爆爪爬爱爵父片版牌牒牙牛牝牡牢牦牧物牲牵特牺犀犁犄"
+ "犧犬犯状狂狄狈狐狗狙狞狡狩独狭狮狰狱狷狸狼猁猎猛猜猪猫献猴"
+ "猿獒獠獾玄率玉王玖玛玩玫环现玷玻珀珂珊珍珠班球理琉琐琥琳琴"
+ "琵琼瑕瑙瑚瑞瑟瑰璃璞璧瓜瓣瓦瓮瓯瓶瓷甘生用甩甫田由甲电画畅"
+ "界畏留略畸畿疆疏疑疗疚疡疣疤疫疮疯疲疵疹疽疾病症痕痛痞痢痨"
+ "痪痴痹瘟瘠瘤瘫瘴癣癫癸登白百的皆皇皈皮皱皿盆盈盐监盒盔盖盗"
+ "盘盛盟目盲直相盾省看真眠眨眩眷眺眼着睁睡督睥睨睿瞄瞒瞥瞪瞬"
+ "瞭瞰瞳矛矢知矫短矮石矾矿码砂砍研砖砦砧破砸砾础硕硫硬确碍碎"
+ "碑碟碧碰碳碻碾磁磊磨磷磺礁示礼社祀祈祖祝神祟祠祥票祭祷祸禁"
+ "禄福离禽私秃秉秋种科秘秣秤秩积称移秽稀程税稚稳稻穆穗穴究穷"
+ "穹空穿突窃窍窒窖窗窘窜窝窟窥立竖站竞章童竭端竹笏笑笔笛笞符"
+ "第笼等筑筒答策筛筝筹签简箔算箝管箭箱篓篮篱篷簇簧簪米类粉粒"
+ "粗粮粹精糊糙糟系素索紧紫累繁纂纠红约级纪纬纯纱纳纵纷纸纹纺"
+ "纽线练组绅细织终绊绍经绒结绕绘给绚络绝绞统绥继绩绪续绮绯绳"
+ "维绵综绽绿缀缄缅缆缇缉缎缓缕编缘缚缝缠缩缪缰缸缺罅网罔罗罚"
+ "罡罩罪置羁羊美羚羞群羽翁翅翎翔翠翡翰翱翻翼耀老考者而耍耐耕"
+ "耗耘耙耳耶职联聚聪肃肆肇肉肌肖肝肠肢肤肥肩肯育肴肺肿胀胁胃"
+ "胆背胎胖胜胞胡胧胫胶胸能脂脆脉脊脏脑脓脚脱脸腐腔腕腥腱腹腾"
+ "腿膂膏膛膜膝臂臃臣自臭至致舌舍舒舞舟航般舰舱船艇良艰色艺艾"
+ "节芒芙芜芥芬芭芮花芳芽苇苍苏苔苗苛苜苟若苦英茁茂范茉茎茜茧"
+ "茨茫茸荆草荒荚荡荣荨荫药荷莉莎莓莫莱莲莳获莽菁菇菊菌菜菲萃"
+ "萌萍萎萝营萦萧萨萼落著葛葬葵蒂蒙蒸蓄蓑蓝蓟蓿蔑蔓蔚蔷蔻蔽蕈"
+ "蕊蕨蕴蕾薄薇薙薪藏藐藓藤藻虎虏虐虑虔虚虫虱虹蚀蚁蚊蚋蚣蚺蛆"
+ "蛇蛊蛋蛎蛙蛛蛞蛭蛮蛰蛸蛾蜂蜈蜉蜒蜕蜗蜘蜜蜡蜥蜴蜷蜿蝇蝎蝓蝗"
+ "蝙蝠蝣蝾螂螅融螫螳螺蟀蟋蟑蟒蟹蠕蠢血行衍街衡衣补表衫衰袂袋"
+ "袍袖被袭裁裂装裔裘褐褛褪褫褴褶襄西要覆见观规觅视览觉觊角解"
+ "触言詹誉誓警计认讧讨让训议讯记讲许论讽设访诀证评诅识诈诉词"
+ "试诗诘诚诛话诞诡该详诫语误诱诲说诵请诸诺读调谆谈谊谋谍谐谕"
+ "谗谜谟谢谣谦谧谨谬谭谱谴谵谷豁象豪豹豺貂貌贝贞负贡财责贤败"
+ "货质贩贪贫贬购贮贯贱贴贵贷贸费贺贼贾贿赂赃资赋赌赎赏赐赖赘"
+ "赛赞赠赢赤赦赫走赶起超越趋足跃跑跖跚跛距跟跨路跳践跺踏踝踢"
+ "踩踪踵踽蹂蹄蹊蹋蹒蹦蹬躁躏身躯躲車车轨轩转轭轮软轰轴轻载较"
+ "辉辑输辖辗辙辛辜辞辟辨辩辫辰辱边达迁迂迅过迈迎运近返还这进"
+ "远违连迟迦迩迪迫迭述迳迷迸迹追退送适逃逆选逊透逐递途通逝逞"
+ "速造逢逮逸逻逼遁遇遍遏道遗遣遥遨遭遮避邀還那邦邪邬邸郊郎部"
+ "都鄙酋配酒酬酷酸酿醉醒采釉释里重野量金鉴针钉钓钗钙钜钝钟钢"
+ "钥钦钨钩钮钯钱钳钵钻钽铁铃铅铎铜铠铬铭铲银铸铺链销锁锄锅锈"
+ "锋锐错锡锢锤锥锦锭键锯锻镇镖镜镬镰镶长間闇门闩闪闭问闯闲间"
+ "闷闸闹闻阀阁阅队阱防阳阴阵阶阻阿陀附际陆陋降限院除陨险陪陲"
+ "陵陶陷隆随隐隔隘障隧隶隼难雀雄雅集雇雏雕雨雪雯雳零雷雹雾需"
+ "霆震霉霍霓霖霜霞霰露霸霹青靖静非靠靡面革靴靶鞍鞑鞭韧音韵韶"
+ "页顶项顺须顽顾顿颂预颅领颈颊题颚颜额颠颤风飒飓飘飙飞食餍餐"
+ "餮饕饥饭饮饰饱饵饶饿馆馈馐馑首香馨马驭驮驯驰驱驳驹驻驼驽驾"
+ "驿骁骂骄骆骇验骏骐骑骗骚骤骨骰骷骸骼髅髓高鬃鬓鬣鬼魁魂魄魅"
+ "魇魈魏魔鰴鱼鲁鲜鲤鲨鲮鲸鲽鳃鳄鳍鳐鳗鳝鳞鸟鸠鸡鸢鸣鸦鸽鹅鹉"
+ "鹊鹏鹗鹞鹤鹦鹫鹭鹰鹿麋麒麟麦麻黄黎黏黑默黛黜點黠黯鼎鼓鼠鼬"
+ "齿龇龙龟";
final PixmapPacker packer = new PixmapPacker(pageSize, pageSize, Pixmap.Format.RGBA8888, 2, false);
final FreeTypeFontParameter parameter = new FreeTypeFontParameter();
parameter.characters = chars;

View File

@@ -183,7 +183,7 @@ public class CardImageRenderer {
//draw name for card
x += padding;
w -= 2 * padding;
g.drawText(state.getName(), NAME_FONT, Color.BLACK, x, y, w - manaCostWidth - padding, h, false, Align.left, true);
g.drawText(CardTranslation.getTranslatedName(state.getName()), NAME_FONT, Color.BLACK, x, y, w - manaCostWidth - padding, h, false, Align.left, true);
}
public static final FBufferedImage forgeArt;
@@ -260,7 +260,7 @@ public class CardImageRenderer {
g.drawImage(image, x + (w - iconSize) / 2, y + (h - iconSize) / 2, iconSize, iconSize);
}
else {
final String text = card.getText(state);
final String text = card.getText(state, CardTranslation.getTranslationTexts(state.getName(), ""));
if (StringUtils.isEmpty(text)) { return; }
float padding = TEXT_FONT.getCapHeight() * 0.75f;

View File

@@ -255,7 +255,7 @@ public class CardRenderer {
state.getLoyalty(), count, suffix, x, y, w, h, compactMode);
}
else { //if fake card, just draw card name centered
String name = state.getName();
String name = CardTranslation.getTranslatedName(state.getName());
if (count > 0) { //preface name with count if applicable
name = count + " " + name;
}
@@ -317,7 +317,7 @@ public class CardRenderer {
CardFaceSymbols.drawManaCost(g, mainManaCost, x + w - manaCostWidth, y, MANA_SYMBOL_SIZE);
x += cardArtWidth;
String name = card.getCurrentState().getName();
String name = CardTranslation.getTranslatedName(card.getCurrentState().getName());
if (count > 0) { //preface name with count if applicable
name = count + " " + name;
}
@@ -448,7 +448,7 @@ public class CardRenderer {
//draw name and mana cost overlays if card is small or default card image being used
if (h <= NAME_COST_THRESHOLD && canShow) {
if (showCardNameOverlay(card)) {
g.drawOutlinedText(details.getName(), FSkinFont.forHeight(h * 0.18f), Color.WHITE, Color.BLACK, x + padding, y + padding, w - 2 * padding, h * 0.4f, true, Align.left, false);
g.drawOutlinedText(CardTranslation.getTranslatedName(details.getName()), FSkinFont.forHeight(h * 0.18f), Color.WHITE, Color.BLACK, x + padding, y + padding, w - 2 * padding, h * 0.4f, true, Align.left, false);
}
if (showCardManaCostOverlay(card)) {
float manaSymbolSize = w / 4;

View File

@@ -26,6 +26,7 @@ import forge.toolbox.FGroupList;
import forge.toolbox.FList;
import forge.toolbox.FOptionPane;
import forge.util.Callback;
import forge.util.Localizer;
import forge.util.Utils;
import java.util.ArrayList;
@@ -38,28 +39,30 @@ public class SettingsPage extends TabPage<SettingsScreen> {
public SettingsPage() {
super("Settings", FSkinImage.SETTINGS);
final Localizer localizer = Localizer.getInstance();
lstSettings.setListItemRenderer(new SettingRenderer());
lstSettings.addGroup("General Settings");
lstSettings.addGroup("Gameplay Options");
lstSettings.addGroup("Random Deck Generation");
lstSettings.addGroup("Advanced Settings");
lstSettings.addGroup("Graphic Options");
lstSettings.addGroup("Card Overlays");
lstSettings.addGroup("Vibration Options");
lstSettings.addGroup("Sound Options");
lstSettings.addGroup(localizer.getMessage("lblGeneralSettings"));
lstSettings.addGroup(localizer.getMessage("lblGameplayOptions"));
lstSettings.addGroup(localizer.getMessage("RandomDeckGeneration"));
lstSettings.addGroup(localizer.getMessage("AdvancedSettings"));
lstSettings.addGroup(localizer.getMessage("GraphicOptions"));
lstSettings.addGroup(localizer.getMessage("lblCardOverlays"));
lstSettings.addGroup(localizer.getMessage("lblVibrationOptions"));
lstSettings.addGroup(localizer.getMessage("SoundOptions"));
//General Settings
lstSettings.addItem(new CustomSelectSetting(FPref.UI_LANGUAGE, "Language",
"Select Language (Excluded Game part. Still a work in progress) (RESTART REQUIRED)",
lstSettings.addItem(new CustomSelectSetting(FPref.UI_LANGUAGE, localizer.getMessage("cbpSelectLanguage"),
localizer.getMessage("nlSelectLanguage"),
FLanguage.getAllLanguages()) {
@Override
public void valueChanged(String newValue) {
FLanguage.changeLanguage(newValue);
}
}, 0);
lstSettings.addItem(new CustomSelectSetting(FPref.UI_SKIN, "Theme",
"Sets the theme that determines how display components are skinned.",
lstSettings.addItem(new CustomSelectSetting(FPref.UI_SKIN, localizer.getMessage("lblTheme"),
localizer.getMessage("nlTheme"),
FSkin.getAllSkins()) {
@Override
public void valueChanged(String newValue) {
@@ -67,15 +70,15 @@ public class SettingsPage extends TabPage<SettingsScreen> {
}
}, 0);
lstSettings.addItem(new BooleanSetting(FPref.UI_LANDSCAPE_MODE,
"Landscape Mode",
"Use landscape (horizontal) orientation for app instead of portrait (vertical).") {
localizer.getMessage("lblLandscapeMode"),
localizer.getMessage("nlLandscapeMode")) {
@Override
public void select() {
super.select();
boolean landscapeMode = FModel.getPreferences().getPrefBoolean(FPref.UI_LANDSCAPE_MODE);
Forge.getDeviceAdapter().setLandscapeMode(landscapeMode); //ensure device able to save off ini file so landscape change takes effect
if (Forge.isLandscapeMode() != landscapeMode) {
FOptionPane.showConfirmDialog("You must restart Forge for this change to take effect.", "Restart Forge", "Restart", "Later", new Callback<Boolean>() {
FOptionPane.showConfirmDialog(localizer.getMessage("lblRestartForgeDescription"), localizer.getMessage("lblRestartForge"), "Restart", localizer.getMessage("lblLater"), new Callback<Boolean>() {
@Override
public void run(Boolean result) {
if (result) {
@@ -87,17 +90,17 @@ public class SettingsPage extends TabPage<SettingsScreen> {
}
}, 0);
lstSettings.addItem(new BooleanSetting(FPref.UI_ANDROID_MINIMIZE_ON_SCRLOCK,
"Minimize on Screen Lock",
"Minimize Forge when screen is locked (enable if you experience graphic glitches after locking your screen)."),
localizer.getMessage("lblMinimizeScreenLock"),
localizer.getMessage("nlMinimizeScreenLock")),
0);
lstSettings.addItem(new BooleanSetting(FPref.USE_SENTRY,
"Automatic Bug Reports",
"Automatically send bug reports to the developers, without prompting."),
localizer.getMessage("lblAutomaticBugReports"),
localizer.getMessage("nlAutomaticBugReports")),
0);
//Gameplay Options
lstSettings.addItem(new CustomSelectSetting(FPref.MULLIGAN_RULE, "Mulligan Rule",
"Choose the version of the Mulligan rule.",
lstSettings.addItem(new CustomSelectSetting(FPref.MULLIGAN_RULE, localizer.getMessage("cbpMulliganRule"),
localizer.getMessage("nlpMulliganRule"),
MulliganDefs.getMulliganRuleNames()) {
@Override
public void valueChanged(String newValue) {
@@ -107,108 +110,108 @@ public class SettingsPage extends TabPage<SettingsScreen> {
}, 1);
lstSettings.addItem(new CustomSelectSetting(FPref.UI_CURRENT_AI_PROFILE,
"AI Personality",
"Choose your AI opponent.",
localizer.getMessage("nlpAiProfiles"),
AiProfileUtil.getProfilesArray()),
1);
lstSettings.addItem(new BooleanSetting(FPref.UI_ANTE,
"Play for Ante",
"Determines whether or not the game is played for ante."),
localizer.getMessage("cbAnte"),
localizer.getMessage("nlAnte")),
1);
lstSettings.addItem(new BooleanSetting(FPref.UI_ANTE_MATCH_RARITY,
"Match Ante Rarity",
"Attempts to make antes the same rarity for all players."),
localizer.getMessage("cbAnteMatchRarity"),
localizer.getMessage("nlAnteMatchRarity")),
1);
lstSettings.addItem(new BooleanSetting(FPref.MATCH_HOT_SEAT_MODE,
"Hot Seat Mode",
"When starting a game with 2 human players, use single prompt to control both players."),
localizer.getMessage("lblHotSeatMode"),
localizer.getMessage("nlHotSeatMode")),
1);
lstSettings.addItem(new BooleanSetting(FPref.UI_ENABLE_AI_CHEATS,
"Allow AI Cheating",
"Allow the AI to cheat to gain advantage (for personalities that have cheat shuffling options set)."),
localizer.getMessage("cbEnableAICheats"),
localizer.getMessage("nlEnableAICheats")),
1);
lstSettings.addItem(new BooleanSetting(FPref.UI_MANABURN,
"Mana Burn",
"Play with mana burn (from pre-Magic 2010 rules)."),
localizer.getMessage("cbManaBurn"),
localizer.getMessage("nlManaBurn")),
1);
lstSettings.addItem(new BooleanSetting(FPref.UI_MANA_LOST_PROMPT,
"Prompt Mana Pool Emptying",
"When enabled, you get a warning if passing priority would cause you to lose mana in your mana pool."),
localizer.getMessage("cbManaLostPrompt"),
localizer.getMessage("nlManaLostPrompt")),
1);
lstSettings.addItem(new BooleanSetting(FPref.ENFORCE_DECK_LEGALITY,
"Deck Conformance",
"Enforces deck legality relevant to each environment (minimum deck sizes, max card count etc)."),
localizer.getMessage("cbEnforceDeckLegality"),
localizer.getMessage("nlEnforceDeckLegality")),
1);
lstSettings.addItem(new BooleanSetting(FPref.PERFORMANCE_MODE,
"Performance Mode",
"Disables additional static abilities checks to speed up the game engine. (Warning: breaks some 'as if had flash' scenarios when casting cards owned by opponents)."),
localizer.getMessage("cbPerformanceMode"),
localizer.getMessage("nlPerformanceMode")),
1);
lstSettings.addItem(new BooleanSetting(FPref.MATCH_SIDEBOARD_FOR_AI,
"Human Sideboard for AI",
"Allows users to sideboard with the AIs deck and sideboard in constructed game formats."),
localizer.getMessage("cbSideboardForAI"),
localizer.getMessage("nlSideboardForAI")),
1);
lstSettings.addItem(new BooleanSetting(FPref.FILTERED_HANDS,
"Filtered Hands",
"Generates two starting hands and keeps the one with the closest to average land count for the deck. (Requires restart)"),
localizer.getMessage("cbFilteredHands"),
localizer.getMessage("nlFilteredHands")),
1);
lstSettings.addItem(new BooleanSetting(FPref.UI_CLONE_MODE_SOURCE,
"Clones Use Original Card Art",
"When enabled clones will use their original art instead of the cloned card's art."),
localizer.getMessage("cbCloneImgSource"),
localizer.getMessage("nlCloneImgSource")),
1);
lstSettings.addItem(new BooleanSetting(FPref.MATCHPREF_PROMPT_FREE_BLOCKS,
"Free Block Handling",
"When enabled, if you would have to pay 0 to block, pay automatically without prompt."),
localizer.getMessage("cbPromptFreeBlocks"),
localizer.getMessage("nlPromptFreeBlocks")),
1);
lstSettings.addItem(new BooleanSetting(FPref.UI_DETAILED_SPELLDESC_IN_PROMPT,
"Spell Description in Payment Prompt",
"When enabled, detailed spell/ability descriptions are shown when choosing targets and paying costs."),
localizer.getMessage("cbDetailedPaymentDesc"),
localizer.getMessage("nlDetailedPaymentDesc")),
1);
lstSettings.addItem(new BooleanSetting(FPref.UI_SHOW_STORM_COUNT_IN_PROMPT,
"Show Storm Count in Prompt Panel",
"When enabled, displays the current storm count in the prompt panel."),
localizer.getMessage("cbShowStormCount"),
localizer.getMessage("nlShowStormCount")),
1);
lstSettings.addItem(new BooleanSetting(FPref.UI_PRESELECT_PREVIOUS_ABILITY_ORDER,
"Preselect Last Order of Abilities",
"When enabled, preselects the last defined simultaneous ability order in the ordering dialog."),
localizer.getMessage("cbPreselectPrevAbOrder"),
localizer.getMessage("nlPreselectPrevAbOrder")),
1);
lstSettings.addItem(new CustomSelectSetting(FPref.UI_ALLOW_ORDER_GRAVEYARD_WHEN_NEEDED,
"Order Graveyard",
"Determines when to allow to order cards going to graveyard (never/always/only with relevant cards).",
localizer.getMessage("lblOrderGraveyard"),
localizer.getMessage("nlOrderGraveyard"),
new String[]{
ForgeConstants.GRAVEYARD_ORDERING_NEVER, ForgeConstants.GRAVEYARD_ORDERING_OWN_CARDS,
ForgeConstants.GRAVEYARD_ORDERING_ALWAYS}),
1);
lstSettings.addItem(new CustomSelectSetting(FPref.UI_AUTO_YIELD_MODE,
"Auto-Yield",
"Defines the granularity level of auto-yields (yield to each unique ability or to each unique card).",
localizer.getMessage("lblAutoYields"),
localizer.getMessage("nlpAutoYieldMode"),
new String[]{ForgeConstants.AUTO_YIELD_PER_ABILITY, ForgeConstants.AUTO_YIELD_PER_CARD}),
1);
lstSettings.addItem(new BooleanSetting(FPref.UI_ALLOW_ESC_TO_END_TURN,
"Use Escape Key To End Turn",
"Allows to use Esc keyboard shortcut to end turn prematurely"),
localizer.getMessage("cbEscapeEndsTurn"),
localizer.getMessage("nlEscapeEndsTurn")),
1);
//Random Deck Generation
lstSettings.addItem(new BooleanSetting(FPref.DECKGEN_NOSMALL,
"Remove Small Creatures",
"Disables 1/1 and 0/X creatures in generated decks."),
localizer.getMessage("cbRemoveSmall"),
localizer.getMessage("nlRemoveSmall")),
2);
lstSettings.addItem(new BooleanSetting(FPref.DECKGEN_CARDBASED,
"Include Card-based Deck Generation",
"Builds more synergistic random decks"),
localizer.getMessage("cbCardBased"),
localizer.getMessage("nlCardBased")),
2);
lstSettings.addItem(new BooleanSetting(FPref.DECKGEN_SINGLETONS,
"Singleton Mode",
"Disables non-land duplicates in generated decks."),
localizer.getMessage("cbSingletons"),
localizer.getMessage("nlSingletons")),
2);
lstSettings.addItem(new BooleanSetting(FPref.DECKGEN_ARTIFACTS,
"Remove Artifacts",
"Disables artifact cards in generated decks."),
localizer.getMessage("cbRemoveArtifacts"),
localizer.getMessage("nlRemoveArtifacts")),
2);
//Advanced Settings
lstSettings.addItem(new BooleanSetting(FPref.DEV_MODE_ENABLED,
"Developer Mode",
"Enables menu with functions for testing during development.") {
localizer.getMessage("cbDevMode"),
localizer.getMessage("nlDevMode")) {
@Override
public void select() {
super.select();
@@ -217,37 +220,39 @@ public class SettingsPage extends TabPage<SettingsScreen> {
}
}, 3);
lstSettings.addItem(new CustomSelectSetting(FPref.DEV_LOG_ENTRY_TYPE,
"Game Log Verbosity",
"Changes how much information is displayed in the game log. Sorted by least to most verbose.",
localizer.getMessage("cbpGameLogEntryType"),
localizer.getMessage("nlGameLogEntryType"),
GameLogEntryType.class),
3);
lstSettings.addItem(new BooleanSetting(FPref.LOAD_CARD_SCRIPTS_LAZILY,
"Load Card Scripts Lazily",
"If turned on, Forge will load card scripts as they're needed instead of at start up. (Warning: Experimental)"), 3);
localizer.getMessage("cbLoadCardsLazily"),
localizer.getMessage("nlLoadCardsLazily")),
3);
lstSettings.addItem(new BooleanSetting(FPref.LOAD_HISTORIC_FORMATS,
"Load Historic Formats",
"If turned on, Forge will load all historic format definitions, this may take slightly longer to load at startup."), 3);
localizer.getMessage("cbLoadHistoricFormats"),
localizer.getMessage("nlLoadHistoricFormats")),
3);
//Graphic Options
lstSettings.addItem(new BooleanSetting(FPref.UI_ENABLE_ONLINE_IMAGE_FETCHER,
"Download missing card art",
"Automatically download missing card art"),
localizer.getMessage("cbImageFetcher"),
localizer.getMessage("nlImageFetcher")),
4);
lstSettings.addItem(new BooleanSetting(FPref.UI_OVERLAY_FOIL_EFFECT,
"Display Foil Overlay",
"Displays foil cards with the visual foil overlay effect."),
localizer.getMessage("cbDisplayFoil"),
localizer.getMessage("nlDisplayFoil")),
4);
lstSettings.addItem(new BooleanSetting(FPref.UI_RANDOM_FOIL,
"Random Foil",
"Adds foil effect to random cards."),
localizer.getMessage("cbRandomFoil"),
localizer.getMessage("nlRandomFoil")),
4);
lstSettings.addItem(new BooleanSetting(FPref.UI_RANDOM_ART_IN_POOLS,
"Randomize Card Art",
"Generates cards with random art in generated limited mode card pools."),
localizer.getMessage("cbRandomArtInPools"),
localizer.getMessage("nlRandomArtInPools")),
4);
lstSettings.addItem(new BooleanSetting(FPref.UI_COMPACT_TABS,
"Compact Tabs",
"Show smaller tabs on the top of tab page screens (such as this screen).") {
localizer.getMessage("lblCompactTabs"),
localizer.getMessage("nlCompactTabs")) {
@Override
public void select() {
super.select();
@@ -257,49 +262,49 @@ public class SettingsPage extends TabPage<SettingsScreen> {
}
}, 4);
lstSettings.addItem(new BooleanSetting(FPref.UI_COMPACT_LIST_ITEMS,
"Compact List Items",
"Show only a single line of text for cards and decks on all list views by default."),
localizer.getMessage("lblCompactListItems"),
localizer.getMessage("nlCompactListItems")),
4);
lstSettings.addItem(new BooleanSetting(FPref.UI_HIDE_REMINDER_TEXT,
"Hide Reminder Text",
"Hide reminder text in Card Detail pane."),
localizer.getMessage("cbHideReminderText"),
localizer.getMessage("nlHideReminderText")),
4);
lstSettings.addItem(new BooleanSetting(FPref.UI_MATCH_IMAGE_VISIBLE,
"Show Match Background",
"Show match background image on battlefield, otherwise background texture shown instead."),
localizer.getMessage("lblShowMatchBackground"),
localizer.getMessage("nlShowMatchBackground")),
4);
lstSettings.addItem(new BooleanSetting(FPref.UI_LIBGDX_TEXTURE_FILTERING,
"Battlefield Texture Filtering",
"Filter card art on battlefield to make it less pixelated on large screens (restart required, may reduce performance)."),
localizer.getMessage("lblBattlefieldTextureFiltering"),
localizer.getMessage("nlBattlefieldTextureFiltering")),
4);
lstSettings.addItem(new CustomSelectSetting(FPref.UI_DISPLAY_CURRENT_COLORS,
"Detailed Card Color",
"Displays the breakdown of the current color of cards in the card detail information panel.",
localizer.getMessage("cbpDisplayCurrentCardColors"),
localizer.getMessage("nlDisplayCurrentCardColors"),
new String[]{
ForgeConstants.DISP_CURRENT_COLORS_NEVER, ForgeConstants.DISP_CURRENT_COLORS_MULTICOLOR,
ForgeConstants.DISP_CURRENT_COLORS_CHANGED, ForgeConstants.DISP_CURRENT_COLORS_MULTI_OR_CHANGED,
ForgeConstants.DISP_CURRENT_COLORS_ALWAYS}),
4);
lstSettings.addItem(new BooleanSetting(FPref.UI_ROTATE_SPLIT_CARDS,
"Rotate Zoom Image of Split Cards",
"Rotates the zoomed image of split cards."),
localizer.getMessage("lblRotateZoomSplit"),
localizer.getMessage("nlRotateZoomSplit")),
4);
lstSettings.addItem(new BooleanSetting(FPref.UI_ROTATE_PLANE_OR_PHENOMENON,
"Rotate Zoom Image of Planes/Phenomena",
"Rotates the zoomed image of Plane or Phenomenon cards."),
localizer.getMessage("lblRotateZoomPlanesPhenomena"),
localizer.getMessage("nlRotateZoomPlanesPhenomena")),
4);
lstSettings.addItem(new BooleanSetting(FPref.UI_DYNAMIC_PLANECHASE_BG,
"Dynamic Background Planechase",
"Use current plane images as background (Planes Card images must be on the cache/pics/planechase folder)."),
localizer.getMessage("lblDynamicBackgroundPlanechase"),
localizer.getMessage("nlDynamicBackgroundPlanechase")),
4);
lstSettings.addItem(new BooleanSetting(FPref.UI_DISABLE_IMAGES_EFFECT_CARDS,
"Disable Card 'Effect' Images",
"Disable the zoomed image for the 'Effect' cards."),
localizer.getMessage("lblDisableCardEffect"),
localizer.getMessage("nlDisableCardEffect")),
4);
lstSettings.addItem(new CustomSelectSetting(FPref.UI_CARD_COUNTER_DISPLAY_TYPE,
"Counter Display Type",
"Selects the style of the in-game counter display for cards. Text-based is a new tab-like display on the cards. Image-based is the old counter image. Hybrid displays both at once.",
localizer.getMessage("cbpCounterDisplayType"),
localizer.getMessage("nlCounterDisplayType"),
new String[]{
ForgeConstants.CounterDisplayType.TEXT.getName(), ForgeConstants.CounterDisplayType.IMAGE.getName(),
ForgeConstants.CounterDisplayType.HYBRID.getName(), ForgeConstants.CounterDisplayType.OLD_WHEN_SMALL.getName()}),
@@ -307,44 +312,44 @@ public class SettingsPage extends TabPage<SettingsScreen> {
//Card Overlays
lstSettings.addItem(new BooleanSetting(FPref.UI_SHOW_CARD_OVERLAYS,
"Show Card Overlays",
"Show name, mana cost, p/t, and id overlays for cards, otherwise they're hidden."),
localizer.getMessage("lblShowCardOverlays"),
localizer.getMessage("nlShowCardOverlays")),
5);
lstSettings.addItem(new BooleanSetting(FPref.UI_OVERLAY_CARD_NAME,
"Show Card Name Overlays",
"Show name overlays for cards, otherwise they're hidden."),
localizer.getMessage("lblShowCardNameOverlays"),
localizer.getMessage("nlShowCardNameOverlays")),
5);
lstSettings.addItem(new BooleanSetting(FPref.UI_OVERLAY_CARD_MANA_COST,
"Show Card Mana Cost Overlays",
"Show mana cost overlays for cards, otherwise they're hidden."),
localizer.getMessage("lblShowCardManaCostOverlays"),
localizer.getMessage("nlShowCardManaCostOverlays")),
5);
lstSettings.addItem(new BooleanSetting(FPref.UI_OVERLAY_CARD_POWER,
"Show Card P/T Overlays",
"Show power/toughness/loyalty overlays for cards, otherwise they're hidden."),
localizer.getMessage("lblShowCardPTOverlays"),
localizer.getMessage("nlShowCardPTOverlays")),
5);
lstSettings.addItem(new BooleanSetting(FPref.UI_OVERLAY_CARD_ID,
"Show Card ID Overlays",
"Show id overlays for cards, otherwise they're hidden."),
localizer.getMessage("lblShowCardIDOverlays"),
localizer.getMessage("nlShowCardIDOverlays")),
5);
//Vibration Options
lstSettings.addItem(new BooleanSetting(FPref.UI_VIBRATE_ON_LIFE_LOSS,
"Vibrate When Losing Life",
"Enable vibration when your player loses life or takes damage during a game."),
localizer.getMessage("lblVibrateWhenLosingLife"),
localizer.getMessage("nlVibrateWhenLosingLife")),
6);
lstSettings.addItem(new BooleanSetting(FPref.UI_VIBRATE_ON_LONG_PRESS,
"Vibrate After Long Press",
"Enable quick vibration to signify a long press, such as for card zooming."),
localizer.getMessage("lblVibrateAfterLongPress"),
localizer.getMessage("nlVibrateAfterLongPress")),
6);
//Sound Options
lstSettings.addItem(new BooleanSetting(FPref.UI_ENABLE_SOUNDS,
"Enable Sounds",
"Enable sound effects during the game."),
localizer.getMessage("cbEnableSounds"),
localizer.getMessage("nlEnableSounds")),
7);
lstSettings.addItem(new BooleanSetting(FPref.UI_ENABLE_MUSIC,
"Enable Music",
"Enable background music during the game.") {
localizer.getMessage("cbEnableMusic"),
localizer.getMessage("nlEnableMusic")) {
@Override
public void select() {
super.select();

View File

@@ -3,5 +3,5 @@ ManaCost:3 U R
Types:Instant
A:SP$ Draw | Cost$ 3 U R | NumCards$ 2 | SubAbility$ DBImmediateTrigger | SpellDescription$ Draw two cards. Then you may discard a nonland card. When you do, CARDNAME deals 4 damage to target creature.
SVar:DBImmediateTrigger:DB$ ImmediateTrigger | Execute$ TrigDealDmg | TriggerDescription$ You may discard a nonland card. When you do, CARDNAME deals 4 damage to target creature. | UnlessCost$ Discard<1/Card.nonLand/nonland card> | UnlessPayer$ You | UnlessSwitched$ True
SVar:TrigDealDmg:DB$ DealDamage | Cost$ Discard<1/Card.nonLand/nonland card> | ValidTgts$ Creature | TgtPrompt$ Select target creature | NumDmg$ 4 | SpellDescription$ CARDNAME deals 4 damage to target creature.
SVar:TrigDealDmg:DB$ DealDamage | ValidTgts$ Creature | TgtPrompt$ Select target creature | NumDmg$ 4 | SpellDescription$ CARDNAME deals 4 damage to target creature.
Oracle:Draw two cards. Then you may discard a nonland card. When you do, Hypothesizzle deals 4 damage to target creature.

View File

@@ -3,7 +3,7 @@ ManaCost:2 G G
Types:Enchantment
T:Mode$ Drawn | ValidCard$ Card.YouOwn | Number$ 1 | Static$ True | Execute$ DBReveal | TriggerZones$ Battlefield | TriggerDescription$ Reveal the first card you draw each turn. Whenever you reveal a creature card this way, draw a card.
SVar:DBReveal:DB$ Reveal | Defined$ You | RevealDefined$ TriggeredCard | RememberRevealed$ True | SubAbility$ DBTrigger
SVar:DBTrigger:DB$ ImmediateTrigger | ConditionDefined$ Remembered | ConditionPresent$ Creature+YouCtrl | SubAbility$ DBCleanup | Execute$ TrigDraw | TriggerDescription$ Whenever you reveal a creature card this way, draw a card.
SVar:DBTrigger:DB$ ImmediateTrigger | ConditionDefined$ Remembered | ConditionPresent$ Card.Creature+YouCtrl | SubAbility$ DBCleanup | Execute$ TrigDraw | TriggerDescription$ Whenever you reveal a creature card this way, draw a card.
SVar:TrigDraw:DB$Draw | NumCards$ 1
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
SVar:Picture:http://www.wizards.com/global/images/magic/general/primitive_etchings.jpg

View File

@@ -0,0 +1,11 @@
Name:Ayara, First of Locthwain
ManaCost:B B B
Types:Legendary Creature Elf Noble
PT:2/3
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigDrain | TriggerDescription$ Whenever CARDNAME or another black creature enters the battlefield under your control, each opponent loses 1 life and you gain 1 life.
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Creature.Black+Other+YouCtrl | TriggerZones$ Battlefield | Execute$ TrigDrain | Secondary$ True | TriggerDescription$ Whenever CARDNAME or another black creature enters the battlefield under your control, each opponent loses 1 life and you gain 1 life.
SVar:TrigDrain:DB$ LoseLife | Defined$ Player.Opponent | LifeAmount$ 1 | SubAbility$ DBGainLife
SVar:DBGainLife:DB$ GainLife | Defined$ You | LifeAmount$ 1
A:AB$ Draw | Cost$ T Sac<1/Creature.Other+Black/another black creature> | NumCards$ 1 | SpellDescription$ Draw a card.
AI:RemoveDeck:Random
Oracle:Whenever Ayara, First of Locthwain or another black creature enters the battlefield under your control, each opponent loses 1 life and you gain 1 life.\n{T}, Sacrifice another black creature: Draw a card.

View File

@@ -0,0 +1,7 @@
Name:Bramblefort Fink
ManaCost:1 G
Types:Creature Ouphe
PT:2/2
A:AB$ Animate | Cost$ 8 | Defined$ Self | Power$ 10 | Toughness$ 10 | IsPresent$ Planeswalker.YouCtrl+Oko | SpellDescription$ CARDNAME has base power and toughness 10/10 until end of turn. Activate this ability only if you control an Oko planeswalker.
AI:RemoveDeck:Random
Oracle:{8}: Bramblefort Fink has base power and toughness 10/10 until end of turn. Activate this ability only if you control an Oko planeswalker.

View File

@@ -0,0 +1,11 @@
Name:Brimstone Trebuchet
ManaCost:2 R
Types:Artifact Creature Wall
PT:1/3
K:Defender
K:Reach
A:AB$ DealDamage | Cost$ T | NumDmg$ 1 | Defined$ Player.Opponent | SpellDescription$ CARDNAME deals 1 damage to each opponent.
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Knight+YouCtrl | TriggerZones$ Battlefield | Execute$ TrigUntap | TriggerDescription$ Whenever a Knight enters the battlefield under your control, untap CARDNAME.
SVar:TrigUntap:DB$ Untap | Defined$ Self
SVar:BuffedBy:Knight
Oracle:Defender, reach\n{T}: Brimstone Trebuchet deals 1 damage to each opponent.\nWhenever a Knight enters the battlefield under your control, untap Brimstone Trebuchet.

View File

@@ -0,0 +1,10 @@
Name:Burning-Yard Trainer
ManaCost:4 R
Types:Creature Human Knight
PT:3/3
K:Haste
K:Trample
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigPump | TriggerDescription$ When CARDNAME enters the battlefield, another target Knight you control gets +2/+2 and gains trample and haste until end of turn.
SVar:TrigPump:DB$ Pump | ValidTgts$ Knight.Other+YouCtrl | TgtPrompt$ Select another target Knight you control | NumAtt$ 2 | NumDef$ 2 | KW$ Trample & Haste
SVar:PlayMain1:TRUE
Oracle:Trample, haste\nWhen Burning-Yard Trainer enters the battlefield, another target Knight you control gets +2/+2 and gains trample and haste until end of turn.

View File

@@ -0,0 +1,14 @@
Name:Charming Prince
ManaCost:1 W
Types:Creature Human Noble
PT:2/2
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigCharm | TriggerDescription$ When CARDNAME enters the battlefield, ABILITY
SVar:TrigCharm:DB$ Charm | Choices$ DBScry,DBGainLife,DBFlicker
SVar:DBScry:DB$ Scry | ScryNum$ 2 | SpellDescription$ Scry 2.
SVar:DBGainLife:DB$ GainLife | LifeAmount$ 3 | SpellDescription$ You gain 3 life.
SVar:DBFlicker:DB$ ChangeZone | ValidTgts$ Creature.YouOwn+Other | TgtPrompt$ Select another target creature you own | Origin$ Battlefield | Destination$ Exile | RememberChanged$ True | SubAbility$ DelTrig | SpellDescription$ Exile another target creature you own. Return it to the battlefield under your control at the beginning of the next end step.
SVar:DelTrig:DB$ DelayedTrigger | Mode$ Phase | Phase$ End of Turn | Execute$ TrigReturn | RememberObjects$ Remembered | TriggerDescription$ Return exiled card to the battlefield. | SubAbility$ DBCleanup
SVar:TrigReturn:DB$ ChangeZone | Origin$ Exile | Destination$ Battlefield | Defined$ DelayTriggerRemembered
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
DeckHas:Ability$LifeGain
Oracle:When Charming Prince enters the battlefield, choose one —\n• Scry 2.\n• You gain 3 life.\n• Exile another target creature you own. Return it to the battlefield under your control at the beginning of the next end step.

View File

@@ -0,0 +1,5 @@
Name:Claim the Firstborn
ManaCost:R
Types:Sorcery
A:SP$ GainControl | Cost$ R | ValidTgts$ Creature.cmcLE3 | TgtPrompt$ Select target creature with converted mana cost 3 or less | LoseControl$ EOT | Untap$ True | AddKWs$ Haste | SpellDescription$ Gain control of target creature with converted mana cost 3 or less until end of turn. Untap that creature. It gains haste until end of turn.
Oracle:Gain control of target creature with converted mana cost 3 or less until end of turn. Untap that creature. It gains haste until end of turn.

View File

@@ -0,0 +1,13 @@
Name:Doom Foretold
ManaCost:2 W B
Types:Enchantment
T:Mode$ Phase | Phase$ Upkeep | TriggerZones$ Battlefield | Execute$ TrigSac | TriggerDescription$ At the beginning of each player's upkeep, that player sacrifices a nonland, nontoken permanent. If that player can't, they discard a card, they lose 2 life, you draw a card, you gain 2 life, you create a 2/2 white Knight creature token with vigilance, then you sacrifice CARDNAME.
SVar:TrigSac:DB$ Sacrifice | SacValid$ Permanent.nonLand+nonToken | Defined$ TriggeredPlayer | RememberSacrificed$ True | SubAbility$ DBDiscard
SVar:DBDiscard:DB$ Discard | Defined$ TriggeredPlayer | NumCards$ 1 | Mode$ TgtChoose | ConditionDefined$ Remembered | ConditionPresent$ Card | ConditionCompare$ EQ0 | SubAbility$ DBLoseLife
SVar:DBLoseLife:DB$ LoseLife | LifeAmount$ 2 | Defined$ TriggeredPlayer | ConditionDefined$ Remembered | ConditionPresent$ Card | ConditionCompare$ EQ0 | SubAbility$ DBDraw
SVar:DBDraw:DB$ Draw | NumCards$ 1 | Defined$ You | ConditionDefined$ Remembered | ConditionPresent$ Card | ConditionCompare$ EQ0 | SubAbility$ DBGainLife
SVar:DBGainLife:DB$ GainLife | LifeAmount$ 2 | Defined$ You | ConditionDefined$ Remembered | ConditionPresent$ Card | ConditionCompare$ EQ0 | SubAbility$ DBToken
SVar:DBToken:DB$ Token | TokenAmount$ 1 | TokenScript$ w_2_2_knight_vigilance | TokenOwner$ You | LegacyImage$ w 2 2 knight vigilance eld | ConditionDefined$ Remembered | ConditionPresent$ Card | ConditionCompare$ EQ0 | SubAbility$ DBSac
SVar:DBSac:DB$ SacrificeAll | Defined$ Self | ConditionDefined$ Remembered | ConditionPresent$ Card | ConditionCompare$ EQ0 | SubAbility$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
Oracle:At the beginning of each player's upkeep, that player sacrifices a nonland, nontoken permanent. If that player can't, they discard a card, they lose 2 life, you draw a card, you gain 2 life, you create a 2/2 white Knight creature token with vigilance, then you sacrifice Doom Foretold.

View File

@@ -0,0 +1,14 @@
Name:Embercleave
ManaCost:4 R R
Types:Legendary Artifact Equipment
K:Flash
S:Mode$ ReduceCost | ValidCard$ Card.Self | Type$ Spell | Amount$ X | References$ X | EffectZone$ All | Description$ This spell costs {1} less to cast for each attacking creature you control.
SVar:X:Count$Valid Creature.attacking+YouCtrl
SVar:BuffedBy:Creature.attacking
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigAttach | TriggerDescription$ When CARDNAME enters the battlefield, attach it to target creature you control.
SVar:TrigAttach:DB$ Attach | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control.
SVar:NeedsToPlay:Creature.YouCtrl+inZoneBattlefield
AI:RemoveDeck:All
S:Mode$ Continuous | Affected$ Creature.EquippedBy | AddPower$ 1 | AddToughness$ 1 | AddKeyword$ Double Strike & Trample | Description$ Equipped creature gets +1/+1 and has double strike and trample.
K:Equip:3
Oracle:Flash\nThis spell costs {1} less to cast for each attacking creature you control.\nWhen Embercleave enters the battlefield, attach it to target creature you control.\nEquipped creature gets +1/+1 and has double strike and trample.\nEquip {3}

View File

@@ -0,0 +1,7 @@
Name:Enchanted Carriage
ManaCost:5
Types:Artifact Vehicle
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigToken | TriggerDescription$ When CARDNAME enters the battlefield, create two 1/1 white Goblin creature tokens.
SVar:TrigToken:DB$ Token | TokenAmount$ 2 | TokenScript$ w_1_1_mouse | TokenOwner$ You | LegacyImage$ w 1 1 mouse eld
K:Crew:2
Oracle:When Enchanted Carriage enters the battlefield, create two 1/1 white Mouse creature tokens.\nCrew 2 (Tap any number of creatures you control with total power 2 or more: This Vehicle becomes an artifact creature until end of turn.)

View File

@@ -0,0 +1,11 @@
Name:Feasting Troll King
ManaCost:2 G G G G
Types:Creature Troll Noble
PT:7/6
K:Vigilance
K:Trample
T:Mode$ ChangesZone | ValidCard$ Card.wasCastFromHand+Self | Destination$ Battlefield | Execute$ TrigToken | TriggerDescription$ When CARDNAME enters the battlefield, if you cast it from your hand, create three Food tokens.
SVar:TrigToken:DB$ Token | TokenAmount$ 3 | TokenScript$ c_a_food_sac | TokenOwner$ You | LegacyImage$ c a food sac eld
A:AB$ ChangeZone | Cost$ Sac<3/Food> | Origin$ Graveyard | Destination$ Battlefield | ActivationZone$ Graveyard | PlayerTurn$ True | SpellDescription$ Return CARDNAME from your graveyard to the battlefield. Activate this ability only during your turn.
SVar:DiscardMe:1
Oracle:Vigilance, trample\nWhen Feasting Troll King enters the battlefield, if you cast it from your hand, create three Food tokens.\nSacrifice three Foods: Return Feasting Troll King from your graveyard to the battlefield. Activate this ability only during your turn.

View File

@@ -0,0 +1,9 @@
Name:Garrison Griffin
ManaCost:2 W
Types:Creature Griffin
PT:2/2
K:Flying
T:Mode$ Attacks | ValidCard$ Card.Self | TriggerZones$ Battlefield | Execute$ TrigPump | TriggerDescription$ Whenever CARDNAME attacks, target Knight you control gains flying until end of turn.
SVar:TrigPump:DB$ Pump | ValidTgts$ Knight.YouCtrl | TgtPrompt$ Select target Knight you control | KW$ Flying
DeckNeeds:Type$Knight
Oracle:Flying\nWhenever Garrison Griffin attacks, target Knight you control gains flying until end of turn.

View File

@@ -0,0 +1,6 @@
Name:Joust
ManaCost:1 R
Types:Sorcery
A:SP$ Pump | Cost$ 1 R | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | NumAtt$ +2 | NumDef$ +1 | ConditionDefined$ ThisTargetedCard | ConditionPresent$ Knight | SubAbility$ DBFight | SpellDescription$ Choose target creature you control and target creature you don't control. The creature you control gets +2/+1 until end of turn if it's a Knight. Then those creatures fight each other. (Each deals damage equal to its power to the other.)
SVar:DBFight:DB$ Fight | Defined$ ParentTarget | ValidTgts$ Creature.YouDontCtrl | AILogic$ Always | TgtPrompt$ Choose target creature you don't control
Oracle:Choose target creature you control and target creature you don't control. The creature you control gets +2/+1 until end of turn if it's a Knight. Then those creatures fight each other. (Each deals damage equal to its power to the other.)

View File

@@ -0,0 +1,6 @@
Name:Jousting Dummy
ManaCost:2
Types:Artifact Creature Scarecrow Knight
PT:2/1
A:AB$ Pump | Cost$ 3 | NumAtt$ +1 | SpellDescription$ CARDNAME gets +1/+0 until end of turn.
Oracle:{3}: Jousting Dummy gets +1/+0 until end of turn.

View File

@@ -0,0 +1,10 @@
Name:Kenrith, the Returned King
ManaCost:4 W
Types:Legendary Creature Human Noble
PT:5/5
A:AB$ PumpAll | Cost$ R | ValidCards$ Creature | KW$ Trample & Haste | SpellDescription$ All creatures gain trample and haste until end of turn.
A:AB$ PutCounter | Cost$ 1 G | ValidTgts$ Creature | TgtPrompt$ Select target creature | CounterType$ P1P1 | CounterNum$ 1 | SpellDescription$ Put a +1/+1 counter on target creature.
A:AB$ GainLife | Cost$ 2 W | LifeAmount$ 5 | ValidTgts$ Player | TgtPrompt$ Choose a player | SpellDescription$ Target player gains 5 life.
A:AB$ Draw | Cost$ 3 U | NumCards$ 1 | ValidTgts$ Player | TgtPrompt$ Choose a player | SpellDescription$ Target player draws a card.
A:AB$ ChangeZone | Cost$ 4 B | Origin$ Graveyard | Destination$ Battlefield | ValidTgts$ Creature | ChangeNum$ 1 | SpellDescription$ Put target creature card from a graveyard onto the battlefield under its owner's control.
Oracle:{R}: All creatures gain trample and haste until end of turn.\n{1}{G}: Put a +1/+1 counter on target creature.\n{2}{W}: Target player gains 5 life.\n{3}{U}: Target player draws a card.\n{4}{B}: Put target creature card from a graveyard onto the battlefield under its owner's control.

View File

@@ -0,0 +1,12 @@
Name:Lochmere Serpent
ManaCost:4 U B
Types:Creature Serpent
PT:7/7
K:Flash
A:AB$ Pump | Cost$ U Sac<1/Island> | KW$ HIDDEN Unblockable | SpellDescription$ CARDNAME can't be blocked this turn.
A:AB$ GainLife | Cost$ B Sac<1/Swamp> | Defined$ You | LifeAmount$ 1 | SubAbility$ DBDraw | SpellDescription$ You gain 1 life and draw a card.
SVar:DBDraw:DB$ Draw | NumCards$ 1
AI:RemoveDeck:Random
A:AB$ ChangeZone | Cost$ U B | TargetMin$ 5 | TargetMax$ 5 | Origin$ Graveyard | Destination$ Exile | ValidTgts$ Card.OppOwn | SorcerySpeed$ True | SubAbility$ DBReturn | ActivationZone$ Graveyard | SpellDescription$ Exile five target cards from an opponent's graveyard. Return CARDNAME from your graveyard to your hand. Activate this ability only any time you could cast a sorcery.
SVar:DBReturn:DB$ ChangeZone | Origin$ Graveyard | Destination$ Hand | Defined$ Self
Oracle:Flash\n{U}, Sacrifice an Island: Lochmere Serpent can't be blocked this turn.\n{B}, Sacrifice a Swamp: You gain 1 life and draw a card.\n{U}{B}: Exile five target cards from an opponent's graveyard. Return Lochmere Serpent from your graveyard to your hand. Activate this ability only any time you could cast a sorcery.

View File

@@ -0,0 +1,9 @@
Name:Oko, the Trickster
ManaCost:4 G U
Types:Legendary Planeswalker Oko
Loyalty:4
A:AB$ PutCounter | Cost$ AddCounter<1/LOYALTY> | Planeswalker$ True | TargetMin$ 0 | TargetMax$ 1 | CounterType$ P1P1 | CounterNum$ 2 | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | SpellDescription$ Put two +1/+1 counters on up to one target creature you control.
A:AB$ Clone | Cost$ AddCounter<0/LOYALTY> | ValidTgts$ Creature.YouCtrl | Planeswalker$ True | TgtPrompt$ Select target creature you control | SubAbility$ DBPrevent | StackDescription$ SpellDescription | SpellDescription$ Until end of turn, CARDNAME becomes a copy of target creature you control. Prevent all damage that would be dealt to him this turn.
SVar:DBPrevent:DB$ Pump | Defined$ Self | KW$ Prevent all damage that would be dealt to CARDNAME.
A:AB$ AnimateAll | Cost$ SubCounter<7/LOYALTY> | Planeswalker$ True | Ultimate$ True | ValidCards$ Creature.YouCtrl | Power$ 10 | Toughness$ 10 | Keywords$ Trample | SpellDescription$ Until end of turn, each creature you control has base power and toughness 10/10 and gains trample.
Oracle:[+1]: Put two +1/+1 counters on up to one target creature you control.\n[0]: Until end of turn, Oko, the Trickster becomes a copy of target creature you control. Prevent all damage that would be dealt to him this turn.\n[7]: Until end of turn, each creature you control has base power and toughness 10/10 and gains trample.

View File

@@ -0,0 +1,6 @@
Name:Oko's Accomplices
ManaCost:2 U
Types:Creature Faerie
PT:2/3
K:Flying
Oracle:Flying

View File

@@ -0,0 +1,7 @@
Name:Oko's Hospitality
ManaCost:3 G U
Types:Instant
A:SP$ AnimateAll | Cost$ 3 G U | ValidCards$ Creature.YouCtrl | Power$ 3 | Toughness$ 3 | SubAbility$ DBSearch | SpellDescription$ Creatures you control have base power and toughness 3/3 until end of turn. You may search your library and/or graveyard for a card named Oko, the Trickster, reveal it, and put it into your hand. If you search your library this way, shuffle it.
SVar:DBSearch:DB$ ChangeZone | Origin$ Library,Graveyard | Destination$ Hand | ChangeType$ Card.namedOko; the Trickster | ChangeNum$ 1 | Optional$ True
DeckNeeds:Name$Oko, the Trickster
Oracle:Creatures you control have base power and toughness 3/3 until end of turn. You may search your library and/or graveyard for a card named Oko, the Trickster, reveal it, and put it into your hand. If you search your library this way, shuffle it.

View File

@@ -0,0 +1,9 @@
Name:Outlaws' Merriment
ManaCost:1 R W W
Types:Enchantment
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | Execute$ TrigCharm | TriggerZones$ Battlefield | TriggerDescription$ At the beginning of your upkeep, choose one at random. Create a red and white creature token with those characteristics.
SVar:TrigCharm:DB$ Charm | Random$ True | Choices$ DBToken1,DBToken2,DBToken3
SVar:DBToken1:DB$ Token | TokenAmount$ 1 | TokenScript$ rw_3_1_human_warrior_trample_haste | TokenOwner$ You | LegacyImage$ rw 3 1 human warrior trample haste eld | SpellDescription$ 3/1 Human Warrior with trample and haste.
SVar:DBToken2:DB$ Token | TokenAmount$ 1 | TokenScript$ rw_2_1_human_cleric_lifelink_haste | TokenOwner$ You | LegacyImage$ rw 2 1 human cleric lifelink haste eld | SpellDescription$ 2/1 Human Cleric with lifelink and haste.
SVar:DBToken3:DB$ Token | TokenAmount$ 1 | TokenScript$ rw_1_2_human_rogue_haste_damage | TokenOwner$ You | LegacyImage$ rw 1 2 human rogue haste damage eld | SpellDescription$ 1/2 Human Rogue with haste and "When this creature enters the battlefield, it deals 1 damage to any target."
Oracle:At the beginning of your upkeep, choose one at random. Create a red and white creature token with those characteristics.\n• 3/1 Human Warrior with trample and haste.\n• 2/1 Human Cleric with lifelink and haste.\n• 1/2 Human Rogue with haste and "When this creature enters the battlefield, it deals 1 damage to any target."

View File

@@ -0,0 +1,7 @@
Name:Roving Keep
ManaCost:7
Types:Artifact Creature Wall
PT:5/7
K:Defender
A:AB$ Pump | Cost$ 7 | NumAtt$ +2 | KW$ Trample & HIDDEN CARDNAME can attack as though it didn't have defender. | SpellDescription$ CARDNAME gets +2/+0 and gains trample until end of turn. It can attack this turn as though it didn't have defender.
Oracle:Defender\n{7}: Roving Keep gets +2/+0 and gains trample until end of turn. It can attack this turn as though it didn't have defender.

View File

@@ -0,0 +1,9 @@
Name:Rowan, Fearless Sparkmage
ManaCost:3 R R
Types:Legendary Planeswalker Rowan
Loyalty:5
A:AB$ Pump | Cost$ AddCounter<1/LOYALTY> | Planeswalker$ True | NumAtt$ +3 | KW$ First Strike | TargetMin$ 0 | TargetMax$ 1 | SpellDescription$ Up to one target creature gets +3/+0 and gains first strike until end of turn.
A:AB$ DealDamage | Cost$ SubCounter<2/LOYALTY> | Planeswalker$ True | ValidTgts$ Creature | TargetMin$ 0 | TargetMax$ 2 | TgtPrompt$ Select target creature | NumDmg$ 1 | SubAbility$ DBPump | SpellDescription$ CARDNAME deals 1 damage to each of up to two target creatures. Those creatures can't block this turn.
SVar:DBPump:DB$ Pump | Defined$ Targeted | KW$ HIDDEN CARDNAME can't block.
A:AB$ GainControl | Cost$ SubCounter<9/LOYALTY> | Planeswalker$ True | Ultimate$ True | AllValid$ Creature | LoseControl$ EOT | AddKWs$ Haste | Untap$ True | SpellDescription$ Gain control of all creatures until end of turn. Untap them. They gain haste until end of turn.
Oracle:[+1]: Up to one target creature gets +3/+0 and gains first strike until end of turn.\n[2]: Rowan, Fearless Sparkmage deals 1 damage to each of up to two target creatures. Those creatures can't block this turn.\n[9]: Gain control of all creatures until end of turn. Untap them. They gain haste until end of turn.

View File

@@ -0,0 +1,9 @@
Name:Rowan's Battleguard
ManaCost:3 R
Types:Creature Human Knight
PT:3/3
K:First Strike
S:Mode$ Continuous | Affected$ Card.Self | AddPower$ 3 | IsPresent$ Planeswalker.YouCtrl+Rowan | Description$ As long as you control a Rowan planeswalker, CARDNAME gets +3/+0.
SVar:BuffedBy:Planeswalker.Rowan
DeckNeeds:Type$Rowan
Oracle:First strike\nAs long as you control a Rowan planeswalker, Rowan's Battleguard gets +3/+0.

View File

@@ -0,0 +1,8 @@
Name:Rowan's Stalwarts
ManaCost:4 R
Types:Creature Human Knight
PT:5/2
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigSearch | OptionalDecider$ You | TriggerDescription$ When CARDNAME enters the battlefield, you may search your library and/or graveyard for a card named Rowan, Fearless Sparkmage, reveal it, and put it into your hand. If you search your library this way, shuffle it.
SVar:TrigSearch:DB$ ChangeZone | Origin$ Library,Graveyard | Destination$ Hand | ChangeType$ Card.namedRowan; Fearless Sparkmage | ChangeNum$ 1 | Optional$ True
DeckHints:Name$Rowan, Fearless Sparkmage
Oracle:When Rowan's Stalwarts enters the battlefield, you may search your library and/or graveyard for a card named Rowan, Fearless Sparkmage, reveal it, and put it into your hand. If you search your library this way, shuffle it.

View File

@@ -0,0 +1,5 @@
Name:Scorching Dragonfire
ManaCost:1 R
Types:Instant
A:SP$ DealDamage | Cost$ 1 R | ValidTgts$ Creature,Planeswalker | TgtPrompt$ Select target creature or planeswalker | NumDmg$ 3 | ReplaceDyingDefined$ Targeted | SpellDescription$ CARDNAME deals 3 damage to target creature or planeswalker. If that creature or planeswalker would die this turn, exile it instead.
Oracle:Scorching Dragonfire deals 3 damage to target creature or planeswalker. If that creature or planeswalker would die this turn, exile it instead.

View File

@@ -0,0 +1,7 @@
Name:Searing Barrage
ManaCost:4 R
Types:Instant
A:SP$ DealDamage | Cost$ 4 R | ValidTgts$ Creature | TgtPrompt$ Select target creature | NumDmg$ 5 | SpellDescription$ CARDNAME deals 5 damage to target creature. Adamant — If at least three red mana was spent to cast this spell, CARDNAME deals 3 damage to that creature's controller. | SubAbility$ DBDmg
SVar:DBDmg:DB$ DealDamage | Defined$ TargetedController | NumDmg$ 3 | ConditionCheckSVar$ X | References$ X
SVar:X:Count$Adamant.Red.1.0
Oracle:Searing Barrage deals 5 damage to target creature.\nAdamant — If at least three red mana was spent to cast this spell, Searing Barrage deals 3 damage to that creature's controller.

View File

@@ -0,0 +1,9 @@
Name:Skullknocker Ogre
ManaCost:3 R
Types:Creature Ogre
PT:4/3
T:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Opponent | TriggerZones$ Battlefield | Execute$ TrigDiscard | TriggerDescription$ Whenever CARDNAME deals damage to an opponent, that player discards a card at random. If the player does, they draw a card.
SVar:TrigDiscard:DB$ Discard | Defined$ TriggeredTarget | NumCards$ 1 | Mode$ Random | RememberDiscarded$ True | SubAbility$ DBDraw
SVar:DBDraw:DB$ Draw | NumCards$ 1 | Defined$ TriggeredTarget | ConditionDefined$ Remembered | ConditionPresent$ Card | ConditionCompare$ GE1 | SubAbility$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
Oracle:Whenever Skullknocker Ogre deals damage to an opponent, that player discards a card at random. If the player does, they draw a card.

View File

@@ -0,0 +1,12 @@
Name:Syr Carah, the Bold
ManaCost:3 R R
Types:Legendary Creature Human Knight
PT:3/3
T:Mode$ DamageDone | ValidSource$ Spell.Instant+YouCtrl,Spell.Sorcery+YouCtrl,Card.Self | ValidTarget$ Player | Execute$ TrigExile | TriggerZones$ Battlefield | TriggerDescription$ When CARDNAME or an instant or sorcery spell you control deals damage to a player, exile the top card of your library. You may play that card this turn.
SVar:TrigExile:DB$ Mill | Defined$ You | NumCards$ 1 | Destination$ Exile | RememberMilled$ True | SubAbility$ DBEffect
SVar:DBEffect:DB$ Effect | Duration$ EndOfTurn | RememberObjects$ Remembered | StaticAbilities$ STPlay | SubAbility$ DBCleanup | ExileOnMoved$ Exile
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
SVar:STPlay:Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Exile | Description$ You may play the exiled card.
A:AB$ DealDamage | Cost$ T | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | NumDmg$ 1 | SpellDescription$ CARDNAME deals 1 damage to any target.
SVar:NonCombatPriority:1
Oracle:When Syr Carah, the Bold or an instant or sorcery spell you control deals damage to a player, exile the top card of your library. You may play that card this turn.\n{T}: Syr Carah deals 1 damage to any target.

View File

@@ -0,0 +1,11 @@
Name:Syr Elenora the Discerning
ManaCost:3 U U
Types:Legendary Creature Human Knight
PT:*/4
S:Mode$ Continuous | EffectZone$ All | CharacteristicDefining$ True | SetPower$ X | Description$ CARDNAME's power is equal to the number of cards in your hand.
SVar:X:Count$InYourHand
AI:RemoveDeck:All
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigDraw | TriggerDescription$ When CARDNAME enters the battlefield, draw a card.
SVar:TrigDraw:DB$ Draw | Defined$ You | NumCards$ 1
S:Mode$ RaiseCost | ValidTarget$ Card.Self | Activator$ Player.Opponent | Type$ Spell | Amount$ 2 | Description$ Spells your opponents cast that target CARDNAME cost {2} more to cast.
Oracle:Syr Elenora the Discerning's power is equal to the number of cards in your hand.\nWhen Syr Elenora enters the battlefield, draw a card.\nSpells your opponents cast that target Syr Elenora cost {2} more to cast.

View File

@@ -0,0 +1,13 @@
Name:The Royal Scions
ManaCost:1 U R
Types:Legendary Planeswalker Will Rowan
Loyalty:5
A:AB$ Draw | Cost$ AddCounter<1/LOYALTY> | Planeswalker$ True | NumCards$ 1 | SpellDescription$ Draw a card, then discard a card. | SubAbility$ DBDiscard
SVar:DBDiscard:DB$ Discard | Defined$ You | NumCards$ 1 | Mode$ TgtChoose
AI:RemoveDeck:All
A:AB$ Pump | Cost$ AddCounter<1/LOYALTY> | Planeswalker$ True | ValidTgts$ Creature | TgtPrompt$ Select target creature | NumAtt$ +2 | KW$ First Strike | SpellDescription$ Target creature gets +2/+0 and gains first strike until end of turn.
A:AB$ Draw | Cost$ SubCounter<8/LOYALTY> | Planeswalker$ True | Ultimate$ True | NumCards$ 4 | SubAbility$ DBTrigger | SpellDescription$ Draw four cards. When you do, CARDNAME deals damage to any target equal to the number of cards in your hand.
SVar:DBTrigger:DB$ ImmediateTrigger | Execute$ DBDamage | TriggerDescription$ When you do, CARDNAME deals damage to any target equal to the number of cards in your hand.
SVar:DBDamage:DB$ DealDamage | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | NumDmg$ X | References$ X
SVar:X:Count$InYourHand
Oracle:[+1]: Draw a card, then discard a card.\n[+1]: Target creature gets +2/+0 and gains first strike until end of turn.\n[8]: Draw four cards. When you do, The Royal Scions deals damage to any target equal to the number of cards in your hand.

View File

@@ -0,0 +1,8 @@
Name:Turn into a Pumpkin
ManaCost:3 U
Types:Instant
A:SP$ ChangeZone | Cost$ 3 U | ValidTgts$ Permanent.nonLand | TgtPrompt$ Select target nonland permanent | Origin$ Battlefield | Destination$ Hand | SubAbility$ DBDraw | SpellDescription$ Return target nonland permanent to its owner's hand. Draw a card. Adamant — If at least three blue mana was spent to cast this spell, create a Food token. (It's an artifact with "{2}, {T}, Sacrifice this artifact: You gain 3 life.")
SVar:DBDraw:DB$ Draw | NumCards$ 1 | SubAbility$ DBToken
SVar:DBToken:DB$ Token | TokenAmount$ X | TokenScript$ c_a_food_sac | TokenOwner$ You | LegacyImage$ c a food sac eld | References$ X
SVar:X:Count$Adamant.Blue.1.0
Oracle:Return target nonland permanent to its owner's hand. Draw a card.\nAdamant — If at least three blue mana was spent to cast this spell, create a Food token. (It's an artifact with "{2}, {T}, Sacrifice this artifact: You gain 3 life.")

View File

@@ -0,0 +1,6 @@
Name:Weaselback Redcap
ManaCost:R
Types:Creature Goblin Knight
PT:1/1
A:AB$ Pump | Cost$ 1 R | NumAtt$ +2 | SpellDescription$ CARDNAME gets +2/+0 until end of turn.
Oracle:{1}{R}: Weaselback Redcap gets +2/+0 until end of turn.

View File

@@ -0,0 +1,8 @@
Name:Wicked Guardian
ManaCost:3 B
Types:Creature Human Noble
PT:4/2
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigDamage | OptionalDecider$ You | TriggerDescription$ When CARDNAME enters the battlefield, you may have it deal 2 damage to another creature you control. If you do, draw a card.
SVar:TrigDamage:DB$ DealDamage | ValidTgts$ Creature.Other+YouCtrl | TgtPrompt$ Select another creature you control | NumDmg$ 2 | SubAbility$ DBDraw
SVar:DBDraw:DB$ Draw | NumCards$ 1
Oracle:When Wicked Guardian enters the battlefield, you may have it deal 2 damage to another creature you control. If you do, draw a card.

View File

@@ -0,0 +1,10 @@
Name:Wicked Wolf
ManaCost:2 G G
Types:Creature Wolf
PT:3/3
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigFight | TriggerDescription$ When CARDNAME enters the battlefield, it fights up to one target creature you don't control.
SVar:TrigFight:DB$ Fight | Defined$ TriggeredCardLKICopy | ValidTgts$ Creature.YouDontCtrl | TgtPrompt$ Choose target creature you don't control | TargetMin$ 0 | TargetMax$ 1
A:AB$ PutCounter | Cost$ Sac<1/Food> | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1 | SubAbility$ DBPump | SpellDescription$ Put a +1/+1 counter on CARDNAME. It gains indestructible until end of turn. Tap it.
SVar:DBPump:DB$ Pump | KW$ Indestructible | SubAbility$ DBTap
SVar:DBTap:DB$ Tap | Defined$ Self
Oracle:When Wicked Wolf enters the battlefield, it fights up to one target creature you don't control.\nSacrifice a Food: Put a +1/+1 counter on Wicked Wolf. It gains indestructible until end of turn. Tap it.

View File

@@ -0,0 +1,9 @@
Name:Wildwood Tracker
ManaCost:G
Types:Creature Elf Warrior
PT:1/1
T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigPump | TriggerZones$ Battlefield | IsPresent$ Creature.nonHuman+Other+YouCtrl | TriggerDescription$ Whenever CARDNAME attacks or blocks, if you control another non-Human creature, CARDNAME gets +1/+1 until end of turn.
T:Mode$ Blocks | ValidCard$ Card.Self | Execute$ TrigPump | TriggerZones$ Battlefield | IsPresent$ Creature.nonHuman+Other+YouCtrl | Secondary$ True | TriggerDescription$ Whenever CARDNAME attacks or blocks, if you control another non-Human creature, CARDNAME gets +1/+1 until end of turn.
SVar:TrigPump:DB$ Pump | NumAtt$ 1 | NumDef$ 1 | Defined$ Self
SVar:BuffedBy:Creature.nonHuman
Oracle:Whenever Wildwood Tracker attacks or blocks, if you control another non-Human creature, Wildwood Tracker gets +1/+1 until end of turn.

View File

@@ -0,0 +1,6 @@
Name:Witch's Vengeance
ManaCost:1 B B
Types:Sorcery
A:SP$ ChooseType | Cost$ 1 B B | Defined$ You | Type$ Creature | AILogic$ MostProminentHumanControls | SubAbility$ DBPumpAll | SpellDescription$ Creatures of the creature type of your choice get -3/-3 until end of turn.
SVar:DBPumpAll:DB$ PumpAll | ValidCards$ Creature.ChosenType | NumAtt$ -3 | NumDef$ -3 | IsCurse$ True
Oracle:Creatures of the creature type of your choice get -3/-3 until end of turn.

View File

@@ -0,0 +1,9 @@
Name:Worthy Knight
ManaCost:1 W
Types:Creature Human Knight
PT:2/2
T:Mode$ SpellCast | ValidCard$ Card.Knight | ValidActivatingPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigToken | TriggerDescription$ Whenever you cast a Knight spell, create a 1/1 white Human creature token.
SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenOwner$ You | TokenScript$ w_1_1_human | LegacyImage$ w 1 1 human eld
SVar:BuffedBy:Knight
DeckHints:Type$Knight
Oracle:Whenever you cast a Knight spell, create a 1/1 white Human creature token.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -888,3 +888,52 @@ lblNewGame=Neues Spiel
lblLoadGame=Spiel laden
lblPlayOnline=Online spielen
lblSettings=Spieleinstellungen
#SettingsPage.java
lblAutomaticBugReports=Automatic Bug Reports
lblBattlefieldTextureFiltering=Battlefield Texture Filtering
lblCompactListItems=Compact List Items
lblCompactTabs=Compact Tabs
lblCardOverlays=Card Overlays
lblDisableCardEffect=Disable Card 'Effect' Images
lblDynamicBackgroundPlanechase=Dynamic Background Planechase
lblGameplayOptions=Gameplay Options
lblGeneralSettings=General Settings
lblHotSeatMode=Hot Seat Mode
lblLandscapeMode=Landscape Mode
lblLater=Later
lblMinimizeScreenLock=Minimize on Screen Lock
lblOrderGraveyard=Order Graveyard
lblRestartForge=Restart Forge
lblRestartForgeDescription=You must restart Forge for this change to take effect.
lblRotateZoomPlanesPhenomena=Rotate Zoom Image of Planes/Phenomena
lblRotateZoomSplit=Rotate Zoom Image of Split Cards
lblShowCardIDOverlays=Show Card ID Overlays
lblShowCardManaCostOverlays=Show Card Mana Cost Overlays
lblShowCardNameOverlays=Show Card Name Overlays
lblShowCardOverlays=Show Card Overlays
lblShowCardPTOverlays=Show Card P/T Overlays
lblShowMatchBackground=Show Match Background
lblVibrateAfterLongPress=Vibrate After Long Press
lblVibrateWhenLosingLife=Vibrate When Losing Life
lblVibrationOptions=Vibration Options
nlAutomaticBugReports=Automatically send bug reports to the developers, without prompting.
nlBattlefieldTextureFiltering=Filter card art on battlefield to make it less pixelated on large screens (restart required, may reduce performance).
nlCompactListItems=Show only a single line of text for cards and decks on all list views by default.
nlCompactTabs=Show smaller tabs on the top of tab page screens (such as this screen).
nlDisableCardEffect=Disable the zoomed image for the 'Effect' cards.
nlDynamicBackgroundPlanechase=Use current plane images as background (Planes Card images must be on the cache/pics/planechase folder).
nlHotSeatMode=When starting a game with 2 human players, use single prompt to control both players.
nlLandscapeMode=Use landscape (horizontal) orientation for app instead of portrait (vertical).
nlMinimizeScreenLock=Minimize Forge when screen is locked (enable if you experience graphic glitches after locking your screen).
nlOrderGraveyard=Determines when to allow to order cards going to graveyard (never/always/only with relevant cards).
nlRotateZoomPlanesPhenomena=Rotates the zoomed image of Plane or Phenomenon cards.
nlRotateZoomSplit=Rotates the zoomed image of split cards.
nlShowCardIDOverlays=Show id overlays for cards, otherwise they're hidden.
nlShowCardManaCostOverlays=Show mana cost overlays for cards, otherwise they're hidden.
nlShowCardNameOverlays=Show name overlays for cards, otherwise they're hidden.
nlShowCardOverlays=Show name, mana cost, p/t, and id overlays for cards, otherwise they're hidden.
nlShowCardPTOverlays=Show power/toughness/loyalty overlays for cards, otherwise they're hidden.
nlShowMatchBackground=Show match background image on battlefield, otherwise background texture shown instead.
nlTheme=Sets the theme that determines how display components are skinned.
nlVibrateAfterLongPress=Enable quick vibration to signify a long press, such as for card zooming.
nlVibrateWhenLosingLife=Enable vibration when your player loses life or takes damage during a game.

View File

@@ -888,3 +888,52 @@ lblNewGame=New Game
lblLoadGame=Load Game
lblPlayOnline=Play Online
lblSettings=Settings
#SettingsPage.java
lblAutomaticBugReports=Automatic Bug Reports
lblBattlefieldTextureFiltering=Battlefield Texture Filtering
lblCompactListItems=Compact List Items
lblCompactTabs=Compact Tabs
lblCardOverlays=Card Overlays
lblDisableCardEffect=Disable Card 'Effect' Images
lblDynamicBackgroundPlanechase=Dynamic Background Planechase
lblGameplayOptions=Gameplay Options
lblGeneralSettings=General Settings
lblHotSeatMode=Hot Seat Mode
lblLandscapeMode=Landscape Mode
lblLater=Later
lblMinimizeScreenLock=Minimize on Screen Lock
lblOrderGraveyard=Order Graveyard
lblRestartForge=Restart Forge
lblRestartForgeDescription=You must restart Forge for this change to take effect.
lblRotateZoomPlanesPhenomena=Rotate Zoom Image of Planes/Phenomena
lblRotateZoomSplit=Rotate Zoom Image of Split Cards
lblShowCardIDOverlays=Show Card ID Overlays
lblShowCardManaCostOverlays=Show Card Mana Cost Overlays
lblShowCardNameOverlays=Show Card Name Overlays
lblShowCardOverlays=Show Card Overlays
lblShowCardPTOverlays=Show Card P/T Overlays
lblShowMatchBackground=Show Match Background
lblVibrateAfterLongPress=Vibrate After Long Press
lblVibrateWhenLosingLife=Vibrate When Losing Life
lblVibrationOptions=Vibration Options
nlAutomaticBugReports=Automatically send bug reports to the developers, without prompting.
nlBattlefieldTextureFiltering=Filter card art on battlefield to make it less pixelated on large screens (restart required, may reduce performance).
nlCompactListItems=Show only a single line of text for cards and decks on all list views by default.
nlCompactTabs=Show smaller tabs on the top of tab page screens (such as this screen).
nlDisableCardEffect=Disable the zoomed image for the 'Effect' cards.
nlDynamicBackgroundPlanechase=Use current plane images as background (Planes Card images must be on the cache/pics/planechase folder).
nlHotSeatMode=When starting a game with 2 human players, use single prompt to control both players.
nlLandscapeMode=Use landscape (horizontal) orientation for app instead of portrait (vertical).
nlMinimizeScreenLock=Minimize Forge when screen is locked (enable if you experience graphic glitches after locking your screen).
nlOrderGraveyard=Determines when to allow to order cards going to graveyard (never/always/only with relevant cards).
nlRotateZoomPlanesPhenomena=Rotates the zoomed image of Plane or Phenomenon cards.
nlRotateZoomSplit=Rotates the zoomed image of split cards.
nlShowCardIDOverlays=Show id overlays for cards, otherwise they're hidden.
nlShowCardManaCostOverlays=Show mana cost overlays for cards, otherwise they're hidden.
nlShowCardNameOverlays=Show name overlays for cards, otherwise they're hidden.
nlShowCardOverlays=Show name, mana cost, p/t, and id overlays for cards, otherwise they're hidden.
nlShowCardPTOverlays=Show power/toughness/loyalty overlays for cards, otherwise they're hidden.
nlShowMatchBackground=Show match background image on battlefield, otherwise background texture shown instead.
nlTheme=Sets the theme that determines how display components are skinned.
nlVibrateAfterLongPress=Enable quick vibration to signify a long press, such as for card zooming.
nlVibrateWhenLosingLife=Enable vibration when your player loses life or takes damage during a game.

View File

@@ -153,7 +153,7 @@ GraphicOptions=Opciones gráficas
nlDefaultFontSize=El tamaño de fuente predeterminado dentro de la interfaz de usuario. Todos los elementos de fuente se escalan en relación a esto. (Necesita reinicio)
cbpMulliganRule=Regla de Mulligan
nlImageFetcher=Permite la descarga instantánea de imágenes de cartas faltantes.
nlDisplayFoil=Mostrar cartas foil con un capa que da efecto foil sobre la carta
nlDisplayFoil=Mostrar cartas foil con un capa que da efecto foil sobre la carta
nlRandomFoil=Agrega efecto de foil a cartas aleatorias.
nlScaleLarger=Permite que las imágenes de las cartas se amplíen más que su tamaño original.
nlRenderBlackCardBorders=Hacer bordes negros alrededor de las imágenes de las cartas.
@@ -888,3 +888,52 @@ lblNewGame=Nueva partida
lblLoadGame=Cargar partida
lblPlayOnline=Jugar en linea
lblSettings=Configuración
#SettingsPage.java
lblAutomaticBugReports=Informes automáticos de errores
lblBattlefieldTextureFiltering=Filtrado de la Textura del Campo de batalla
lblCompactListItems=Items de la lista compactos
lblCompactTabs=Pestañas compactas
lblCardOverlays=Superposiciones de las cartas
lblDisableCardEffect=Desactivar las imágenes de 'Efecto' de las cartas
lblDynamicBackgroundPlanechase=Fondo Dinámico Planechase
lblGameplayOptions=Opciones de Juego
lblGeneralSettings=Configuración general
lblHotSeatMode=Modo de Silla Caliente
lblLandscapeMode=Modo apaisado
lblLater=Más tarde
lblMinimizeScreenLock=Minimizar al bloquear la pantalla
lblOrderGraveyard=Orden del Cementerio
lblRestartForge=Reiniciar Forge
lblRestartForgeDescription=Debes reiniciar Forge para que este cambio surta efecto.
lblRotateZoomPlanesPhenomena=Girar la imagen ampliada de Planos/Fenómenos
lblRotateZoomSplit=Girar la imagen ampliada de las tarjetas divididas
lblShowCardIDOverlays=Mostrar superposiciones de identificador de carta
lblShowCardManaCostOverlays=Mostrar superposiciones de costes de maná de cartas
lblShowCardNameOverlays=Mostrar superposiciones de nombres de cartas
lblShowCardOverlays=Mostrar superposiciones de cartas
lblShowCardPTOverlays=Mostrar la superposición en la carta de P/T
lblShowMatchBackground=Mostrar el fondo de la partida
lblVibrateAfterLongPress=Vibrar después de una pulsación larga
lblVibrateWhenLosingLife=Vibrar al perder vida
lblVibrationOptions=Opciones de Vibración
nlAutomaticBugReports=Envía automáticamente informes de errores a los desarrolladores, sin preguntar.
nlBattlefieldTextureFiltering=Filtra el arte de la carta en el campo de batalla para que sea menos pixelada en pantallas grandes (se requiere reiniciar, puede reducir el rendimiento).
nlCompactListItems=Muestra de forma predeterminada solo una línea de texto para las cartas y mazos en todas las vistas de lista.
nlCompactTabs=Muestra pestañas más pequeñas en la parte superior de las pantallas de la página de pestañas (como esta pantalla).
nlDisableCardEffect=Deshabilita la imagen ampliada para las tarjetas 'Efecto'.
nlDynamicBackgroundPlanechase=Utiliza las imágenes de Planos actuales como fondo (las imágenes de los Planos deben estar en la carpeta cache/pics/planechase).
nlHotSeatMode=Cuando comience un juego con 2 jugadores humanos, utiliza una petición única para controlar a ambos jugadores.
nlLandscapeMode=Utiliza la orientación horizontal para la aplicación en lugar de la orientación vertical.
nlMinimizeScreenLock=Minimiza Forge cuando la pantalla está bloqueada (habilítalo si experimentas fallos gráficos después de bloquear la pantalla).
nlOrderGraveyard=Determina cuándo permitir ordenar las cartas que van al cementerio (nunca/siempre/solo con cartas relevantes).
nlRotateZoomPlanesPhenomena=Gira la imagen ampliada de las cartas Plano o Fenómeno.
nlRotateZoomSplit=Gira la imagen ampliada de las tarjetas divididas.
nlShowCardIDOverlays=Muestra superposiciones del identificador de la carta, de lo contrario, ocúltalas.
nlShowCardManaCostOverlays=Muestra superposiciones de coste de maná para las cartas, de lo contrario, ocúltalas.
nlShowCardNameOverlays=Muestra superposiciones de nombres para las cartas, de lo contrario, ocúltalas.
nlShowCardOverlays=Muestra superposiciones de nombre, coste de maná, P/T, e identificador de las cartas, de lo contrario, ocúltalas.
nlShowCardPTOverlays=Muestra superposiciones de fuerza/resistencia/lealtad para las cartas, de lo contrario, ocúltalas.
nlShowMatchBackground=Muestra la imagen de fondo de la partida en el campo de batalla; de lo contrario, se muestra la textura de fondo.
nlTheme=Establece el tema que determina el aspecto global del juego.
nlVibrateAfterLongPress=Habilita la vibración rápida cuando se realice una pulsación prolongada, como p.ej. al realizar zoom de la carta.
nlVibrateWhenLosingLife=Habilita la vibración cuando tu jugador pierde vida o sufre daños durante un juego.

View File

@@ -1,4 +1,4 @@
language.name=Chinese (ZH)
language.name=Chinese (CN)
#SplashScreen.java
splash.loading.examining-cards=加载牌张,检查文件夹
splash.loading.cards-folders=从文件夹加载牌张
@@ -523,7 +523,7 @@ lblQuestDesc2=随着冒险之旅的进行,你的牌池逐渐增长,可以构
lblQuestDesc3=并且在与AI的决斗和挑战对抗中可以解锁更多牌。
lblBuildaNewDeck=构建一个新套牌
#Decktype.java
lblCustomUserDecks=自定义用户套牌
lblCustomUserDecks=用户自定义套牌
lblConstructedDecks=构筑套牌
lblCommanderDecks=指挥官套牌
lblRandomCommanderDecks=随机指挥官套牌
@@ -888,3 +888,52 @@ lblNewGame=新游戏
lblLoadGame=加载游戏
lblPlayOnline=在线游戏
lblSettings=设置
#SettingsPage.java
lblAutomaticBugReports=自动报告BUG
lblBattlefieldTextureFiltering=战场纹理过滤
lblCompactListItems=紧凑的项目列表
lblCompactTabs=紧凑标签
lblCardOverlays=牌张叠加层
lblDisableCardEffect=禁用卡牌“效果”图
lblDynamicBackgroundPlanechase=动态时空背景
lblGameplayOptions=游戏选项
lblGeneralSettings=常规设置
lblHotSeatMode=热座模式
lblLandscapeMode=横屏模式
lblLater=以后
lblMinimizeScreenLock=锁屏时最小化
lblOrderGraveyard=坟场顺序
lblRestartForge=重启Forge
lblRestartForgeDescription=您必须重启Forge才能使此更改生效
lblRotateZoomPlanesPhenomena=旋转缩放时空/异象图
lblRotateZoomSplit=旋转缩放连体牌
lblShowCardIDOverlays=显示牌张ID叠加层
lblShowCardManaCostOverlays=显示牌张法术力费用叠加层
lblShowCardNameOverlays=显示牌张名称叠加层
lblShowCardOverlays=显示牌张叠加层
lblShowCardPTOverlays=显示攻击/防御叠加层
lblShowMatchBackground=显示比赛背景
lblVibrateAfterLongPress=长按后震动
lblVibrateWhenLosingLife=失去生命时震动
lblVibrationOptions=振动选项
nlAutomaticBugReports=在没有提示的情况下自动向开发人员报告错误
nlBattlefieldTextureFiltering=在战场上过滤闪卡特效,使其在大屏幕上不像素化(需要重启,可能会降低性能)。
nlCompactListItems=默认情况下,在所有视图列表中只显示卡牌和套牌的单行文本。
nlCompactTabs=在标签页屏幕顶部显示较小的标签(例如此屏幕)。
nlDisableCardEffect=禁用“效果”卡的缩放图片。
nlDynamicBackgroundPlanechase=使用当前时空图片作为背景时空图片必须位于cache/pics/planechase文件夹中
nlHotSeatMode=当用两个人类玩家开始游戏的时候,用单个提示控制两个玩家。
nlLandscapeMode=使用横向(水平)而不是纵向(垂直)。
nlMinimizeScreenLock=锁定屏幕时最小化Forge锁屏以后出现图形故障使用
nlOrderGraveyard=确定何时让玩家确定同时进入坟场的牌的顺序(绝不/总是/只对有关卡牌)。
nlRotateZoomPlanesPhenomena=旋转缩放时空或异象图片。
nlRotateZoomSplit=旋转缩放连体牌图片。
nlShowCardIDOverlays=显示牌张的ID叠加层否则他们被隐藏。
nlShowCardManaCostOverlays=显示牌张的法术力费用叠加层,否则他们被隐藏。
nlShowCardNameOverlays=显示牌张的名称费用叠加层,否则他们被隐藏。
nlShowCardOverlays=显示牌张名称,法术力费用,力量/防御和ID叠加层否则他们被隐藏。
nlShowCardPTOverlays=显示力量/防御/忠诚叠加层,否则他们被隐藏。
nlShowMatchBackground=在战场显示背景图片,否则显示背景纹理。
nlTheme=设置显示的组件使用的外观主题。
nlVibrateAfterLongPress=启用长按触发震动,例如长按缩放卡牌图片。
nlVibrateWhenLosingLife=启用当玩家在游戏中失去生命或收到伤害时震动。

View File

@@ -147,6 +147,7 @@ Mongoose:Mongooses
Monk:Monks
Monkey:Monkeys
Moonfolk:Moonfolks
Mouse:Mice
Mutant:Mutants
Myr:Myrs
Mystic:Mystics
@@ -170,6 +171,7 @@ Ouphe:Ouphes
Ox:Oxen
Oyster:Oysters
Pangolin:Pangolins
Peasant:Peasants
Pegasus:Pegasi
Pentavite:Pentavites
Pest:Pests

View File

@@ -0,0 +1,9 @@
Name:Human Rogue
Types:Creature Human Rogue
Colors:red,white
ManaCost:no cost
PT:1/2
K:Haste
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigDealDamage | TriggerDescription$ When this creature enters the battlefield, it deals 1 damage to any target.
SVar:TrigDealDamage:DB$ DealDamage | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | NumDmg$ 1
Oracle:Haste\nWhen this creature enters the battlefield, it deals 1 damage to any target.

View File

@@ -0,0 +1,8 @@
Name:Human Cleric
Types:Creature Human Cleric
Colors:red,white
ManaCost:no cost
PT:2/1
K:Lifelink
K:Haste
Oracle:Lifelink, haste

View File

@@ -0,0 +1,8 @@
Name:Human Warrior
Types:Creature Human Warrior
Colors:red,white
ManaCost:no cost
PT:3/1
K:Trample
K:Haste
Oracle:Trample, haste

View File

@@ -0,0 +1,6 @@
Name:Mouse
ManaCost:no cost
Types:Creature Mouse
Colors:white
PT:1/1
Oracle:

View File

@@ -160,11 +160,13 @@ public class CardDetailUtil {
public static String formatCardName(final CardView card, final boolean canShow, final boolean forAltState) {
final String name = forAltState ? card.getAlternateState().getName() : card.getName();
return StringUtils.isEmpty(name) || !canShow ? "???" : name.trim();
String translatedname = CardTranslation.getTranslatedName(name);
return StringUtils.isEmpty(translatedname) || !canShow ? "???" : translatedname.trim();
}
public static String formatCardType(final CardStateView card, final boolean canShow) {
return canShow ? card.getType().toString() : (card.getState() == CardStateName.FaceDown ? "Creature" : "---");
String translatedtype = CardTranslation.getTranslatedType(card.getName(), card.getType().toString());
return canShow ? translatedtype : (card.getState() == CardStateName.FaceDown ? "Creature" : "---");
}
public static String formatPowerToughness(final CardStateView card, final boolean canShow) {
@@ -276,7 +278,9 @@ public class CardDetailUtil {
if (area.length() != 0) {
area.append("\n");
}
String text = card.getText(state);
String text = card.getText(state, CardTranslation.getTranslationTexts(state.getName(), ""));
// LEVEL [0-9]+-[0-9]+
// LEVEL [0-9]+\+

View File

@@ -0,0 +1,91 @@
package forge.card;
import com.esotericsoftware.minlog.Log;
import com.google.common.base.Charsets;
import forge.properties.ForgeConstants;
import forge.util.LineReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class CardTranslation {
private static Map <String, String> translatednames;
private static Map <String, String> translatedtypes;
private static Map <String, String> translatedoracles;
private static String languageSelected;
private static void readTranslationFile(String language) {
String filename = "cardnames-" + language + ".txt";
try (LineReader translationFile = new LineReader(new FileInputStream(ForgeConstants.LANG_DIR + filename), Charsets.UTF_8)) {
for (String line : translationFile.readLines()) {
String[] matches = line.split("\\|");
if (matches.length >= 2) {
translatednames.put(matches[0], matches[1]);
}
if (matches.length >= 3) {
translatedtypes.put(matches[0], matches[2]);
}
if (matches.length >= 4) {
translatedoracles.put(matches[0], matches[3].replace("\\n", "\n\n"));
}
}
} catch (IOException e) {
Log.error("Error reading translation file: cardnames-" + language + ".txt");
}
}
public static String getTranslatedName(String name) {
if (needsTranslation()) {
String tname = translatednames.get(name);
return tname == null ? name : tname;
}
return name;
}
public static String getTranslatedType(String name, String originaltype) {
if (needsTranslation()) {
String ttype = translatedtypes.get(name);
return ttype == null ? originaltype : ttype;
}
return originaltype;
}
public static String getTranslatedOracle(String name) {
if (needsTranslation()) {
String toracle = translatedoracles.get(name);
return toracle == null ? "" : toracle;
}
return "";
}
public static HashMap<String, String> getTranslationTexts(String cardname, String altcardname) {
HashMap<String, String> translations = new HashMap<String, String>();
translations.put("name", getTranslatedName(cardname));
translations.put("oracle", getTranslatedOracle(cardname));
translations.put("altname", getTranslatedName(altcardname));
translations.put("altoracle", getTranslatedOracle(altcardname));
return translations;
}
private static boolean needsTranslation() {
return !languageSelected.equals("en-US");
}
public static void preloadTranslation(String language) {
languageSelected = language;
if (needsTranslation()) {
translatednames = new HashMap<>();
translatedtypes = new HashMap<>();
translatedoracles = new HashMap<>();
readTranslationFile(languageSelected);
}
}
}

View File

@@ -24,6 +24,7 @@ import forge.CardStorageReader.ProgressObserver;
import forge.achievement.*;
import forge.ai.AiProfileUtil;
import forge.card.CardPreferences;
import forge.card.CardTranslation;
import forge.card.CardType;
import forge.deck.CardArchetypeLDAGenerator;
import forge.deck.CardRelationMatrixGenerator;
@@ -146,6 +147,7 @@ public final class FModel {
final CardStorageReader tokenReader = new CardStorageReader(ForgeConstants.TOKEN_DATA_DIR, progressBarBridge,
FModel.getPreferences().getPrefBoolean(FPref.LOAD_CARD_SCRIPTS_LAZILY));
magicDb = new StaticData(reader, tokenReader, ForgeConstants.EDITIONS_DIR, ForgeConstants.BLOCK_DATA_DIR);
CardTranslation.preloadTranslation(preferences.getPref(FPref.UI_LANGUAGE));
//create profile dirs if they don't already exist
for (final String dname : ForgeConstants.PROFILE_DIRS) {

View File

@@ -2433,9 +2433,9 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont
TextUtil.concatWithSpace("Should", forgeCard.toString(), "be added to the top or to the bottom of the library?"), true, Arrays.asList("Top", "Bottom"));
}
if (lastTopOfTheLibrary) {
game.getAction().moveToLibrary(forgeCard, null, null);
game.getAction().moveToLibrary(forgeCard, null);
} else {
game.getAction().moveToBottomOfLibrary(forgeCard, null, null);
game.getAction().moveToBottomOfLibrary(forgeCard, null);
}
} else {
game.getAction().moveTo(targetZone, forgeCard, null);

View File

@@ -0,0 +1,6 @@
Forest|Bosque|Tierra básica - Bosque|({T}: Agrega {G}.)
Island|Isla|Tierra básica - Isla|({T}: Agrega {U}.)
Mountain|Montaña|Tierra básica - Montaña|({T}: Agrega {R}.)
Plains|Llanura|Tierra básica - Llanura|({T}: Agrega {W}.)
Swamp|Pantano|Tierra básica - Pantano|({T}: Agrega {B}.)

View File

@@ -0,0 +1,6 @@
Forest|树林|基本地 ~树林|({T}: 添加 {G}.)
Island|海岛|基本地 ~海岛|({T}: 添加 {U}.)
Mountain|山脉|基本地 ~山脉|({T}: 添加 {R}.)
Plains|平原|基本地 ~平原|({T}: 添加 {W}.)
Swamp|沼泽|基本地 ~沼泽|({T}: 添加 {B}.)

View File

@@ -0,0 +1,178 @@
#!/usr/bin/env python3
import json
import os
import re
import urllib.request
database = 'scryfall-all-cards.json'
scryfalldburl = 'https://archive.scryfall.com/json/' + database
# 'scryfall lang code':'ISO 639 lang code'
languages = {'es': 'es-ES', 'de': 'de-DE',
'zhs': 'zh-CN'}
langfiles = {'es': None, 'de': None, 'zhs': None}
urllib.request.urlretrieve(scryfalldburl, database)
# Sort file and remove duplicates
def cleanfile(filename):
names_seen = set()
outfile = open(filename + ".tmp2", "w", encoding='utf8')
with open(filename + ".tmp", "r", encoding='utf8') as r:
for line in sorted(r):
name = line.split('|')[0]
if name not in names_seen:
outfile.write(line)
names_seen.add(name)
outfile.close()
os.remove(filename + ".tmp")
# Manual patch of file translations
def patchtranslations(filename):
ffinal = open(filename + '.txt', 'w', encoding='utf8')
try:
fpatch = open(filename + '-patch.txt', 'r', encoding='utf8')
except FileNotFoundError:
open(filename + '-patch.txt', 'w', encoding='utf8').close()
fpatch = open(filename + '-patch.txt', 'r', encoding='utf8')
patchline = fpatch.readline()
with open(filename + '.tmp2', 'r', encoding='utf8') as temp:
for templine in temp:
tempname = templine.split('|')[0]
patchname = patchline.split('|')[0]
if patchname == tempname:
ffinal.write(patchline)
patchline = fpatch.readline()
else:
ffinal.write(templine)
ffinal.close()
fpatch.close()
os.remove(filename + '.tmp2')
with open(database, mode='r', encoding='utf8') as json_file:
# todo:all cards json size >= 800MB,using json iteration library,avoid load all content in to memory.
data = json.load(json_file)
for lang in languages.keys():
langfiles[lang] = open(
'cardnames-{0}.tmp'.format(languages[lang]), 'w', encoding='utf8')
for card in data:
if card['lang'] in languages.keys():
try:
name = card['name']
except:
pass
# Parse simple card
if ' // ' not in name:
tname = ttype = toracle = ''
try:
tname = card['printed_name']
except:
pass
try:
ttype = card['printed_type_line']
except:
pass
try:
toracle = card['printed_text']
except:
pass
output = name + '|' + tname + '|' + ttype
output = output + '|' + toracle
output = output.replace('\n', '\\n')
output = output + '\n'
for lang in languages.keys():
if card['lang'] == lang:
langfiles[lang].write(output)
# Parse double card
else:
tname0 = tname1 = ttype0 = ttype1 = toracle0 = toracle1 = ''
cardfaces = card['card_faces']
try:
name0 = cardfaces[0]['name']
except:
pass
try:
name1 = cardfaces[1]['name']
except:
pass
try:
tname0 = cardfaces[0]['printed_name']
except:
pass
try:
tname1 = cardfaces[1]['printed_name']
except:
pass
try:
ttype0 = cardfaces[0]['printed_type_line']
except:
pass
try:
ttype1 = cardfaces[1]['printed_type_line']
except:
pass
try:
toracle0 = cardfaces[0]['printed_text']
except:
pass
try:
toracle1 = cardfaces[1]['printed_text']
except:
pass
# Output Card0
output0 = name0 + '|' + tname0 + '|' + ttype0
output0 = output0 + '|' + toracle0
output0 = output0.replace('\n', '\\n')
for lang in languages.keys():
if card['lang'] == lang:
langfiles[lang].write(output0 + '\n')
# Output Card1
output1 = name1 + '|' + tname1 + '|' + ttype1
output1 = output1 + '|' + toracle1
output1 = output1.replace('\n', '\\n')
for lang in languages.keys():
if card['lang'] == lang:
langfiles[lang].write(output1 + '\n')
for lang in languages.keys():
langfiles[lang].close()
# Sort file and remove duplicates
for lang in languages.keys():
cleanfile("cardnames-{0}".format(languages[lang]))
# Patch language files
for lang in languages.keys():
patchtranslations("cardnames-{0}".format(languages[lang]))