Merge remote-tracking branch 'core/master'

This commit is contained in:
Anthony Calosa
2021-10-21 12:40:54 +08:00
34 changed files with 170 additions and 232 deletions

View File

@@ -30,7 +30,7 @@ public class ControlExchangeAi extends SpellAbilityAi {
sa.resetTargets();
CardCollection list =
CardLists.getValidCards(AiAttackController.choosePreferredDefenderPlayer(ai).getCardsIn(ZoneType.Battlefield), tgt.getValidTgts(), ai, sa.getHostCard(), sa);
CardLists.getValidCards(ai.getOpponents().getCardsIn(ZoneType.Battlefield), tgt.getValidTgts(), ai, sa.getHostCard(), sa);
// AI won't try to grab cards that are filtered out of AI decks on purpose
list = CardLists.filter(list, new Predicate<Card>() {
@Override
@@ -65,7 +65,7 @@ public class ControlExchangeAi extends SpellAbilityAi {
}
} else {
if (mandatory) {
return chkAIDrawback(sa, aiPlayer);
return chkAIDrawback(sa, aiPlayer) || sa.isTargetNumberValid();
} else {
return canPlayAI(aiPlayer, sa);
}
@@ -97,8 +97,12 @@ public class ControlExchangeAi extends SpellAbilityAi {
Card best = ComputerUtilCard.getBestAI(list);
// add best Target:
// do it here already even if we don't want to play this as it might be for targeting a trigger
sa.getTargets().add(best);
// if Param has Defined, check if the best Target is better than the Defined
if (sa.hasParam("Defined")) {
if (sa.hasParam("Defined") && (!sa.isTrigger() || sa.getRootAbility().isOptionalTrigger())) {
final Card object = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("Defined"), sa).get(0);
// TODO add evaluate Land if able
final Card realBest = ComputerUtilCard.getBestAI(Lists.newArrayList(best, object));
@@ -109,9 +113,6 @@ public class ControlExchangeAi extends SpellAbilityAi {
}
}
// add best Target
sa.getTargets().add(best);
// second target needed (the AI's own worst)
if ("TrigTwoTargets".equals(sa.getParam("AILogic"))) {
return doTrigTwoTargetsLogic(aiPlayer, sa, best);

View File

@@ -37,7 +37,7 @@ public class CountersPutAllAi extends SpellAbilityAi {
final boolean curse = sa.isCurse();
final TargetRestrictions tgt = sa.getTargetRestrictions();
if ("OwnCreatsAndOtherPWs".equals(sa.getParam("AILogic"))) {
if ("OwnCreatsAndOtherPWs".equals(logic)) {
hList = CardLists.getValidCards(ai.getWeakestOpponent().getCardsIn(ZoneType.Battlefield), "Creature.YouCtrl,Planeswalker.YouCtrl+Other", source.getController(), source, sa);
cList = CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), "Creature.YouCtrl,Planeswalker.YouCtrl+Other", source.getController(), source, sa);
} else {

View File

@@ -94,7 +94,7 @@ public class DamageAllAi extends SpellAbilityAi {
private Player determineOppToKill(Player ai, SpellAbility sa, Card source, int x) {
// Attempt to determine which opponent can be finished off such that the most players
// are killed at the same time, given X damage tops
final String validP = sa.hasParam("ValidPlayers") ? sa.getParam("ValidPlayers") : "";
final String validP = sa.getParamOrDefault("ValidPlayers", "");
int aiLife = ai.getLife();
Player bestOpp = null; // default opponent, if all else fails
@@ -125,7 +125,7 @@ public class DamageAllAi extends SpellAbilityAi {
computerList.clear();
}
final String validP = sa.hasParam("ValidPlayers") ? sa.getParam("ValidPlayers") : "";
final String validP = sa.getParamOrDefault("ValidPlayers", "");
// TODO: if damage is dependant on mana paid, maybe have X be human's max life
// Don't kill yourself
if (validP.equals("Player") && (ai.getLife() <= ComputerUtilCombat.predictDamageTo(ai, dmg, source, false))) {
@@ -191,7 +191,7 @@ public class DamageAllAi extends SpellAbilityAi {
@Override
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
final Card source = sa.getHostCard();
String validP = "";
final String validP = sa.getParamOrDefault("ValidPlayers", "");
final String damage = sa.getParam("NumDmg");
int dmg;
@@ -203,10 +203,6 @@ public class DamageAllAi extends SpellAbilityAi {
dmg = AbilityUtils.calculateAmount(source, damage, sa);
}
if (sa.hasParam("ValidPlayers")) {
validP = sa.getParam("ValidPlayers");
}
// Evaluate creatures getting killed
Player enemy = ai.getWeakestOpponent();
final CardCollection humanList = getKillableCreatures(sa, enemy, dmg);
@@ -251,7 +247,7 @@ public class DamageAllAi extends SpellAbilityAi {
*/
private CardCollection getKillableCreatures(final SpellAbility sa, final Player player, final int dmg) {
final Card source = sa.getHostCard();
String validC = sa.hasParam("ValidCards") ? sa.getParam("ValidCards") : "";
String validC = sa.getParamOrDefault("ValidCards", "");
// TODO: X may be something different than X paid
CardCollection list =
@@ -273,7 +269,7 @@ public class DamageAllAi extends SpellAbilityAi {
@Override
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
final Card source = sa.getHostCard();
String validP = "";
final String validP = sa.getParamOrDefault("ValidPlayers", "");
final String damage = sa.getParam("NumDmg");
int dmg;
@@ -286,10 +282,6 @@ public class DamageAllAi extends SpellAbilityAi {
dmg = AbilityUtils.calculateAmount(source, damage, sa);
}
if (sa.hasParam("ValidPlayers")) {
validP = sa.getParam("ValidPlayers");
}
// Evaluate creatures getting killed
Player enemy = ai.getWeakestOpponent();
final CardCollection humanList = getKillableCreatures(sa, enemy, dmg);

View File

@@ -81,10 +81,7 @@ public class DestroyAllAi extends SpellAbilityAi {
return true; // e.g. Tetzimoc, Primal Death, where we want to cast the permanent even if the removal trigger does nothing
}
String valid = "";
if (sa.hasParam("ValidCards")) {
valid = sa.getParam("ValidCards");
}
String valid = sa.getParamOrDefault("ValidCards", "");
if (valid.contains("X") && sa.getSVar("X").equals("Count$xPaid")) {
// Set PayX here to maximum value.

View File

@@ -33,7 +33,6 @@ public class PumpAllAi extends PumpAiBase {
*/
@Override
protected boolean canPlayAI(final Player ai, final SpellAbility sa) {
String valid = "";
final Card source = sa.getHostCard();
final Game game = ai.getGame();
final Combat combat = game.getCombat();
@@ -78,9 +77,7 @@ public class PumpAllAi extends PumpAiBase {
final List<String> keywords = sa.hasParam("KW") ? Arrays.asList(sa.getParam("KW").split(" & ")) : new ArrayList<>();
final PhaseType phase = game.getPhaseHandler().getPhase();
if (sa.hasParam("ValidCards")) {
valid = sa.getParam("ValidCards");
}
final String valid = sa.getParamOrDefault("ValidCards", "");
CardCollection comp = CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), valid, source.getController(), source, sa);
CardCollection human = CardLists.getValidCards(opp.getCardsIn(ZoneType.Battlefield), valid, source.getController(), source, sa);

View File

@@ -26,11 +26,7 @@ public class RegenerateAllAi extends SpellAbilityAi {
final Game game = ai.getGame();
// filter AIs battlefield by what I can target
String valid = "";
if (sa.hasParam("ValidCards")) {
valid = sa.getParam("ValidCards");
}
final String valid = sa.getParamOrDefault("ValidCards", "");
CardCollectionView list = game.getCardsIn(ZoneType.Battlefield);
list = CardLists.getValidCards(list, valid.split(","), hostCard.getController(), hostCard, sa);

View File

@@ -34,10 +34,7 @@ public class TapAllAi extends SpellAbilityAi {
return false;
}
String valid = "";
if (sa.hasParam("ValidCards")) {
valid = sa.getParam("ValidCards");
}
final String valid = sa.getParamOrDefault("ValidCards", "");
CardCollectionView validTappables = game.getCardsIn(ZoneType.Battlefield);
@@ -99,10 +96,7 @@ public class TapAllAi extends SpellAbilityAi {
protected boolean doTriggerAINoCost(final Player ai, SpellAbility sa, boolean mandatory) {
final Card source = sa.getHostCard();
String valid = "";
if (sa.hasParam("ValidCards")) {
valid = sa.getParam("ValidCards");
}
final String valid = sa.getParamOrDefault("ValidCards", "");
CardCollectionView validTappables = getTapAllTargets(valid, source, sa);

View File

@@ -24,10 +24,7 @@ public class TwoPilesAi extends SpellAbilityAi {
zone = ZoneType.smartValueOf(sa.getParam("Zone"));
}
String valid = "";
if (sa.hasParam("ValidCards")) {
valid = sa.getParam("ValidCards");
}
final String valid = sa.getParamOrDefault("ValidCards", "");
final Player opp = AiAttackController.choosePreferredDefenderPlayer(ai);

View File

@@ -24,11 +24,8 @@ public class UntapAllAi extends SpellAbilityAi {
&& source.getGame().getPhaseHandler().getPhase().isBefore(PhaseType.COMBAT_END)) {
return false;
}
String valid = "";
CardCollectionView list = CardLists.filter(aiPlayer.getGame().getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.TAPPED);
if (sa.hasParam("ValidCards")) {
valid = sa.getParam("ValidCards");
}
final String valid = sa.getParamOrDefault("ValidCards", "");
list = CardLists.getValidCards(list, valid.split(","), source.getController(), source, sa);
// don't untap if only opponent benefits
PlayerCollection goodControllers = aiPlayer.getAllies();

View File

@@ -121,11 +121,7 @@ public class AnimateAllEffect extends AnimateEffectBase {
sVars.addAll(Arrays.asList(sa.getParam("sVars").split(",")));
}
String valid = "";
if (sa.hasParam("ValidCards")) {
valid = sa.getParam("ValidCards");
}
final String valid = sa.getParamOrDefault("ValidCards", "");
CardCollectionView list;

View File

@@ -20,7 +20,7 @@ public class CountersPutAllEffect extends SpellAbilityEffect {
final CounterType cType = CounterType.getType(sa.getParam("CounterType"));
final int amount = AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("CounterNum"), sa);
final String zone = sa.hasParam("ValidZone") ? sa.getParam("ValidZone") : "Battlefield";
final String zone = sa.getParamOrDefault("ValidZone", "Battlefield");
sb.append("Put ").append(amount).append(" ").append(cType.getName()).append(" counter");
if (amount != 1) {

View File

@@ -22,7 +22,7 @@ public class CountersRemoveAllEffect extends SpellAbilityEffect {
final CounterType cType = CounterType.getType(sa.getParam("CounterType"));
final int amount = AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("CounterNum"), sa);
final String zone = sa.hasParam("ValidZone") ? sa.getParam("ValidZone") : "Battlefield";
final String zone = sa.getParamOrDefault("ValidZone", "Battlefield");
String amountString = Integer.toString(amount);
if (sa.hasParam("AllCounters")) {

View File

@@ -22,10 +22,7 @@ public class DamageAllEffect extends DamageBaseEffect {
protected String getStackDescription(SpellAbility sa) {
final StringBuilder sb = new StringBuilder();
String desc = "";
if (sa.hasParam("ValidDescription")) {
desc = sa.getParam("ValidDescription");
}
final String desc = sa.getParamOrDefault("ValidDescription", "");
final String damage = sa.getParam("NumDmg");
final int dmg = AbilityUtils.calculateAmount(sa.getHostCard(), damage, sa);
@@ -63,11 +60,7 @@ public class DamageAllEffect extends DamageBaseEffect {
//Remember params from this effect have been moved to dealDamage in GameAction
Player targetPlayer = sa.getTargets().getFirstTargetedPlayer();
String players = "";
if (sa.hasParam("ValidPlayers")) {
players = sa.getParam("ValidPlayers");
}
final String players = sa.getParamOrDefault("ValidPlayers", "");
CardCollectionView list;
if (sa.hasParam("ValidCards")) {

View File

@@ -15,11 +15,7 @@ public class DamagePreventAllEffect extends DamagePreventEffectBase {
final Game game = sa.getActivatingPlayer().getGame();
final int numDam = AbilityUtils.calculateAmount(source, sa.getParam("Amount"), sa);
String players = "";
if (sa.hasParam("ValidPlayers")) {
players = sa.getParam("ValidPlayers");
}
final String players = sa.getParamOrDefault("ValidPlayers", "");
if (sa.hasParam("ValidCards")) {
CardCollectionView list = game.getCardsIn(ZoneType.Battlefield);

View File

@@ -24,7 +24,6 @@ public class DestroyAllEffect extends SpellAbilityEffect {
@Override
protected String getStackDescription(SpellAbility sa) {
if (sa.hasParam("SpellDescription")) {
return sa.getParam("SpellDescription");
}
@@ -51,11 +50,7 @@ public class DestroyAllEffect extends SpellAbilityEffect {
Player targetPlayer = sa.getTargets().getFirstTargetedPlayer();
String valid = "";
if (sa.hasParam("ValidCards")) {
valid = sa.getParam("ValidCards");
}
String valid = sa.getParamOrDefault("ValidCards", "");
// Ugh. If calculateAmount needs to be called with DestroyAll it _needs_
// to use the X variable

View File

@@ -72,7 +72,7 @@ public class DigEffect extends SpellAbilityEffect {
int libraryPosition = sa.hasParam("LibraryPosition") ? Integer.parseInt(sa.getParam("LibraryPosition")) : -1;
int destZone1ChangeNum = 1;
final boolean mitosis = sa.hasParam("Mitosis");
String changeValid = sa.hasParam("ChangeValid") ? sa.getParam("ChangeValid") : "";
String changeValid = sa.getParamOrDefault("ChangeValid", "");
final boolean anyNumber = sa.hasParam("AnyNumber");
final int libraryPosition2 = sa.hasParam("LibraryPosition2") ? Integer.parseInt(sa.getParam("LibraryPosition2")) : -1;
@@ -86,7 +86,7 @@ public class DigEffect extends SpellAbilityEffect {
// These parameters are used to indicate that a dialog box must be show to the player asking if the player wants to proceed
// with an optional ability, otherwise the optional ability is skipped.
final boolean mayBeSkipped = sa.hasParam("PromptToSkipOptionalAbility");
final String optionalAbilityPrompt = sa.hasParam("OptionalAbilityPrompt") ? sa.getParam("OptionalAbilityPrompt") : "";
final String optionalAbilityPrompt = sa.getParamOrDefault("OptionalAbilityPrompt", "");
boolean remZone1 = false;
boolean remZone2 = false;

View File

@@ -35,7 +35,7 @@ public class DigMultipleEffect extends SpellAbilityEffect {
int libraryPosition = sa.hasParam("LibraryPosition") ? Integer.parseInt(sa.getParam("LibraryPosition")) : -1;
final int libraryPosition2 = sa.hasParam("LibraryPosition2") ? Integer.parseInt(sa.getParam("LibraryPosition2")) : -1;
String changeValid = sa.hasParam("ChangeValid") ? sa.getParam("ChangeValid") : "";
String changeValid = sa.getParamOrDefault("ChangeValid", "");
boolean chooseOptional = sa.hasParam("Optional");
CardZoneTable table = new CardZoneTable();

View File

@@ -60,7 +60,7 @@ public class FlipCoinEffect extends SpellAbilityEffect {
}
final boolean noCall = sa.hasParam("NoCall");
String varName = sa.hasParam("SaveNumFlipsToSVar") ? sa.getParam("SaveNumFlipsToSVar") : "X";
String varName = sa.getParamOrDefault("SaveNumFlipsToSVar", "X");
boolean victory = false;
int amount = 1;
if (sa.hasParam("Amount")) {

View File

@@ -27,7 +27,7 @@ public class ManifestEffect extends SpellAbilityEffect {
final int amount = sa.hasParam("Amount") ? AbilityUtils.calculateAmount(source,
sa.getParam("Amount"), sa) : 1;
// Most commonly "defined" is Top of Library
final String defined = sa.hasParam("Defined") ? sa.getParam("Defined") : "TopOfLibrary";
final String defined = sa.getParamOrDefault("Defined", "TopOfLibrary");
CardCollectionView lastStateBattlefield = game.copyLastStateBattlefield();
CardCollectionView lastStateGraveyard = game.copyLastStateGraveyard();

View File

@@ -31,12 +31,8 @@ public class MultiplePilesEffect extends SpellAbilityEffect {
final StringBuilder sb = new StringBuilder();
final List<Player> tgtPlayers = getTargetPlayers(sa);
String valid = "";
String piles = sa.getParam("Piles");
if (sa.hasParam("ValidCards")) {
valid = sa.getParam("ValidCards");
}
final String valid = sa.getParamOrDefault("ValidCards", "");
sb.append("Separate all ").append(valid).append(" cards ");
@@ -58,10 +54,7 @@ public class MultiplePilesEffect extends SpellAbilityEffect {
final int piles = Integer.parseInt(sa.getParam("Piles"));
final Map<Player, List<CardCollectionView>> record = Maps.newHashMap();
String valid = "";
if (sa.hasParam("ValidCards")) {
valid = sa.getParam("ValidCards");
}
final String valid = sa.getParamOrDefault("ValidCards", "");
final TargetRestrictions tgt = sa.getTargetRestrictions();
final List<Player> tgtPlayers = getTargetPlayers(sa);

View File

@@ -37,8 +37,8 @@ public class PeekAndRevealEffect extends SpellAbilityEffect {
Card source = sa.getHostCard();
final boolean rememberRevealed = sa.hasParam("RememberRevealed");
final boolean imprintRevealed = sa.hasParam("ImprintRevealed");
String revealValid = sa.hasParam("RevealValid") ? sa.getParam("RevealValid") : "Card";
String peekAmount = sa.hasParam("PeekAmount") ? sa.getParam("PeekAmount") : "1";
String revealValid = sa.getParamOrDefault("RevealValid", "Card");
String peekAmount = sa.getParamOrDefault("PeekAmount", "1");
int numPeek = AbilityUtils.calculateAmount(sa.getHostCard(), peekAmount, sa);
// Right now, this is only used on your own library.

View File

@@ -82,10 +82,7 @@ public class ProtectAllEffect extends SpellAbilityEffect {
}
// Deal with permanents
String valid = "";
if (sa.hasParam("ValidCards")) {
valid = sa.getParam("ValidCards");
}
final String valid = sa.getParamOrDefault("ValidCards", "");
if (!valid.equals("")) {
CardCollectionView list = game.getCardsIn(ZoneType.Battlefield);
list = CardLists.getValidCards(list, valid, sa.getActivatingPlayer(), host, sa);
@@ -113,10 +110,7 @@ public class ProtectAllEffect extends SpellAbilityEffect {
}
// Deal with Players
String players = "";
if (sa.hasParam("ValidPlayers")) {
players = sa.getParam("ValidPlayers");
}
final String players = sa.getParamOrDefault("ValidPlayers", "");
if (!players.equals("")) {
final List<Player> playerList = AbilityUtils.getDefinedPlayers(host, players, sa);
for (final Player player : playerList) {

View File

@@ -137,10 +137,7 @@ public class PumpAllEffect extends SpellAbilityEffect {
list = tgtPlayers.getCardsIn(affectedZones);
}
String valid = "";
if (sa.hasParam("ValidCards")) {
valid = sa.getParam("ValidCards");
}
final String valid = sa.getParamOrDefault("ValidCards", "");
list = AbilityUtils.filterListByType(list, valid, sa);

View File

@@ -26,11 +26,7 @@ public class RegenerateAllEffect extends RegenerateBaseEffect {
public void resolve(SpellAbility sa) {
final Card hostCard = sa.getHostCard();
final Game game = hostCard.getGame();
String valid = "";
if (sa.hasParam("ValidCards")) {
valid = sa.getParam("ValidCards");
}
final String valid = sa.getParamOrDefault("ValidCards", "");
CardCollectionView list = game.getCardsIn(ZoneType.Battlefield);
list = CardLists.getValidCards(list, valid.split(","), hostCard.getController(), hostCard, sa);

View File

@@ -76,11 +76,7 @@ public class RepeatEffect extends SpellAbilityEffect {
if (sa.hasParam("RepeatPresent")) {
final String repeatPresent = sa.getParam("RepeatPresent");
String repeatCompare = "GE1";
if (sa.hasParam("RepeatCompare")) {
repeatCompare = sa.getParam("RepeatCompare");
}
String repeatCompare = sa.getParamOrDefault("RepeatCompare", "GE1");
CardCollectionView list;
if (sa.hasParam("RepeatDefined")) {

View File

@@ -243,7 +243,6 @@ public class SacrificeEffect extends SpellAbilityEffect {
removeCandidates(validTargets, validTargetsList, union, index + 1, included, amount);
CardCollection candidate = validTargetsList.get(index);
if (candidate.isEmpty()) {
return;

View File

@@ -28,8 +28,7 @@ public class TapOrUntapAllEffect extends SpellAbilityEffect {
if (sa.hasParam("ValidMessage")) {
sb.append(sa.getParam("ValidMessage"));
}
else {
} else {
final List<Card> tgtCards = getTargetCards(sa);
sb.append(StringUtils.join(tgtCards, ", "));
}

View File

@@ -26,10 +26,7 @@ public class TwoPilesEffect extends SpellAbilityEffect {
final List<Player> tgtPlayers = getTargetPlayers(sa);
String valid = "";
if (sa.hasParam("ValidCards")) {
valid = sa.getParam("ValidCards");
}
final String valid = sa.getParamOrDefault("ValidCards", "");
sb.append("Separate all ").append(valid).append(" cards ");
@@ -54,10 +51,7 @@ public class TwoPilesEffect extends SpellAbilityEffect {
zone = ZoneType.smartValueOf(sa.getParam("Zone"));
}
String valid = "Card";
if (sa.hasParam("ValidCards")) {
valid = sa.getParam("ValidCards");
}
final String valid = sa.getParamOrDefault("ValidCards", "");
final TargetRestrictions tgt = sa.getTargetRestrictions();
final List<Player> tgtPlayers = getTargetPlayers(sa);

View File

@@ -24,15 +24,9 @@ public class UntapAllEffect extends SpellAbilityEffect {
@Override
public void resolve(SpellAbility sa) {
final Card card = sa.getHostCard();
String valid = "";
CardCollectionView list;
List<Player> tgtPlayers = getTargetPlayers(sa);
if (sa.hasParam("ValidCards")) {
valid = sa.getParam("ValidCards");
}
final String valid = sa.getParamOrDefault("ValidCards", "");
if (!sa.usesTargeting() && !sa.hasParam("Defined")) {
list = sa.getActivatingPlayer().getGame().getCardsIn(ZoneType.Battlefield);

View File

@@ -213,7 +213,7 @@ public final class LayoutMenu {
private static ActionListener getSetWindowSizeAction() {
return new ActionListener() {
@Override public void actionPerformed(final ActionEvent e) {
final String[] options = {"800x600", "1024x768", "1280x720"};
final String[] options = {"800x600", "1024x768", "1280x720", "1600x900", "1920x1080", "2560x1440", "3840x2160"};
final Localizer localizer = Localizer.getInstance();
final String choice = GuiChoose.oneOrNone(localizer.getMessage("lblChooseNewWindowSize"), options);
if (choice != null) {

View File

@@ -0,0 +1,26 @@
Name:Hawkins National Laboratory
ManaCost:no cost
Types:Legendary Land
A:AB$ Mana | Cost$ T | Produced$ C | SpellDescription$ Add {C}.
A:AB$ Investigate | Cost$ 4 T | SpellDescription$ Investigate.
T:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ You | TriggerZones$ Battlefield | CheckSVar$ ClueResearch | SVarCompare$ GE3 | Execute$ TrigTransform | TriggerDescription$ At the beginning of your end step, if you sacrificed three or more clues this turn, transform CARDNAME.
SVar:TrigTransform:DB$ SetState | Defined$ Self | Mode$ Transform
SVar:ClueResearch:Count$SacrificedThisTurn Clue
AlternateMode:DoubleFaced
DeckHints:Ability$Investigate
DeckHas:Ability$Investigate & Ability$Token & Ability$Sacrifice
Oracle:{T}: Add {C}.\n{4}, {T}: Investigate.\nAt the beginning of your end step, if you sacrificed three or more clues this turn, transform Hawkins National Laboratory.
ALTERNATE
Name:The Upside Down
ManaCost:no cost
Types:Legendary Land
T:Mode$ Transformed | ValidCard$ Card.Self | Execute$ TrigReturn | TriggerDescription$ When this land transforms into CARDNAME, return target creature card from your graveyard to the battlefield.
SVar:TrigReturn:DB$ ChangeZone | ValidTgts$ Creature.YouOwn | TgtPrompt$ Select target creature card from your graveyard | Origin$ Graveyard | Destination$ Battlefield | RememberTargets$ True
T:Mode$ ChangesZone | ValidCard$ Card.IsRemembered | Origin$ Battlefield | Destination$ Any | Execute$ TrigTransform2 | TriggerZones$ Battlefield | TriggerDescription$ When the creature card put onto the battlefield with CARDNAME leaves the battlefield, transform CARDNAME.
SVar:TrigTransform2:DB$ SetState | Defined$ Self | Mode$ Transform | SubAbility$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
A:AB$ Mana | Cost$ T PayLife<1> | Produced$ B | SpellDescription$ Add {B}.
DeckHas:Ability$Graveyard
Oracle:When this land transforms into The Upside Down, return target creature card from your graveyard to the battlefield.\nWhen the creature card put onto the battlefield with The Upside Down leaves the battlefield, transform The Upside Down.\n{T}, Pay 1 life: Add {B}.

View File

@@ -168,7 +168,7 @@ nlRemoveSmall=Verhindert 1/1- und 0/X-Kreaturen in erzeugten Decks
nlSingletons=Verhindert Duplikate von Nicht-Landkarten in erzeugten Decks
nlRemoveArtifacts=Verhindert Artefakte in erzeugten Decks
nlCardBased=Erzeugt stimmigere zufällig erzeugte Decks (Erfordert Neustart)
DeckEditorOptions=Deck Editor Optionen
DeckEditorOptions=Deck-Editor-Optionen
nlFilterLandsByColorId=Macht es einfacher die richtigen Länder zu finden, wenn Farbfilter genutzt werden.
AdvancedSettings=Erweiterte Optionen
nlDevMode=Ativiert ein Menü mit Funktionen, welche das Testen vereinfachen.
@@ -466,7 +466,7 @@ lblBrawlDesc=Jeder Spieler hat eine legendäre \"General\"-Karte, welche (fast)
lblPlaneswalker=Planeswalker
lblPlaneswalkerDesc=Jeder Spieler hat eine Planeswalker-Karte, welche (fast) jederzeit gespielt werden kann.
lblPlanechase=Weltenjagd
lblPlanechaseDesc=Weltenkarten haben globale Effekte. Wenn ein Spieler auf dem Weltenwürfel \"Planeswalk\"wirft, wechselt die Weltenkarte.
lblPlanechaseDesc=Weltenkarten haben globale Effekte. Wenn ein Spieler auf dem Weltenwürfel \"Planeswalk\" wirft, wechselt die Weltenkarte.
lblArchenemyDesc=Ein Spieler ist der Erzfeind und kämpft gegen die anderen Spieler. Er hat Komplottkarten.\nEiner gegen alle.
lblArchenemyRumble=Erzfeind Rumble
lblArchenemyRumbleDesc=Alle Spieler sind Erzfeinde und haben Komplottkarten.\nJeder gegen jeden.
@@ -1355,7 +1355,7 @@ lblRemoveCardBelongingWitchPlayer=Entferne Karte(n) von welchem Spielers?
lblRemoveCardFromWhichZone=Entferne Karte(n) aus welcher Zone?
lblChooseCardsRemoveFromGame=Welche Karten aus dem Spiel entfernen?
lblRemoved=Entfernt
lblEnterASequence=Gib Abfolge ein (Karten-ID und/oder "opponen"/"me"(z.B. 7, opponent, 18)).
lblEnterASequence=Gib Abfolge ein (Karten-ID und/oder "opponent"/"me"(z.B. 7, opponent, 18)).
lblActionSequenceCleared=Aktion gelöscht
lblRestartingActionSequence=Starte Aktion neu.
lblErrorPleaseCheckID=Fehler: Prüfe IDs und ob sie durch Leerzeichen und/oder Kommas getrennt sind.
@@ -2636,95 +2636,90 @@ lblTargetingArcsAlwaysOn=Zielpfleile: Immer an
#ListCardArea.java
lblDone=Fertig
#DeckImport.java
lblDeckImporterPanelTitle=Deck Importer - {0}
ttUseOnlySetsReleasedBefore=Use in combination with Card Art Preference
nlUseOnlySetsReleasedBefore=Filter card editions by release date
lblUseFormatFilter=Choose a Format for the Decklist
lblIgnoreBnR=Also import Banned and Restricted Cards
ttIgnoreBnR=If activated, any Banned or Restricted Card will be also imported in the Deck.
nlIgnoreBnR=Warning: The Deck may not be playable if Deck Conformance is enabled.
lblExtraOptions=Show Options
lblHideOptions=Hide Options
lblCardPreview=Card Preview
lblCardListTitle=Card List
lblDecklistTitle=Decklist
lblSummaryStats=Summary Statistics
lblDeckSection=Section
lblNewDeckCheckbox=Create a New Deck
lblImportCardsCmd=Import Cards
lblCreateNewCmd=New Deck
lblErrNotAllowedCard=Set not allowed in {0}
lblDeckImporterPanelTitle=Deck-Importer - {0}
ttUseOnlySetsReleasedBefore=In Kombination mit bevorzugtem Kartenbild nutzen
nlUseOnlySetsReleasedBefore=Filtere Karten-Editionen nach Erscheinungsdatum
lblUseFormatFilter=Wähle ein Format für die Deckliste
lblIgnoreBnR=Importiere auch gebannte und eingeschränkte Karten
ttIgnoreBnR=Wenn aktiviert, erden auch gebannte oder eingeschränkte Karten ins Deck importiert.
nlIgnoreBnR=Warnung: Das Deck kann bei eingeschalteter Deckkonkonformität nicht spielbar sein.
lblExtraOptions=Zeige Optionen
lblHideOptions=Verstecke Optionen
lblCardPreview=Karten-Vorschau
lblCardListTitle=Karten-Liste
lblDecklistTitle=Deckliste
lblSummaryStats=Gesamt-Statistik
lblDeckSection=Bereich
lblNewDeckCheckbox=Erzeuge ein neues Deck
lblImportCardsCmd=Importiere Karten
lblCreateNewCmd=Neues Deck
lblErrNotAllowedCard=Set ist nicht erlaubt in {0}
lblWarnLimitedCard={0} in {1}
lblErrCardEditionDate=Set not compliant with Release Date option
lblErrUnsupportedCard=Not allowed in {0}
lblWarnUnknownCardMsg=Unknown Card or Unsupported in Forge
lblWarnTooManyCommanders=Current {0} Section contains {1} potential Commander Cards: {2}
lblWarnCommandersInSideExtra=Please check and move one to the Commander Section, in case.
lblWarnDeckSectionNotAllowedInEditor={0} Section is not allowed in {1}
lblWarnCardInInvalidSection={0} moved from {1} to {2}.
lblWarningMsgPrefix=WARNING
lblNewDeckName=New Deck
lblCurrentDecklist=Current Decklist
lblDeckName=Deck Name
nlGuideTitle=Quick Instructions
nlGuideQuickInstructions=To start using the Deck Importer just type or paste the names of the \
cards you want in the Card List (one per line). The Card list can contain either a few spare cards, \
or a full deck in one of the most popular M:TG formats (Please see below the list of supported Deck formats). \n \
Each line in the Card list is processed immediately, and any recognized entry will appear in the Decklist in one \
of the following colors: {0}
lblGuideImportCard=Card successfully recognized and ready to be imported.
lblGuideWarnMessage=Unknown or Limited Card (i.e. Banned or Restricted), or general warning message.
lblGuideNoImportCard=Invalid or Unsupported Card that cannot be imported.
nlGuideTipsTitle=Importer Options and Customizations
nlGuideTipsText=Please read this section if you want to know the many ways the Deck Importer allows to refine and organize \
your Decklist. \n {0}
lblGuideTipsTitleCount=Card Amount
lblGuideTipsCount={0}: Prepend to each card line the number of copies to import (default: 1). \n\
For example, both {1} and {2} are accepted entries.
lblGuideTipsTitleSet=Card Art
lblGuideTipsSet={0}: Specify the code of an M:TG set (right before or right after card name) to choose a \
specific card art. Also add card collector number or the art index at the end of the line, if the chosen \
set has multiple arts for the same card (See more examples below).
lblGuideTipsTitleFoil=Foil Cards
lblGuideTipsFoil={0}: Any card name ending with the "+" sign will be imported foiled, e.g. {1}. Alternatively, \
appending the {2} symbol at the end of card line (as it is customary in some M:TG deck formats) will \
do the same (Please see the Examples Section below).
lblGuideTipsTitlePlaceholder=Card Groups
lblGuideTipsPlaceholder={0}: Special placeholder keywords can be used to organize the Card list. They can be either \
Core Card Types (e.g. {1}), Card Rarity (i.e. {2}), Mana Cost (e.g. {3}), or Mana Colors (e.g. {4}).
lblGuideTipsTitleDeckSections=Deck Sections
lblGuideTipsDeckSection={0}: Similar to Card Groups, the Card list can also be organized in Deck Sections \
(e.g. {1}), that will also used by the Importer to assign cards in the deck (default: {2}). \
For this reason, the Importer also integrates an automated validation of cards in sections to \
reduce the risk of errors.
lblGuideTipsTitleDeckName=Deck Name
lblGuideTipsDeckName={0}: Assign a name to your Deck. Just type "Deck:" or "Name:", followed by the name \
to assign to the deck.
lblGuideTipsTitleDeckFormat=Deck Formats
lblGuideTipsDeckFormats={0}: Several M:TG deck formats are supported: MTG Arena; \
MTGO, XMage, MTG Goldfish (all export); TappedOut (all export, but CSV); DeckStats.net; ".dec" files.
nlGuideExamplesTitle=Examples
lblErrCardEditionDate=Set verträgt sich nicht mit der Erscheinungsdatum-Option
lblErrUnsupportedCard=Ist nicht erlaubt in {0}
lblWarnUnknownCardMsg=Unbekannte oder in Forge nicht unterstützte Karte
lblWarnTooManyCommanders=Aktueller {0}-Bereich enthält {1} mögliche Commander-Karten: {2}
lblWarnCommandersInSideExtra=Bitte prüfen und, falls nötig, min. eine Karte in den Commander-Bereich verschieben.
lblWarnDeckSectionNotAllowedInEditor=In {1} ist der {0}-Bereich nicht erlaubt.
lblWarnCardInInvalidSection={0} wurde verschoben von {1} nach {2}.
lblWarningMsgPrefix=WARNUNG
lblNewDeckName=Neues Deck
lblCurrentDecklist=Aktuelle Deckliste
lblDeckName=Deck-Name
nlGuideTitle=Kurz-Anleitung
nlGuideQuickInstructions=Um den Deck-Importer zu nutzen tippe oder kopiere den (englischen) Namen einer Karte (eine pro Zeile) in die \
Kartenliste ein. Die Kartenliste kann entweder einige beliebige Karten, oder ein ganzes Deck in einem der beliebten M:TG-Formate enthalten \
(Eine Liste der unterstützten Formate findet sich unten). \n \
Jede Zeile wird umgehend verarbeitet, und jeder erkannte Eintrag erscheint in der Deckliste in einer der folgenden Farben: {0}
lblGuideImportCard=Karte wurde erfolgreich erkannt und ist fertig zum Übernehmen.
lblGuideWarnMessage=Unbekannte oder eingeschränkte Karte (steht auf der Bann- o. Beschränkungsliste), oder generälle Warnung.
lblGuideNoImportCard=Ungültige oder nicht unterstützte Karte, welche nicht übernommen werden kann.
nlGuideTipsTitle=Importer-Optionen und Anpassungen
nlGuideTipsText=Bitte lese diesen Abschnitt, wenn du mehr über die Möglicjkeiten wissen willst, wie der Importer deine Deckliste bearbeiten \
und organisieren kann. \n {0}
lblGuideTipsTitleCount=Karten-Anzahl
lblGuideTipsCount={0}: Stelle jeder Karte die zu importierende Anzahl voran (Vorgabe: 1). \n\
Zum Beispiel sind {1} und {2} beides akzeptierte Einträge.
lblGuideTipsTitleSet=Kartenbild
lblGuideTipsSet={0}: Gebe den Code eines M:TG-Sets (direkt vor oder nach dem Kartennamen) an, um ein spezielles Kartenbild zu wählen. \
Außerdem gib die Sammlernummer oder eine Bild-Versionsnummer an, falls mehrere Bildversionen einer Karte im Set existieren \
(weitere Beispiele unten).
lblGuideTipsTitleFoil=Foil-Karten
lblGuideTipsFoil={0}: Jede Karte endend mit "+" wird als Foil importiert, z.B. {1}. Anhängen des {2}-Symbols am Ende der Kartenzeile \
(üblich in einigen beliebten Deck-Formaten) bewirkt das Gleiche (siehe Beispiele unten).
lblGuideTipsTitlePlaceholder=Karten-Gruppierung
lblGuideTipsPlaceholder={0}: Spezielle Schlüsselwörter können zur Organisation der Deckliste genutzt werden. Sie können \
Kartentypen (z.B. {1}), Seltenheit (z.B. {2}), Manakosten (z.B. {3}), or Manafarben (z.B. {4}) sein.
lblGuideTipsTitleDeckSections=Deck-Bereiche
lblGuideTipsDeckSection={0}: Genauso wie Grupierungen kann die Kartenliste in Bereiche eingeteilt werden \
(z.B. {1}), welche der Importer nutzt, um die Karten im Deck zuzuweisen (Vorgabe: {2}). \
Aus diesem Grund nutzt der Importer eine automatische Gegenprüfung der Karten in den Bereichen um das Risiko von Fehler zu verringern.
lblGuideTipsTitleDeckName=Deck-Name
lblGuideTipsDeckName={0}: Um dem Deck einen Namen zuzuweisen, gib "Deck:" oder "Name:" ein, gefolgt vom gewünschten Namen des Decks.
lblGuideTipsTitleDeckFormat=Deck-Formate
lblGuideTipsDeckFormats={0}: Diverse M:TG-Deckformate werden unterstützt: MTG Arena, \
MTGO, XMage, MTG Goldfish (alle Exporte); TappedOut (alle Exporte, außer CSV); DeckStats.net; ".dec"-Dateien.
nlGuideExamplesTitle=Beispiele
lblExample1=10x Islands+ MIR 2
nlExample1=10 FOIL "Island" from Set "Mirage", Art Index 2\.
nlExample1=10 FOIL "Insel" vom Set "Trugbilder", Bildversion 2\.
lblExample2=4 Darksteel Citadel (M15) 242
nlExample2=4 "Darksteel Citadel" from "M15", Collector Nr. "242".\n\n \
This is an exemplar entry in MTGArena format as exported by TappedOut.
nlExample2=4 "Nachtstahl-Zitadelle" von "M15", Sammlernummer "242".\n\n \
Dies ist ein Beispieleintrag im MTGArena-Format exportiert von TappedOut.
lblExample3=18 Forest &lt;254&gt; [THB] (F)
nlExample3=18 FOIL "Forest" from "THB", Collector Nr. "254".\n\n \
This is an exemplar entry in MTGArena deck format as exported by MTGGoldfish. \
The "(F)" in the end denotes a foil card, and the Collector Number in angular brackets is \
is used to select a specific Card Art).
nlExample3=18 FOIL "Wald" von "THB", Sammlernummer "254".\n\n \
Dies ist ein Beispielentrag im MTGArena-Deckformat exportiert von MTGGoldfish. \
Das "(F)" am Ende bezeichnet eine Foil-Karte, und die Sammlernummer in gewinkelten Klammern wird genutzt eine \
bestimmte Kartenbildversion zu wählen.
lblExample4=4x TMP|Counterspell
nlExample4=4 copies of "Counterspell" from "Tempest".\n\n \
In this entry, the Set code is specified before the card name using the (optional) pipe separator.\
Alternative valid separators for set codes are braces, brackets, and parenthesis.
nlExample4=4 Kopien von "Gegenzauber" von "Sturmwind".\n\n \
In diesem Eintrag wird der Set-Code vor dem Kartennamen unter Nutzung des (optionalen) Trenners Pipe.\
Andere gültige Trenner für Set-Codes sind geschweifte, eckige und runde Klammern.
lblExample5=SB: 2 [M19:28] Mighty Leap
nlExample5=2 copies of "Mighty Leap" from "M19", Collector Nr. "28" to be added in "Sideboard".\n\n \
This is an exemplar entry in XMage deck format. In this format, the Set code and the Collector Number are \
column-separated, and specified in brackets right before card name. \
Moreover, an optional Prefix (i.e. SB:) is used to denote the Deck Section where the card should be added to. \
The Deck Importer in Forge further extends this idea by also adding support to two extra prefixes, i.e. "MB:" and \
"CM:" as for "Main" and "Commander" Section, respectively.
nlExample5=2 Kopien von "Mächtiger Sprung" von "M19", Sammlernummer "28" werden dem "Sideboard" zugeordnet.\n\n \
Dies ist ein Beispieleintrag im XMage-Deckformat. In diesem Format sind der Set-Code und die Sammlernummer durch einen Doppelpunkt \
getrennt, werden direkt vor dem Kartennamen in eckigen Klammern angegeben. \
Zusätzlich wird ein optionaler Prefix (z.B. SB:) genutzt um den gewünschten Bereich für die Karte anzugeben. \
Der Deck-Importer in Forge unterstützt zwei weitere Prefixe, z.B. "MB:" und "CM:" für den "Haupt-" und den "Commander-"Bereich.
#CEditorTokenViewer.java
lblAllTokens=Alle Spielsteine
#StartRenderer.java
@@ -2748,14 +2743,14 @@ htmlPhaseCleanupTooltip=<html>Phase: Aufräumen<br>Klicke zum Umschalten.</html>
lblSideboardForPlayer=Sideboard für {0}
lblOtherInteger=Andere...
#DeckImportController.java
lblConfirmCardImport=You are about to merge the Decklist in the {0} deck.{1}\n\nWould you like to proceed?\n\n Note: \
Please remember to click on the "Save" button in the Deck Editor to finalize the changes to current deck!
lblCardImportWarning=\nWarning: The deck {0} will be renamed as {1}.
lblConfirmCreateNewDeck=You are about to create a new deck {0}. {1}\n\nWould you like to proceed?\n\n Note: \
Please remember to click on the "Save" button in the Deck Editor to add the new deck to the Catalog!
lblNewDeckWarning=\nWarning: Any unsaved changes to the current deck {0} will be lost.
lblImportCardsDialogTitle=Import cards in the Current Deck
lblNewDeckDialogTitle=Create a New Deck
lblConfirmCardImport=Du bist dabei die Deckliste ins {0}-Deck einzufügen.{1}\n\nWillst du fortfahren?\n\n Hinweis: \
Bitte denk daran den "Save"-Knopf im Deck-Editor zu klicken, um die Änderungen am aktuellen Deck zu bestätigen!
lblCardImportWarning=\nWarnung: Das Deck {0} wird umbenannt in {1}.
lblConfirmCreateNewDeck=Du bist dabei das neue Deck {0} zu erzeugen. {1}\n\nWillst du fortfahren?\n\n Hinweis: \
Bitte denk daran den "Save"-Knopf im Deck-Editor zu klicken, um das neue Deck dem Deck-Katalog hinzuzufügen!
lblNewDeckWarning=\nWarnung: Alle ungesicherten Änderungen am aktuellen Deck {0} werden verlorengehen.
lblImportCardsDialogTitle=Importiere Karten in aktuelles Deck
lblNewDeckDialogTitle=Erzeuge neues Deck
#FNetOverlay.java
lblChat=Chat
lblSend=Senden

View File

@@ -16,6 +16,7 @@ import forge.gamemodes.quest.data.QuestPreferences;
import forge.gamemodes.tournament.system.TournamentBracket;
import forge.gamemodes.tournament.system.TournamentPairing;
import forge.gamemodes.tournament.system.TournamentPlayer;
import forge.gui.FThreads;
import forge.gui.GuiBase;
import forge.gui.interfaces.IGuiGame;
import forge.gui.util.SGuiChoose;
@@ -406,10 +407,15 @@ public class QuestTournamentController {
drafting = true;
final BoosterDraft draft = draftEvent.enter();
FThreads.invokeInEdtLater(new Runnable() {
@Override
public void run() {
view.startDraft(draft);
}
});
}
});
}
public boolean cancelDraft() {
if (SOptionPane.showConfirmDialog(localizer.getMessage("lblLeaveDraftConfirm"), localizer.getMessage("lblLeaveDraft") + "?", localizer.getMessage("lblLeave"), localizer.getMessage("lblCancel"), false)) {

View File

@@ -102,8 +102,7 @@ public abstract class AchievementCollection implements Iterable<Achievement> {
add(new TotalMatchWins(100, 250, 500, 1000));
if (isLimitedFormat) { //make need for speed goal more realistic for limited formats
add(new NeedForSpeed(8, 6, 4, 2));
}
else {
} else {
add(new NeedForSpeed(5, 3, 1, 0));
}
add(new Overkill(-25, -50, -100, -200));
@@ -114,8 +113,7 @@ public abstract class AchievementCollection implements Iterable<Achievement> {
add(new ManaScrewed());
if (isLimitedFormat) { //lower gold and mythic thresholds based on smaller decks
add(new ManaFlooded(8, 11, 14, 17));
}
else {
} else {
add(new ManaFlooded(8, 12, 18, 24));
}
add(new RagsToRiches());