Merge remote-tracking branch 'upstream/master' into display_localization_cardname
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<artifactId>forge</artifactId>
|
||||
<groupId>forge</groupId>
|
||||
<version>1.6.33-SNAPSHOT</version>
|
||||
<version>1.6.34-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>forge-ai</artifactId>
|
||||
|
||||
@@ -91,9 +91,6 @@ public class ComputerUtil {
|
||||
}
|
||||
}
|
||||
|
||||
source.setCastSA(sa);
|
||||
sa.setLastStateBattlefield(game.getLastStateBattlefield());
|
||||
sa.setLastStateGraveyard(game.getLastStateGraveyard());
|
||||
sa.setHostCard(game.getAction().moveToStack(source, sa));
|
||||
}
|
||||
|
||||
@@ -219,9 +216,6 @@ public class ComputerUtil {
|
||||
|
||||
final Card source = sa.getHostCard();
|
||||
if (sa.isSpell() && !source.isCopiedSpell()) {
|
||||
source.setCastSA(sa);
|
||||
sa.setLastStateBattlefield(game.getLastStateBattlefield());
|
||||
sa.setLastStateGraveyard(game.getLastStateGraveyard());
|
||||
sa.setHostCard(game.getAction().moveToStack(source, sa));
|
||||
}
|
||||
|
||||
@@ -246,9 +240,6 @@ public class ComputerUtil {
|
||||
|
||||
final Card source = sa.getHostCard();
|
||||
if (sa.isSpell() && !source.isCopiedSpell()) {
|
||||
source.setCastSA(sa);
|
||||
sa.setLastStateBattlefield(game.getLastStateBattlefield());
|
||||
sa.setLastStateGraveyard(game.getLastStateGraveyard());
|
||||
sa.setHostCard(game.getAction().moveToStack(source, sa));
|
||||
}
|
||||
|
||||
@@ -267,9 +258,6 @@ public class ComputerUtil {
|
||||
|
||||
final Card source = newSA.getHostCard();
|
||||
if (newSA.isSpell() && !source.isCopiedSpell()) {
|
||||
source.setCastSA(newSA);
|
||||
sa.setLastStateBattlefield(game.getLastStateBattlefield());
|
||||
sa.setLastStateGraveyard(game.getLastStateGraveyard());
|
||||
newSA.setHostCard(game.getAction().moveToStack(source, sa));
|
||||
|
||||
if (newSA.getApi() == ApiType.Charm && !newSA.isWrapper()) {
|
||||
@@ -290,9 +278,6 @@ public class ComputerUtil {
|
||||
if (ComputerUtilCost.canPayCost(sa, ai)) {
|
||||
final Card source = sa.getHostCard();
|
||||
if (sa.isSpell() && !source.isCopiedSpell()) {
|
||||
source.setCastSA(sa);
|
||||
sa.setLastStateBattlefield(game.getLastStateBattlefield());
|
||||
sa.setLastStateGraveyard(game.getLastStateGraveyard());
|
||||
sa.setHostCard(game.getAction().moveToStack(source, sa));
|
||||
}
|
||||
|
||||
@@ -2338,7 +2323,7 @@ public class ComputerUtil {
|
||||
return chosen;
|
||||
}
|
||||
|
||||
public static Object vote(Player ai, List<Object> options, SpellAbility sa, Multimap<Object, Player> votes) {
|
||||
public static Object vote(Player ai, List<Object> options, SpellAbility sa, Multimap<Object, Player> votes, Player forPlayer) {
|
||||
final Card source = sa.getHostCard();
|
||||
final Player controller = source.getController();
|
||||
final Game game = controller.getGame();
|
||||
|
||||
@@ -1447,7 +1447,8 @@ public class ComputerUtilCard {
|
||||
}
|
||||
if (pumpedDmg > dmg) {
|
||||
if ((!c.hasKeyword(Keyword.INFECT) && pumpedDmg >= opp.getLife())
|
||||
|| (c.hasKeyword(Keyword.INFECT) && opp.canReceiveCounters(CounterType.POISON) && pumpedDmg >= opp.getPoisonCounters())) {
|
||||
|| (c.hasKeyword(Keyword.INFECT) && opp.canReceiveCounters(CounterType.POISON) && pumpedDmg >= opp.getPoisonCounters())
|
||||
|| ("PumpForTrample".equals(sa.getParam("AILogic")))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -981,14 +981,27 @@ public abstract class GameState {
|
||||
spellDef = spellDef.substring(0, spellDef.indexOf("->")).trim();
|
||||
}
|
||||
|
||||
PaperCard pc = StaticData.instance().getCommonCards().getCard(spellDef);
|
||||
Card c = null;
|
||||
|
||||
if (pc == null) {
|
||||
System.err.println("ERROR: Could not find a card with name " + spellDef + " to precast!");
|
||||
return;
|
||||
if (StringUtils.isNumeric(spellDef)) {
|
||||
// Precast from a specific host
|
||||
c = idToCard.get(Integer.parseInt(spellDef));
|
||||
if (c == null) {
|
||||
System.err.println("ERROR: Could not find a card with ID " + spellDef + " to precast!");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// Precast from a card by name
|
||||
PaperCard pc = StaticData.instance().getCommonCards().getCard(spellDef);
|
||||
|
||||
if (pc == null) {
|
||||
System.err.println("ERROR: Could not find a card with name " + spellDef + " to precast!");
|
||||
return;
|
||||
}
|
||||
|
||||
c = Card.fromPaperCard(pc, activator);
|
||||
}
|
||||
|
||||
Card c = Card.fromPaperCard(pc, activator);
|
||||
SpellAbility sa = null;
|
||||
|
||||
if (!scriptID.isEmpty()) {
|
||||
|
||||
@@ -513,8 +513,8 @@ public class PlayerControllerAi extends PlayerController {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object vote(SpellAbility sa, String prompt, List<Object> options, ListMultimap<Object, Player> votes) {
|
||||
return ComputerUtil.vote(player, options, sa, votes);
|
||||
public Object vote(SpellAbility sa, String prompt, List<Object> options, ListMultimap<Object, Player> votes, Player forPlayer) {
|
||||
return ComputerUtil.vote(player, options, sa, votes, forPlayer);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1295,6 +1295,26 @@ public class SpecialCardAi {
|
||||
}
|
||||
}
|
||||
|
||||
// Timmerian Fiends
|
||||
public static class TimmerianFiends {
|
||||
public static boolean consider(final Player ai, final SpellAbility sa) {
|
||||
final Card targeted = sa.getParentTargetingCard().getTargetCard();
|
||||
if (targeted == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (targeted.isCreature()) {
|
||||
if (ComputerUtil.aiLifeInDanger(ai, true, 0)) {
|
||||
return true; // do it, hoping to save a valuable potential blocker etc.
|
||||
}
|
||||
return ComputerUtilCard.evaluateCreature(targeted) >= 200; // might need tweaking
|
||||
} else {
|
||||
// TODO: this currently compares purely by CMC. To be somehow improved, especially for stuff like the Power Nine etc.
|
||||
return ComputerUtilCard.evaluatePermanentList(new CardCollection(targeted)) >= 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Volrath's Shapeshifter
|
||||
public static class VolrathsShapeshifter {
|
||||
public static boolean consider(final Player ai, final SpellAbility sa) {
|
||||
|
||||
@@ -34,12 +34,12 @@ import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.player.PlayerCollection;
|
||||
import forge.game.player.PlayerPredicates;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.spellability.TargetRestrictions;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.Aggregates;
|
||||
import forge.util.collect.FCollectionView;
|
||||
|
||||
|
||||
//AB:GainControl|ValidTgts$Creature|TgtPrompt$Select target legendary creature|LoseControl$Untap,LoseControl|SpellDescription$Gain control of target xxxxxxx
|
||||
@@ -54,8 +54,6 @@ import forge.util.collect.FCollectionView;
|
||||
// (as a "&"-separated list; like Haste, Sacrifice CARDNAME at EOT, any standard keyword)
|
||||
// OppChoice - set to True if opponent chooses creature (for Preacher) - not implemented yet
|
||||
// Untap - set to True if target card should untap when control is taken
|
||||
// DestroyTgt - actions upon which the tgt should be destroyed. same list as LoseControl
|
||||
// NoRegen - set if destroyed creature can't be regenerated. used only with DestroyTgt
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@@ -77,7 +75,7 @@ public class ControlGainAi extends SpellAbilityAi {
|
||||
|
||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||
final Game game = ai.getGame();
|
||||
final FCollectionView<Player> opponents = ai.getOpponents();
|
||||
final PlayerCollection opponents = ai.getOpponents();
|
||||
|
||||
// if Defined, then don't worry about targeting
|
||||
if (tgt == null) {
|
||||
@@ -94,18 +92,19 @@ public class ControlGainAi extends SpellAbilityAi {
|
||||
sa.setTargetingPlayer(targetingPlayer);
|
||||
return targetingPlayer.getController().chooseTargetsFor(sa);
|
||||
}
|
||||
if (tgt.isRandomTarget()) {
|
||||
sa.getTargets().add(Aggregates.random(tgt.getAllCandidates(sa, false)));
|
||||
}
|
||||
|
||||
if (tgt.canOnlyTgtOpponent()) {
|
||||
List<Player> oppList = Lists
|
||||
.newArrayList(Iterables.filter(opponents, PlayerPredicates.isTargetableBy(sa)));
|
||||
List<Player> oppList = opponents.filter(PlayerPredicates.isTargetableBy(sa));
|
||||
|
||||
if (oppList.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
sa.getTargets().add(oppList.get(0));
|
||||
if (tgt.isRandomTarget()) {
|
||||
sa.getTargets().add(Aggregates.random(oppList));
|
||||
} else {
|
||||
sa.getTargets().add(oppList.get(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import forge.card.mana.ManaCost;
|
||||
import forge.game.Game;
|
||||
import forge.game.ability.ApiType;
|
||||
import forge.game.card.*;
|
||||
import forge.game.combat.Combat;
|
||||
import forge.game.combat.CombatUtil;
|
||||
import forge.game.cost.Cost;
|
||||
import forge.game.keyword.Keyword;
|
||||
@@ -307,6 +308,16 @@ public class EffectAi extends SpellAbilityAi {
|
||||
if (!ComputerUtil.targetPlayableSpellCard(ai, list, sa, false)) {
|
||||
return false;
|
||||
}
|
||||
} else if (logic.equals("Bribe")) {
|
||||
Card host = sa.getHostCard();
|
||||
Combat combat = game.getCombat();
|
||||
if (combat != null && combat.isAttacking(host, ai) && !combat.isBlocked(host)
|
||||
&& game.getPhaseHandler().is(PhaseType.COMBAT_DECLARE_BLOCKERS)
|
||||
&& !AiCardMemory.isRememberedCard(ai, host, AiCardMemory.MemorySet.ACTIVATED_THIS_TURN)) {
|
||||
AiCardMemory.rememberCard(ai, host, AiCardMemory.MemorySet.ACTIVATED_THIS_TURN); // ideally needs once per combat or something
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} else { //no AILogic
|
||||
return false;
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
package forge.ai.ability;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import forge.ai.ComputerUtil;
|
||||
import forge.ai.ComputerUtilMana;
|
||||
import forge.ai.SpecialCardAi;
|
||||
import forge.ai.SpellAbilityAi;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.card.Card;
|
||||
@@ -24,6 +19,11 @@ import forge.game.spellability.SpellAbility;
|
||||
import forge.game.spellability.TargetRestrictions;
|
||||
import forge.game.zone.ZoneType;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class MillAi extends SpellAbilityAi {
|
||||
|
||||
@Override
|
||||
@@ -196,6 +196,10 @@ public class MillAi extends SpellAbilityAi {
|
||||
*/
|
||||
@Override
|
||||
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
||||
if ("TimmerianFiends".equals(sa.getParam("AILogic"))) {
|
||||
return SpecialCardAi.TimmerianFiends.consider(player, sa);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package forge.ai.ability;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import forge.ai.*;
|
||||
import forge.game.Game;
|
||||
@@ -12,11 +12,9 @@ import forge.game.card.CardPredicates;
|
||||
import forge.game.combat.Combat;
|
||||
import forge.game.combat.CombatUtil;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.phase.PhaseHandler;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.spellability.TargetRestrictions;
|
||||
import forge.game.zone.ZoneType;
|
||||
|
||||
import java.util.List;
|
||||
@@ -28,7 +26,6 @@ public class MustBlockAi extends SpellAbilityAi {
|
||||
final Card source = sa.getHostCard();
|
||||
final Game game = aiPlayer.getGame();
|
||||
final Combat combat = game.getCombat();
|
||||
final PhaseHandler ph = game.getPhaseHandler();
|
||||
final boolean onlyLethal = !"AllowNonLethal".equals(sa.getParam("AILogic"));
|
||||
|
||||
if (combat == null || !combat.isAttacking(source)) {
|
||||
@@ -39,7 +36,6 @@ public class MustBlockAi extends SpellAbilityAi {
|
||||
return false;
|
||||
}
|
||||
|
||||
final TargetRestrictions abTgt = sa.getTargetRestrictions();
|
||||
final List<Card> list = determineGoodBlockers(source, aiPlayer, combat.getDefenderPlayerByAttacker(source), sa, onlyLethal,false);
|
||||
|
||||
if (!list.isEmpty()) {
|
||||
@@ -69,7 +65,6 @@ public class MustBlockAi extends SpellAbilityAi {
|
||||
@Override
|
||||
protected boolean doTriggerAINoCost(final Player ai, SpellAbility sa, boolean mandatory) {
|
||||
final Card source = sa.getHostCard();
|
||||
final TargetRestrictions abTgt = sa.getTargetRestrictions();
|
||||
|
||||
// only use on creatures that can attack
|
||||
if (!ai.getGame().getPhaseHandler().getPhase().isBefore(PhaseType.MAIN2)) {
|
||||
@@ -94,7 +89,7 @@ public class MustBlockAi extends SpellAbilityAi {
|
||||
|
||||
boolean chance = false;
|
||||
|
||||
if (abTgt != null) {
|
||||
if (sa.usesTargeting()) {
|
||||
final List<Card> list = determineGoodBlockers(definedAttacker, ai, ai.getWeakestOpponent(), sa, true,true);
|
||||
if (list.isEmpty()) {
|
||||
return false;
|
||||
@@ -119,6 +114,9 @@ public class MustBlockAi extends SpellAbilityAi {
|
||||
|
||||
sa.getTargets().add(blocker);
|
||||
chance = true;
|
||||
} else if (sa.hasParam("Choices")) {
|
||||
// currently choice is attacked player
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
@@ -126,16 +124,9 @@ public class MustBlockAi extends SpellAbilityAi {
|
||||
return chance;
|
||||
}
|
||||
|
||||
private List<Card> determineGoodBlockers(final Card attacker, final Player ai, Player defender, SpellAbility sa,
|
||||
private List<Card> determineBlockerFromList(final Card attacker, final Player ai, Iterable<Card> options, SpellAbility sa,
|
||||
final boolean onlyLethal, final boolean testTapped) {
|
||||
final Card source = sa.getHostCard();
|
||||
final TargetRestrictions abTgt = sa.getTargetRestrictions();
|
||||
|
||||
List<Card> list = Lists.newArrayList();
|
||||
list = CardLists.filter(defender.getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.CREATURES);
|
||||
list = CardLists.getTargetableCards(list, sa);
|
||||
list = CardLists.getValidCards(list, abTgt.getValidTgts(), source.getController(), source, sa);
|
||||
list = CardLists.filter(list, new Predicate<Card>() {
|
||||
List<Card> list = CardLists.filter(options, new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(final Card c) {
|
||||
boolean tapped = c.isTapped();
|
||||
@@ -161,4 +152,40 @@ public class MustBlockAi extends SpellAbilityAi {
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
private List<Card> determineGoodBlockers(final Card attacker, final Player ai, Player defender, SpellAbility sa,
|
||||
final boolean onlyLethal, final boolean testTapped) {
|
||||
|
||||
List<Card> list = Lists.newArrayList();
|
||||
list = CardLists.filter(defender.getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.CREATURES);
|
||||
|
||||
if (sa.usesTargeting()) {
|
||||
list = CardLists.getTargetableCards(list, sa);
|
||||
}
|
||||
return determineBlockerFromList(attacker, ai, list, sa, onlyLethal, testTapped);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional,
|
||||
Player targetedPlayer) {
|
||||
final Card host = sa.getHostCard();
|
||||
|
||||
Card attacker = host;
|
||||
|
||||
if (sa.hasParam("DefinedAttacker")) {
|
||||
List<Card> attackers = AbilityUtils.getDefinedCards(host, sa.getParam("DefinedAttacker"), sa);
|
||||
attacker = Iterables.getFirst(attackers, null);
|
||||
}
|
||||
if (attacker == null) {
|
||||
return Iterables.getFirst(options, null);
|
||||
}
|
||||
|
||||
List<Card> better = determineBlockerFromList(attacker, ai, options, sa, false, false);
|
||||
|
||||
if (!better.isEmpty()) {
|
||||
return Iterables.getFirst(options, null);
|
||||
}
|
||||
|
||||
return Iterables.getFirst(options, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,6 +46,12 @@ public class VoteAi extends SpellAbilityAi {
|
||||
|
||||
@Override
|
||||
public int chooseNumber(Player player, SpellAbility sa, int min, int max, Map<String, Object> params) {
|
||||
if (params.containsKey("Voter")) {
|
||||
Player p = (Player)params.get("Voter");
|
||||
if (p.isOpponentOf(player)) {
|
||||
return min;
|
||||
}
|
||||
}
|
||||
if (sa.getActivatingPlayer().isOpponentOf(player)) {
|
||||
return min;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<artifactId>forge</artifactId>
|
||||
<groupId>forge</groupId>
|
||||
<version>1.6.33-SNAPSHOT</version>
|
||||
<version>1.6.34-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>forge-core</artifactId>
|
||||
|
||||
@@ -113,7 +113,11 @@ public final class ImageKeys {
|
||||
}
|
||||
//try fullborder...
|
||||
if (filename.contains(".full")) {
|
||||
file = findFile(dir, TextUtil.fastReplace(filename, ".full", ".fullborder"));
|
||||
String fullborderFile = TextUtil.fastReplace(filename, ".full", ".fullborder");
|
||||
file = findFile(dir, fullborderFile);
|
||||
if (file != null) { return file; }
|
||||
// if there's an art variant try without it
|
||||
file = findFile(dir, TextUtil.fastReplace(fullborderFile, "1.fullborder", ".fullborder"));
|
||||
if (file != null) { return file; }
|
||||
}
|
||||
//if an image, like phenomenon or planes is missing .full in their filenames but you have an existing images that have .full/.fullborder
|
||||
|
||||
@@ -312,17 +312,21 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
|
||||
return tryGetCard(request);
|
||||
}
|
||||
|
||||
public int getCardCollectorNumber(String cardName, String reqEdition) {
|
||||
public String getCardCollectorNumber(String cardName, String reqEdition, int artIndex) {
|
||||
cardName = getName(cardName);
|
||||
CardEdition edition = editions.get(reqEdition);
|
||||
if (edition == null)
|
||||
return -1;
|
||||
return null;
|
||||
int numMatches = 0;
|
||||
for (CardInSet card : edition.getCards()) {
|
||||
if (card.name.equalsIgnoreCase(cardName)) {
|
||||
return card.collectorNumber;
|
||||
numMatches += 1;
|
||||
if (numMatches == artIndex) {
|
||||
return card.collectorNumber;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
return null;
|
||||
}
|
||||
|
||||
private PaperCard tryGetCard(CardRequest request) {
|
||||
|
||||
@@ -38,6 +38,8 @@ import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
||||
/**
|
||||
@@ -75,10 +77,10 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
|
||||
|
||||
public static class CardInSet {
|
||||
public final CardRarity rarity;
|
||||
public final int collectorNumber;
|
||||
public final String collectorNumber;
|
||||
public final String name;
|
||||
|
||||
public CardInSet(final String name, final int collectorNumber, final CardRarity rarity) {
|
||||
public CardInSet(final String name, final String collectorNumber, final CardRarity rarity) {
|
||||
this.name = name;
|
||||
this.collectorNumber = collectorNumber;
|
||||
this.rarity = rarity;
|
||||
@@ -86,7 +88,7 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
|
||||
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (collectorNumber != -1) {
|
||||
if (collectorNumber != null) {
|
||||
sb.append(collectorNumber);
|
||||
sb.append(' ');
|
||||
}
|
||||
@@ -190,6 +192,7 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
|
||||
public boolean getSmallSetOverride() { return smallSetOverride; }
|
||||
public String getBoosterMustContain() { return boosterMustContain; }
|
||||
public CardInSet[] getCards() { return cards; }
|
||||
public boolean isModern() { return getDate().after(parseDate("2003-07-27")); } //8ED and above are modern except some promo cards and others
|
||||
|
||||
public Map<String, Integer> getTokens() { return tokenNormalized; }
|
||||
|
||||
@@ -266,24 +269,33 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
|
||||
Map<String, Integer> tokenNormalized = new HashMap<>();
|
||||
List<CardEdition.CardInSet> processedCards = new ArrayList<>();
|
||||
if (contents.containsKey("cards")) {
|
||||
final Pattern pattern = Pattern.compile(
|
||||
/*
|
||||
The following pattern will match the WAR Japanese art entries,
|
||||
it should also match the Un-set and older alternate art cards
|
||||
like Merseine from FEM (should the editions files ever be updated)
|
||||
*/
|
||||
//"(^(?<cnum>[0-9]+.?) )?((?<rarity>[SCURML]) )?(?<name>.*)$"
|
||||
/* Ideally we'd use the named group above, but Android 6 and
|
||||
earlier don't appear to support named groups.
|
||||
So, untill support for those devices is officially dropped,
|
||||
we'll have to suffice with numbered groups.
|
||||
We are looking for:
|
||||
* cnum - grouping #2
|
||||
* rarity - grouping #4
|
||||
* name - grouping #5
|
||||
*/
|
||||
"(^([0-9]+.?) )?(([SCURML]) )?(.*)$"
|
||||
);
|
||||
for(String line : contents.get("cards")) {
|
||||
if (StringUtils.isBlank(line))
|
||||
continue;
|
||||
|
||||
// Optional collector number at the start.
|
||||
String[] split = line.split(" ", 2);
|
||||
int collectorNumber = -1;
|
||||
if (split.length >= 2 && StringUtils.isNumeric(split[0])) {
|
||||
collectorNumber = Integer.parseInt(split[0]);
|
||||
line = split[1];
|
||||
Matcher matcher = pattern.matcher(line);
|
||||
if (matcher.matches()) {
|
||||
String collectorNumber = matcher.group(2);
|
||||
CardRarity r = CardRarity.smartValueOf(matcher.group(4));
|
||||
String cardName = matcher.group(5);
|
||||
CardInSet cis = new CardInSet(cardName, collectorNumber, r);
|
||||
processedCards.add(cis);
|
||||
}
|
||||
|
||||
// You may omit rarity for early development
|
||||
CardRarity r = CardRarity.smartValueOf(line.substring(0, 1));
|
||||
boolean hadRarity = r != CardRarity.Unknown && line.charAt(1) == ' ';
|
||||
String cardName = hadRarity ? line.substring(2) : line;
|
||||
CardInSet cis = new CardInSet(cardName, collectorNumber, r);
|
||||
processedCards.add(cis);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -222,7 +222,12 @@ public final class CardRules implements ICardCharacteristics {
|
||||
|
||||
public boolean canBeBrawlCommander() {
|
||||
CardType type = mainPart.getType();
|
||||
return (type.isLegendary() && type.isCreature()) || type.isPlaneswalker();
|
||||
return type.isLegendary() && (type.isCreature() || type.isPlaneswalker());
|
||||
}
|
||||
|
||||
public boolean canBeTinyLeadersCommander() {
|
||||
CardType type = mainPart.getType();
|
||||
return type.isLegendary() && (type.isCreature() || type.isPlaneswalker());
|
||||
}
|
||||
|
||||
public String getMeldWith() {
|
||||
|
||||
@@ -595,8 +595,10 @@ public final class CardRulesPredicates {
|
||||
public static final Predicate<CardRules> IS_VANGUARD = CardRulesPredicates.coreType(true, CardType.CoreType.Vanguard);
|
||||
public static final Predicate<CardRules> IS_CONSPIRACY = CardRulesPredicates.coreType(true, CardType.CoreType.Conspiracy);
|
||||
public static final Predicate<CardRules> IS_NON_LAND = CardRulesPredicates.coreType(false, CardType.CoreType.Land);
|
||||
public static final Predicate<CardRules> CAN_BE_BRAWL_COMMANDER = Predicates.or(Presets.IS_PLANESWALKER,
|
||||
Predicates.and(Presets.IS_CREATURE, Presets.IS_LEGENDARY));
|
||||
public static final Predicate<CardRules> CAN_BE_BRAWL_COMMANDER = Predicates.and(Presets.IS_LEGENDARY,
|
||||
Predicates.or(Presets.IS_CREATURE, Presets.IS_PLANESWALKER));
|
||||
public static final Predicate<CardRules> CAN_BE_TINY_LEADERS_COMMANDER = Predicates.and(Presets.IS_LEGENDARY,
|
||||
Predicates.or(Presets.IS_CREATURE, Presets.IS_PLANESWALKER));
|
||||
|
||||
/** The Constant IS_NON_CREATURE_SPELL. **/
|
||||
public static final Predicate<CardRules> IS_NON_CREATURE_SPELL = com.google.common.base.Predicates
|
||||
|
||||
@@ -73,7 +73,7 @@ public enum DeckFormat {
|
||||
private final Set<String> bannedCards = ImmutableSet.of(
|
||||
"Ancestral Recall", "Balance", "Black Lotus", "Black Vise", "Channel", "Chaos Orb", "Contract From Below", "Counterbalance", "Darkpact", "Demonic Attorney", "Demonic Tutor", "Earthcraft", "Edric, Spymaster of Trest", "Falling Star",
|
||||
"Fastbond", "Flash", "Goblin Recruiter", "Grindstone", "Hermit Druid", "Imperial Seal", "Jeweled Bird", "Karakas", "Library of Alexandria", "Mana Crypt", "Mana Drain", "Mana Vault", "Metalworker", "Mind Twist", "Mishra's Workshop",
|
||||
"Mox Emerald", "Mox Jet", "Mox Pearl", "Mox Ruby", "Mox Sapphire", "Necropotence", "Shahrazad", "Skullclamp", "Sol Ring", "Strip Mine", "Survival of the Fittest", "Sword of Body and Mind", "Time Vault", "Time Walk", "Timetwister",
|
||||
"Mox Emerald", "Mox Jet", "Mox Pearl", "Mox Ruby", "Mox Sapphire", "Najeela, the Blade Blossom", "Necropotence", "Shahrazad", "Skullclamp", "Sol Ring", "Strip Mine", "Survival of the Fittest", "Sword of Body and Mind", "Time Vault", "Time Walk", "Timetwister",
|
||||
"Timmerian Fiends", "Tolarian Academy", "Umezawa's Jitte", "Vampiric Tutor", "Wheel of Fortune", "Yawgmoth's Will");
|
||||
|
||||
@Override
|
||||
@@ -463,6 +463,9 @@ public enum DeckFormat {
|
||||
if (this.equals(DeckFormat.Brawl)) {
|
||||
return rules.canBeBrawlCommander();
|
||||
}
|
||||
if (this.equals(DeckFormat.TinyLeaders)) {
|
||||
return rules.canBeTinyLeadersCommander();
|
||||
}
|
||||
return rules.canBeCommander();
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import forge.card.CardRarity;
|
||||
import forge.card.CardRules;
|
||||
import forge.card.CardType.CoreType;
|
||||
import forge.card.MagicColor;
|
||||
import forge.util.PredicateCard;
|
||||
import forge.util.PredicateString;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@@ -60,6 +61,8 @@ public interface IPaperCard extends InventoryItem {
|
||||
return new PredicateNames(what);
|
||||
}
|
||||
|
||||
public static PredicateCards cards(final List<PaperCard> what) { return new PredicateCards(what); }
|
||||
|
||||
private static final class PredicateColor implements Predicate<PaperCard> {
|
||||
|
||||
private final byte operand;
|
||||
@@ -161,6 +164,25 @@ public interface IPaperCard extends InventoryItem {
|
||||
}
|
||||
}
|
||||
|
||||
private static final class PredicateCards extends PredicateCard<PaperCard> {
|
||||
private final List<PaperCard> operand;
|
||||
|
||||
@Override
|
||||
public boolean apply(final PaperCard card) {
|
||||
for (final PaperCard element : this.operand) {
|
||||
if (this.op(card, element)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private PredicateCards(final List<PaperCard> operand) {
|
||||
super(StringOp.EQUALS);
|
||||
this.operand = operand;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pre-built predicates are stored here to allow their re-usage and
|
||||
* easier access from code.
|
||||
|
||||
@@ -566,12 +566,8 @@ public class BoosterGenerator {
|
||||
toAdd = IPaperCard.Predicates.printedInSets(sets);
|
||||
} else if (operator.startsWith("fromSheet(") && invert) {
|
||||
String sheetName = StringUtils.strip(operator.substring(9), "()\" ");
|
||||
Iterable<PaperCard> src = StaticData.instance().getPrintSheets().get(sheetName).toFlatList();
|
||||
List<String> cardNames = Lists.newArrayList();
|
||||
for (PaperCard card : src) {
|
||||
cardNames.add(card.getName());
|
||||
}
|
||||
toAdd = IPaperCard.Predicates.names(Lists.newArrayList(cardNames));
|
||||
Iterable<PaperCard> cards = StaticData.instance().getPrintSheets().get(sheetName).toFlatList();
|
||||
toAdd = IPaperCard.Predicates.cards(Lists.newArrayList(cards));
|
||||
}
|
||||
|
||||
if (toAdd == null) {
|
||||
|
||||
83
forge-core/src/main/java/forge/util/PredicateCard.java
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Forge: Play Magic: the Gathering.
|
||||
* Copyright (C) 2020 Jamin W. Collins
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package forge.util;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import forge.item.PaperCard;
|
||||
|
||||
/**
|
||||
* Special predicate class to perform string operations.
|
||||
*
|
||||
* @param <T>
|
||||
* the generic type
|
||||
*/
|
||||
public abstract class PredicateCard<T> implements Predicate<T> {
|
||||
/** Possible operators for string operands. */
|
||||
public enum StringOp {
|
||||
/** The EQUALS. */
|
||||
EQUALS,
|
||||
}
|
||||
|
||||
/** The operator. */
|
||||
private final StringOp operator;
|
||||
|
||||
/**
|
||||
* Op.
|
||||
*
|
||||
* @param op1
|
||||
* the op1
|
||||
* @param op2
|
||||
* the op2
|
||||
* @return true, if successful
|
||||
*/
|
||||
protected final boolean op(final PaperCard op1, final PaperCard op2) {
|
||||
switch (this.getOperator()) {
|
||||
case EQUALS:
|
||||
return op1.equals(op2);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new predicate string.
|
||||
*
|
||||
* @param operator
|
||||
* the operator
|
||||
*/
|
||||
public PredicateCard(final StringOp operator) {
|
||||
this.operator = operator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the operator
|
||||
*/
|
||||
public StringOp getOperator() {
|
||||
return operator;
|
||||
}
|
||||
|
||||
public static PredicateCard<PaperCard> equals(final PaperCard what) {
|
||||
return new PredicateCard<PaperCard>(StringOp.EQUALS) {
|
||||
@Override
|
||||
public boolean apply(PaperCard subject) {
|
||||
return op(subject, what);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,6 +5,8 @@ import forge.item.PaperCard;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
@@ -17,6 +19,22 @@ import java.util.Map.Entry;
|
||||
*/
|
||||
public class TextUtil {
|
||||
|
||||
static ImmutableSortedMap<Integer,String> romanMap = ImmutableSortedMap.<Integer,String>naturalOrder()
|
||||
.put(1000, "M").put(900, "CM")
|
||||
.put(500, "D").put(400, "CD")
|
||||
.put(100, "C").put(90, "XC")
|
||||
.put(50, "L").put(40, "XL")
|
||||
.put(10, "X").put(9, "IX")
|
||||
.put(5, "V").put(4, "IV").put(1, "I").build();
|
||||
|
||||
public final static String toRoman(int number) {
|
||||
if (number <= 0) {
|
||||
return "";
|
||||
}
|
||||
int l = romanMap.floorKey(number);
|
||||
return romanMap.get(l) + toRoman(number-l);
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely converts an object to a String.
|
||||
*
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<artifactId>forge</artifactId>
|
||||
<groupId>forge</groupId>
|
||||
<version>1.6.33-SNAPSHOT</version>
|
||||
<version>1.6.34-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>forge-game</artifactId>
|
||||
|
||||
@@ -914,4 +914,17 @@ public class Game {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public Player getControlVote() {
|
||||
Player result = null;
|
||||
long maxValue = 0;
|
||||
for (Player p : getPlayers()) {
|
||||
Long v = p.getHighestControlVote();
|
||||
if (v != null && v > maxValue) {
|
||||
maxValue = v;
|
||||
result = p;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -375,7 +375,7 @@ public class GameAction {
|
||||
// the LKI needs to be the Card itself,
|
||||
// or it might not updated correctly
|
||||
// TODO be reworked when ZoneTrigger Update is done
|
||||
if (toBattlefield) {
|
||||
if (toBattlefield || zoneTo.is(ZoneType.Stack)) {
|
||||
lastKnownInfo = c;
|
||||
}
|
||||
|
||||
@@ -547,6 +547,13 @@ public class GameAction {
|
||||
c.setCastSA(null);
|
||||
} else if (zoneTo.is(ZoneType.Stack)) {
|
||||
c.setCastFrom(zoneFrom.getZoneType());
|
||||
if (cause != null && cause.isSpell() && c.equals(cause.getHostCard()) && !c.isCopiedSpell()) {
|
||||
cause.setLastStateBattlefield(game.getLastStateBattlefield());
|
||||
cause.setLastStateGraveyard(game.getLastStateGraveyard());
|
||||
c.setCastSA(cause);
|
||||
} else {
|
||||
c.setCastSA(null);
|
||||
}
|
||||
} else if (!(zoneTo.is(ZoneType.Battlefield) && zoneFrom.is(ZoneType.Stack))) {
|
||||
c.setCastFrom(null);
|
||||
c.setCastSA(null);
|
||||
@@ -565,16 +572,31 @@ public class GameAction {
|
||||
public final void controllerChangeZoneCorrection(final Card c) {
|
||||
System.out.println("Correcting zone for " + c.toString());
|
||||
final Zone oldBattlefield = game.getZoneOf(c);
|
||||
if (oldBattlefield == null || oldBattlefield.getZoneType() == ZoneType.Stack) {
|
||||
|
||||
if (oldBattlefield == null || oldBattlefield.is(ZoneType.Stack)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Player original = oldBattlefield.getPlayer();
|
||||
final PlayerZone newBattlefield = c.getController().getZone(oldBattlefield.getZoneType());
|
||||
final Player controller = c.getController();
|
||||
if (original == null || controller == null || original.equals(controller)) {
|
||||
return;
|
||||
}
|
||||
final PlayerZone newBattlefield = controller.getZone(oldBattlefield.getZoneType());
|
||||
|
||||
if (newBattlefield == null || oldBattlefield.equals(newBattlefield)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 702.94e A paired creature becomes unpaired if any of the following occur:
|
||||
// another player gains control of it or the creature it’s paired with
|
||||
if (c.isPaired()) {
|
||||
Card partner = c.getPairedWith();
|
||||
c.setPairedWith(null);
|
||||
partner.setPairedWith(null);
|
||||
partner.updateStateForView();
|
||||
}
|
||||
|
||||
game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);
|
||||
for (Player p : game.getPlayers()) {
|
||||
((PlayerZoneBattlefield) p.getZone(ZoneType.Battlefield)).setTriggers(false);
|
||||
|
||||
@@ -22,9 +22,10 @@ import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import forge.card.CardStateName;
|
||||
import forge.card.MagicColor;
|
||||
import forge.card.mana.ManaCost;
|
||||
import forge.card.mana.ManaCostParser;
|
||||
import forge.game.ability.AbilityFactory;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.ApiType;
|
||||
import forge.game.card.*;
|
||||
@@ -33,9 +34,15 @@ import forge.game.cost.Cost;
|
||||
import forge.game.keyword.KeywordInterface;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.player.PlayerController;
|
||||
import forge.game.replacement.ReplacementEffect;
|
||||
import forge.game.replacement.ReplacementHandler;
|
||||
import forge.game.replacement.ReplacementLayer;
|
||||
import forge.game.spellability.*;
|
||||
import forge.game.trigger.Trigger;
|
||||
import forge.game.trigger.TriggerHandler;
|
||||
import forge.game.trigger.TriggerType;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.Lang;
|
||||
import forge.util.TextUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@@ -78,68 +85,9 @@ public final class GameActionUtil {
|
||||
if (sa.isSpell() && !source.isInZone(ZoneType.Battlefield)) {
|
||||
boolean lkicheck = false;
|
||||
|
||||
// need to be done before so it works with Vivien and Zoetic Cavern
|
||||
if (source.isFaceDown() && source.isInZone(ZoneType.Exile)) {
|
||||
if (!source.isLKI()) {
|
||||
source = CardUtil.getLKICopy(source);
|
||||
}
|
||||
|
||||
source.turnFaceUp(false, false);
|
||||
lkicheck = true;
|
||||
}
|
||||
|
||||
if (sa.isBestow() && !source.isBestowed() && !source.isInZone(ZoneType.Battlefield)) {
|
||||
if (!source.isLKI()) {
|
||||
source = CardUtil.getLKICopy(source);
|
||||
}
|
||||
|
||||
source.animateBestow(false);
|
||||
lkicheck = true;
|
||||
} else if (sa.isCastFaceDown()) {
|
||||
// need a copy of the card to turn facedown without trigger anything
|
||||
if (!source.isLKI()) {
|
||||
source = CardUtil.getLKICopy(source);
|
||||
}
|
||||
source.turnFaceDownNoUpdate();
|
||||
lkicheck = true;
|
||||
} else if (sa.isAdventure()) {
|
||||
if (!source.isLKI()) {
|
||||
source = CardUtil.getLKICopy(source);
|
||||
}
|
||||
|
||||
source.setState(CardStateName.Adventure, false);
|
||||
|
||||
// need to reset CMC
|
||||
source.setLKICMC(-1);
|
||||
source.setLKICMC(source.getCMC());
|
||||
lkicheck = true;
|
||||
} else if (source.isSplitCard() && (sa.isLeftSplit() || sa.isRightSplit())) {
|
||||
if (!source.isLKI()) {
|
||||
source = CardUtil.getLKICopy(source);
|
||||
}
|
||||
if (sa.isLeftSplit()) {
|
||||
if (!source.hasState(CardStateName.LeftSplit)) {
|
||||
source.addAlternateState(CardStateName.LeftSplit, false);
|
||||
source.getState(CardStateName.LeftSplit).copyFrom(
|
||||
sa.getHostCard().getState(CardStateName.LeftSplit), true);
|
||||
}
|
||||
|
||||
source.setState(CardStateName.LeftSplit, false);
|
||||
}
|
||||
|
||||
if (sa.isRightSplit()) {
|
||||
if (!source.hasState(CardStateName.RightSplit)) {
|
||||
source.addAlternateState(CardStateName.RightSplit, false);
|
||||
source.getState(CardStateName.RightSplit).copyFrom(
|
||||
sa.getHostCard().getState(CardStateName.RightSplit), true);
|
||||
}
|
||||
|
||||
source.setState(CardStateName.RightSplit, false);
|
||||
}
|
||||
|
||||
// need to reset CMC
|
||||
source.setLKICMC(-1);
|
||||
source.setLKICMC(source.getCMC());
|
||||
Card newHost = ((Spell)sa).getAlternateHost(source);
|
||||
if (newHost != null) {
|
||||
source = newHost;
|
||||
lkicheck = true;
|
||||
}
|
||||
|
||||
@@ -218,6 +166,7 @@ public final class GameActionUtil {
|
||||
final Cost escapeCost = new Cost(k[1], true);
|
||||
|
||||
final SpellAbility newSA = sa.copyWithDefinedCost(escapeCost);
|
||||
newSA.setActivatingPlayer(activator);
|
||||
|
||||
newSA.getMapParams().put("PrecostDesc", "Escape—");
|
||||
newSA.getMapParams().put("CostDesc", escapeCost.toString());
|
||||
@@ -276,6 +225,7 @@ public final class GameActionUtil {
|
||||
if (sa.isCycling() && activator.hasKeyword("CyclingForZero")) {
|
||||
// set the cost to this directly to buypass non mana cost
|
||||
final SpellAbility newSA = sa.copyWithDefinedCost("Discard<1/CARDNAME>");
|
||||
newSA.setActivatingPlayer(activator);
|
||||
newSA.setBasicSpell(false);
|
||||
newSA.getMapParams().put("CostDesc", ManaCostParser.parse("0"));
|
||||
// makes new SpellDescription
|
||||
@@ -421,10 +371,11 @@ public final class GameActionUtil {
|
||||
}
|
||||
SpellAbility result = null;
|
||||
final Card host = sa.getHostCard();
|
||||
final Game game = host.getGame();
|
||||
final Player activator = sa.getActivatingPlayer();
|
||||
final PlayerController pc = activator.getController();
|
||||
|
||||
host.getGame().getAction().checkStaticAbilities(false);
|
||||
game.getAction().checkStaticAbilities(false);
|
||||
|
||||
boolean reset = false;
|
||||
|
||||
@@ -487,7 +438,60 @@ public final class GameActionUtil {
|
||||
int v = pc.chooseNumberForKeywordCost(sa, cost, ki, str, Integer.MAX_VALUE);
|
||||
|
||||
if (v > 0) {
|
||||
host.addReplacementEffect(CardFactoryUtil.makeEtbCounter("etbCounter:P1P1:" + v, host, false));
|
||||
|
||||
final Card eff = new Card(game.nextCardId(), game);
|
||||
eff.setTimestamp(game.getNextTimestamp());
|
||||
eff.setName(c.getName() + "'s Effect");
|
||||
eff.addType("Effect");
|
||||
eff.setToken(true); // Set token to true, so when leaving play it gets nuked
|
||||
eff.setOwner(activator);
|
||||
|
||||
eff.setImageKey(c.getImageKey());
|
||||
eff.setColor(MagicColor.COLORLESS);
|
||||
eff.setImmutable(true);
|
||||
// try to get the SpellAbility from the mana ability
|
||||
//eff.setEffectSource((SpellAbility)null);
|
||||
|
||||
eff.addRemembered(host);
|
||||
|
||||
String abStr = "DB$ PutCounter | Defined$ ReplacedCard | CounterType$ P1P1 | ETB$ True | CounterNum$ " + v;
|
||||
|
||||
SpellAbility saAb = AbilityFactory.getAbility(abStr, c);
|
||||
|
||||
CardFactoryUtil.setupETBReplacementAbility(saAb);
|
||||
|
||||
String desc = "It enters the battlefield with ";
|
||||
desc += Lang.nounWithNumeral(v, CounterType.P1P1.getName() + " counter");
|
||||
desc += " on it.";
|
||||
|
||||
String repeffstr = "Event$ Moved | ValidCard$ Card.IsRemembered | Destination$ Battlefield | Description$ " + desc;
|
||||
|
||||
ReplacementEffect re = ReplacementHandler.parseReplacement(repeffstr, eff, true);
|
||||
re.setLayer(ReplacementLayer.Other);
|
||||
re.setOverridingAbility(saAb);
|
||||
|
||||
eff.addReplacementEffect(re);
|
||||
|
||||
// Forgot Trigger
|
||||
String trig = "Mode$ ChangesZone | ValidCard$ Card.IsRemembered | Origin$ Stack | Destination$ Any | TriggerZones$ Command | Static$ True";
|
||||
String forgetEffect = "DB$ Pump | ForgetObjects$ TriggeredCard";
|
||||
String exileEffect = "DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile"
|
||||
+ " | ConditionDefined$ Remembered | ConditionPresent$ Card | ConditionCompare$ EQ0";
|
||||
|
||||
SpellAbility saForget = AbilityFactory.getAbility(forgetEffect, eff);
|
||||
AbilitySub saExile = (AbilitySub) AbilityFactory.getAbility(exileEffect, eff);
|
||||
saForget.setSubAbility(saExile);
|
||||
|
||||
final Trigger parsedTrigger = TriggerHandler.parseTrigger(trig, eff, true);
|
||||
parsedTrigger.setOverridingAbility(saForget);
|
||||
eff.addTrigger(parsedTrigger);
|
||||
eff.updateStateForView();
|
||||
|
||||
// TODO: Add targeting to the effect so it knows who it's dealing with
|
||||
game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);
|
||||
game.getAction().moveTo(ZoneType.Command, eff, null);
|
||||
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
|
||||
|
||||
if (result == null) {
|
||||
result = sa.copy();
|
||||
}
|
||||
|
||||
@@ -177,7 +177,6 @@ public class StaticEffect {
|
||||
final CardCollectionView remove() {
|
||||
final CardCollectionView affectedCards = getAffectedCards();
|
||||
final List<Player> affectedPlayers = getAffectedPlayers();
|
||||
//final Map<String, String> params = getParams();
|
||||
|
||||
String changeColorWordsTo = null;
|
||||
|
||||
@@ -245,6 +244,10 @@ public class StaticEffect {
|
||||
|
||||
p.removeMaxLandPlays(getTimestamp());
|
||||
p.removeMaxLandPlaysInfinite(getTimestamp());
|
||||
|
||||
p.removeControlVote(getTimestamp());
|
||||
p.removeAdditionalVote(getTimestamp());
|
||||
p.removeAdditionalOptionalVote(getTimestamp());
|
||||
}
|
||||
|
||||
// modify the affected card
|
||||
|
||||
@@ -1318,9 +1318,16 @@ public class AbilityUtils {
|
||||
return;
|
||||
}
|
||||
|
||||
Player pl = sa.getActivatingPlayer();
|
||||
final Game game = pl.getGame();
|
||||
|
||||
if (sa.isTrigger() && sa.getParent() == null && sa.getPayCosts() != null) {
|
||||
// when trigger cost are paid before the effect does resolve, need to clean the trigger
|
||||
game.getTriggerHandler().resetActiveTriggers();
|
||||
}
|
||||
|
||||
// do blessing there before condition checks
|
||||
if (sa.isSpell() && sa.isBlessing() && !sa.getHostCard().isPermanent()) {
|
||||
Player pl = sa.getActivatingPlayer();
|
||||
if (pl != null && pl.getZone(ZoneType.Battlefield).size() >= 10) {
|
||||
pl.setBlessing(true);
|
||||
}
|
||||
@@ -1335,7 +1342,7 @@ public class AbilityUtils {
|
||||
return;
|
||||
}
|
||||
|
||||
AbilityUtils.resolveApiAbility(sa, sa.getActivatingPlayer().getGame());
|
||||
AbilityUtils.resolveApiAbility(sa, game);
|
||||
}
|
||||
|
||||
private static void resolveSubAbilities(final SpellAbility sa, final Game game) {
|
||||
|
||||
@@ -62,7 +62,9 @@ public class AttachEffect extends SpellAbilityEffect {
|
||||
if (sa.hasParam("ChooseAnObject")) {
|
||||
Card c = p.getController().chooseSingleEntityForEffect(attachments, sa, sa.getParam("ChooseAnObject"));
|
||||
attachments.clear();
|
||||
attachments.add(c);
|
||||
if (c != null) {
|
||||
attachments.add(c);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
attachments = new CardCollection(source);
|
||||
|
||||
@@ -6,7 +6,6 @@ import java.util.List;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import forge.GameCommand;
|
||||
import forge.card.mana.ManaCost;
|
||||
import forge.game.Game;
|
||||
import forge.game.GameEntity;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
@@ -18,7 +17,6 @@ import forge.game.combat.Combat;
|
||||
import forge.game.event.GameEventCardStatsChanged;
|
||||
import forge.game.event.GameEventCombatChanged;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.Ability;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.collect.FCollectionView;
|
||||
@@ -26,9 +24,7 @@ import forge.util.Localizer;
|
||||
import forge.util.CardTranslation;
|
||||
|
||||
public class ControlGainEffect extends SpellAbilityEffect {
|
||||
/* (non-Javadoc)
|
||||
* @see forge.card.abilityfactory.SpellEffect#getStackDescription(java.util.Map, forge.card.spellability.SpellAbility)
|
||||
*/
|
||||
|
||||
@Override
|
||||
protected String getStackDescription(SpellAbility sa) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
@@ -67,15 +63,17 @@ public class ControlGainEffect extends SpellAbilityEffect {
|
||||
if (null == c || c.hasKeyword("Other players can't gain control of CARDNAME.")) {
|
||||
return;
|
||||
}
|
||||
final Game game = host.getGame();
|
||||
if (c.isInPlay()) {
|
||||
c.removeTempController(tStamp);
|
||||
|
||||
game.getAction().controllerChangeZoneCorrection(c);
|
||||
|
||||
if (tapOnLose) {
|
||||
c.tap();
|
||||
}
|
||||
} // if
|
||||
host.removeGainControlTargets(c);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -84,11 +82,9 @@ public class ControlGainEffect extends SpellAbilityEffect {
|
||||
|
||||
final boolean bUntap = sa.hasParam("Untap");
|
||||
final boolean bTapOnLose = sa.hasParam("TapOnLose");
|
||||
final boolean bNoRegen = sa.hasParam("NoRegen");
|
||||
final boolean remember = sa.hasParam("RememberControlled");
|
||||
final boolean forget = sa.hasParam("ForgetControlled");
|
||||
final boolean attacking = sa.hasParam("Attacking");
|
||||
final List<String> destroyOn = sa.hasParam("DestroyTgt") ? Arrays.asList(sa.getParam("DestroyTgt").split(",")) : null;
|
||||
final List<String> keywords = sa.hasParam("AddKWs") ? Arrays.asList(sa.getParam("AddKWs").split(" & ")) : null;
|
||||
final List<String> lose = sa.hasParam("LoseControl") ? Arrays.asList(sa.getParam("LoseControl").split(",")) : null;
|
||||
|
||||
@@ -189,18 +185,6 @@ public class ControlGainEffect extends SpellAbilityEffect {
|
||||
}
|
||||
}
|
||||
|
||||
if (destroyOn != null) {
|
||||
if (destroyOn.contains("LeavesPlay")) {
|
||||
sa.getHostCard().addLeavesPlayCommand(getDestroyCommand(tgtC, source, bNoRegen));
|
||||
}
|
||||
if (destroyOn.contains("Untap")) {
|
||||
sa.getHostCard().addUntapCommand(getDestroyCommand(tgtC, source, bNoRegen));
|
||||
}
|
||||
if (destroyOn.contains("LoseControl")) {
|
||||
sa.getHostCard().addChangeControllerCommand(getDestroyCommand(tgtC, source, bNoRegen));
|
||||
}
|
||||
}
|
||||
|
||||
if (keywords != null) {
|
||||
// Add keywords only until end of turn
|
||||
final GameCommand untilKeywordEOT = new GameCommand() {
|
||||
@@ -241,43 +225,6 @@ public class ControlGainEffect extends SpellAbilityEffect {
|
||||
} // end foreach target
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* getDestroyCommand.
|
||||
* </p>
|
||||
*
|
||||
* @param i
|
||||
* a int.
|
||||
* @return a {@link forge.GameCommand} object.
|
||||
*/
|
||||
private static GameCommand getDestroyCommand(final Card c, final Card hostCard, final boolean bNoRegen) {
|
||||
final GameCommand destroy = new GameCommand() {
|
||||
private static final long serialVersionUID = 878543373519872418L;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
final Game game = hostCard.getGame();
|
||||
final Ability ability = new Ability(hostCard, ManaCost.ZERO) {
|
||||
@Override
|
||||
public void resolve() {
|
||||
game.getAction().destroy(c, null, !bNoRegen, null);
|
||||
}
|
||||
};
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append(hostCard).append(" - destroy ").append(c.getName()).append(".");
|
||||
if (bNoRegen) {
|
||||
sb.append(" It can't be regenerated.");
|
||||
}
|
||||
ability.setStackDescription(sb.toString());
|
||||
ability.setTrigger(true);
|
||||
|
||||
game.getStack().addSimultaneousStackEntry(ability);
|
||||
}
|
||||
|
||||
};
|
||||
return destroy;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* getLoseControlCommand.
|
||||
|
||||
@@ -35,61 +35,77 @@ import java.util.List;
|
||||
|
||||
public class CountersPutEffect extends SpellAbilityEffect {
|
||||
@Override
|
||||
protected String getStackDescription(SpellAbility sa) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
final Card card = sa.getHostCard();
|
||||
final boolean dividedAsYouChoose = sa.hasParam("DividedAsYouChoose");
|
||||
protected String getStackDescription(SpellAbility spellAbility) {
|
||||
final StringBuilder stringBuilder = new StringBuilder();
|
||||
final Card card = spellAbility.getHostCard();
|
||||
final boolean dividedAsYouChoose = spellAbility.hasParam("DividedAsYouChoose");
|
||||
|
||||
|
||||
final int amount = AbilityUtils.calculateAmount(card, sa.getParamOrDefault("CounterNum", "1"), sa);
|
||||
if (sa.hasParam("Bolster")) {
|
||||
sb.append("Bolster ").append(amount);
|
||||
return sb.toString();
|
||||
final int amount = AbilityUtils.calculateAmount(card, spellAbility.getParamOrDefault("CounterNum", "1"), spellAbility);
|
||||
if (spellAbility.hasParam("Bolster")) {
|
||||
stringBuilder.append("Bolster ").append(amount);
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
if (dividedAsYouChoose) {
|
||||
sb.append("Distribute ");
|
||||
stringBuilder.append("Distribute ");
|
||||
} else {
|
||||
sb.append("Put ");
|
||||
stringBuilder.append("Put ");
|
||||
}
|
||||
if (sa.hasParam("UpTo")) {
|
||||
sb.append("up to ");
|
||||
if (spellAbility.hasParam("UpTo")) {
|
||||
stringBuilder.append("up to ");
|
||||
}
|
||||
|
||||
sb.append(amount).append(" ");
|
||||
stringBuilder.append(amount).append(" ");
|
||||
|
||||
String type = sa.getParam("CounterType");
|
||||
String type = spellAbility.getParam("CounterType");
|
||||
if (type.equals("ExistingCounter")) {
|
||||
sb.append("of an existing counter");
|
||||
stringBuilder.append("of an existing counter");
|
||||
} else {
|
||||
|
||||
sb.append( CounterType.valueOf(type).getName()).append(" counter");
|
||||
stringBuilder.append(CounterType.valueOf(type).getName()).append(" counter");
|
||||
}
|
||||
|
||||
if (amount != 1) {
|
||||
sb.append("s");
|
||||
stringBuilder.append("s");
|
||||
}
|
||||
|
||||
if (dividedAsYouChoose) {
|
||||
sb.append(" among ");
|
||||
stringBuilder.append(" among ");
|
||||
} else {
|
||||
sb.append(" on ");
|
||||
stringBuilder.append(" on ");
|
||||
}
|
||||
final List<Card> tgtCards = getTargetCards(sa);
|
||||
|
||||
final Iterator<Card> it = tgtCards.iterator();
|
||||
while (it.hasNext()) {
|
||||
final Card tgtC = it.next();
|
||||
if (tgtC.isFaceDown()) {
|
||||
sb.append("Morph");
|
||||
} else {
|
||||
sb.append(tgtC);
|
||||
// if use targeting we show all targets and corresponding counters
|
||||
if(spellAbility.usesTargeting()) {
|
||||
final List<Card> targetCards = SpellAbilityEffect.getTargetCards(spellAbility);
|
||||
for(int i = 0; i < targetCards.size(); i++) {
|
||||
Card targetCard = targetCards.get(i);
|
||||
stringBuilder.append(targetCard).append(" (").append(spellAbility.getTargetRestrictions().getDividedMap().get(targetCard)).append(" counter)");
|
||||
|
||||
if(i == targetCards.size() - 2) {
|
||||
stringBuilder.append(" and ");
|
||||
}
|
||||
else if(i + 1 < targetCards.size()) {
|
||||
stringBuilder.append(", ");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
final List<Card> targetCards = SpellAbilityEffect.getTargetCards(spellAbility);
|
||||
final Iterator<Card> it = targetCards.iterator();
|
||||
while (it.hasNext()) {
|
||||
final Card targetCard = it.next();
|
||||
if (targetCard.isFaceDown()) {
|
||||
stringBuilder.append("Morph");
|
||||
} else {
|
||||
stringBuilder.append(targetCard);
|
||||
}
|
||||
|
||||
if (it.hasNext()) {
|
||||
sb.append(", ");
|
||||
if (it.hasNext()) {
|
||||
stringBuilder.append(", ");
|
||||
}
|
||||
}
|
||||
}
|
||||
sb.append(".");
|
||||
stringBuilder.append(".");
|
||||
|
||||
return sb.toString();
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -156,7 +172,7 @@ public class CountersPutEffect extends SpellAbilityEffect {
|
||||
if (existingCounter) {
|
||||
final List<CounterType> choices = Lists.newArrayList();
|
||||
if (obj instanceof GameEntity) {
|
||||
GameEntity entity = (GameEntity)obj;
|
||||
GameEntity entity = (GameEntity) obj;
|
||||
// get types of counters
|
||||
for (CounterType ct : entity.getCounters().keySet()) {
|
||||
if (entity.canReceiveCounters(ct)) {
|
||||
@@ -166,7 +182,7 @@ public class CountersPutEffect extends SpellAbilityEffect {
|
||||
}
|
||||
|
||||
if (eachExistingCounter) {
|
||||
for(CounterType ct : choices) {
|
||||
for (CounterType ct : choices) {
|
||||
if (obj instanceof Player) {
|
||||
((Player) obj).addCounter(ct, counterAmount, placer, true, table);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import forge.game.Game;
|
||||
import forge.game.GameEntityCounterTable;
|
||||
import forge.game.GameObject;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardDamageMap;
|
||||
@@ -25,45 +26,86 @@ public class DamageDealEffect extends DamageBaseEffect {
|
||||
* @see forge.game.ability.SpellAbilityEffect#getStackDescription(forge.game.spellability.SpellAbility)
|
||||
*/
|
||||
@Override
|
||||
protected String getStackDescription(SpellAbility sa) {
|
||||
protected String getStackDescription(SpellAbility spellAbility) {
|
||||
// when damageStackDescription is called, just build exactly what is happening
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
final String damage = sa.getParam("NumDmg");
|
||||
final int dmg = AbilityUtils.calculateAmount(sa.getHostCard(), damage, sa);
|
||||
final StringBuilder stringBuilder = new StringBuilder();
|
||||
final String damage = spellAbility.getParam("NumDmg");
|
||||
final int dmg = AbilityUtils.calculateAmount(spellAbility.getHostCard(), damage, spellAbility);
|
||||
|
||||
List<GameObject> tgts = getTargets(sa);
|
||||
if (tgts.isEmpty())
|
||||
List<GameObject> targets = SpellAbilityEffect.getTargets(spellAbility);
|
||||
if (targets.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
final List<Card> definedSources = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("DamageSource"), sa);
|
||||
final List<Card> definedSources = AbilityUtils.getDefinedCards(spellAbility.getHostCard(), spellAbility.getParam("DamageSource"), spellAbility);
|
||||
|
||||
if (!definedSources.isEmpty() && definedSources.get(0) != sa.getHostCard()) {
|
||||
sb.append(definedSources.get(0).toString()).append(" deals");
|
||||
if (!definedSources.isEmpty() && definedSources.get(0) != spellAbility.getHostCard()) {
|
||||
stringBuilder.append(definedSources.get(0).toString()).append(" deals");
|
||||
} else {
|
||||
sb.append("Deals");
|
||||
stringBuilder.append("Deals");
|
||||
}
|
||||
|
||||
sb.append(" ").append(dmg).append(" damage ");
|
||||
stringBuilder.append(" ").append(dmg).append(" damage ");
|
||||
|
||||
if (sa.hasParam("DivideEvenly")) {
|
||||
sb.append("divided evenly (rounded down) ");
|
||||
} else if (sa.hasParam("DividedAsYouChoose")) {
|
||||
sb.append("divided as you choose ");
|
||||
// if use targeting we show all targets and corresponding damage
|
||||
if (spellAbility.usesTargeting()) {
|
||||
if (spellAbility.hasParam("DivideEvenly")) {
|
||||
stringBuilder.append("divided evenly (rounded down) to\n");
|
||||
} else if (spellAbility.hasParam("DividedAsYouChoose")) {
|
||||
stringBuilder.append("divided to\n");
|
||||
}
|
||||
|
||||
final List<Card> targetCards = SpellAbilityEffect.getTargetCards(spellAbility);
|
||||
final List<Player> players = SpellAbilityEffect.getTargetPlayers(spellAbility);
|
||||
|
||||
int targetCount = targetCards.size() + players.size();
|
||||
|
||||
// target cards
|
||||
for (int i = 0; i < targetCards.size(); i++) {
|
||||
Card targetCard = targetCards.get(i);
|
||||
stringBuilder.append(targetCard).append(" (").append(spellAbility.getTargetRestrictions().getDividedMap().get(targetCard)).append(" damage)");
|
||||
|
||||
if (i == targetCount - 2) {
|
||||
stringBuilder.append(" and ");
|
||||
} else if (i + 1 < targetCount) {
|
||||
stringBuilder.append(", ");
|
||||
}
|
||||
}
|
||||
|
||||
// target players
|
||||
for (int i = 0; i < players.size(); i++) {
|
||||
|
||||
Player targetPlayer = players.get(i);
|
||||
stringBuilder.append(targetPlayer).append(" (").append(spellAbility.getTargetRestrictions().getDividedMap().get(targetPlayer)).append(" damage)");
|
||||
|
||||
if (i == players.size() - 2) {
|
||||
stringBuilder.append(" and ");
|
||||
} else if (i + 1 < players.size()) {
|
||||
stringBuilder.append(", ");
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
if (spellAbility.hasParam("DivideEvenly")) {
|
||||
stringBuilder.append("divided evenly (rounded down) ");
|
||||
} else if (spellAbility.hasParam("DividedAsYouChoose")) {
|
||||
stringBuilder.append("divided as you choose ");
|
||||
}
|
||||
stringBuilder.append("to ").append(Lang.joinHomogenous(targets));
|
||||
}
|
||||
sb.append("to ").append(Lang.joinHomogenous(tgts));
|
||||
|
||||
if (sa.hasParam("Radiance")) {
|
||||
sb.append(" and each other ").append(sa.getParam("ValidTgts"))
|
||||
if (spellAbility.hasParam("Radiance")) {
|
||||
stringBuilder.append(" and each other ").append(spellAbility.getParam("ValidTgts"))
|
||||
.append(" that shares a color with ");
|
||||
if (tgts.size() > 1) {
|
||||
sb.append("them");
|
||||
if (targets.size() > 1) {
|
||||
stringBuilder.append("them");
|
||||
} else {
|
||||
sb.append("it");
|
||||
stringBuilder.append("it");
|
||||
}
|
||||
}
|
||||
|
||||
sb.append(". ");
|
||||
return sb.toString();
|
||||
stringBuilder.append(".");
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
@@ -153,7 +195,7 @@ public class DamageDealEffect extends DamageBaseEffect {
|
||||
// Do we have a way of doing this in a better fashion?
|
||||
for (GameObject obj : tgts) {
|
||||
if (obj instanceof Card) {
|
||||
assigneeCards.add((Card)obj);
|
||||
assigneeCards.add((Card) obj);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,8 +237,7 @@ public class DamageDealEffect extends DamageBaseEffect {
|
||||
c.setDamage(0);
|
||||
c.setHasBeenDealtDeathtouchDamage(false);
|
||||
c.clearAssignedDamage();
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
c.addDamage(dmg, sourceLKI, false, noPrevention, damageMap, preventMap, counterTable, sa);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ public class DebuffEffect extends SpellAbilityEffect {
|
||||
for (final Card tgtC : getTargetCards(sa)) {
|
||||
final List<String> addedKW = Lists.newArrayList();
|
||||
final List<String> removedKW = Lists.newArrayList();
|
||||
if (tgtC.isInPlay() && tgtC.canBeTargetedBy(sa)) {
|
||||
if (tgtC.isInPlay() && (!sa.usesTargeting() || tgtC.canBeTargetedBy(sa))) {
|
||||
if (sa.hasParam("AllSuffixKeywords")) {
|
||||
String suffix = sa.getParam("AllSuffixKeywords");
|
||||
for (final KeywordInterface kw : tgtC.getKeywords()) {
|
||||
|
||||
@@ -1,34 +1,62 @@
|
||||
package forge.game.ability.effects;
|
||||
|
||||
import forge.game.Game;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.spellability.TargetRestrictions;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.Localizer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
public class MustBlockEffect extends SpellAbilityEffect {
|
||||
|
||||
@Override
|
||||
public void resolve(SpellAbility sa) {
|
||||
final Card host = sa.getHostCard();
|
||||
final Player activator = sa.getActivatingPlayer();
|
||||
final Game game = activator.getGame();
|
||||
|
||||
List<Card> tgtCards = Lists.newArrayList();
|
||||
if (sa.hasParam("Choices")) {
|
||||
Player chooser = activator;
|
||||
if (sa.hasParam("Chooser")) {
|
||||
final String choose = sa.getParam("Chooser");
|
||||
chooser = AbilityUtils.getDefinedPlayers(sa.getHostCard(), choose, sa).get(0);
|
||||
}
|
||||
|
||||
CardCollectionView choices = game.getCardsIn(ZoneType.Battlefield);
|
||||
choices = CardLists.getValidCards(choices, sa.getParam("Choices"), activator, host);
|
||||
if (!choices.isEmpty()) {
|
||||
String title = sa.hasParam("ChoiceTitle") ? sa.getParam("ChoiceTitle") : Localizer.getInstance().getMessage("lblChooseaCard") +" ";
|
||||
|
||||
Card choosen = chooser.getController().chooseSingleEntityForEffect(choices, sa, title, false);
|
||||
|
||||
if (choosen != null) {
|
||||
tgtCards.add(choosen);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
tgtCards = getTargetCards(sa);
|
||||
}
|
||||
|
||||
List<Card> tgtCards = getTargetCards(sa);
|
||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||
final boolean mustBlockAll = sa.hasParam("BlockAllDefined");
|
||||
|
||||
List<Card> cards;
|
||||
if (sa.hasParam("DefinedAttacker")) {
|
||||
cards = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("DefinedAttacker"), sa);
|
||||
} else {
|
||||
cards = new ArrayList<>();
|
||||
cards.add(host);
|
||||
cards = Lists.newArrayList(host);
|
||||
}
|
||||
|
||||
for (final Card c : tgtCards) {
|
||||
if ((tgt == null) || c.canBeTargetedBy(sa)) {
|
||||
if ((!sa.usesTargeting()) || c.canBeTargetedBy(sa)) {
|
||||
if (mustBlockAll) {
|
||||
c.addMustBlockCards(cards);
|
||||
} else {
|
||||
@@ -48,8 +76,6 @@ public class MustBlockEffect extends SpellAbilityEffect {
|
||||
|
||||
// end standard pre-
|
||||
|
||||
final List<Card> tgtCards = getTargetCards(sa);
|
||||
|
||||
String attacker = null;
|
||||
if (sa.hasParam("DefinedAttacker")) {
|
||||
final List<Card> cards = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("DefinedAttacker"), sa);
|
||||
@@ -58,10 +84,13 @@ public class MustBlockEffect extends SpellAbilityEffect {
|
||||
attacker = host.toString();
|
||||
}
|
||||
|
||||
for (final Card c : tgtCards) {
|
||||
sb.append(c).append(" must block ").append(attacker).append(" if able.");
|
||||
if (sa.hasParam("Choices")) {
|
||||
sb.append("Choosen creature ").append(" must block ").append(attacker).append(" if able.");
|
||||
} else {
|
||||
for (final Card c : getTargetCards(sa)) {
|
||||
sb.append(c).append(" must block ").append(attacker).append(" if able.");
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package forge.game.ability.effects;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -9,9 +10,9 @@ import forge.game.ability.AbilityKey;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.google.common.collect.ArrayListMultimap;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.ListMultimap;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import forge.game.Game;
|
||||
import forge.game.ability.AbilityFactory;
|
||||
@@ -19,10 +20,7 @@ import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.card.CardPredicates;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.player.PlayerCollection;
|
||||
import forge.game.player.PlayerPredicates;
|
||||
import forge.game.spellability.AbilitySub;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.trigger.TriggerType;
|
||||
@@ -67,33 +65,29 @@ public class VoteEffect extends SpellAbilityEffect {
|
||||
return;
|
||||
}
|
||||
|
||||
// starting with the activator
|
||||
int pSize = tgtPlayers.size();
|
||||
Player activator = sa.getActivatingPlayer();
|
||||
while (tgtPlayers.contains(activator) && !activator.equals(Iterables.getFirst(tgtPlayers, null))) {
|
||||
tgtPlayers.add(pSize - 1, tgtPlayers.remove(0));
|
||||
|
||||
// starting with the activator
|
||||
int aidx = tgtPlayers.indexOf(activator);
|
||||
if (aidx != -1) {
|
||||
Collections.rotate(tgtPlayers, -aidx);
|
||||
}
|
||||
|
||||
ListMultimap<Object, Player> votes = ArrayListMultimap.create();
|
||||
|
||||
Player voter = null;
|
||||
|
||||
PlayerCollection voters = game.getPlayers().filter(PlayerPredicates.hasKeyword("You choose how each player votes this turn."));
|
||||
|
||||
if (voters.size() > 1) {
|
||||
List<Card> illusions = CardLists.filter(voters.getCardsIn(ZoneType.Command), CardPredicates.nameEquals("Illusion of Choice Effect"));
|
||||
voter = Collections.max(illusions, CardPredicates.compareByTimestamp()).getController();
|
||||
} else if (voters.size() == 1) {
|
||||
voter = voters.get(0);
|
||||
}
|
||||
Player voter = game.getControlVote();
|
||||
|
||||
for (final Player p : tgtPlayers) {
|
||||
int voteAmount = p.getKeywords().getAmount("You get an additional vote.") + 1;
|
||||
int optionalVotes = p.getKeywords().getAmount("You may vote an additional time.");
|
||||
voteAmount += p.getController().chooseNumber(sa, Localizer.getInstance().getMessage("lblHowManyAdditionalVotesDoYouWant"), 0, optionalVotes);
|
||||
int voteAmount = p.getAdditionalVotesAmount() + 1;
|
||||
int optionalVotes = p.getAdditionalOptionalVotesAmount();
|
||||
Player realVoter = voter == null ? p : voter;
|
||||
|
||||
Map<String, Object> params = Maps.newHashMap();
|
||||
params.put("Voter", realVoter);
|
||||
voteAmount += p.getController().chooseNumber(sa, Localizer.getInstance().getMessage("lblHowManyAdditionalVotesDoYouWant"), 0, optionalVotes, params);
|
||||
|
||||
for (int i = 0; i < voteAmount; i++) {
|
||||
Object result = realVoter.getController().vote(sa, host + Localizer.getInstance().getMessage("lblVote") + ":", voteType, votes);
|
||||
Object result = realVoter.getController().vote(sa, host + Localizer.getInstance().getMessage("lblVote") + ":", voteType, votes, p);
|
||||
|
||||
votes.put(result, p);
|
||||
host.getGame().getAction().nofityOfValue(sa, p, result + "\r\n" + Localizer.getInstance().getMessage("lblCurrentVote") + ":" + votes, p);
|
||||
@@ -104,34 +98,49 @@ public class VoteEffect extends SpellAbilityEffect {
|
||||
runParams.put(AbilityKey.AllVotes, votes);
|
||||
game.getTriggerHandler().runTrigger(TriggerType.Vote, runParams, false);
|
||||
|
||||
List<String> subAbs = Lists.newArrayList();
|
||||
final List<Object> mostVotes = getMostVotes(votes);
|
||||
if (sa.hasParam("Tied") && mostVotes.size() > 1) {
|
||||
subAbs.add(sa.getParam("Tied"));
|
||||
} else if (sa.hasParam("VoteSubAbility")) {
|
||||
for (final Object o : mostVotes) {
|
||||
host.addRemembered(o);
|
||||
}
|
||||
subAbs.add(sa.getParam("VoteSubAbility"));
|
||||
} else {
|
||||
for (Object type : mostVotes) {
|
||||
subAbs.add(sa.getParam("Vote" + type.toString()));
|
||||
}
|
||||
}
|
||||
if (sa.hasParam("StoreVoteNum")) {
|
||||
for (final Object type : voteType) {
|
||||
host.setSVar("VoteNum" + type, "Number$" + votes.get(type).size());
|
||||
}
|
||||
} else {
|
||||
for (final String subAb : subAbs) {
|
||||
final SpellAbility action = AbilityFactory.getAbility(host.getSVar(subAb), host);
|
||||
if (sa.hasParam("EachVote")) {
|
||||
for (Map.Entry<Object, Collection<Player>> e : votes.asMap().entrySet()) {
|
||||
final SpellAbility action = AbilityFactory.getAbility(host, sa.getParam("Vote" + e.getKey().toString()));
|
||||
|
||||
action.setActivatingPlayer(sa.getActivatingPlayer());
|
||||
((AbilitySub) action).setParent(sa);
|
||||
AbilityUtils.resolve(action);
|
||||
|
||||
for (Player p : e.getValue()) {
|
||||
host.addRemembered(p);
|
||||
AbilityUtils.resolve(action);
|
||||
host.removeRemembered(p);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
List<String> subAbs = Lists.newArrayList();
|
||||
final List<Object> mostVotes = getMostVotes(votes);
|
||||
if (sa.hasParam("Tied") && mostVotes.size() > 1) {
|
||||
subAbs.add(sa.getParam("Tied"));
|
||||
} else if (sa.hasParam("VoteSubAbility")) {
|
||||
for (final Object o : mostVotes) {
|
||||
host.addRemembered(o);
|
||||
}
|
||||
subAbs.add(sa.getParam("VoteSubAbility"));
|
||||
} else {
|
||||
for (Object type : mostVotes) {
|
||||
subAbs.add(sa.getParam("Vote" + type.toString()));
|
||||
}
|
||||
}
|
||||
if (sa.hasParam("StoreVoteNum")) {
|
||||
for (final Object type : voteType) {
|
||||
host.setSVar("VoteNum" + type, "Number$" + votes.get(type).size());
|
||||
}
|
||||
} else {
|
||||
for (final String subAb : subAbs) {
|
||||
final SpellAbility action = AbilityFactory.getAbility(host, subAb);
|
||||
action.setActivatingPlayer(sa.getActivatingPlayer());
|
||||
((AbilitySub) action).setParent(sa);
|
||||
AbilityUtils.resolve(action);
|
||||
}
|
||||
}
|
||||
if (sa.hasParam("VoteSubAbility")) {
|
||||
host.clearRemembered();
|
||||
}
|
||||
}
|
||||
if (sa.hasParam("VoteSubAbility")) {
|
||||
host.clearRemembered();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3938,7 +3938,9 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
keywordsGrantedByTextChanges.add(newKw);
|
||||
}
|
||||
}
|
||||
addChangedCardKeywordsInternal(addKeywords, removeKeywords, false, false, timestamp, true);
|
||||
if (!addKeywords.isEmpty() || !removeKeywords.isEmpty()) {
|
||||
addChangedCardKeywordsInternal(addKeywords, removeKeywords, false, false, timestamp, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateKeywordsOnRemoveChangedText(final KeywordsChange k) {
|
||||
@@ -6353,6 +6355,10 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
removeSVar("PayX"); // Temporary AI X announcement variable
|
||||
removeSVar("IsCastFromPlayEffect"); // Temporary SVar indicating that the spell is cast indirectly via AF Play
|
||||
setSunburstValue(0); // Sunburst
|
||||
setXManaCostPaid(0);
|
||||
setXManaCostPaidByColor(null);
|
||||
setKickerMagnitude(0);
|
||||
setPseudoMultiKickerMagnitude(0);
|
||||
}
|
||||
|
||||
public final int getFinalChapterNr() {
|
||||
|
||||
@@ -20,7 +20,6 @@ package forge.game.card;
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Predicates;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
@@ -3010,24 +3009,43 @@ public class CardFactoryUtil {
|
||||
|
||||
inst.addTrigger(parsedTrigger);
|
||||
} else if (keyword.startsWith("Saga")) {
|
||||
// Saga there doesn't need Max value anymore?
|
||||
final String[] k = keyword.split(":");
|
||||
final String[] abs = k[2].split(",");
|
||||
final List<String> abs = Arrays.asList(k[2].split(","));
|
||||
if (abs.size() != Integer.valueOf(k[1])) {
|
||||
throw new RuntimeException("Saga max differ from Ability amount");
|
||||
}
|
||||
|
||||
int i = 1;
|
||||
for (String ab : abs) {
|
||||
SpellAbility sa = AbilityFactory.getAbility(card, ab);
|
||||
sa.setChapter(i);
|
||||
int idx = 0;
|
||||
int skipId = 0;
|
||||
for(String ab : abs) {
|
||||
idx += 1;
|
||||
if (idx <= skipId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO better logic for Roman numbers
|
||||
// In the Description try to merge Chapter trigger with the Same Effect
|
||||
String trigStr = "Mode$ CounterAdded | ValidCard$ Card.Self | TriggerZones$ Battlefield"
|
||||
+ "| CounterType$ LORE | CounterAmount$ EQ" + i
|
||||
+ "| TriggerDescription$ " + Strings.repeat("I", i) + " - " + sa.getDescription();
|
||||
final Trigger t = TriggerHandler.parseTrigger(trigStr, card, intrinsic);
|
||||
t.setOverridingAbility(sa);
|
||||
inst.addTrigger(t);
|
||||
++i;
|
||||
skipId = idx + abs.subList(idx - 1, abs.size()).lastIndexOf(ab);
|
||||
StringBuilder desc = new StringBuilder();
|
||||
for (int i = idx; i <= skipId; i++) {
|
||||
if (i != idx) {
|
||||
desc.append(", ");
|
||||
}
|
||||
desc.append(TextUtil.toRoman(i));
|
||||
}
|
||||
|
||||
for (int i = idx; i <= skipId; i++) {
|
||||
SpellAbility sa = AbilityFactory.getAbility(card, ab);
|
||||
sa.setChapter(i);
|
||||
|
||||
StringBuilder trigStr = new StringBuilder("Mode$ CounterAdded | ValidCard$ Card.Self | TriggerZones$ Battlefield");
|
||||
trigStr.append("| CounterType$ LORE | CounterAmount$ EQ").append(i);
|
||||
if (i != idx) {
|
||||
trigStr.append(" | Secondary$ True");
|
||||
}
|
||||
trigStr.append("| TriggerDescription$ ").append(desc).append(" — ").append(sa.getDescription());
|
||||
final Trigger t = TriggerHandler.parseTrigger(trigStr.toString(), card, intrinsic);
|
||||
t.setOverridingAbility(sa);
|
||||
inst.addTrigger(t);
|
||||
}
|
||||
}
|
||||
} else if (keyword.equals("Soulbond")) {
|
||||
// Setup ETB trigger for card with Soulbond keyword
|
||||
|
||||
@@ -1518,27 +1518,38 @@ public class CardProperty {
|
||||
} else if (property.startsWith("blockedByThisTurn")) {
|
||||
return !card.getBlockedByThisTurn().isEmpty();
|
||||
} else if (property.startsWith("blockedValidThisTurn ")) {
|
||||
if (card.getBlockedThisTurn() == null) {
|
||||
CardCollectionView blocked = card.getBlockedThisTurn();
|
||||
if (blocked == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String valid = property.split(" ")[1];
|
||||
for(Card c : card.getBlockedThisTurn()) {
|
||||
for(Card c : blocked) {
|
||||
if (c.isValid(valid, card.getController(), source, spellAbility)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
for(Card c : AbilityUtils.getDefinedCards(source, valid, spellAbility)) {
|
||||
if (blocked.contains(c)) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
return false;
|
||||
} else if (property.startsWith("blockedByValidThisTurn ")) {
|
||||
if (card.getBlockedByThisTurn() == null) {
|
||||
CardCollectionView blocked = card.getBlockedByThisTurn();
|
||||
if (blocked == null) {
|
||||
return false;
|
||||
}
|
||||
String valid = property.split(" ")[1];
|
||||
for(Card c : card.getBlockedByThisTurn()) {
|
||||
for(Card c : blocked) {
|
||||
if (c.isValid(valid, card.getController(), source, spellAbility)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
for(Card c : AbilityUtils.getDefinedCards(source, valid, spellAbility)) {
|
||||
if (blocked.contains(c)) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
return false;
|
||||
} else if (property.startsWith("blockedBySourceThisTurn")) {
|
||||
return source.getBlockedByThisTurn().contains(card);
|
||||
|
||||
@@ -203,27 +203,22 @@ public class CardView extends GameEntityView {
|
||||
void updateCommander(Card c) {
|
||||
boolean isCommander = c.isCommander();
|
||||
set(TrackableProperty.IsCommander, isCommander);
|
||||
if (c.getGame().getRules().hasAppliedVariant(GameType.Oathbreaker)) {
|
||||
//store alternate type for oathbreaker or signature spell for display in card text
|
||||
if (isCommander) {
|
||||
if (isCommander) {
|
||||
if (c.getGame().getRules().hasAppliedVariant(GameType.Oathbreaker)) {
|
||||
//store alternate type for oathbreaker or signature spell for display in card text
|
||||
if (c.getPaperCard().getRules().canBeSignatureSpell()) {
|
||||
set(TrackableProperty.CommanderAltType, "Signature Spell");
|
||||
}
|
||||
else {
|
||||
set(TrackableProperty.CommanderAltType, "Oathbreaker");
|
||||
}
|
||||
}
|
||||
else {
|
||||
set(TrackableProperty.CommanderAltType, null);
|
||||
} else {
|
||||
set(TrackableProperty.CommanderAltType, "Commander");
|
||||
}
|
||||
}
|
||||
}
|
||||
public String getCommanderType() {
|
||||
String type = get(TrackableProperty.CommanderAltType);
|
||||
if (type == null) {
|
||||
type = "Commander";
|
||||
}
|
||||
return type;
|
||||
return get(TrackableProperty.CommanderAltType);
|
||||
}
|
||||
|
||||
public Map<CounterType, Integer> getCounters() {
|
||||
|
||||
@@ -882,6 +882,10 @@ public class Combat {
|
||||
return true; // is blocking something at the moment
|
||||
}
|
||||
|
||||
if (!blocker.isLKI()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CombatLki lki = lkiCache.get(blocker);
|
||||
return null != lki && !lki.isAttacker; // was blocking something anyway
|
||||
}
|
||||
@@ -893,6 +897,10 @@ public class Combat {
|
||||
return true; // is blocking the attacker's band at the moment
|
||||
}
|
||||
|
||||
if (!blocker.isLKI()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CombatLki lki = lkiCache.get(blocker);
|
||||
return null != lki && !lki.isAttacker && lki.relatedBands.contains(ab); // was blocking that very band
|
||||
}
|
||||
|
||||
@@ -254,7 +254,7 @@ public class ManaPool extends ManaConversionMatrix implements Iterable<Mana> {
|
||||
}
|
||||
}
|
||||
if (mana.addsCounters(sa)) {
|
||||
mana.getManaAbility().createETBCounters(host);
|
||||
mana.getManaAbility().createETBCounters(host, this.owner);
|
||||
}
|
||||
if (mana.triggersWhenSpent()) {
|
||||
mana.getManaAbility().addTriggersWhenSpent(sa, host);
|
||||
|
||||
@@ -162,6 +162,10 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
private Card blessingEffect = null;
|
||||
private Card keywordEffect = null;
|
||||
|
||||
private Map<Long, Integer> additionalVotes = Maps.newHashMap();
|
||||
private Map<Long, Integer> additionalOptionalVotes = Maps.newHashMap();
|
||||
private SortedSet<Long> controlVotes = Sets.newTreeSet();
|
||||
|
||||
private final AchievementTracker achievementTracker = new AchievementTracker();
|
||||
private final PlayerView view;
|
||||
|
||||
@@ -2706,6 +2710,8 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
|
||||
public void incCommanderCast(Card commander) {
|
||||
commanderCast.put(commander, getCommanderCast(commander) + 1);
|
||||
getView().updateCommanderCast(this, commander);
|
||||
getGame().fireEvent(new GameEventPlayerStatsChanged(this, false));
|
||||
}
|
||||
|
||||
public int getTotalCommanderCast() {
|
||||
@@ -3065,4 +3071,86 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
this.updateZoneForView(com);
|
||||
return keywordEffect;
|
||||
}
|
||||
|
||||
public void addAdditionalVote(long timestamp, int value) {
|
||||
additionalVotes.put(timestamp, value);
|
||||
getView().updateAdditionalVote(this);
|
||||
getGame().fireEvent(new GameEventPlayerStatsChanged(this, false));
|
||||
}
|
||||
|
||||
public void removeAdditionalVote(long timestamp) {
|
||||
if (additionalVotes.remove(timestamp) != null) {
|
||||
getView().updateAdditionalVote(this);
|
||||
getGame().fireEvent(new GameEventPlayerStatsChanged(this, false));
|
||||
}
|
||||
}
|
||||
|
||||
public int getAdditionalVotesAmount() {
|
||||
int value = 0;
|
||||
for (Integer i : additionalVotes.values()) {
|
||||
value += i;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public void addAdditionalOptionalVote(long timestamp, int value) {
|
||||
additionalOptionalVotes.put(timestamp, value);
|
||||
getView().updateOptionalAdditionalVote(this);
|
||||
getGame().fireEvent(new GameEventPlayerStatsChanged(this, false));
|
||||
}
|
||||
public void removeAdditionalOptionalVote(long timestamp) {
|
||||
if (additionalOptionalVotes.remove(timestamp) != null) {
|
||||
getView().updateOptionalAdditionalVote(this);
|
||||
getGame().fireEvent(new GameEventPlayerStatsChanged(this, false));
|
||||
}
|
||||
}
|
||||
|
||||
public int getAdditionalOptionalVotesAmount() {
|
||||
int value = 0;
|
||||
for (Integer i : additionalOptionalVotes.values()) {
|
||||
value += i;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public boolean addControlVote(long timestamp) {
|
||||
if (controlVotes.add(timestamp)) {
|
||||
updateControlVote();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public boolean removeControlVote(long timestamp) {
|
||||
if (controlVotes.remove(timestamp)) {
|
||||
updateControlVote();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void updateControlVote() {
|
||||
// need to update all players because it can't know
|
||||
Player control = getGame().getControlVote();
|
||||
for (Player pl : getGame().getPlayers()) {
|
||||
pl.getView().updateControlVote(pl.equals(control));
|
||||
getGame().fireEvent(new GameEventPlayerStatsChanged(pl, false));
|
||||
}
|
||||
}
|
||||
|
||||
public Set<Long> getControlVote() {
|
||||
return controlVotes;
|
||||
}
|
||||
|
||||
public void setControlVote(Set<Long> value) {
|
||||
controlVotes.clear();
|
||||
controlVotes.addAll(value);
|
||||
updateControlVote();
|
||||
}
|
||||
|
||||
public Long getHighestControlVote() {
|
||||
if (controlVotes.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return controlVotes.last();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,7 +166,7 @@ public abstract class PlayerController {
|
||||
return chooseSomeType(kindOfType, sa, validTypes, invalidTypes, false);
|
||||
}
|
||||
|
||||
public abstract Object vote(SpellAbility sa, String prompt, List<Object> options, ListMultimap<Object, Player> votes);
|
||||
public abstract Object vote(SpellAbility sa, String prompt, List<Object> options, ListMultimap<Object, Player> votes, Player forPlayer);
|
||||
public abstract boolean confirmReplacementEffect(ReplacementEffect replacementEffect, SpellAbility effectSA, String question);
|
||||
|
||||
public abstract CardCollectionView getCardsToMulligan(Player firstPlayer);
|
||||
|
||||
@@ -7,7 +7,6 @@ import forge.card.CardType;
|
||||
import forge.card.mana.ManaAtom;
|
||||
import forge.game.card.CounterType;
|
||||
|
||||
import forge.util.TextUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
@@ -107,8 +106,7 @@ public class PlayerView extends GameEntityView {
|
||||
}
|
||||
|
||||
public boolean isOpponentOf(final PlayerView other) {
|
||||
FCollectionView<PlayerView> opponents = getOpponents();
|
||||
return opponents != null && opponents.contains(other);
|
||||
return getOpponents().contains(other);
|
||||
}
|
||||
|
||||
public final String getCommanderInfo(CardView v) {
|
||||
@@ -117,14 +115,15 @@ public class PlayerView extends GameEntityView {
|
||||
}
|
||||
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
Iterable<PlayerView> opponents = getOpponents();
|
||||
if (opponents == null) {
|
||||
opponents = Collections.emptyList();
|
||||
}
|
||||
for (final PlayerView p : Iterables.concat(Collections.singleton(this), opponents)) {
|
||||
|
||||
sb.append(Localizer.getInstance().getMessage("lblCommanderCastCard", String.valueOf(getCommanderCast(v))));
|
||||
sb.append("\n");
|
||||
|
||||
for (final PlayerView p : Iterables.concat(Collections.singleton(this), getOpponents())) {
|
||||
final int damage = p.getCommanderDamage(v);
|
||||
if (damage > 0) {
|
||||
sb.append(Localizer.getInstance().getMessage("lblCommanderDealNDamageToPlayer", p.toString(), CardTranslation.getTranslatedName(v.getName()), String.valueOf(damage)));
|
||||
sb.append("\n");
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
@@ -144,7 +143,11 @@ public class PlayerView extends GameEntityView {
|
||||
}
|
||||
|
||||
final List<String> info = Lists.newArrayListWithExpectedSize(opponents.size());
|
||||
info.add(TextUtil.concatWithSpace("Commanders:", Lang.joinHomogenous(commanders)));
|
||||
|
||||
info.add("Commanders:");
|
||||
for (final CardView v : commanders) {
|
||||
info.add(Localizer.getInstance().getMessage("lblCommanderCastPlayer", CardTranslation.getTranslatedName(v.getName()), String.valueOf(getCommanderCast(v))));
|
||||
}
|
||||
|
||||
// own commanders
|
||||
for (final CardView v : commanders) {
|
||||
@@ -268,6 +271,27 @@ public class PlayerView extends GameEntityView {
|
||||
set(TrackableProperty.NumDrawnThisTurn, p.getNumDrawnThisTurn());
|
||||
}
|
||||
|
||||
public int getAdditionalVote() {
|
||||
return get(TrackableProperty.AdditionalVote);
|
||||
}
|
||||
public void updateAdditionalVote(Player p) {
|
||||
set(TrackableProperty.AdditionalVote, p.getAdditionalVotesAmount());
|
||||
}
|
||||
|
||||
public int getOptionalAdditionalVote() {
|
||||
return get(TrackableProperty.OptionalAdditionalVote);
|
||||
}
|
||||
public void updateOptionalAdditionalVote(Player p) {
|
||||
set(TrackableProperty.OptionalAdditionalVote, p.getAdditionalOptionalVotesAmount());
|
||||
}
|
||||
|
||||
public boolean getControlVote() {
|
||||
return get(TrackableProperty.ControlVotes);
|
||||
}
|
||||
public void updateControlVote(boolean val) {
|
||||
set(TrackableProperty.ControlVotes, val);
|
||||
}
|
||||
|
||||
public ImmutableMultiset<String> getKeywords() {
|
||||
return get(TrackableProperty.Keywords);
|
||||
}
|
||||
@@ -300,13 +324,29 @@ public class PlayerView extends GameEntityView {
|
||||
return damage == null ? 0 : damage.intValue();
|
||||
}
|
||||
void updateCommanderDamage(Player p) {
|
||||
Map<Integer, Integer> map = new HashMap<>();
|
||||
Map<Integer, Integer> map = Maps.newHashMap();
|
||||
for (Entry<Card, Integer> entry : p.getCommanderDamage()) {
|
||||
map.put(entry.getKey().getId(), entry.getValue());
|
||||
}
|
||||
set(TrackableProperty.CommanderDamage, map);
|
||||
}
|
||||
|
||||
public int getCommanderCast(CardView commander) {
|
||||
Map<Integer, Integer> map = get(TrackableProperty.CommanderCast);
|
||||
if (map == null) { return 0; }
|
||||
Integer damage = map.get(commander.getId());
|
||||
return damage == null ? 0 : damage.intValue();
|
||||
}
|
||||
|
||||
void updateCommanderCast(Player p, Card c) {
|
||||
Map<Integer, Integer> map = get(TrackableProperty.CommanderCast);
|
||||
if (map == null) {
|
||||
map = Maps.newHashMap();
|
||||
}
|
||||
map.put(c.getId(), p.getCommanderCast(c));
|
||||
set(TrackableProperty.CommanderCast, map);
|
||||
}
|
||||
|
||||
public PlayerView getMindSlaveMaster() {
|
||||
return get(TrackableProperty.MindSlaveMaster);
|
||||
}
|
||||
@@ -478,6 +518,19 @@ public class PlayerView extends GameEntityView {
|
||||
details.add(Localizer.getInstance().getMessage("lblCardDrawnThisTurnHas", String.valueOf(getNumDrawnThisTurn())));
|
||||
details.add(Localizer.getInstance().getMessage("lblDamagepreventionHas", String.valueOf(getPreventNextDamage())));
|
||||
|
||||
int v = getAdditionalVote();
|
||||
if (v > 0) {
|
||||
details.add(Localizer.getInstance().getMessage("lblAdditionalVotes", String.valueOf(v)));
|
||||
}
|
||||
v = getOptionalAdditionalVote();
|
||||
if (v > 0) {
|
||||
details.add(Localizer.getInstance().getMessage("lblOptionalAdditionalVotes", String.valueOf(v)));
|
||||
}
|
||||
|
||||
if (getControlVote()) {
|
||||
details.add(Localizer.getInstance().getMessage("lblControlsVote"));
|
||||
}
|
||||
|
||||
if (getIsExtraTurn()) {
|
||||
details.add(Localizer.getInstance().getMessage("lblIsExtraTurn"));
|
||||
}
|
||||
|
||||
@@ -19,9 +19,11 @@ package forge.game.spellability;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import forge.card.ColorSet;
|
||||
import forge.card.MagicColor;
|
||||
import forge.card.mana.ManaAtom;
|
||||
import forge.game.Game;
|
||||
import forge.game.ability.AbilityFactory;
|
||||
import forge.game.ability.AbilityKey;
|
||||
import forge.game.card.Card;
|
||||
@@ -34,6 +36,8 @@ import forge.game.replacement.*;
|
||||
import forge.game.trigger.Trigger;
|
||||
import forge.game.trigger.TriggerHandler;
|
||||
import forge.game.trigger.TriggerType;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.Lang;
|
||||
import forge.util.TextUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@@ -227,10 +231,26 @@ public class AbilityManaPart implements java.io.Serializable {
|
||||
/**
|
||||
* createETBCounters
|
||||
*/
|
||||
public void createETBCounters(Card c) {
|
||||
public void createETBCounters(Card c, Player controller) {
|
||||
String[] parse = this.addsCounters.split("_");
|
||||
// Convert random SVars if there are other cards with this effect
|
||||
if (c.isValid(parse[0], c.getController(), c, null)) {
|
||||
final Game game = this.sourceCard.getGame();
|
||||
final Card eff = new Card(game.nextCardId(), game);
|
||||
eff.setTimestamp(game.getNextTimestamp());
|
||||
eff.setName(sourceCard.getName() + "'s Effect");
|
||||
eff.addType("Effect");
|
||||
eff.setToken(true); // Set token to true, so when leaving play it gets nuked
|
||||
eff.setOwner(controller);
|
||||
|
||||
eff.setImageKey(sourceCard.getImageKey());
|
||||
eff.setColor(MagicColor.COLORLESS);
|
||||
eff.setImmutable(true);
|
||||
// try to get the SpellAbility from the mana ability
|
||||
//eff.setEffectSource((SpellAbility)null);
|
||||
|
||||
eff.addRemembered(c);
|
||||
|
||||
String abStr = "DB$ PutCounter | Defined$ ReplacedCard | CounterType$ " + parse[1]
|
||||
+ " | ETB$ True | CounterNum$ " + parse[2];
|
||||
|
||||
@@ -240,15 +260,37 @@ public class AbilityManaPart implements java.io.Serializable {
|
||||
}
|
||||
CardFactoryUtil.setupETBReplacementAbility(sa);
|
||||
|
||||
String repeffstr = "Event$ Moved | ValidCard$ Card.Self | Destination$ Battlefield "
|
||||
+ " | Secondary$ True | Description$ CARDNAME"
|
||||
+ " enters the battlefield with " + CounterType.valueOf(parse[1]).getName() + " counters.";
|
||||
String desc = "It enters the battlefield with ";
|
||||
desc += Lang.nounWithNumeral(parse[2], CounterType.valueOf(parse[1]).getName() + " counter");
|
||||
desc += " on it.";
|
||||
|
||||
ReplacementEffect re = ReplacementHandler.parseReplacement(repeffstr, c, false);
|
||||
String repeffstr = "Event$ Moved | ValidCard$ Card.IsRemembered | Destination$ Battlefield | Description$ " + desc;
|
||||
|
||||
ReplacementEffect re = ReplacementHandler.parseReplacement(repeffstr, eff, true);
|
||||
re.setLayer(ReplacementLayer.Other);
|
||||
re.setOverridingAbility(sa);
|
||||
|
||||
c.addReplacementEffect(re);
|
||||
eff.addReplacementEffect(re);
|
||||
|
||||
// Forgot Trigger
|
||||
String trig = "Mode$ ChangesZone | ValidCard$ Card.IsRemembered | Origin$ Stack | Destination$ Any | TriggerZones$ Command | Static$ True";
|
||||
String forgetEffect = "DB$ Pump | ForgetObjects$ TriggeredCard";
|
||||
String exileEffect = "DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile"
|
||||
+ " | ConditionDefined$ Remembered | ConditionPresent$ Card | ConditionCompare$ EQ0";
|
||||
|
||||
SpellAbility saForget = AbilityFactory.getAbility(forgetEffect, eff);
|
||||
AbilitySub saExile = (AbilitySub) AbilityFactory.getAbility(exileEffect, eff);
|
||||
saForget.setSubAbility(saExile);
|
||||
|
||||
final Trigger parsedTrigger = TriggerHandler.parseTrigger(trig, eff, true);
|
||||
parsedTrigger.setOverridingAbility(saForget);
|
||||
eff.addTrigger(parsedTrigger);
|
||||
eff.updateStateForView();
|
||||
|
||||
// TODO: Add targeting to the effect so it knows who it's dealing with
|
||||
game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);
|
||||
game.getAction().moveTo(ZoneType.Command, eff, null);
|
||||
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -75,12 +75,12 @@ public abstract class Spell extends SpellAbility implements java.io.Serializable
|
||||
@Override
|
||||
public boolean canPlay() {
|
||||
Card card = this.getHostCard();
|
||||
if (card.isInZone(ZoneType.Battlefield)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Save the original cost and the face down info for a later check since the LKI copy will overwrite them
|
||||
ManaCost origCost = card.getState(card.isFaceDown() ? CardStateName.Original : card.getCurrentStateName()).getManaCost();
|
||||
boolean wasFaceDownInstant = card.isFaceDown()
|
||||
&& !card.getLastKnownZone().is(ZoneType.Battlefield)
|
||||
&& card.getState(CardStateName.Original).getType().isInstant();
|
||||
|
||||
Player activator = this.getActivatingPlayer();
|
||||
if (activator == null) {
|
||||
@@ -95,61 +95,29 @@ public abstract class Spell extends SpellAbility implements java.io.Serializable
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean isInstant = card.isInstant();
|
||||
// special case for split cards
|
||||
if (card.isSplitCard()) {
|
||||
CardStateName name = isLeftSplit() ? CardStateName.LeftSplit : CardStateName.RightSplit;
|
||||
isInstant = card.getState(name).getType().isInstant();
|
||||
} else if (isAdventure()) {
|
||||
if (card.hasState(CardStateName.Adventure)) {
|
||||
isInstant = card.getState(CardStateName.Adventure).getType().isInstant();
|
||||
}
|
||||
}
|
||||
|
||||
boolean lkicheck = false;
|
||||
boolean flash = false;
|
||||
|
||||
// do performanceMode only for cases where the activator is different than controller
|
||||
if (!Spell.performanceMode && activator != null && !card.getController().equals(activator)
|
||||
&& !card.isInZone(ZoneType.Battlefield)) {
|
||||
if (!Spell.performanceMode && activator != null && !card.getController().equals(activator)) {
|
||||
// always make a lki copy in this case?
|
||||
card = CardUtil.getLKICopy(card);
|
||||
card.setController(activator, 0);
|
||||
lkicheck = true;
|
||||
}
|
||||
|
||||
if (isBestow() && !card.isBestowed() && !card.isInZone(ZoneType.Battlefield)) {
|
||||
// Rule 601.3: cast Bestow with Flash
|
||||
// for the check the card does need to be animated
|
||||
// otherwise the StaticAbility will not found them
|
||||
if (!card.isLKI()) {
|
||||
card = CardUtil.getLKICopy(card);
|
||||
}
|
||||
card.animateBestow(false);
|
||||
lkicheck = true;
|
||||
} else if (isCastFaceDown()) {
|
||||
// need a copy of the card to turn facedown without trigger anything
|
||||
if (!card.isLKI()) {
|
||||
card = CardUtil.getLKICopy(card);
|
||||
}
|
||||
card.turnFaceDownNoUpdate();
|
||||
lkicheck = true;
|
||||
} else if (isAdventure()) {
|
||||
if (!card.isLKI()) {
|
||||
card = CardUtil.getLKICopy(card);
|
||||
}
|
||||
|
||||
card.setState(CardStateName.Adventure, false);
|
||||
Card lkiHost = getAlternateHost(card);
|
||||
if (lkiHost != null) {
|
||||
card = lkiHost;
|
||||
lkicheck = true;
|
||||
}
|
||||
|
||||
|
||||
if (lkicheck) {
|
||||
game.getTracker().freeze(); //prevent views flickering during while updating for state-based effects
|
||||
game.getAction().checkStaticAbilities(false, Sets.newHashSet(card), new CardCollection(card));
|
||||
}
|
||||
|
||||
flash = card.withFlash(activator);
|
||||
boolean isInstant = card.isInstant();
|
||||
boolean flash = card.withFlash(activator);
|
||||
|
||||
// reset static abilities
|
||||
if (lkicheck) {
|
||||
@@ -160,8 +128,7 @@ public abstract class Spell extends SpellAbility implements java.io.Serializable
|
||||
}
|
||||
|
||||
if (!(isInstant || activator.canCastSorcery() || flash || getRestrictions().isInstantSpeed()
|
||||
|| hasSVar("IsCastFromPlayEffect")
|
||||
|| wasFaceDownInstant)) {
|
||||
|| hasSVar("IsCastFromPlayEffect"))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -235,4 +202,74 @@ public abstract class Spell extends SpellAbility implements java.io.Serializable
|
||||
this.castFaceDown = faceDown;
|
||||
}
|
||||
|
||||
public Card getAlternateHost(Card source) {
|
||||
boolean lkicheck = false;
|
||||
|
||||
// need to be done before so it works with Vivien and Zoetic Cavern
|
||||
if (source.isFaceDown() && source.isInZone(ZoneType.Exile)) {
|
||||
if (!source.isLKI()) {
|
||||
source = CardUtil.getLKICopy(source);
|
||||
}
|
||||
|
||||
source.turnFaceUp(false, false);
|
||||
lkicheck = true;
|
||||
}
|
||||
|
||||
if (isBestow() && !source.isBestowed()) {
|
||||
if (!source.isLKI()) {
|
||||
source = CardUtil.getLKICopy(source);
|
||||
}
|
||||
|
||||
source.animateBestow(false);
|
||||
lkicheck = true;
|
||||
} else if (isCastFaceDown()) {
|
||||
// need a copy of the card to turn facedown without trigger anything
|
||||
if (!source.isLKI()) {
|
||||
source = CardUtil.getLKICopy(source);
|
||||
}
|
||||
source.turnFaceDownNoUpdate();
|
||||
lkicheck = true;
|
||||
} else if (isAdventure()) {
|
||||
if (!source.isLKI()) {
|
||||
source = CardUtil.getLKICopy(source);
|
||||
}
|
||||
|
||||
source.setState(CardStateName.Adventure, false);
|
||||
|
||||
// need to reset CMC
|
||||
source.setLKICMC(-1);
|
||||
source.setLKICMC(source.getCMC());
|
||||
lkicheck = true;
|
||||
} else if (source.isSplitCard() && (isLeftSplit() || isRightSplit())) {
|
||||
if (!source.isLKI()) {
|
||||
source = CardUtil.getLKICopy(source);
|
||||
}
|
||||
if (isLeftSplit()) {
|
||||
if (!source.hasState(CardStateName.LeftSplit)) {
|
||||
source.addAlternateState(CardStateName.LeftSplit, false);
|
||||
source.getState(CardStateName.LeftSplit).copyFrom(
|
||||
getHostCard().getState(CardStateName.LeftSplit), true);
|
||||
}
|
||||
|
||||
source.setState(CardStateName.LeftSplit, false);
|
||||
}
|
||||
|
||||
if (isRightSplit()) {
|
||||
if (!source.hasState(CardStateName.RightSplit)) {
|
||||
source.addAlternateState(CardStateName.RightSplit, false);
|
||||
source.getState(CardStateName.RightSplit).copyFrom(
|
||||
getHostCard().getState(CardStateName.RightSplit), true);
|
||||
}
|
||||
|
||||
source.setState(CardStateName.RightSplit, false);
|
||||
}
|
||||
|
||||
// need to reset CMC
|
||||
source.setLKICMC(-1);
|
||||
source.setLKICMC(source.getCMC());
|
||||
lkicheck = true;
|
||||
}
|
||||
|
||||
return lkicheck ? source : null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1299,6 +1299,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
||||
String announce = getParam("Announce");
|
||||
if (StringUtils.isBlank(announce)) {
|
||||
mapParams.put("Announce", variable);
|
||||
originalMapParams.put("Announce", variable);
|
||||
return;
|
||||
}
|
||||
String[] announcedOnes = TextUtil.split(announce, ',');
|
||||
@@ -1308,6 +1309,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
||||
}
|
||||
}
|
||||
mapParams.put("Announce", announce + ";" + variable);
|
||||
originalMapParams.put("Announce", announce + ";" + variable);
|
||||
}
|
||||
|
||||
public boolean isXCost() {
|
||||
|
||||
@@ -181,15 +181,9 @@ public class StaticAbility extends CardTraitBase implements IIdentifiable, Clone
|
||||
layers.add(StaticAbilityLayer.MODIFYPT);
|
||||
}
|
||||
|
||||
if (hasParam("AddHiddenKeyword")) {
|
||||
layers.add(StaticAbilityLayer.RULES);
|
||||
}
|
||||
|
||||
if (hasParam("IgnoreEffectCost") || hasParam("Goad") || hasParam("CanBlockAny") || hasParam("CanBlockAmount")) {
|
||||
layers.add(StaticAbilityLayer.RULES);
|
||||
}
|
||||
|
||||
if (hasParam("AdjustLandPlays")) {
|
||||
if (hasParam("AddHiddenKeyword")
|
||||
|| hasParam("IgnoreEffectCost") || hasParam("Goad") || hasParam("CanBlockAny") || hasParam("CanBlockAmount")
|
||||
|| hasParam("AdjustLandPlays") || hasParam("ControlVote") || hasParam("AdditionalVote") || hasParam("AdditionalOptionalVote")) {
|
||||
layers.add(StaticAbilityLayer.RULES);
|
||||
}
|
||||
|
||||
|
||||
@@ -513,6 +513,20 @@ public final class StaticAbilityContinuous {
|
||||
}
|
||||
}
|
||||
|
||||
if (params.containsKey("ControlVote")) {
|
||||
p.addControlVote(se.getTimestamp());
|
||||
}
|
||||
if (params.containsKey("AdditionalVote")) {
|
||||
String mhs = params.get("AdditionalVote");
|
||||
int add = AbilityUtils.calculateAmount(hostCard, mhs, stAb);
|
||||
p.addAdditionalVote(se.getTimestamp(), add);
|
||||
}
|
||||
if (params.containsKey("AdditionalOptionalVote")) {
|
||||
String mhs = params.get("AdditionalOptionalVote");
|
||||
int add = AbilityUtils.calculateAmount(hostCard, mhs, stAb);
|
||||
p.addAdditionalOptionalVote(se.getTimestamp(), add);
|
||||
}
|
||||
|
||||
if (params.containsKey("RaiseMaxHandSize")) {
|
||||
String rmhs = params.get("RaiseMaxHandSize");
|
||||
int rmax = AbilityUtils.calculateAmount(hostCard, rmhs, stAb);
|
||||
|
||||
@@ -274,7 +274,7 @@ public class TriggerHandler {
|
||||
}
|
||||
|
||||
private void runStateTrigger(final Map<AbilityKey, Object> runParams) {
|
||||
for (final Trigger t: activeTriggers) {
|
||||
for (final Trigger t: Lists.newArrayList(activeTriggers)) {
|
||||
if (canRunTrigger(t, TriggerType.Always, runParams)) {
|
||||
runSingleTrigger(t, runParams);
|
||||
}
|
||||
@@ -542,13 +542,20 @@ public class TriggerHandler {
|
||||
}
|
||||
|
||||
sa = AbilityFactory.getAbility(host, name);
|
||||
// need to set as Overriding Abiltiy so it can be copied better
|
||||
regtrig.setOverridingAbility(sa);
|
||||
}
|
||||
sa.setActivatingPlayer(host.getController());
|
||||
|
||||
if (regtrig.isIntrinsic()) {
|
||||
sa.setIntrinsic(true);
|
||||
sa.changeText();
|
||||
}
|
||||
} else {
|
||||
// need to copy the SA because of TriggeringObjects
|
||||
sa = sa.copy();
|
||||
sa = sa.copy(host, host.getController(), false);
|
||||
}
|
||||
|
||||
sa.setHostCard(host);
|
||||
sa.setLastStateBattlefield(game.getLastStateBattlefield());
|
||||
sa.setLastStateGraveyard(game.getLastStateGraveyard());
|
||||
|
||||
@@ -560,9 +567,7 @@ public class TriggerHandler {
|
||||
sa.setTriggeringObjects(regtrig.getStoredTriggeredObjects());
|
||||
}
|
||||
|
||||
if (sa.getActivatingPlayer() == null) { // overriding delayed trigger should have set activator
|
||||
sa.setActivatingPlayer(host.getController());
|
||||
} else if (sa.getDeltrigActivatingPlayer() != null) {
|
||||
if (sa.getDeltrigActivatingPlayer() != null) {
|
||||
// make sure that the original delayed trigger activator is restored
|
||||
// (may have been overwritten by the AI simulation routines, e.g. Rainbow Vale)
|
||||
sa.setActivatingPlayer(sa.getDeltrigActivatingPlayer());
|
||||
@@ -577,11 +582,6 @@ public class TriggerHandler {
|
||||
host.addRemembered(sa.getActivatingPlayer());
|
||||
}
|
||||
|
||||
if (regtrig.isIntrinsic() && regtrig.getOverridingAbility() == null) {
|
||||
sa.setIntrinsic(true);
|
||||
sa.changeText();
|
||||
}
|
||||
|
||||
sa.setStackDescription(sa.toString());
|
||||
if (sa.getApi() == ApiType.Charm && !sa.isWrapper()) {
|
||||
if (!CharmEffect.makeChoices(sa)) {
|
||||
|
||||
@@ -134,8 +134,12 @@ public enum TrackableProperty {
|
||||
HasUnlimitedLandPlay(TrackableTypes.BooleanType),
|
||||
NumLandThisTurn(TrackableTypes.IntegerType),
|
||||
NumDrawnThisTurn(TrackableTypes.IntegerType),
|
||||
AdditionalVote(TrackableTypes.IntegerType),
|
||||
OptionalAdditionalVote(TrackableTypes.IntegerType),
|
||||
ControlVotes(TrackableTypes.BooleanType),
|
||||
Keywords(TrackableTypes.KeywordCollectionViewType, FreezeMode.IgnoresFreeze),
|
||||
Commander(TrackableTypes.CardViewCollectionType, FreezeMode.IgnoresFreeze),
|
||||
CommanderCast(TrackableTypes.IntegerMapType),
|
||||
CommanderDamage(TrackableTypes.IntegerMapType),
|
||||
MindSlaveMaster(TrackableTypes.PlayerViewType),
|
||||
Ante(TrackableTypes.CardViewCollectionType, FreezeMode.IgnoresFreeze),
|
||||
|
||||
@@ -100,17 +100,21 @@ public class TrackableTypes {
|
||||
if (newCollection != null) {
|
||||
//swap in objects in old collection for objects in new collection
|
||||
for (int i = 0; i < newCollection.size(); i++) {
|
||||
T newObj = newCollection.get(i);
|
||||
if (newObj != null) {
|
||||
T existingObj = from.getTracker().getObj(itemType, newObj.getId());
|
||||
if (existingObj != null) { //if object exists already, update its changed properties
|
||||
existingObj.copyChangedProps(newObj);
|
||||
newCollection.remove(i);
|
||||
newCollection.add(i, existingObj);
|
||||
}
|
||||
else { //if object is new, cache in object lookup
|
||||
from.getTracker().putObj(itemType, newObj.getId(), newObj);
|
||||
try {
|
||||
T newObj = newCollection.get(i);
|
||||
if (newObj != null) {
|
||||
T existingObj = from.getTracker().getObj(itemType, newObj.getId());
|
||||
if (existingObj != null) { //if object exists already, update its changed properties
|
||||
existingObj.copyChangedProps(newObj);
|
||||
newCollection.remove(i);
|
||||
newCollection.add(i, existingObj);
|
||||
}
|
||||
else { //if object is new, cache in object lookup
|
||||
from.getTracker().putObj(itemType, newObj.getId(), newObj);
|
||||
}
|
||||
}
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
System.err.println("got an IndexOutOfBoundsException, trying to continue ...");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
<uses-sdk
|
||||
android:minSdkVersion="19"
|
||||
android:targetSdkVersion="21" />
|
||||
android:targetSdkVersion="26" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <!-- This one needs Android Runtime Permission for Android 6+ -->
|
||||
<uses-permission android:name="android.permission.VIBRATE"/>
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<parent>
|
||||
<artifactId>forge</artifactId>
|
||||
<groupId>forge</groupId>
|
||||
<version>1.6.33-SNAPSHOT</version>
|
||||
<version>1.6.34-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>forge-gui-android</artifactId>
|
||||
@@ -142,7 +142,7 @@
|
||||
<debug>true</debug>
|
||||
</sign>
|
||||
<sdk>
|
||||
<platform>25</platform>
|
||||
<platform>26</platform>
|
||||
</sdk>
|
||||
<dexForceJumbo>true</dexForceJumbo>
|
||||
<androidManifestFile>${project.basedir}/AndroidManifest.xml</androidManifestFile>
|
||||
@@ -183,7 +183,7 @@
|
||||
<debug>false</debug>
|
||||
</sign>
|
||||
<sdk>
|
||||
<platform>25</platform>
|
||||
<platform>26</platform>
|
||||
</sdk>
|
||||
<zipalign>
|
||||
<verbose>false</verbose>
|
||||
|
||||
@@ -9,4 +9,4 @@
|
||||
|
||||
# Project target.
|
||||
project.type=0
|
||||
target=android-20
|
||||
target=android-26
|
||||
|
||||
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.7 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 7.8 KiB After Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 5.9 KiB |
|
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 7.2 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 9.5 KiB After Width: | Height: | Size: 8.9 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_background">#f0f0f0</color>
|
||||
<color name="ic_launcher_background">#ffffff</color>
|
||||
</resources>
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>forge</artifactId>
|
||||
<groupId>forge</groupId>
|
||||
<version>1.6.33-SNAPSHOT</version>
|
||||
<version>1.6.34-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>forge-gui-desktop</artifactId>
|
||||
@@ -213,7 +213,7 @@
|
||||
<plugin>
|
||||
<groupId>com.akathist.maven.plugins.launch4j</groupId>
|
||||
<artifactId>launch4j-maven-plugin</artifactId>
|
||||
<version>1.5.2</version>
|
||||
<version>1.7.25</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>l4j-gui</id>
|
||||
@@ -235,7 +235,10 @@
|
||||
</classPath>
|
||||
<jre>
|
||||
<minVersion>1.8.0</minVersion>
|
||||
<maxHeapSize>1024</maxHeapSize>
|
||||
<maxHeapSize>4096</maxHeapSize>
|
||||
<opts>
|
||||
<opt>-Dfile.encoding=UTF-8</opt>
|
||||
</opts>
|
||||
</jre>
|
||||
<versionInfo>
|
||||
<fileVersion>
|
||||
@@ -367,7 +370,7 @@
|
||||
<plugin>
|
||||
<groupId>com.akathist.maven.plugins.launch4j</groupId>
|
||||
<artifactId>launch4j-maven-plugin</artifactId>
|
||||
<version>1.5.2</version>
|
||||
<version>1.7.25</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>l4j-gui</id>
|
||||
@@ -389,7 +392,10 @@
|
||||
</classPath>
|
||||
<jre>
|
||||
<minVersion>1.8.0</minVersion>
|
||||
<maxHeapSize>1024</maxHeapSize>
|
||||
<maxHeapSize>4096</maxHeapSize>
|
||||
<opts>
|
||||
<opt>-Dfile.encoding=UTF-8</opt>
|
||||
</opts>
|
||||
</jre>
|
||||
<versionInfo>
|
||||
<fileVersion>
|
||||
@@ -564,7 +570,7 @@
|
||||
<option value="-Dcom.apple.macos.useScreenMenuBar=true" />
|
||||
<option value="-Dcom.apple.mrj.application.apple.menu.about.name=Forge" />
|
||||
<option value="-Dcom.apple.smallTabs=true" />
|
||||
<option value="-Xmx1024M" />
|
||||
<option value="-Xmx4096M" />
|
||||
<option value="-Dapp.dir=$APP_ROOT/Contents/Resources/" />
|
||||
</bundleapp>
|
||||
<copy todir="${project.build.directory}/${project.build.finalName}-osx/Forge.app/Contents/Resources/res">
|
||||
@@ -675,7 +681,7 @@
|
||||
<option value="-Dcom.apple.macos.useScreenMenuBar=true" />
|
||||
<option value="-Dcom.apple.mrj.application.apple.menu.about.name=Forge" />
|
||||
<option value="-Dcom.apple.smallTabs=true" />
|
||||
<option value="-Xmx1024M" />
|
||||
<option value="-Xmx4096M" />
|
||||
<option value="-Dapp.dir=$APP_ROOT/Contents/Resources/" />
|
||||
</bundleapp>
|
||||
<copy todir="${project.build.directory}/${project.build.finalName}-osx/Forge.app/Contents/Resources/res">
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
cd "`dirname \"$0\"`"
|
||||
java -Xmx1024m -jar $project.build.finalName$
|
||||
cd $(dirname "${0}")
|
||||
java -Xmx4096m -Dfile.encoding=UTF-8 -jar $project.build.finalName$
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
cd "`dirname \"$0\"`"
|
||||
java -Xmx1024m -jar $project.build.finalName$
|
||||
cd $(dirname "${0}")
|
||||
java -Xmx4096m -Dfile.encoding=UTF-8 -jar $project.build.finalName$
|
||||
|
||||
@@ -82,7 +82,7 @@ public class DeckStarRenderer extends ItemCellRenderer {
|
||||
}
|
||||
else if (DeckPreferences.getPrefs(deck).getStarCount() == 0) {
|
||||
this.setToolTipText("Click to add " + deck.getName() + " to your favorites");
|
||||
skinImage = FSkin.getImage(FSkinProp.IMG_STAR_OUTINE);
|
||||
skinImage = FSkin.getImage(FSkinProp.IMG_STAR_OUTLINE);
|
||||
}
|
||||
else { //TODO: consider supporting more than 1 star
|
||||
this.setToolTipText("Click to remove " + deck.getName() + " from your favorites");
|
||||
|
||||
@@ -83,7 +83,7 @@ public class StarRenderer extends ItemCellRenderer {
|
||||
}
|
||||
else if (CardPreferences.getPrefs(card).getStarCount() == 0) {
|
||||
this.setToolTipText("Click to add " + card.getName() + " to your favorites");
|
||||
skinImage = FSkin.getImage(FSkinProp.IMG_STAR_OUTINE);
|
||||
skinImage = FSkin.getImage(FSkinProp.IMG_STAR_OUTLINE);
|
||||
}
|
||||
else { //TODO: consider supporting more than 1 star
|
||||
this.setToolTipText("Click to remove " + card.getName() + " from your favorites");
|
||||
|
||||
@@ -103,7 +103,7 @@ public final class CEditorConstructed extends CDeckEditor<Deck> {
|
||||
case TinyLeaders:
|
||||
allSections.add(DeckSection.Commander);
|
||||
|
||||
commanderFilter = CardRulesPredicates.Presets.CAN_BE_COMMANDER;
|
||||
commanderFilter = CardRulesPredicates.Presets.CAN_BE_TINY_LEADERS_COMMANDER;
|
||||
commanderPool = ItemPool.createFrom(FModel.getMagicDb().getCommonCards().getAllCards(Predicates.compose(commanderFilter, PaperCard.FN_GET_RULES)), PaperCard.class);
|
||||
normalPool = ItemPool.createFrom(FModel.getMagicDb().getCommonCards().getAllCards(), PaperCard.class);
|
||||
|
||||
|
||||
@@ -23,6 +23,13 @@ public enum CSubmenuDownloaders implements ICDoc {
|
||||
VSubmenuDownloaders.SINGLETON_INSTANCE.showLicensing();
|
||||
}
|
||||
};
|
||||
private final UiCommand cmdCheckForUpdates = new UiCommand() {
|
||||
@Override
|
||||
public void run() {
|
||||
new AutoUpdater(false).attemptToUpdate();
|
||||
}
|
||||
};
|
||||
|
||||
private final UiCommand cmdPicDownload = new UiCommand() {
|
||||
@Override public void run() {
|
||||
new GuiDownloader(new GuiDownloadPicturesLQ()).show();
|
||||
@@ -84,6 +91,7 @@ public enum CSubmenuDownloaders implements ICDoc {
|
||||
@Override
|
||||
public void initialize() {
|
||||
final VSubmenuDownloaders view = VSubmenuDownloaders.SINGLETON_INSTANCE;
|
||||
view.setCheckForUpdatesCommand(cmdCheckForUpdates);
|
||||
view.setDownloadPicsCommand(cmdPicDownload);
|
||||
view.setDownloadPicsHQCommand(cmdPicDownloadHQ);
|
||||
view.setDownloadSetPicsCommand(cmdSetDownload);
|
||||
|
||||
@@ -3,6 +3,7 @@ package forge.screens.home.settings;
|
||||
import forge.*;
|
||||
import forge.ai.AiProfileUtil;
|
||||
import forge.control.FControl.CloseAction;
|
||||
import forge.download.AutoUpdater;
|
||||
import forge.game.GameLogEntryType;
|
||||
import forge.gui.framework.FScreen;
|
||||
import forge.gui.framework.ICDoc;
|
||||
@@ -225,6 +226,7 @@ public enum CSubmenuPreferences implements ICDoc {
|
||||
initializeGameLogVerbosityComboBox();
|
||||
initializeCloseActionComboBox();
|
||||
initializeDefaultFontSizeComboBox();
|
||||
initializeAutoUpdaterComboBox();
|
||||
initializeMulliganRuleComboBox();
|
||||
initializeAiProfilesComboBox();
|
||||
initializeStackAdditionsComboBox();
|
||||
@@ -378,6 +380,16 @@ public enum CSubmenuPreferences implements ICDoc {
|
||||
panel.setComboBox(comboBox, selectedItem);
|
||||
}
|
||||
|
||||
private void initializeAutoUpdaterComboBox() {
|
||||
// TODO: Ideally we would filter out update paths based on the type of Forge people have
|
||||
final String[] updatePaths = AutoUpdater.updateChannels;
|
||||
final FPref updatePreference = FPref.AUTO_UPDATE;
|
||||
final FComboBoxPanel<String> panel = this.view.getCbpAutoUpdater();
|
||||
final FComboBox<String> comboBox = createComboBox(updatePaths, updatePreference);
|
||||
final String selectedItem = this.prefs.getPref(updatePreference);
|
||||
panel.setComboBox(comboBox, selectedItem);
|
||||
}
|
||||
|
||||
private void initializeMulliganRuleComboBox() {
|
||||
final String [] choices = MulliganDefs.getMulliganRuleNames();
|
||||
final FPref userSetting = FPref.MULLIGAN_RULE;
|
||||
|
||||
@@ -55,6 +55,7 @@ public enum VSubmenuDownloaders implements IVSubmenu<CSubmenuDownloaders> {
|
||||
private final JPanel pnlContent = new JPanel(new MigLayout("insets 0, gap 0, wrap, ay center"));
|
||||
private final FScrollPane scrContent = new FScrollPane(pnlContent, false);
|
||||
|
||||
private final FLabel btnCheckForUpdates = _makeButton(localizer.getMessage("btnCheckForUpdates"));
|
||||
private final FLabel btnDownloadSetPics = _makeButton(localizer.getMessage("btnDownloadSetPics"));
|
||||
private final FLabel btnDownloadPics = _makeButton(localizer.getMessage("btnDownloadPics"));
|
||||
private final FLabel btnDownloadPicsHQ = _makeButton(localizer.getMessage("btnDownloadPicsHQ"));
|
||||
@@ -80,6 +81,9 @@ public enum VSubmenuDownloaders implements IVSubmenu<CSubmenuDownloaders> {
|
||||
|
||||
if (javaRecentEnough()) {
|
||||
|
||||
pnlContent.add(btnCheckForUpdates, constraintsBTN);
|
||||
pnlContent.add(_makeLabel(localizer.getMessage("lblCheckForUpdates")), constraintsLBL);
|
||||
|
||||
pnlContent.add(btnDownloadPics, constraintsBTN);
|
||||
pnlContent.add(_makeLabel(localizer.getMessage("lblDownloadPics")), constraintsLBL);
|
||||
|
||||
@@ -162,6 +166,7 @@ public enum VSubmenuDownloaders implements IVSubmenu<CSubmenuDownloaders> {
|
||||
return EMenuGroup.SETTINGS;
|
||||
}
|
||||
|
||||
public void setCheckForUpdatesCommand(UiCommand command) { btnCheckForUpdates.setCommand(command); }
|
||||
public void setDownloadPicsCommand(UiCommand command) { btnDownloadPics.setCommand(command); }
|
||||
public void setDownloadPicsHQCommand(UiCommand command) { btnDownloadPicsHQ.setCommand(command); }
|
||||
public void setDownloadSetPicsCommand(UiCommand command) { btnDownloadSetPics.setCommand(command); }
|
||||
|
||||
@@ -25,8 +25,8 @@ import java.awt.event.FocusAdapter;
|
||||
import java.awt.event.FocusEvent;
|
||||
import java.awt.event.KeyAdapter;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
|
||||
/**
|
||||
@@ -123,6 +123,7 @@ public enum VSubmenuPreferences implements IVSubmenu<CSubmenuPreferences> {
|
||||
private final FComboBoxPanel<String> cbpCounterDisplayLocation =new FComboBoxPanel<>(localizer.getMessage("cbpCounterDisplayLocation")+":");
|
||||
private final FComboBoxPanel<String> cbpGraveyardOrdering = new FComboBoxPanel<>(localizer.getMessage("cbpGraveyardOrdering")+":");
|
||||
private final FComboBoxPanel<String> cbpDefaultLanguage = new FComboBoxPanel<>(localizer.getMessage("cbpSelectLanguage")+":");
|
||||
private final FComboBoxPanel<String> cbpAutoUpdater = new FComboBoxPanel<>(localizer.getMessage("cbpAutoUpdater")+":");
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
@@ -157,6 +158,10 @@ public enum VSubmenuPreferences implements IVSubmenu<CSubmenuPreferences> {
|
||||
pnlPrefs.add(new SectionLabel(localizer.getMessage("GeneralConfiguration")), sectionConstraints);
|
||||
|
||||
// language
|
||||
|
||||
pnlPrefs.add(cbpAutoUpdater, comboBoxConstraints);
|
||||
pnlPrefs.add(new NoteLabel(localizer.getMessage("nlAutoUpdater")), descriptionConstraints);
|
||||
|
||||
pnlPrefs.add(cbpDefaultLanguage, comboBoxConstraints);
|
||||
pnlPrefs.add(new NoteLabel(localizer.getMessage("nlSelectLanguage")), descriptionConstraints);
|
||||
|
||||
@@ -531,6 +536,10 @@ public enum VSubmenuPreferences implements IVSubmenu<CSubmenuPreferences> {
|
||||
}
|
||||
}
|
||||
|
||||
public final FComboBoxPanel<String> getCbpAutoUpdater() {
|
||||
return cbpAutoUpdater;
|
||||
}
|
||||
|
||||
/** @return {@link javax.swing.JCheckBox} */
|
||||
public final JCheckBox getCbCompactMainMenu() {
|
||||
return cbCompactMainMenu;
|
||||
|
||||
@@ -1162,7 +1162,7 @@ public class FSkin {
|
||||
}
|
||||
|
||||
final Localizer localizer = Localizer.getInstance();
|
||||
FView.SINGLETON_INSTANCE.setSplashProgessBarMessage(localizer.getMessage("splash.loading.processingimagesprites") + ": ", 8);
|
||||
FView.SINGLETON_INSTANCE.setSplashProgessBarMessage(localizer.getMessage("splash.loading.processingimagesprites") + ": ", 12);
|
||||
|
||||
// Grab and test various sprite files.
|
||||
final String defaultDir = ForgeConstants.DEFAULT_SKINS_DIR;
|
||||
|
||||
@@ -81,7 +81,7 @@ public final class Main {
|
||||
break;
|
||||
|
||||
default:
|
||||
System.out.println("Unknown mode.\nKnown mode is 'sim' ");
|
||||
System.out.println("Unknown mode.\nKnown mode is 'sim', 'parse' ");
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -486,7 +486,7 @@ public class PlayerControllerForTests extends PlayerController {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object vote(SpellAbility sa, String prompt, List<Object> options, ListMultimap<Object, Player> votes) {
|
||||
public Object vote(SpellAbility sa, String prompt, List<Object> options, ListMultimap<Object, Player> votes, Player forPlayer) {
|
||||
return chooseItem(options);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<parent>
|
||||
<artifactId>forge</artifactId>
|
||||
<groupId>forge</groupId>
|
||||
<version>1.6.33-SNAPSHOT</version>
|
||||
<version>1.6.34-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>forge-gui-ios</artifactId>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>forge</artifactId>
|
||||
<groupId>forge</groupId>
|
||||
<version>1.6.33-SNAPSHOT</version>
|
||||
<version>1.6.34-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>forge-gui-mobile-dev</artifactId>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>forge</artifactId>
|
||||
<groupId>forge</groupId>
|
||||
<version>1.6.33-SNAPSHOT</version>
|
||||
<version>1.6.34-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>forge-gui-mobile</artifactId>
|
||||
|
||||
@@ -66,6 +66,8 @@ public class Forge implements ApplicationListener {
|
||||
public static boolean enableUIMask = false;
|
||||
public static boolean enablePreloadExtendedArt = false;
|
||||
public static String locale = "en-US";
|
||||
public static boolean hdbuttons = false;
|
||||
public static boolean hdstart = false;
|
||||
|
||||
public static ApplicationListener getApp(Clipboard clipboard0, IDeviceAdapter deviceAdapter0, String assetDir0, boolean value) {
|
||||
if (GuiBase.getInterface() == null) {
|
||||
|
||||
@@ -90,6 +90,10 @@ public class FSkin {
|
||||
public static void loadLight(String skinName, final SplashScreen splashScreen) {
|
||||
preferredName = skinName.toLowerCase().replace(' ', '_');
|
||||
|
||||
//reset hd buttons/icons
|
||||
Forge.hdbuttons = false;
|
||||
Forge.hdstart = false;
|
||||
|
||||
//ensure skins directory exists
|
||||
final FileHandle dir = Gdx.files.absolute(ForgeConstants.SKINS_DIR);
|
||||
if (!dir.exists() || !dir.isDirectory()) {
|
||||
@@ -190,6 +194,8 @@ public class FSkin {
|
||||
final FileHandle f8 = getDefaultSkinFile(ForgeConstants.SPRITE_SLEEVES_FILE);
|
||||
final FileHandle f9 = getDefaultSkinFile(ForgeConstants.SPRITE_SLEEVES2_FILE);
|
||||
final FileHandle f10 = getDefaultSkinFile(ForgeConstants.SPRITE_BORDER_FILE);
|
||||
final FileHandle f11 = getSkinFile(ForgeConstants.SPRITE_BUTTONS_FILE);
|
||||
final FileHandle f12 = getSkinFile(ForgeConstants.SPRITE_START_FILE);
|
||||
|
||||
try {
|
||||
textures.put(f1.path(), new Texture(f1));
|
||||
@@ -204,10 +210,22 @@ public class FSkin {
|
||||
}
|
||||
if (f7.exists()){
|
||||
Texture t = new Texture(f7, true);
|
||||
t.setFilter(Texture.TextureFilter.MipMapLinearLinear, Texture.TextureFilter.Linear);
|
||||
//t.setFilter(Texture.TextureFilter.MipMapLinearLinear, Texture.TextureFilter.Linear);
|
||||
textures.put(f7.path(), t);
|
||||
}
|
||||
|
||||
//hdbuttons
|
||||
if (f11.exists()) {
|
||||
Texture t = new Texture(f11, true);
|
||||
t.setFilter(Texture.TextureFilter.MipMapLinearLinear, Texture.TextureFilter.Linear);
|
||||
textures.put(f11.path(), t);
|
||||
Forge.hdbuttons = true;
|
||||
} else { Forge.hdbuttons = false; } //how to refresh buttons when a theme don't have hd buttons?
|
||||
if (f12.exists()) {
|
||||
Texture t = new Texture(f12, true);
|
||||
t.setFilter(Texture.TextureFilter.MipMapLinearLinear, Texture.TextureFilter.Linear);
|
||||
textures.put(f12.path(), t);
|
||||
Forge.hdstart = true;
|
||||
} else { Forge.hdstart = false; }
|
||||
//update colors
|
||||
for (final FSkinColor.Colors c : FSkinColor.Colors.values()) {
|
||||
c.setColor(new Color(preferredIcons.getPixel(c.getX(), c.getY())));
|
||||
|
||||
@@ -19,10 +19,22 @@ import forge.util.ImageUtil;
|
||||
public enum FSkinImage implements FImage {
|
||||
//Zones
|
||||
HAND (FSkinProp.IMG_ZONE_HAND, SourceFile.ICONS),
|
||||
HDHAND (FSkinProp.IMG_HDZONE_HAND, SourceFile.BUTTONS),
|
||||
|
||||
LIBRARY (FSkinProp.IMG_ZONE_LIBRARY, SourceFile.ICONS),
|
||||
HDLIBRARY (FSkinProp.IMG_HDZONE_LIBRARY, SourceFile.BUTTONS),
|
||||
|
||||
EXILE (FSkinProp.IMG_ZONE_EXILE, SourceFile.ICONS),
|
||||
HDEXILE (FSkinProp.IMG_HDZONE_EXILE, SourceFile.BUTTONS),
|
||||
|
||||
FLASHBACK (FSkinProp.IMG_ZONE_FLASHBACK, SourceFile.ICONS),
|
||||
HDFLASHBACK (FSkinProp.IMG_HDZONE_FLASHBACK, SourceFile.BUTTONS),
|
||||
|
||||
GRAVEYARD (FSkinProp.IMG_ZONE_GRAVEYARD, SourceFile.ICONS),
|
||||
HDGRAVEYARD (FSkinProp.IMG_HDZONE_GRAVEYARD, SourceFile.BUTTONS),
|
||||
|
||||
HDMANAPOOL (FSkinProp.IMG_HDZONE_MANAPOOL, SourceFile.BUTTONS),
|
||||
|
||||
POISON (FSkinProp.IMG_ZONE_POISON, SourceFile.ICONS),
|
||||
|
||||
//Mana symbols
|
||||
@@ -114,6 +126,14 @@ public enum FSkinImage implements FImage {
|
||||
ARCSON (FSkinProp.ICO_ARCSON, SourceFile.ICONS),
|
||||
ARCSHOVER (FSkinProp.ICO_ARCSHOVER, SourceFile.ICONS),
|
||||
|
||||
//choice-search-misc
|
||||
HDCHOICE (FSkinProp.ICO_HDCHOICE, SourceFile.BUTTONS),
|
||||
HDSIDEBOARD (FSkinProp.ICO_HDSIDEBOARD, SourceFile.BUTTONS),
|
||||
HDPREFERENCE (FSkinProp.ICO_HDPREFERENCE, SourceFile.BUTTONS),
|
||||
HDIMPORT (FSkinProp.ICO_HDIMPORT, SourceFile.BUTTONS),
|
||||
HDEXPORT (FSkinProp.ICO_HDEXPORT, SourceFile.BUTTONS),
|
||||
BLANK (FSkinProp.ICO_BLANK, SourceFile.ICONS),
|
||||
|
||||
//Achievement Trophies
|
||||
COMMON_TROPHY (FSkinProp.IMG_COMMON_TROPHY, SourceFile.TROPHIES),
|
||||
UNCOMMON_TROPHY (FSkinProp.IMG_UNCOMMON_TROPHY, SourceFile.TROPHIES),
|
||||
@@ -156,31 +176,82 @@ public enum FSkinImage implements FImage {
|
||||
QUEST_MINUS (FSkinProp.ICO_QUEST_MINUS, SourceFile.ICONS),
|
||||
QUEST_PLUS (FSkinProp.ICO_QUEST_PLUS, SourceFile.ICONS),
|
||||
QUEST_PLUSPLUS (FSkinProp.ICO_QUEST_PLUSPLUS, SourceFile.ICONS),
|
||||
QUEST_BIG_ELIXIR (FSkinProp.ICO_QUEST_BIG_ELIXIR, SourceFile.ICONS),
|
||||
QUEST_BIG_BREW (FSkinProp.ICO_QUEST_BIG_BREW, SourceFile.ICONS),
|
||||
QUEST_BIG_BM (FSkinProp.ICO_QUEST_BIG_BM, SourceFile.ICONS),
|
||||
QUEST_BIG_STAKES (FSkinProp.ICO_QUEST_BIG_STAKES, SourceFile.ICONS),
|
||||
QUEST_BIG_HOUSE (FSkinProp.ICO_QUEST_BIG_HOUSE, SourceFile.ICONS),
|
||||
QUEST_BIG_COIN (FSkinProp.ICO_QUEST_BIG_COIN, SourceFile.ICONS),
|
||||
QUEST_BIG_BOOK (FSkinProp.ICO_QUEST_BIG_BOOK, SourceFile.ICONS),
|
||||
QUEST_BIG_MAP (FSkinProp.ICO_QUEST_BIG_MAP, SourceFile.ICONS),
|
||||
QUEST_BIG_ZEP (FSkinProp.ICO_QUEST_BIG_ZEP, SourceFile.ICONS),
|
||||
QUEST_BIG_CHARM (FSkinProp.ICO_QUEST_BIG_CHARM, SourceFile.ICONS),
|
||||
QUEST_BIG_BOOTS (FSkinProp.ICO_QUEST_BIG_BOOTS, SourceFile.ICONS),
|
||||
QUEST_BIG_SHIELD (FSkinProp.ICO_QUEST_BIG_SHIELD, SourceFile.ICONS),
|
||||
QUEST_BIG_ARMOR (FSkinProp.ICO_QUEST_BIG_ARMOR, SourceFile.ICONS),
|
||||
QUEST_BIG_AXE (FSkinProp.ICO_QUEST_BIG_AXE, SourceFile.ICONS),
|
||||
QUEST_BIG_SWORD (FSkinProp.ICO_QUEST_BIG_SWORD, SourceFile.ICONS),
|
||||
QUEST_BIG_BAG (FSkinProp.ICO_QUEST_BIG_BAG, SourceFile.ICONS),
|
||||
|
||||
//menu icon
|
||||
MENU_GALAXY (FSkinProp.ICO_MENU_GALAXY, SourceFile.ICONS),
|
||||
MENU_STATS (FSkinProp.ICO_MENU_STATS, SourceFile.ICONS),
|
||||
MENU_PUZZLE (FSkinProp.ICO_MENU_PUZZLE, SourceFile.ICONS),
|
||||
MENU_GAUNTLET (FSkinProp.ICO_MENU_GAUNTLET, SourceFile.ICONS),
|
||||
MENU_SEALED (FSkinProp.ICO_MENU_SEALED, SourceFile.ICONS),
|
||||
MENU_DRAFT (FSkinProp.ICO_MENU_DRAFT, SourceFile.ICONS),
|
||||
MENU_CONSTRUCTED (FSkinProp.ICO_MENU_CONSTRUCTED, SourceFile.ICONS),
|
||||
|
||||
//Interface icons
|
||||
QUESTION (FSkinProp.ICO_QUESTION, SourceFile.ICONS),
|
||||
INFORMATION (FSkinProp.ICO_INFORMATION, SourceFile.ICONS),
|
||||
WARNING (FSkinProp.ICO_WARNING, SourceFile.ICONS),
|
||||
ERROR (FSkinProp.ICO_ERROR, SourceFile.ICONS),
|
||||
|
||||
DELETE (FSkinProp.ICO_DELETE, SourceFile.ICONS),
|
||||
HDDELETE (FSkinProp.ICO_HDDELETE, SourceFile.BUTTONS),
|
||||
|
||||
DELETE_OVER (FSkinProp.ICO_DELETE_OVER, SourceFile.ICONS),
|
||||
|
||||
EDIT (FSkinProp.ICO_EDIT, SourceFile.ICONS),
|
||||
HDEDIT (FSkinProp.ICO_HDEDIT, SourceFile.BUTTONS),
|
||||
|
||||
EDIT_OVER (FSkinProp.ICO_EDIT_OVER, SourceFile.ICONS),
|
||||
|
||||
OPEN (FSkinProp.ICO_OPEN, SourceFile.ICONS),
|
||||
HDOPEN (FSkinProp.ICO_HDOPEN, SourceFile.BUTTONS),
|
||||
|
||||
MINUS (FSkinProp.ICO_MINUS, SourceFile.ICONS),
|
||||
HDMINUS (FSkinProp.ICO_HDMINUS, SourceFile.BUTTONS),
|
||||
|
||||
NEW (FSkinProp.ICO_NEW, SourceFile.ICONS),
|
||||
|
||||
PLUS (FSkinProp.ICO_PLUS, SourceFile.ICONS),
|
||||
HDPLUS (FSkinProp.ICO_HDPLUS, SourceFile.BUTTONS),
|
||||
|
||||
PRINT (FSkinProp.ICO_PRINT, SourceFile.ICONS),
|
||||
|
||||
SAVE (FSkinProp.ICO_SAVE, SourceFile.ICONS),
|
||||
HDSAVE (FSkinProp.ICO_HDSAVE, SourceFile.BUTTONS),
|
||||
SAVEAS (FSkinProp.ICO_SAVEAS, SourceFile.ICONS),
|
||||
HDSAVEAS (FSkinProp.ICO_HDSAVEAS, SourceFile.BUTTONS),
|
||||
|
||||
CLOSE (FSkinProp.ICO_CLOSE, SourceFile.ICONS),
|
||||
LIST (FSkinProp.ICO_LIST, SourceFile.ICONS),
|
||||
CARD_IMAGE (FSkinProp.ICO_CARD_IMAGE, SourceFile.ICONS),
|
||||
|
||||
FOLDER (FSkinProp.ICO_FOLDER, SourceFile.ICONS),
|
||||
HDFOLDER (FSkinProp.ICO_HDFOLDER, SourceFile.BUTTONS),
|
||||
|
||||
SEARCH (FSkinProp.ICO_SEARCH, SourceFile.ICONS),
|
||||
HDSEARCH (FSkinProp.ICO_HDSEARCH, SourceFile.BUTTONS),
|
||||
|
||||
UNKNOWN (FSkinProp.ICO_UNKNOWN, SourceFile.ICONS),
|
||||
LOGO (FSkinProp.ICO_LOGO, SourceFile.ICONS),
|
||||
|
||||
FLIPCARD (FSkinProp.ICO_FLIPCARD, SourceFile.ICONS),
|
||||
HDFLIPCARD (FSkinProp.ICO_HDFLIPCARD, SourceFile.BUTTONS),
|
||||
|
||||
FAVICON (FSkinProp.ICO_FAVICON, SourceFile.ICONS),
|
||||
LOCK (FSkinProp.ICO_LOCK, SourceFile.ICONS),
|
||||
|
||||
@@ -193,14 +264,19 @@ public enum FSkinImage implements FImage {
|
||||
CUR_TAB (FSkinProp.IMG_CUR_TAB, SourceFile.ICONS),
|
||||
|
||||
//Editor images
|
||||
STAR_OUTINE (FSkinProp.IMG_STAR_OUTINE, SourceFile.ICONS),
|
||||
STAR_OUTLINE (FSkinProp.IMG_STAR_OUTLINE, SourceFile.ICONS),
|
||||
HDSTAR_OUTLINE (FSkinProp.IMG_HDSTAR_OUTLINE, SourceFile.BUTTONS),
|
||||
STAR_FILLED (FSkinProp.IMG_STAR_FILLED, SourceFile.ICONS),
|
||||
HDSTAR_FILLED (FSkinProp.IMG_HDSTAR_FILLED, SourceFile.BUTTONS),
|
||||
|
||||
ARTIFACT (FSkinProp.IMG_ARTIFACT, SourceFile.MANAICONS),
|
||||
CREATURE (FSkinProp.IMG_CREATURE, SourceFile.MANAICONS),
|
||||
ENCHANTMENT (FSkinProp.IMG_ENCHANTMENT, SourceFile.MANAICONS),
|
||||
INSTANT (FSkinProp.IMG_INSTANT, SourceFile.MANAICONS),
|
||||
LAND (FSkinProp.IMG_LAND, SourceFile.MANAICONS),
|
||||
LANDLOGO (FSkinProp.IMG_LANDLOGO, SourceFile.MANAICONS),
|
||||
MULTI (FSkinProp.IMG_MULTI, SourceFile.ICONS),
|
||||
HDMULTI (FSkinProp.IMG_HDMULTI, SourceFile.MANAICONS),
|
||||
PLANESWALKER (FSkinProp.IMG_PLANESWALKER, SourceFile.MANAICONS),
|
||||
PACK (FSkinProp.IMG_PACK, SourceFile.ICONS),
|
||||
SORCERY (FSkinProp.IMG_SORCERY, SourceFile.MANAICONS),
|
||||
@@ -228,6 +304,28 @@ public enum FSkinImage implements FImage {
|
||||
BTN_DISABLED_LEFT (FSkinProp.IMG_BTN_DISABLED_LEFT, SourceFile.ICONS),
|
||||
BTN_DISABLED_CENTER (FSkinProp.IMG_BTN_DISABLED_CENTER, SourceFile.ICONS),
|
||||
BTN_DISABLED_RIGHT (FSkinProp.IMG_BTN_DISABLED_RIGHT, SourceFile.ICONS),
|
||||
//Hdbuttons
|
||||
HDBTN_START_UP (FSkinProp.IMG_HDBTN_START_UP, SourceFile.BTNSTART),
|
||||
HDBTN_START_OVER (FSkinProp.IMG_HDBTN_START_OVER, SourceFile.BTNSTART),
|
||||
HDBTN_START_DOWN (FSkinProp.IMG_HDBTN_START_DOWN, SourceFile.BTNSTART),
|
||||
HDBTN_UP_LEFT (FSkinProp.IMG_HDBTN_UP_LEFT, SourceFile.BUTTONS),
|
||||
HDBTN_UP_CENTER (FSkinProp.IMG_HDBTN_UP_CENTER, SourceFile.BUTTONS),
|
||||
HDBTN_UP_RIGHT (FSkinProp.IMG_HDBTN_UP_RIGHT, SourceFile.BUTTONS),
|
||||
HDBTN_OVER_LEFT (FSkinProp.IMG_HDBTN_OVER_LEFT, SourceFile.BUTTONS),
|
||||
HDBTN_OVER_CENTER (FSkinProp.IMG_HDBTN_OVER_CENTER, SourceFile.BUTTONS),
|
||||
HDBTN_OVER_RIGHT (FSkinProp.IMG_HDBTN_OVER_RIGHT, SourceFile.BUTTONS),
|
||||
HDBTN_DOWN_LEFT (FSkinProp.IMG_HDBTN_DOWN_LEFT, SourceFile.BUTTONS),
|
||||
HDBTN_DOWN_CENTER (FSkinProp.IMG_HDBTN_DOWN_CENTER, SourceFile.BUTTONS),
|
||||
HDBTN_DOWN_RIGHT (FSkinProp.IMG_HDBTN_DOWN_RIGHT, SourceFile.BUTTONS),
|
||||
HDBTN_FOCUS_LEFT (FSkinProp.IMG_HDBTN_FOCUS_LEFT, SourceFile.BUTTONS),
|
||||
HDBTN_FOCUS_CENTER (FSkinProp.IMG_HDBTN_FOCUS_CENTER, SourceFile.BUTTONS),
|
||||
HDBTN_FOCUS_RIGHT (FSkinProp.IMG_HDBTN_FOCUS_RIGHT, SourceFile.BUTTONS),
|
||||
HDBTN_TOGGLE_LEFT (FSkinProp.IMG_HDBTN_TOGGLE_LEFT, SourceFile.BUTTONS),
|
||||
HDBTN_TOGGLE_CENTER (FSkinProp.IMG_HDBTN_TOGGLE_CENTER, SourceFile.BUTTONS),
|
||||
HDBTN_TOGGLE_RIGHT (FSkinProp.IMG_HDBTN_TOGGLE_RIGHT, SourceFile.BUTTONS),
|
||||
HDBTN_DISABLED_LEFT (FSkinProp.IMG_HDBTN_DISABLED_LEFT, SourceFile.BUTTONS),
|
||||
HDBTN_DISABLED_CENTER (FSkinProp.IMG_HDBTN_DISABLED_CENTER, SourceFile.BUTTONS),
|
||||
HDBTN_DISABLED_RIGHT (FSkinProp.IMG_HDBTN_DISABLED_RIGHT, SourceFile.BUTTONS),
|
||||
|
||||
//Foils
|
||||
FOIL_01 (FSkinProp.FOIL_01, SourceFile.FOILS),
|
||||
@@ -312,6 +410,8 @@ public enum FSkinImage implements FImage {
|
||||
TROPHIES(ForgeConstants.SPRITE_TROPHIES_FILE),
|
||||
ABILITIES(ForgeConstants.SPRITE_ABILITY_FILE),
|
||||
BORDERS(ForgeConstants.SPRITE_BORDER_FILE),
|
||||
BUTTONS(ForgeConstants.SPRITE_BUTTONS_FILE),
|
||||
BTNSTART(ForgeConstants.SPRITE_START_FILE),
|
||||
MANAICONS(ForgeConstants.SPRITE_MANAICONS_FILE),
|
||||
PLANAR_CONQUEST(ForgeConstants.SPRITE_PLANAR_CONQUEST_FILE);
|
||||
|
||||
|
||||
@@ -164,7 +164,7 @@ public class ImageCache {
|
||||
}
|
||||
return image;
|
||||
}
|
||||
public static void preloadCache(Iterable keys) {
|
||||
public static void preloadCache(Iterable<String> keys) {
|
||||
cache.getAll(keys);
|
||||
}
|
||||
public static TextureRegion croppedBorderImage(Texture image, boolean fullborder) {
|
||||
@@ -173,10 +173,9 @@ public class ImageCache {
|
||||
float rscale = 0.96f;
|
||||
int rw = Math.round(image.getWidth()*rscale);
|
||||
int rh = Math.round(image.getHeight()*rscale);
|
||||
int rx = Math.round((image.getWidth() - rw)/2);
|
||||
int ry = Math.round((image.getHeight() - rh)/2)-2;
|
||||
TextureRegion rimage = new TextureRegion(image, rx, ry, rw, rh);
|
||||
return rimage;
|
||||
int rx = Math.round((image.getWidth() - rw)/2f);
|
||||
int ry = Math.round((image.getHeight() - rh)/2f)-2;
|
||||
return new TextureRegion(image, rx, ry, rw, rh);
|
||||
}
|
||||
public static boolean isWhiteBordered(IPaperCard c) {
|
||||
if (c == null)
|
||||
|
||||
@@ -35,6 +35,9 @@ import org.apache.commons.lang3.StringUtils;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static forge.card.CardRenderer.CROP_MULTIPLIER;
|
||||
import static forge.card.CardRenderer.isModernFrame;
|
||||
|
||||
public class CardImageRenderer {
|
||||
private static final float BASE_IMAGE_WIDTH = 360;
|
||||
private static final float BASE_IMAGE_HEIGHT = 504;
|
||||
@@ -357,13 +360,19 @@ public class CardImageRenderer {
|
||||
float new_yRotate = (dispH - new_w) /2;
|
||||
boolean rotateSplit = FModel.getPreferences().getPrefBoolean(ForgePreferences.FPref.UI_ROTATE_SPLIT_CARDS);
|
||||
boolean rotatePlane = FModel.getPreferences().getPrefBoolean(ForgePreferences.FPref.UI_ROTATE_PLANE_OR_PHENOMENON);
|
||||
float croppedArea = isModernFrame(card) ? CROP_MULTIPLIER : 0.97f;
|
||||
float minusxy = isModernFrame(card) ? 0.0f : 0.13f*radius;
|
||||
if (card.getCurrentState().getSetCode().equals("LEA")||card.getCurrentState().getSetCode().equals("LEB")) {
|
||||
croppedArea = 0.975f;
|
||||
minusxy = 0.135f*radius;
|
||||
}
|
||||
if (rotatePlane && (card.getCurrentState().isPhenomenon() || card.getCurrentState().isPlane())) {
|
||||
if (Forge.enableUIMask){
|
||||
if (ImageCache.isExtendedArt(card))
|
||||
g.drawRotatedImage(image, new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, -90);
|
||||
else {
|
||||
g.drawRotatedImage(FSkin.getBorders().get(0), new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, -90);
|
||||
g.drawRotatedImage(ImageCache.croppedBorderImage(image, fullborder), new_x+radius/2, new_y+radius/2, new_w*0.96f, new_h*0.96f, (new_x+radius/2) + (new_w*0.96f) / 2, (new_y+radius/2) + (new_h*0.96f) / 2, -90);
|
||||
g.drawRotatedImage(ImageCache.croppedBorderImage(image, fullborder), new_x+radius/2-minusxy, new_y+radius/2-minusxy, new_w*croppedArea, new_h*croppedArea, (new_x+radius/2-minusxy) + (new_w*croppedArea) / 2, (new_y+radius/2-minusxy) + (new_h*croppedArea) / 2, -90);
|
||||
}
|
||||
} else
|
||||
g.drawRotatedImage(image, new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, -90);
|
||||
@@ -374,7 +383,7 @@ public class CardImageRenderer {
|
||||
g.drawRotatedImage(image, new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, isAftermath ? 90 : -90);
|
||||
else {
|
||||
g.drawRotatedImage(FSkin.getBorders().get(ImageCache.getFSkinBorders(card)), new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, isAftermath ? 90 : -90);
|
||||
g.drawRotatedImage(ImageCache.croppedBorderImage(image, fullborder), new_x + radius / 2, new_y + radius / 2, new_w * 0.96f, new_h * 0.96f, (new_x + radius / 2) + (new_w * 0.96f) / 2, (new_y + radius / 2) + (new_h * 0.96f) / 2, isAftermath ? 90 : -90);
|
||||
g.drawRotatedImage(ImageCache.croppedBorderImage(image, fullborder), new_x + radius / 2-minusxy, new_y + radius / 2-minusxy, new_w * croppedArea, new_h * croppedArea, (new_x + radius / 2-minusxy) + (new_w * croppedArea) / 2, (new_y + radius / 2-minusxy) + (new_h * croppedArea) / 2, isAftermath ? 90 : -90);
|
||||
}
|
||||
} else
|
||||
g.drawRotatedImage(image, new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, isAftermath ? 90 : -90);
|
||||
@@ -384,7 +393,7 @@ public class CardImageRenderer {
|
||||
g.drawImage(image, x, y, w, h);
|
||||
else {
|
||||
g.drawImage(ImageCache.getBorderImage(card, canshow), x, y, w, h);
|
||||
g.drawImage(ImageCache.croppedBorderImage(image, fullborder), x + radius / 2.4f, y + radius / 2, w * 0.96f, h * 0.96f);
|
||||
g.drawImage(ImageCache.croppedBorderImage(image, fullborder), x + radius / 2.4f-minusxy, y + radius / 2-minusxy, w * croppedArea, h * croppedArea);
|
||||
}
|
||||
} else {
|
||||
if (canshow && !ImageKeys.getTokenKey(ImageKeys.MORPH_IMAGE).equals(card.getState(altState).getImageKey()))
|
||||
|
||||
@@ -105,6 +105,7 @@ public class CardRenderer {
|
||||
private static final float NAME_COST_THRESHOLD = Utils.scale(200);
|
||||
private static final float BORDER_THICKNESS = Utils.scale(1);
|
||||
public static final float PADDING_MULTIPLIER = 0.021f;
|
||||
public static final float CROP_MULTIPLIER = 0.96f;
|
||||
|
||||
private static Map<Integer, BitmapFont> counterFonts = new HashMap<>();
|
||||
private static final Color counterBackgroundColor = new Color(0f, 0f, 0f, 0.9f);
|
||||
@@ -142,6 +143,49 @@ public class CardRenderer {
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isModernFrame(IPaperCard c) {
|
||||
if (c == null)
|
||||
return false;
|
||||
|
||||
CardEdition ed = FModel.getMagicDb().getEditions().get(c.getEdition());
|
||||
if (ed != null) {
|
||||
switch (ed.getCode()) {
|
||||
case "MED":
|
||||
case "ME2":
|
||||
case "ME3":
|
||||
case "ME4":
|
||||
case "TSB":
|
||||
return false;
|
||||
default:
|
||||
return ed.isModern();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean isModernFrame(CardView c) {
|
||||
if (c == null)
|
||||
return false;
|
||||
|
||||
CardView.CardStateView state = c.getCurrentState();
|
||||
CardEdition ed = FModel.getMagicDb().getEditions().get(state.getSetCode());
|
||||
if (ed != null) {
|
||||
switch (ed.getCode()) {
|
||||
case "MED":
|
||||
case "ME2":
|
||||
case "ME3":
|
||||
case "ME4":
|
||||
case "TSB":
|
||||
return false;
|
||||
default:
|
||||
return ed.isModern();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static float getCardListItemHeight(boolean compactMode) {
|
||||
if (compactMode) {
|
||||
return MANA_SYMBOL_SIZE + 2 * FList.PADDING;
|
||||
@@ -402,7 +446,12 @@ public class CardRenderer {
|
||||
public static void drawCard(Graphics g, IPaperCard pc, float x, float y, float w, float h, CardStackPosition pos) {
|
||||
Texture image = new RendererCachedCardImage(pc, false).getImage();
|
||||
float radius = (h - w)/8;
|
||||
|
||||
float croppedArea = isModernFrame(pc) ? CROP_MULTIPLIER : 0.97f;
|
||||
float minusxy = isModernFrame(pc) ? 0.0f : 0.13f*radius;
|
||||
if (pc.getEdition().equals("LEA")||pc.getEdition().equals("LEB")) {
|
||||
croppedArea = 0.975f;
|
||||
minusxy = 0.135f*radius;
|
||||
}
|
||||
if (image != null) {
|
||||
if (image == ImageCache.defaultImage) {
|
||||
CardImageRenderer.drawCardImage(g, CardView.getCardForUi(pc), false, x, y, w, h, pos);
|
||||
@@ -413,7 +462,7 @@ public class CardRenderer {
|
||||
g.drawImage(image, x, y, w, h);
|
||||
else {
|
||||
g.drawImage(ImageCache.getBorderImage(pc), x, y, w, h);
|
||||
g.drawImage(ImageCache.croppedBorderImage(image, fullborder), x + radius / 2.4f, y + radius / 2, w * 0.96f, h * 0.96f);
|
||||
g.drawImage(ImageCache.croppedBorderImage(image, fullborder), x + radius / 2.4f-minusxy, y + radius / 2-minusxy, w * croppedArea, h * croppedArea);
|
||||
}
|
||||
} else
|
||||
g.drawImage(image, x, y, w, h);
|
||||
@@ -437,7 +486,12 @@ public class CardRenderer {
|
||||
Texture image = new RendererCachedCardImage(card, false).getImage();
|
||||
FImage sleeves = MatchController.getPlayerSleeve(card.getOwner());
|
||||
float radius = (h - w)/8;
|
||||
|
||||
float croppedArea = isModernFrame(card) ? CROP_MULTIPLIER : 0.97f;
|
||||
float minusxy = isModernFrame(card) ? 0.0f : 0.13f*radius;
|
||||
if (card.getCurrentState().getSetCode().equals("LEA")||card.getCurrentState().getSetCode().equals("LEB")) {
|
||||
croppedArea = 0.975f;
|
||||
minusxy = 0.135f*radius;
|
||||
}
|
||||
if (image != null) {
|
||||
if (image == ImageCache.defaultImage) {
|
||||
CardImageRenderer.drawCardImage(g, card, false, x, y, w, h, pos);
|
||||
@@ -450,7 +504,7 @@ public class CardRenderer {
|
||||
g.drawRotatedImage(image, x, y, w, h, x + w / 2, y + h / 2, -90);
|
||||
else {
|
||||
g.drawRotatedImage(FSkin.getBorders().get(0), x, y, w, h, x + w / 2, y + h / 2, -90);
|
||||
g.drawRotatedImage(ImageCache.croppedBorderImage(image, fullborder), x+radius/2.3f, y+radius/2, w*0.96f, h*0.96f, (x+radius/2.3f) + (w*0.96f) / 2, (y+radius/2) + (h*0.96f) / 2, -90);
|
||||
g.drawRotatedImage(ImageCache.croppedBorderImage(image, fullborder), x+radius/2.3f-minusxy, y+radius/2-minusxy, w*croppedArea, h*croppedArea, (x+radius/2.3f-minusxy) + (w*croppedArea) / 2, (y+radius/2-minusxy) + (h*croppedArea) / 2, -90);
|
||||
}
|
||||
} else
|
||||
g.drawRotatedImage(image, x, y, w, h, x + w / 2, y + h / 2, -90);
|
||||
@@ -461,7 +515,7 @@ public class CardRenderer {
|
||||
else {
|
||||
boolean t = (card.getCurrentState().getOriginalColors() != card.getCurrentState().getColors()) || card.getCurrentState().hasChangeColors();
|
||||
g.drawBorderImage(ImageCache.getBorderImage(card, canshow), ImageCache.getTint(card), x, y, w, h, t); //tint check for changed colors
|
||||
g.drawImage(ImageCache.croppedBorderImage(image, fullborder), x + radius / 2.4f, y + radius / 2, w * 0.96f, h * 0.96f);
|
||||
g.drawImage(ImageCache.croppedBorderImage(image, fullborder), x + radius / 2.4f-minusxy, y + radius / 2-minusxy, w * croppedArea, h * croppedArea);
|
||||
}
|
||||
} else {
|
||||
if (canshow)
|
||||
@@ -481,6 +535,9 @@ public class CardRenderer {
|
||||
}
|
||||
|
||||
public static void drawCardWithOverlays(Graphics g, CardView card, float x, float y, float w, float h, CardStackPosition pos) {
|
||||
drawCardWithOverlays(g, card, x, y, w, h, pos, false);
|
||||
}
|
||||
public static void drawCardWithOverlays(Graphics g, CardView card, float x, float y, float w, float h, CardStackPosition pos, boolean stackview) {
|
||||
boolean canShow = MatchController.instance.mayView(card);
|
||||
float oldAlpha = g.getfloatAlphaComposite();
|
||||
boolean unselectable = !MatchController.instance.isSelectable(card) && MatchController.instance.isSelecting();
|
||||
@@ -501,32 +558,10 @@ public class CardRenderer {
|
||||
Color color = FSkinColor.fromRGB(borderColor.r, borderColor.g, borderColor.b);
|
||||
color = FSkinColor.tintColor(Color.WHITE, color, CardRenderer.PT_BOX_TINT);
|
||||
|
||||
//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(CardTranslation.getTranslatedName(details.getName()), FSkinFont.forHeight(h * 0.15f), Color.WHITE, Color.BLACK, x + padding -1f, y + padding, w - 2 * padding, h * 0.4f, true, Align.left, false);
|
||||
}
|
||||
if (showCardManaCostOverlay(card)) {
|
||||
float manaSymbolSize = w / 4.5f;
|
||||
if (card.isSplitCard() && card.hasAlternateState()) {
|
||||
if (!card.isFaceDown()) { // no need to draw mana symbols on face down split cards (e.g. manifested)
|
||||
float dy = manaSymbolSize / 2 + Utils.scale(5);
|
||||
|
||||
PaperCard pc = StaticData.instance().getCommonCards().getCard(card.getName());
|
||||
if (Card.getCardForUi(pc).hasKeyword(Keyword.AFTERMATH)){
|
||||
dy *= -1; // flip card costs for Aftermath cards
|
||||
}
|
||||
|
||||
drawManaCost(g, card.getAlternateState().getManaCost(), x - padding, y - dy, w + 2 * padding, h, manaSymbolSize);
|
||||
drawManaCost(g, card.getCurrentState().getManaCost(), x - padding, y + dy, w + 2 * padding, h, manaSymbolSize);
|
||||
}
|
||||
}
|
||||
else {
|
||||
drawManaCost(g, card.getCurrentState().getManaCost(), x - padding, y, w + 2 * padding, h, manaSymbolSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
//card name && manacost original position is here moved at the bottom...
|
||||
|
||||
if (stackview)
|
||||
return; //override
|
||||
if (pos == CardStackPosition.BehindVert) { return; } //remaining rendering not needed if card is behind another card in a vertical stack
|
||||
boolean onTop = (pos == CardStackPosition.Top);
|
||||
|
||||
@@ -602,6 +637,7 @@ public class CardRenderer {
|
||||
float abiSpace = cw / 5.7f;
|
||||
float abiCount = 0;
|
||||
|
||||
if (unselectable){ g.setAlphaComposite(0.6f); }
|
||||
if (onbattlefield && onTop && showAbilityIcons(card)) {
|
||||
if (card.isToken()){
|
||||
CardFaceSymbols.drawSymbol("token", g, abiX, abiY, abiScale, abiScale);
|
||||
@@ -610,85 +646,63 @@ public class CardRenderer {
|
||||
}
|
||||
if (card.getCurrentState().hasFlying()) {
|
||||
CardFaceSymbols.drawSymbol("flying", g, abiX, abiY, abiScale, abiScale);
|
||||
if (unselectable){
|
||||
g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
|
||||
abiY += abiSpace;
|
||||
abiCount += 1;
|
||||
}
|
||||
if (card.getCurrentState().hasHaste()) {
|
||||
CardFaceSymbols.drawSymbol("haste", g, abiX, abiY, abiScale, abiScale);
|
||||
if (unselectable){
|
||||
g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
|
||||
abiY += abiSpace;
|
||||
abiCount += 1;
|
||||
}
|
||||
if (card.getCurrentState().hasDoubleStrike()) {
|
||||
CardFaceSymbols.drawSymbol("doublestrike", g, abiX, abiY, abiScale, abiScale);
|
||||
if (unselectable){
|
||||
g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
|
||||
abiY += abiSpace;
|
||||
abiCount += 1;
|
||||
}
|
||||
else if (card.getCurrentState().hasFirstStrike()) {
|
||||
CardFaceSymbols.drawSymbol("firststrike", g, abiX, abiY, abiScale, abiScale);
|
||||
if (unselectable){
|
||||
g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
|
||||
abiY += abiSpace;
|
||||
abiCount += 1;
|
||||
}
|
||||
if (card.getCurrentState().hasDeathtouch()) {
|
||||
if (abiCount > 5 ) { abiY = cy + (abiSpace * (abiCount - 6)); abiX = cx + ((cw*2)/1.92f); }
|
||||
CardFaceSymbols.drawSymbol("deathtouch", g, abiX, abiY, abiScale, abiScale);
|
||||
if (unselectable){
|
||||
g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
|
||||
abiY += abiSpace;
|
||||
abiCount += 1;
|
||||
}
|
||||
if (card.getCurrentState().hasIndestructible()) {
|
||||
if (abiCount > 5 ) { abiY = cy + (abiSpace * (abiCount - 6)); abiX = cx + ((cw*2)/1.92f); }
|
||||
CardFaceSymbols.drawSymbol("indestructible", g, abiX, abiY, abiScale, abiScale);
|
||||
if (unselectable){
|
||||
g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
|
||||
abiY += abiSpace;
|
||||
abiCount += 1;
|
||||
}
|
||||
if (card.getCurrentState().hasMenace()) {
|
||||
if (abiCount > 5 ) { abiY = cy + (abiSpace * (abiCount - 6)); abiX = cx + ((cw*2)/1.92f); }
|
||||
CardFaceSymbols.drawSymbol("menace", g, abiX, abiY, abiScale, abiScale);
|
||||
if (unselectable){
|
||||
g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
|
||||
abiY += abiSpace;
|
||||
abiCount += 1;
|
||||
}
|
||||
if (card.getCurrentState().hasFear()) {
|
||||
if (abiCount > 5 ) { abiY = cy + (abiSpace * (abiCount - 6)); abiX = cx + ((cw*2)/1.92f); }
|
||||
CardFaceSymbols.drawSymbol("fear", g, abiX, abiY, abiScale, abiScale);
|
||||
if (unselectable){
|
||||
g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
|
||||
abiY += abiSpace;
|
||||
abiCount += 1;
|
||||
}
|
||||
if (card.getCurrentState().hasIntimidate()) {
|
||||
if (abiCount > 5 ) { abiY = cy + (abiSpace * (abiCount - 6)); abiX = cx + ((cw*2)/1.92f); }
|
||||
CardFaceSymbols.drawSymbol("intimidate", g, abiX, abiY, abiScale, abiScale);
|
||||
if (unselectable){
|
||||
g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
|
||||
abiY += abiSpace;
|
||||
abiCount += 1;
|
||||
}
|
||||
if (card.getCurrentState().hasShadow()) {
|
||||
if (abiCount > 5 ) { abiY = cy + (abiSpace * (abiCount - 6)); abiX = cx + ((cw*2)/1.92f); }
|
||||
CardFaceSymbols.drawSymbol("shadow", g, abiX, abiY, abiScale, abiScale);
|
||||
if (unselectable){
|
||||
g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
|
||||
abiY += abiSpace;
|
||||
abiCount += 1;
|
||||
}
|
||||
if (card.getCurrentState().hasHorsemanship()) {
|
||||
if (abiCount > 5 ) { abiY = cy + (abiSpace * (abiCount - 6)); abiX = cx + ((cw*2)/1.92f); }
|
||||
CardFaceSymbols.drawSymbol("horsemanship", g, abiX, abiY, abiScale, abiScale);
|
||||
if (unselectable){
|
||||
g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
|
||||
abiY += abiSpace;
|
||||
abiCount += 1;
|
||||
}
|
||||
@@ -699,57 +713,41 @@ public class CardRenderer {
|
||||
List<String> listHK = Arrays.asList(splitK);
|
||||
if (listHK.contains("generic")) {
|
||||
CardFaceSymbols.drawSymbol("hexproof", g, abiX, abiY, abiScale, abiScale);
|
||||
if (unselectable){
|
||||
g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
|
||||
abiY += abiSpace;
|
||||
abiCount += 1;
|
||||
}
|
||||
if (listHK.contains("R")) {
|
||||
CardFaceSymbols.drawSymbol("hexproofR", g, abiX, abiY, abiScale, abiScale);
|
||||
if (unselectable){
|
||||
g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
|
||||
abiY += abiSpace;
|
||||
abiCount += 1;
|
||||
}
|
||||
if (listHK.contains("B")) {
|
||||
CardFaceSymbols.drawSymbol("hexproofB", g, abiX, abiY, abiScale, abiScale);
|
||||
if (unselectable){
|
||||
g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
|
||||
abiY += abiSpace;
|
||||
abiCount += 1;
|
||||
}
|
||||
if (listHK.contains("U")) {
|
||||
CardFaceSymbols.drawSymbol("hexproofU", g, abiX, abiY, abiScale, abiScale);
|
||||
if (unselectable){
|
||||
g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
|
||||
abiY += abiSpace;
|
||||
abiCount += 1;
|
||||
}
|
||||
if (listHK.contains("G")) {
|
||||
CardFaceSymbols.drawSymbol("hexproofG", g, abiX, abiY, abiScale, abiScale);
|
||||
if (unselectable){
|
||||
g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
|
||||
abiY += abiSpace;
|
||||
abiCount += 1;
|
||||
}
|
||||
if (listHK.contains("W")) {
|
||||
CardFaceSymbols.drawSymbol("hexproofW", g, abiX, abiY, abiScale, abiScale);
|
||||
if (unselectable){
|
||||
g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
|
||||
abiY += abiSpace;
|
||||
abiCount += 1;
|
||||
}
|
||||
if (listHK.contains("monocolored")) {
|
||||
CardFaceSymbols.drawSymbol("hexproofC", g, abiX, abiY, abiScale, abiScale);
|
||||
if (unselectable){
|
||||
g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
|
||||
abiY += abiSpace;
|
||||
abiCount += 1;
|
||||
}
|
||||
} else {
|
||||
CardFaceSymbols.drawSymbol("hexproof", g, abiX, abiY, abiScale, abiScale);
|
||||
if (unselectable){
|
||||
g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
|
||||
abiY += abiSpace;
|
||||
abiCount += 1;
|
||||
}
|
||||
@@ -757,48 +755,36 @@ public class CardRenderer {
|
||||
else if (card.getCurrentState().hasShroud()) {
|
||||
if (abiCount > 5 ) { abiY = cy + (abiSpace * (abiCount - 6)); abiX = cx + ((cw*2)/1.92f); }
|
||||
CardFaceSymbols.drawSymbol("shroud", g, abiX, abiY, abiScale, abiScale);
|
||||
if (unselectable){
|
||||
g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
|
||||
abiY += abiSpace;
|
||||
abiCount += 1;
|
||||
}
|
||||
if (card.getCurrentState().hasVigilance()) {
|
||||
if (abiCount > 5 ) { abiY = cy + (abiSpace * (abiCount - 6)); abiX = cx + ((cw*2)/1.92f); }
|
||||
CardFaceSymbols.drawSymbol("vigilance", g, abiX, abiY, abiScale, abiScale);
|
||||
if (unselectable){
|
||||
g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
|
||||
abiY += abiSpace;
|
||||
abiCount += 1;
|
||||
}
|
||||
if (card.getCurrentState().hasTrample()) {
|
||||
if (abiCount > 5 ) { abiY = cy + (abiSpace * (abiCount - 6)); abiX = cx + ((cw*2)/1.92f); }
|
||||
CardFaceSymbols.drawSymbol("trample", g, abiX, abiY, abiScale, abiScale);
|
||||
if (unselectable){
|
||||
g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
|
||||
abiY += abiSpace;
|
||||
abiCount += 1;
|
||||
}
|
||||
if (card.getCurrentState().hasReach()) {
|
||||
if (abiCount > 5 ) { abiY = cy + (abiSpace * (abiCount - 6)); abiX = cx + ((cw*2)/1.92f); }
|
||||
CardFaceSymbols.drawSymbol("reach", g, abiX, abiY, abiScale, abiScale);
|
||||
if (unselectable){
|
||||
g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
|
||||
abiY += abiSpace;
|
||||
abiCount += 1;
|
||||
}
|
||||
if (card.getCurrentState().hasLifelink()) {
|
||||
if (abiCount > 5 ) { abiY = cy + (abiSpace * (abiCount - 6)); abiX = cx + ((cw*2)/1.92f); }
|
||||
CardFaceSymbols.drawSymbol("lifelink", g, abiX, abiY, abiScale, abiScale);
|
||||
if (unselectable){
|
||||
g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
|
||||
abiY += abiSpace;
|
||||
abiCount += 1;
|
||||
}
|
||||
if (card.getCurrentState().hasDefender()) {
|
||||
if (abiCount > 5 ) { abiY = cy + (abiSpace * (abiCount - 6)); abiX = cx + ((cw*2)/1.92f); }
|
||||
CardFaceSymbols.drawSymbol("defender", g, abiX, abiY, abiScale, abiScale);
|
||||
if (unselectable){
|
||||
g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
|
||||
abiY += abiSpace;
|
||||
abiCount += 1;
|
||||
}
|
||||
@@ -807,132 +793,138 @@ public class CardRenderer {
|
||||
if (abiCount > 5 ) { abiY = cy + (abiSpace * (abiCount - 6)); abiX = cx + ((cw*2)/1.92f); }
|
||||
if (card.getCurrentState().getProtectionKey().contains("everything") || card.getCurrentState().getProtectionKey().contains("allcolors")) {
|
||||
CardFaceSymbols.drawSymbol("protectAll", g, abiX, abiY, abiScale, abiScale);
|
||||
if (unselectable){
|
||||
g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
|
||||
abiY += abiSpace;
|
||||
abiCount += 1;
|
||||
}
|
||||
else if (card.getCurrentState().getProtectionKey().contains("coloredspells")) {
|
||||
CardFaceSymbols.drawSymbol("protectColoredSpells", g, abiX, abiY, abiScale, abiScale);
|
||||
if (unselectable){
|
||||
g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
|
||||
abiY += abiSpace;
|
||||
abiCount += 1;
|
||||
}
|
||||
else if (card.getCurrentState().getProtectionKey().equals("R")) {
|
||||
CardFaceSymbols.drawSymbol("protectR", g, abiX, abiY, abiScale, abiScale);
|
||||
if (unselectable){
|
||||
g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
|
||||
abiY += abiSpace;
|
||||
abiCount += 1;
|
||||
}
|
||||
else if (card.getCurrentState().getProtectionKey().equals("G")) {
|
||||
CardFaceSymbols.drawSymbol("protectG", g, abiX, abiY, abiScale, abiScale);
|
||||
if (unselectable){
|
||||
g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
|
||||
abiY += abiSpace;
|
||||
abiCount += 1;
|
||||
}
|
||||
else if (card.getCurrentState().getProtectionKey().equals("B")) {
|
||||
CardFaceSymbols.drawSymbol("protectB", g, abiX, abiY, abiScale, abiScale);
|
||||
if (unselectable){
|
||||
g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
|
||||
abiY += abiSpace;
|
||||
abiCount += 1;
|
||||
}
|
||||
else if (card.getCurrentState().getProtectionKey().equals("U")) {
|
||||
CardFaceSymbols.drawSymbol("protectU", g, abiX, abiY, abiScale, abiScale);
|
||||
if (unselectable){
|
||||
g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
|
||||
abiY += abiSpace;
|
||||
abiCount += 1;
|
||||
}
|
||||
else if (card.getCurrentState().getProtectionKey().equals("W")) {
|
||||
CardFaceSymbols.drawSymbol("protectW", g, abiX, abiY, abiScale, abiScale);
|
||||
if (unselectable){
|
||||
g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
|
||||
abiY += abiSpace;
|
||||
abiCount += 1;
|
||||
}
|
||||
else if (card.getCurrentState().getProtectionKey().equals("RG")||card.getCurrentState().getProtectionKey().equals("GR")) {
|
||||
CardFaceSymbols.drawSymbol("protectRG", g, abiX, abiY, abiScale, abiScale);
|
||||
if (unselectable){
|
||||
g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
|
||||
abiY += abiSpace;
|
||||
abiCount += 1;
|
||||
}
|
||||
else if (card.getCurrentState().getProtectionKey().equals("RB")||card.getCurrentState().getProtectionKey().equals("BR")) {
|
||||
CardFaceSymbols.drawSymbol("protectRB", g, abiX, abiY, abiScale, abiScale);
|
||||
if (unselectable){
|
||||
g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
|
||||
abiY += abiSpace;
|
||||
abiCount += 1;
|
||||
}
|
||||
else if (card.getCurrentState().getProtectionKey().equals("RU")||card.getCurrentState().getProtectionKey().equals("UR")) {
|
||||
CardFaceSymbols.drawSymbol("protectRU", g, abiX, abiY, abiScale, abiScale);
|
||||
if (unselectable){
|
||||
g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
|
||||
abiY += abiSpace;
|
||||
abiCount += 1;
|
||||
}
|
||||
else if (card.getCurrentState().getProtectionKey().equals("RW")||card.getCurrentState().getProtectionKey().equals("WR")) {
|
||||
CardFaceSymbols.drawSymbol("protectRW", g, abiX, abiY, abiScale, abiScale);
|
||||
if (unselectable){
|
||||
g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
|
||||
abiY += abiSpace;
|
||||
abiCount += 1;
|
||||
}
|
||||
else if (card.getCurrentState().getProtectionKey().equals("GB")||card.getCurrentState().getProtectionKey().equals("BG")) {
|
||||
CardFaceSymbols.drawSymbol("protectGB", g, abiX, abiY, abiScale, abiScale);
|
||||
if (unselectable){
|
||||
g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
|
||||
abiY += abiSpace;
|
||||
abiCount += 1;
|
||||
}
|
||||
else if (card.getCurrentState().getProtectionKey().equals("GU")||card.getCurrentState().getProtectionKey().equals("UG")) {
|
||||
CardFaceSymbols.drawSymbol("protectGU", g, abiX, abiY, abiScale, abiScale);
|
||||
if (unselectable){
|
||||
g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
|
||||
abiY += abiSpace;
|
||||
abiCount += 1;
|
||||
}
|
||||
else if (card.getCurrentState().getProtectionKey().equals("GW")||card.getCurrentState().getProtectionKey().equals("WG")) {
|
||||
CardFaceSymbols.drawSymbol("protectGW", g, abiX, abiY, abiScale, abiScale);
|
||||
if (unselectable){
|
||||
g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
|
||||
abiY += abiSpace;
|
||||
abiCount += 1;
|
||||
}
|
||||
else if (card.getCurrentState().getProtectionKey().equals("BU")||card.getCurrentState().getProtectionKey().equals("UB")) {
|
||||
CardFaceSymbols.drawSymbol("protectBU", g, abiX, abiY, abiScale, abiScale);
|
||||
if (unselectable){
|
||||
g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
|
||||
abiY += abiSpace;
|
||||
abiCount += 1;
|
||||
}
|
||||
else if (card.getCurrentState().getProtectionKey().equals("BW")||card.getCurrentState().getProtectionKey().equals("WB")) {
|
||||
CardFaceSymbols.drawSymbol("protectBW", g, abiX, abiY, abiScale, abiScale);
|
||||
if (unselectable){
|
||||
g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
|
||||
abiY += abiSpace;
|
||||
abiCount += 1;
|
||||
}
|
||||
else if (card.getCurrentState().getProtectionKey().equals("UW")||card.getCurrentState().getProtectionKey().equals("WU")) {
|
||||
CardFaceSymbols.drawSymbol("protectUW", g, abiX, abiY, abiScale, abiScale);
|
||||
if (unselectable){
|
||||
g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
|
||||
abiY += abiSpace;
|
||||
abiCount += 1;
|
||||
}
|
||||
else if (card.getCurrentState().getProtectionKey().contains("generic") || card.getCurrentState().getProtectionKey().length() > 2) {
|
||||
CardFaceSymbols.drawSymbol("protectGeneric", g, abiX, abiY, abiScale, abiScale);
|
||||
if (unselectable){
|
||||
g.setAlphaComposite(0.6f); g.fillRect(Color.BLACK, abiX, abiY, abiScale, abiScale ); g.setAlphaComposite(oldAlpha);}
|
||||
abiY += abiSpace;
|
||||
abiCount += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
//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)) {
|
||||
float multiplier;
|
||||
switch (Forge.extrawide) {
|
||||
case "default":
|
||||
multiplier = 0.145f; //good for tablets with 16:10 or similar
|
||||
break;
|
||||
case "wide":
|
||||
multiplier = 0.150f;
|
||||
break;
|
||||
case "extrawide":
|
||||
multiplier = 0.155f; //good for tall phones with 21:9 or similar
|
||||
break;
|
||||
default:
|
||||
multiplier = 0.150f;
|
||||
break;
|
||||
}
|
||||
g.drawOutlinedText(CardTranslation.getTranslatedName(details.getName()), FSkinFont.forHeight(h * multiplier), Color.WHITE, Color.BLACK, x + padding -1f, y + padding, w - 2 * padding, h * 0.4f, true, Align.left, false);
|
||||
}
|
||||
if (showCardManaCostOverlay(card)) {
|
||||
float manaSymbolSize = w / 4.5f;
|
||||
if (card.isSplitCard() && card.hasAlternateState()) {
|
||||
if (!card.isFaceDown()) { // no need to draw mana symbols on face down split cards (e.g. manifested)
|
||||
float dy = manaSymbolSize / 2 + Utils.scale(5);
|
||||
|
||||
PaperCard pc = StaticData.instance().getCommonCards().getCard(card.getName());
|
||||
if (Card.getCardForUi(pc).hasKeyword(Keyword.AFTERMATH)){
|
||||
dy *= -1; // flip card costs for Aftermath cards
|
||||
}
|
||||
|
||||
drawManaCost(g, card.getAlternateState().getManaCost(), x - padding, y - dy, w + 2 * padding, h, manaSymbolSize);
|
||||
drawManaCost(g, card.getCurrentState().getManaCost(), x - padding, y + dy, w + 2 * padding, h, manaSymbolSize);
|
||||
}
|
||||
}
|
||||
else {
|
||||
drawManaCost(g, card.getCurrentState().getManaCost(), x - padding, y, w + 2 * padding, h, manaSymbolSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
//reset alpha
|
||||
g.setAlphaComposite(oldAlpha);
|
||||
}
|
||||
|
||||
private static void drawCounterTabs(final CardView card, final Graphics g, final float x, final float y, final float w, final float h) {
|
||||
@@ -1119,8 +1111,14 @@ public class CardRenderer {
|
||||
|
||||
public static void drawFoilEffect(Graphics g, CardView card, float x, float y, float w, float h, boolean inZoomer) {
|
||||
float new_x = x; float new_y = y; float new_w = w; float new_h = h; float radius = (h - w)/8;
|
||||
float croppedArea = isModernFrame(card) ? CROP_MULTIPLIER : 0.97f;
|
||||
float minusxy = isModernFrame(card) ? 0.0f : 0.13f*radius;
|
||||
if (card.getCurrentState().getSetCode().equals("LEA")||card.getCurrentState().getSetCode().equals("LEB")) {
|
||||
croppedArea = 0.975f;
|
||||
minusxy = 0.135f*radius;
|
||||
}
|
||||
if (Forge.enableUIMask) {
|
||||
new_x += radius/2.4f; new_y += radius/2; new_w = w * 0.96f; new_h = h * 0.96f;
|
||||
new_x += radius/2.4f-minusxy; new_y += radius/2-minusxy; new_w = w * croppedArea; new_h = h * croppedArea;
|
||||
}
|
||||
if (isPreferenceEnabled(FPref.UI_OVERLAY_FOIL_EFFECT) && MatchController.instance.mayView(card)) {
|
||||
boolean rotateSplit = isPreferenceEnabled(FPref.UI_ROTATE_SPLIT_CARDS) && card.isSplitCard() && inZoomer;
|
||||
|
||||
@@ -219,7 +219,22 @@ public class CardZoom extends FOverlay {
|
||||
float w = getWidth();
|
||||
float h = getHeight();
|
||||
float messageHeight = FDialog.MSG_HEIGHT;
|
||||
float maxCardHeight = h - 2 * messageHeight;
|
||||
float AspectRatioMultiplier;
|
||||
switch (Forge.extrawide) {
|
||||
case "default":
|
||||
AspectRatioMultiplier = 3; //good for tablets with 16:10 or similar
|
||||
break;
|
||||
case "wide":
|
||||
AspectRatioMultiplier = 2.5f;
|
||||
break;
|
||||
case "extrawide":
|
||||
AspectRatioMultiplier = 2; //good for tall phones with 21:9 or similar
|
||||
break;
|
||||
default:
|
||||
AspectRatioMultiplier = 3;
|
||||
break;
|
||||
}
|
||||
float maxCardHeight = h - AspectRatioMultiplier * messageHeight; //maxheight of currently zoomed card
|
||||
|
||||
float cardWidth, cardHeight, y;
|
||||
|
||||
@@ -275,9 +290,15 @@ public class CardZoom extends FOverlay {
|
||||
|
||||
if (flipIconBounds != null) {
|
||||
float imageWidth = cardWidth / 2;
|
||||
float imageHeight = imageWidth * FSkinImage.FLIPCARD.getHeight() / FSkinImage.FLIPCARD.getWidth();
|
||||
flipIconBounds.set(x + (cardWidth - imageWidth) / 2, y + (cardHeight - imageHeight) / 2, imageWidth, imageHeight);
|
||||
g.drawImage(FSkinImage.FLIPCARD, flipIconBounds.x, flipIconBounds.y, flipIconBounds.width, flipIconBounds.height);
|
||||
if (Forge.hdbuttons){
|
||||
float imageHeight = imageWidth * FSkinImage.HDFLIPCARD.getHeight() / FSkinImage.HDFLIPCARD.getWidth();
|
||||
flipIconBounds.set(x + (cardWidth - imageWidth) / 2, y + (cardHeight - imageHeight) / 2, imageWidth, imageHeight);
|
||||
g.drawImage(FSkinImage.HDFLIPCARD, flipIconBounds.x, flipIconBounds.y, flipIconBounds.width, flipIconBounds.height);
|
||||
} else {
|
||||
float imageHeight = imageWidth * FSkinImage.FLIPCARD.getHeight() / FSkinImage.FLIPCARD.getWidth();
|
||||
flipIconBounds.set(x + (cardWidth - imageWidth) / 2, y + (cardHeight - imageHeight) / 2, imageWidth, imageHeight);
|
||||
g.drawImage(FSkinImage.FLIPCARD, flipIconBounds.x, flipIconBounds.y, flipIconBounds.width, flipIconBounds.height);
|
||||
}
|
||||
}
|
||||
|
||||
if (currentActivateAction != null) {
|
||||
|
||||
@@ -6,6 +6,7 @@ import java.util.List;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import forge.Forge;
|
||||
import forge.Graphics;
|
||||
import forge.assets.FImage;
|
||||
import forge.assets.FSkinFont;
|
||||
@@ -27,7 +28,7 @@ public class GameEntityPicker extends TabPageScreen<GameEntityPicker> {
|
||||
|
||||
public GameEntityPicker(String title, Collection<? extends GameEntityView> choiceList, Collection<CardView> revealList, String revealListCaption, FImage revealListImage, boolean isOptional, final Callback<GameEntityView> callback) {
|
||||
super(new PickerTab[] {
|
||||
new PickerTab(choiceList, Localizer.getInstance().getMessage("lblChoices"), FSkinImage.DECKLIST, 1),
|
||||
new PickerTab(choiceList, Localizer.getInstance().getMessage("lblChoices"), Forge.hdbuttons ? FSkinImage.HDCHOICE : FSkinImage.DECKLIST, 1),
|
||||
new PickerTab(revealList, revealListCaption, revealListImage, 0)
|
||||
}, false);
|
||||
|
||||
|
||||
@@ -324,7 +324,7 @@ public class AddBasicLandsDialog extends FDialog {
|
||||
}
|
||||
});
|
||||
lblCount = add(new FLabel.Builder().text("0").font(FSkinFont.get(18)).align(Align.center).build());
|
||||
btnSubtract = add(new FLabel.ButtonBuilder().icon(FSkinImage.MINUS).command(new FEventHandler() {
|
||||
btnSubtract = add(new FLabel.ButtonBuilder().icon(Forge.hdbuttons ? FSkinImage.HDMINUS : FSkinImage.MINUS).command(new FEventHandler() {
|
||||
@Override
|
||||
public void handleEvent(FEvent e) {
|
||||
if (count > 0) {
|
||||
@@ -334,7 +334,7 @@ public class AddBasicLandsDialog extends FDialog {
|
||||
}
|
||||
}
|
||||
}).build());
|
||||
btnAdd = add(new FLabel.ButtonBuilder().icon(FSkinImage.PLUS).command(new FEventHandler() {
|
||||
btnAdd = add(new FLabel.ButtonBuilder().icon(Forge.hdbuttons ? FSkinImage.HDPLUS : FSkinImage.PLUS).command(new FEventHandler() {
|
||||
@Override
|
||||
public void handleEvent(FEvent e) {
|
||||
if (maxCount == 0 || count < maxCount) {
|
||||
|
||||
@@ -44,8 +44,8 @@ import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
public class FDeckEditor extends TabPageScreen<FDeckEditor> {
|
||||
public static FSkinImage MAIN_DECK_ICON = FSkinImage.DECKLIST;
|
||||
public static FSkinImage SIDEBOARD_ICON = FSkinImage.FLASHBACK;
|
||||
public static FSkinImage MAIN_DECK_ICON = Forge.hdbuttons ? FSkinImage.HDLIBRARY :FSkinImage.DECKLIST;
|
||||
public static FSkinImage SIDEBOARD_ICON = Forge.hdbuttons ? FSkinImage.HDSIDEBOARD : FSkinImage.FLASHBACK;
|
||||
private static final float HEADER_HEIGHT = Math.round(Utils.AVG_FINGER_HEIGHT * 0.8f);
|
||||
|
||||
public enum EditorType {
|
||||
@@ -235,7 +235,7 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
|
||||
|
||||
protected final DeckHeader deckHeader = add(new DeckHeader());
|
||||
protected final FLabel lblName = deckHeader.add(new FLabel.Builder().font(FSkinFont.get(16)).insets(new Vector2(Utils.scale(5), 0)).build());
|
||||
private final FLabel btnSave = deckHeader.add(new FLabel.Builder().icon(FSkinImage.SAVE).align(Align.center).pressedColor(Header.BTN_PRESSED_COLOR).build());
|
||||
private final FLabel btnSave = deckHeader.add(new FLabel.Builder().icon(Forge.hdbuttons ? FSkinImage.HDSAVE : FSkinImage.SAVE).align(Align.center).pressedColor(Header.BTN_PRESSED_COLOR).build());
|
||||
private final FLabel btnMoreOptions = deckHeader.add(new FLabel.Builder().text("...").font(FSkinFont.get(20)).align(Align.center).pressedColor(Header.BTN_PRESSED_COLOR).build());
|
||||
|
||||
public FDeckEditor(EditorType editorType0, DeckProxy editDeck, boolean showMainDeck) {
|
||||
@@ -332,7 +332,7 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
|
||||
protected void buildMenu() {
|
||||
final Localizer localizer = Localizer.getInstance();
|
||||
|
||||
addItem(new FMenuItem(localizer.getMessage("lblAddBasicLands"), FSkinImage.LAND, new FEventHandler() {
|
||||
addItem(new FMenuItem(localizer.getMessage("lblAddBasicLands"), FSkinImage.LANDLOGO, new FEventHandler() {
|
||||
@Override
|
||||
public void handleEvent(FEvent e) {
|
||||
CardEdition defaultLandSet;
|
||||
@@ -365,7 +365,7 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
|
||||
}
|
||||
}));
|
||||
if (!isLimitedEditor()) {
|
||||
addItem(new FMenuItem(localizer.getMessage("lblImportFromClipboard"), FSkinImage.OPEN, new FEventHandler() {
|
||||
addItem(new FMenuItem(localizer.getMessage("lblImportFromClipboard"), Forge.hdbuttons ? FSkinImage.HDIMPORT : FSkinImage.OPEN, new FEventHandler() {
|
||||
@Override
|
||||
public void handleEvent(FEvent e) {
|
||||
FDeckImportDialog dialog = new FDeckImportDialog(!deck.isEmpty(), new Callback<Deck>() {
|
||||
@@ -384,7 +384,7 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
|
||||
setSelectedPage(getMainDeckPage()); //select main deck page if needed so main deck if visible below dialog
|
||||
}
|
||||
}));
|
||||
addItem(new FMenuItem(localizer.getMessage("lblSaveAs"), FSkinImage.SAVEAS, new FEventHandler() {
|
||||
addItem(new FMenuItem(localizer.getMessage("lblSaveAs"), Forge.hdbuttons ? FSkinImage.HDSAVEAS : FSkinImage.SAVEAS, new FEventHandler() {
|
||||
@Override
|
||||
public void handleEvent(FEvent e) {
|
||||
String defaultName = editorType.getController().getNextAvailableName();
|
||||
@@ -400,7 +400,7 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
|
||||
}));
|
||||
}
|
||||
if (allowRename()) {
|
||||
addItem(new FMenuItem(localizer.getMessage("lblRenameDeck"), FSkinImage.EDIT, new FEventHandler() {
|
||||
addItem(new FMenuItem(localizer.getMessage("lblRenameDeck"), Forge.hdbuttons ? FSkinImage.HDEDIT : FSkinImage.EDIT, new FEventHandler() {
|
||||
@Override
|
||||
public void handleEvent(FEvent e) {
|
||||
FOptionPane.showInputDialog(localizer.getMessage("lblNewNameDeck"), deck.getName(), new Callback<String>() {
|
||||
@@ -413,7 +413,7 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
|
||||
}));
|
||||
}
|
||||
if (allowDelete()) {
|
||||
addItem(new FMenuItem(localizer.getMessage("lblDeleteDeck"), FSkinImage.DELETE, new FEventHandler() {
|
||||
addItem(new FMenuItem(localizer.getMessage("lblDeleteDeck"), Forge.hdbuttons ? FSkinImage.HDDELETE : FSkinImage.DELETE, new FEventHandler() {
|
||||
@Override
|
||||
public void handleEvent(FEvent e) {
|
||||
FOptionPane.showConfirmDialog(
|
||||
@@ -430,7 +430,7 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
|
||||
}
|
||||
}));
|
||||
}
|
||||
addItem(new FMenuItem(localizer.getMessage("btnCopyToClipboard"), new FEventHandler() {
|
||||
addItem(new FMenuItem(localizer.getMessage("btnCopyToClipboard"), Forge.hdbuttons ? FSkinImage.HDEXPORT : FSkinImage.BLANK, new FEventHandler() {
|
||||
@Override
|
||||
public void handleEvent(FEvent e) {
|
||||
FDeckViewer.copyDeckToClipboard(deck);
|
||||
@@ -856,6 +856,9 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
|
||||
case Brawl:
|
||||
isLegalCommander = card.getRules().canBeBrawlCommander();
|
||||
break;
|
||||
case TinyLeaders:
|
||||
isLegalCommander = card.getRules().canBeTinyLeadersCommander();
|
||||
break;
|
||||
case Oathbreaker:
|
||||
isLegalCommander = card.getRules().canBeOathbreaker();
|
||||
captionSuffix = localizer.getMessage("lblOathbreaker");
|
||||
@@ -974,7 +977,7 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
|
||||
private boolean initialized, needRefreshWhenShown;
|
||||
|
||||
protected CatalogPage(ItemManagerConfig config) {
|
||||
this(config, Localizer.getInstance().getMessage("lblCatalog"), FSkinImage.FOLDER);
|
||||
this(config, Localizer.getInstance().getMessage("lblCatalog"), Forge.hdbuttons ? FSkinImage.HDFOLDER : FSkinImage.FOLDER);
|
||||
}
|
||||
protected CatalogPage(ItemManagerConfig config, String caption0, FImage icon0) {
|
||||
super(config, caption0, icon0);
|
||||
@@ -1166,7 +1169,7 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
|
||||
//add option to add or remove card from favorites
|
||||
final CardPreferences prefs = CardPreferences.getPrefs(card);
|
||||
if (prefs.getStarCount() == 0) {
|
||||
menu.addItem(new FMenuItem(localizer.getMessage("lblAddFavorites"), FSkinImage.STAR_FILLED, new FEventHandler() {
|
||||
menu.addItem(new FMenuItem(localizer.getMessage("lblAddFavorites"), Forge.hdbuttons ? FSkinImage.HDSTAR_FILLED : FSkinImage.STAR_FILLED, new FEventHandler() {
|
||||
@Override
|
||||
public void handleEvent(FEvent e) {
|
||||
prefs.setStarCount(1);
|
||||
@@ -1175,7 +1178,7 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
|
||||
}));
|
||||
}
|
||||
else {
|
||||
menu.addItem(new FMenuItem(localizer.getMessage("lblRemoveFavorites"), FSkinImage.STAR_OUTINE, new FEventHandler() {
|
||||
menu.addItem(new FMenuItem(localizer.getMessage("lblRemoveFavorites"), Forge.hdbuttons ? FSkinImage.HDSTAR_OUTLINE : FSkinImage.STAR_OUTLINE, new FEventHandler() {
|
||||
@Override
|
||||
public void handleEvent(FEvent e) {
|
||||
prefs.setStarCount(0);
|
||||
@@ -1187,7 +1190,7 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
|
||||
//if card has more than one art option, add item to change user's preferred art
|
||||
final List<PaperCard> artOptions = FModel.getMagicDb().getCommonCards().getAllCards(card.getName());
|
||||
if (artOptions != null && artOptions.size() > 1) {
|
||||
menu.addItem(new FMenuItem(localizer.getMessage("lblChangePreferredArt"), FSkinImage.SETTINGS, new FEventHandler() {
|
||||
menu.addItem(new FMenuItem(localizer.getMessage("lblChangePreferredArt"), Forge.hdbuttons ? FSkinImage.HDPREFERENCE : FSkinImage.SETTINGS, new FEventHandler() {
|
||||
@Override
|
||||
public void handleEvent(FEvent e) {
|
||||
//sort options so current option is on top and selected by default
|
||||
@@ -1340,7 +1343,7 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
|
||||
switch (deckSection) {
|
||||
default:
|
||||
case Main:
|
||||
addItem(menu, localizer.getMessage("lblAdd"), null, FSkinImage.PLUS, true, false, new Callback<Integer>() {
|
||||
addItem(menu, localizer.getMessage("lblAdd"), null, Forge.hdbuttons ? FSkinImage.HDPLUS : FSkinImage.PLUS, true, false, new Callback<Integer>() {
|
||||
@Override
|
||||
public void run(Integer result) {
|
||||
if (result == null || result <= 0) { return; }
|
||||
@@ -1355,7 +1358,7 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
|
||||
}
|
||||
});
|
||||
if (!parentScreen.isLimitedEditor()) {
|
||||
addItem(menu, localizer.getMessage("lblRemove"), null, FSkinImage.MINUS, false, false, new Callback<Integer>() {
|
||||
addItem(menu, localizer.getMessage("lblRemove"), null, Forge.hdbuttons ? FSkinImage.HDMINUS : FSkinImage.MINUS, false, false, new Callback<Integer>() {
|
||||
@Override
|
||||
public void run(Integer result) {
|
||||
if (result == null || result <= 0) { return; }
|
||||
@@ -1381,7 +1384,7 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
|
||||
addCommanderItems(menu, card, false, false);
|
||||
break;
|
||||
case Sideboard:
|
||||
addItem(menu, localizer.getMessage("lblAdd"), null, FSkinImage.PLUS, true, false, new Callback<Integer>() {
|
||||
addItem(menu, localizer.getMessage("lblAdd"), null, Forge.hdbuttons ? FSkinImage.HDPLUS : FSkinImage.PLUS, true, false, new Callback<Integer>() {
|
||||
@Override
|
||||
public void run(Integer result) {
|
||||
if (result == null || result <= 0) { return; }
|
||||
@@ -1396,7 +1399,7 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
|
||||
}
|
||||
});
|
||||
if (!parentScreen.isLimitedEditor()) {
|
||||
addItem(menu, localizer.getMessage("lblRemove"), null, FSkinImage.MINUS, false, false, new Callback<Integer>() {
|
||||
addItem(menu, localizer.getMessage("lblRemove"), null, Forge.hdbuttons ? FSkinImage.HDMINUS : FSkinImage.MINUS, false, false, new Callback<Integer>() {
|
||||
@Override
|
||||
public void run(Integer result) {
|
||||
if (result == null || result <= 0) { return; }
|
||||
@@ -1421,7 +1424,7 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
|
||||
break;
|
||||
case Commander:
|
||||
if (parentScreen.editorType != EditorType.PlanarConquest || isPartnerCommander(card)) {
|
||||
addItem(menu, localizer.getMessage("lblRemove"), null, FSkinImage.MINUS, false, false, new Callback<Integer>() {
|
||||
addItem(menu, localizer.getMessage("lblRemove"), null, Forge.hdbuttons ? FSkinImage.HDMINUS : FSkinImage.MINUS, false, false, new Callback<Integer>() {
|
||||
@Override
|
||||
public void run(Integer result) {
|
||||
if (result == null || result <= 0) {
|
||||
@@ -1436,7 +1439,7 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
|
||||
}
|
||||
break;
|
||||
case Avatar:
|
||||
addItem(menu, localizer.getMessage("lblRemove"), null, FSkinImage.MINUS, false, false, new Callback<Integer>() {
|
||||
addItem(menu, localizer.getMessage("lblRemove"), null, Forge.hdbuttons ? FSkinImage.HDMINUS : FSkinImage.MINUS, false, false, new Callback<Integer>() {
|
||||
@Override
|
||||
public void run(Integer result) {
|
||||
if (result == null || result <= 0) { return; }
|
||||
@@ -1446,7 +1449,7 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
|
||||
});
|
||||
break;
|
||||
case Schemes:
|
||||
addItem(menu, localizer.getMessage("lblAdd"), null, FSkinImage.PLUS, true, false, new Callback<Integer>() {
|
||||
addItem(menu, localizer.getMessage("lblAdd"), null, Forge.hdbuttons ? FSkinImage.HDPLUS : FSkinImage.PLUS, true, false, new Callback<Integer>() {
|
||||
@Override
|
||||
public void run(Integer result) {
|
||||
if (result == null || result <= 0) { return; }
|
||||
@@ -1454,7 +1457,7 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
|
||||
addCard(card, result);
|
||||
}
|
||||
});
|
||||
addItem(menu, localizer.getMessage("lblRemove"), null, FSkinImage.MINUS, false, false, new Callback<Integer>() {
|
||||
addItem(menu, localizer.getMessage("lblRemove"), null, Forge.hdbuttons ? FSkinImage.HDMINUS : FSkinImage.MINUS, false, false, new Callback<Integer>() {
|
||||
@Override
|
||||
public void run(Integer result) {
|
||||
if (result == null || result <= 0) { return; }
|
||||
@@ -1464,7 +1467,7 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
|
||||
});
|
||||
break;
|
||||
case Planes:
|
||||
addItem(menu, localizer.getMessage("lblAdd"), null, FSkinImage.PLUS, true, false, new Callback<Integer>() {
|
||||
addItem(menu, localizer.getMessage("lblAdd"), null, Forge.hdbuttons ? FSkinImage.HDPLUS : FSkinImage.PLUS, true, false, new Callback<Integer>() {
|
||||
@Override
|
||||
public void run(Integer result) {
|
||||
if (result == null || result <= 0) { return; }
|
||||
@@ -1472,7 +1475,7 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
|
||||
addCard(card, result);
|
||||
}
|
||||
});
|
||||
addItem(menu, localizer.getMessage("lblRemove"), null, FSkinImage.MINUS, false, false, new Callback<Integer>() {
|
||||
addItem(menu, localizer.getMessage("lblRemove"), null, Forge.hdbuttons ? FSkinImage.HDMINUS : FSkinImage.MINUS, false, false, new Callback<Integer>() {
|
||||
@Override
|
||||
public void run(Integer result) {
|
||||
if (result == null || result <= 0) { return; }
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package forge.itemmanager;
|
||||
|
||||
import forge.Forge;
|
||||
import forge.Graphics;
|
||||
import forge.assets.FSkinColor;
|
||||
import forge.assets.FSkinFont;
|
||||
@@ -124,7 +125,11 @@ public final class DeckManager extends ItemManager<DeckProxy> implements IHasGam
|
||||
}
|
||||
|
||||
//draw favorite, name, path and color on first line
|
||||
g.drawImage(DeckPreferences.getPrefs(deck).getStarCount() > 0 ? FSkinImage.STAR_FILLED : FSkinImage.STAR_OUTINE, x, y, IMAGE_SIZE, IMAGE_SIZE);
|
||||
if (Forge.hdbuttons)
|
||||
g.drawImage(DeckPreferences.getPrefs(deck).getStarCount() > 0 ? FSkinImage.HDSTAR_FILLED : FSkinImage.HDSTAR_OUTLINE, x, y, IMAGE_SIZE, IMAGE_SIZE);
|
||||
else
|
||||
g.drawImage(DeckPreferences.getPrefs(deck).getStarCount() > 0 ? FSkinImage.STAR_FILLED : FSkinImage.STAR_OUTLINE, x, y, IMAGE_SIZE, IMAGE_SIZE);
|
||||
|
||||
x += IMAGE_SIZE + FList.PADDING;
|
||||
ColorSet deckColor = deck.getColor();
|
||||
float availableNameWidth = w - CardFaceSymbols.getWidth(deckColor, IMAGE_SIZE) - IMAGE_SIZE - 2 * FList.PADDING;
|
||||
|
||||
@@ -87,12 +87,12 @@ public abstract class ItemManager<T extends InventoryItem> extends FContainer im
|
||||
private final TextSearchFilter<? extends T> searchFilter;
|
||||
|
||||
private final FLabel btnSearch = new FLabel.ButtonBuilder()
|
||||
.icon(FSkinImage.SEARCH).iconScaleFactor(0.9f).build();
|
||||
.icon(Forge.hdbuttons ? FSkinImage.HDSEARCH : FSkinImage.SEARCH).iconScaleFactor(0.9f).build();
|
||||
private final FLabel btnView = new FLabel.ButtonBuilder()
|
||||
.iconScaleFactor(0.9f).build(); //icon set later
|
||||
private final FLabel btnAdvancedSearchOptions = new FLabel.Builder()
|
||||
.selectable(true).align(Align.center)
|
||||
.icon(FSkinImage.SETTINGS).iconScaleFactor(0.9f)
|
||||
.icon(Forge.hdbuttons ? FSkinImage.HDPREFERENCE : FSkinImage.SETTINGS).iconScaleFactor(0.9f)
|
||||
.build();
|
||||
|
||||
private final FComboBox<ItemColumn> cbxSortOptions;
|
||||
@@ -148,7 +148,7 @@ public abstract class ItemManager<T extends InventoryItem> extends FContainer im
|
||||
FPopupMenu menu = new FPopupMenu() {
|
||||
@Override
|
||||
protected void buildMenu() {
|
||||
addItem(new FMenuItem(Localizer.getInstance().getMessage("lblAdvancedSearch"), FSkinImage.SEARCH, new FEventHandler() {
|
||||
addItem(new FMenuItem(Localizer.getInstance().getMessage("lblAdvancedSearch"), Forge.hdbuttons ? FSkinImage.HDSEARCH : FSkinImage.SEARCH, new FEventHandler() {
|
||||
@Override
|
||||
public void handleEvent(FEvent e) {
|
||||
if (advancedSearchFilter == null) {
|
||||
@@ -158,7 +158,7 @@ public abstract class ItemManager<T extends InventoryItem> extends FContainer im
|
||||
advancedSearchFilter.edit();
|
||||
}
|
||||
}));
|
||||
addItem(new FMenuItem(Localizer.getInstance().getMessage("lblResetFilters"), FSkinImage.DELETE, new FEventHandler() {
|
||||
addItem(new FMenuItem(Localizer.getInstance().getMessage("lblResetFilters"), Forge.hdbuttons ? FSkinImage.HDDELETE : FSkinImage.DELETE, new FEventHandler() {
|
||||
@Override
|
||||
public void handleEvent(FEvent e) {
|
||||
resetFilters();
|
||||
|
||||