mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-20 20:58:03 +00:00
Compare commits
104 Commits
untapRewor
...
forge-1.6.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9a30d78f54 | ||
|
|
db98ce160b | ||
|
|
ce8b5b53e0 | ||
|
|
0c4055726c | ||
|
|
64ae4bae0c | ||
|
|
044cc793e8 | ||
|
|
e310dc30d7 | ||
|
|
f239755249 | ||
|
|
57686c0554 | ||
|
|
5ecde572c3 | ||
|
|
23e9974950 | ||
|
|
ec98d128f1 | ||
|
|
5c07951604 | ||
|
|
7be34625f6 | ||
|
|
5f6cb893ef | ||
|
|
3b636be2fe | ||
|
|
1af940b034 | ||
|
|
8cb1789e60 | ||
|
|
01782de3a6 | ||
|
|
3a7e35c51d | ||
|
|
bff24a1d3d | ||
|
|
8a075000a2 | ||
|
|
5e0761d085 | ||
|
|
2184ddf1bb | ||
|
|
fd032f6ccd | ||
|
|
164c819523 | ||
|
|
987043ead2 | ||
|
|
4aa9c224d0 | ||
|
|
2ac6fa4542 | ||
|
|
2797f95cd3 | ||
|
|
5eb9be6248 | ||
|
|
23eebf9037 | ||
|
|
b59adab68d | ||
|
|
b2d44105be | ||
|
|
2235546f2a | ||
|
|
59d104f68b | ||
|
|
6abdfd391d | ||
|
|
a6ff0b5b10 | ||
|
|
02969cfe5b | ||
|
|
c6a2c35850 | ||
|
|
4c0a71f37d | ||
|
|
74e3bd1895 | ||
|
|
121c9f5012 | ||
|
|
7b1cd816b7 | ||
|
|
6a76cc8bc6 | ||
|
|
9b9c38126e | ||
|
|
e4ee0c768f | ||
|
|
340de153c8 | ||
|
|
2bf477102d | ||
|
|
42a15b40b3 | ||
|
|
a2589cd433 | ||
|
|
7ec7025ed4 | ||
|
|
5edeb6df94 | ||
|
|
001a1981cf | ||
|
|
446fb59473 | ||
|
|
3b58d6df42 | ||
|
|
a80c683901 | ||
|
|
a5b65eaaed | ||
|
|
31182289b7 | ||
|
|
85eb740264 | ||
|
|
4318e23a40 | ||
|
|
4ca7352d5c | ||
|
|
84905bd726 | ||
|
|
18e16368be | ||
|
|
7ed84c4c3f | ||
|
|
f334211395 | ||
|
|
42f4126aff | ||
|
|
5790e29daa | ||
|
|
88a4a2c6cf | ||
|
|
90b72fc11e | ||
|
|
00391df1f0 | ||
|
|
15de0c0bba | ||
|
|
a2fdce9be9 | ||
|
|
fa6fce9589 | ||
|
|
cc1f03fc94 | ||
|
|
8b25f6f129 | ||
|
|
b37937421c | ||
|
|
4c3e4f2170 | ||
|
|
dc12c50c1a | ||
|
|
2b986f5bac | ||
|
|
2accf7543e | ||
|
|
ba37189410 | ||
|
|
d578eee402 | ||
|
|
5107d89ef5 | ||
|
|
72b8b5c98e | ||
|
|
402885391f | ||
|
|
b4d153ab3b | ||
|
|
1a0cb62ac8 | ||
|
|
c0baf70c59 | ||
|
|
166cf2623c | ||
|
|
8567b69073 | ||
|
|
3a4271e66d | ||
|
|
8b723aebd9 | ||
|
|
eb59d6c86b | ||
|
|
a291b75dd9 | ||
|
|
f1a76e1e76 | ||
|
|
a391f7414f | ||
|
|
299de54ba5 | ||
|
|
37bae14dfd | ||
|
|
d780aa43d4 | ||
|
|
91534776d1 | ||
|
|
31bb611ecf | ||
|
|
53c88f0302 | ||
|
|
ad1cc78578 |
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<artifactId>forge</artifactId>
|
||||
<groupId>forge</groupId>
|
||||
<version>1.6.30-SNAPSHOT</version>
|
||||
<version>1.6.30</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>forge-ai</artifactId>
|
||||
|
||||
@@ -1281,7 +1281,8 @@ public class AiBlockController {
|
||||
oppCreatureCount = ComputerUtil.countUsefulCreatures(attackersLeft.get(0).getController());
|
||||
}
|
||||
|
||||
if (attacker.getOwner().equals(ai) && "6".equals(attacker.getSVar("SacMe"))) {
|
||||
if (attacker != null && attacker.getOwner() != null)
|
||||
if (attacker.getOwner().equals(ai) && "6".equals(attacker.getSVar("SacMe"))) {
|
||||
// Temporarily controlled object - don't trade with it
|
||||
// TODO: find a more reliable way to figure out that control will be reestablished next turn
|
||||
return false;
|
||||
|
||||
@@ -2779,7 +2779,7 @@ public class ComputerUtil {
|
||||
// Iceberg does use Ice as Storage
|
||||
|| (type == CounterType.ICE && !"Iceberg".equals(c.getName()))
|
||||
// some lands does use Depletion as Storage Counter
|
||||
|| (type == CounterType.DEPLETION && !c.canUntapPhaseController())
|
||||
|| (type == CounterType.DEPLETION && c.hasKeyword("CARDNAME doesn't untap during your untap step."))
|
||||
// treat Time Counters on suspended Cards as Bad,
|
||||
// and also on Chronozoa
|
||||
|| (type == CounterType.TIME && (!c.isInPlay() || "Chronozoa".equals(c.getName())))
|
||||
|
||||
@@ -1742,7 +1742,7 @@ public class ComputerUtilCard {
|
||||
if (!c.isCreature()) {
|
||||
return false;
|
||||
}
|
||||
if (c.hasKeyword("CARDNAME can't attack or block.") || (!c.canUntapPhaseController() && c.isTapped()) || (c.getOwner() == ai && ai.getOpponents().contains(c.getController()))) {
|
||||
if (c.hasKeyword("CARDNAME can't attack or block.") || (c.hasKeyword("CARDNAME doesn't untap during your untap step.") && c.isTapped()) || (c.getOwner() == ai && ai.getOpponents().contains(c.getController()))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -173,7 +173,7 @@ public class CreatureEvaluator implements Function<Card, Integer> {
|
||||
if (c.hasKeyword("CARDNAME can't attack or block.")) {
|
||||
value = addValue(50 + (c.getCMC() * 5), "useless"); // reset everything - useless
|
||||
}
|
||||
if (!c.canUntapPhaseController()) {
|
||||
if (c.hasKeyword("CARDNAME doesn't untap during your untap step.")) {
|
||||
if (c.isTapped()) {
|
||||
value = addValue(50 + (c.getCMC() * 5), "tapped-useless"); // reset everything - useless
|
||||
} else {
|
||||
|
||||
@@ -11,10 +11,7 @@ import forge.game.Game;
|
||||
import forge.game.GameEntity;
|
||||
import forge.game.ability.AbilityFactory;
|
||||
import forge.game.ability.effects.DetachedCardEffect;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CounterType;
|
||||
import forge.game.card.*;
|
||||
import forge.game.card.token.TokenInfo;
|
||||
import forge.game.combat.Combat;
|
||||
import forge.game.combat.CombatUtil;
|
||||
@@ -24,6 +21,7 @@ import forge.game.mana.ManaPool;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.AbilityManaPart;
|
||||
import forge.game.spellability.AbilitySub;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.ability.AbilityKey;
|
||||
import forge.game.trigger.TriggerType;
|
||||
@@ -362,6 +360,12 @@ public abstract class GameState {
|
||||
if (c.isFaceDown()) {
|
||||
newText.append("|FaceDown"); // Exiled face down
|
||||
}
|
||||
if (c.isAdventureCard() && c.getZone().is(ZoneType.Exile)) {
|
||||
// TODO: this will basically default all exiled cards with Adventure to being "On Adventure".
|
||||
// Need to figure out a better way to detect if it's actually on adventure.
|
||||
newText.append("|OnAdventure");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (zoneType == ZoneType.Battlefield || zoneType == ZoneType.Exile) {
|
||||
@@ -1202,6 +1206,16 @@ public abstract class GameState {
|
||||
c.setState(CardStateName.Flipped, true);
|
||||
} else if (info.startsWith("Meld")) {
|
||||
c.setState(CardStateName.Meld, true);
|
||||
} else if (info.startsWith("OnAdventure")) {
|
||||
String abAdventure = "DB$ Effect | RememberObjects$ Self | StaticAbilities$ Play | ExileOnMoved$ Exile | Duration$ Permanent | ConditionDefined$ Self | ConditionPresent$ Card.nonCopiedSpell";
|
||||
AbilitySub saAdventure = (AbilitySub)AbilityFactory.getAbility(abAdventure, c);
|
||||
StringBuilder sbPlay = new StringBuilder();
|
||||
sbPlay.append("Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered+nonAdventure");
|
||||
sbPlay.append(" | AffectedZone$ Exile | Description$ You may cast the card.");
|
||||
saAdventure.setSVar("Play", sbPlay.toString());
|
||||
saAdventure.setActivatingPlayer(c.getOwner());
|
||||
saAdventure.resolve();
|
||||
c.setExiledWith(c); // This seems to be the way it's set up internally. Potentially not needed here?
|
||||
} else if (info.startsWith("IsCommander")) {
|
||||
// TODO: This doesn't seem to properly restore the ability to play the commander. Why?
|
||||
c.setCommander(true);
|
||||
|
||||
@@ -33,7 +33,6 @@ public enum SpellApiToAi {
|
||||
.put(ApiType.BidLife, BidLifeAi.class)
|
||||
.put(ApiType.Bond, BondAi.class)
|
||||
.put(ApiType.Branch, AlwaysPlayAi.class)
|
||||
.put(ApiType.CantUntapTurn, CantUntapTurnAi.class)
|
||||
.put(ApiType.ChangeCombatants, CannotPlayAi.class)
|
||||
.put(ApiType.ChangeTargets, ChangeTargetsAi.class)
|
||||
.put(ApiType.ChangeX, AlwaysPlayAi.class)
|
||||
|
||||
@@ -1146,7 +1146,6 @@ public class AttachAi extends SpellAbilityAi {
|
||||
final List<String> keywords = new ArrayList<>();
|
||||
boolean grantingAbilities = false;
|
||||
boolean grantingExtraBlock = false;
|
||||
boolean grantingCantUntap = false;
|
||||
|
||||
for (final StaticAbility stAbility : attachSource.getStaticAbilities()) {
|
||||
final Map<String, String> stabMap = stAbility.getMapParams();
|
||||
@@ -1166,7 +1165,6 @@ public class AttachAi extends SpellAbilityAi {
|
||||
|
||||
grantingAbilities |= stabMap.containsKey("AddAbility");
|
||||
grantingExtraBlock |= stabMap.containsKey("CanBlockAmount") || stabMap.containsKey("CanBlockAny");
|
||||
grantingCantUntap |= stabMap.containsKey("CantUntap");
|
||||
|
||||
String kws = stabMap.get("AddKeyword");
|
||||
if (kws != null) {
|
||||
@@ -1199,7 +1197,6 @@ public class AttachAi extends SpellAbilityAi {
|
||||
if (totToughness + totPower < 4 && (!keywords.isEmpty() || grantingExtraBlock)) {
|
||||
final int pow = totPower;
|
||||
final boolean extraBlock = grantingExtraBlock;
|
||||
final boolean cantUntap = grantingCantUntap;
|
||||
prefList = CardLists.filter(prefList, new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(final Card c) {
|
||||
@@ -1218,9 +1215,6 @@ public class AttachAi extends SpellAbilityAi {
|
||||
if (extraBlock && CombatUtil.canBlock(c, true) && !c.canBlockAny()) {
|
||||
return true;
|
||||
}
|
||||
if (cantUntap && c.isTapped()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
@@ -1638,6 +1632,8 @@ public class AttachAi extends SpellAbilityAi {
|
||||
} else if (keyword.endsWith("Prevent all combat damage that would be dealt to and dealt by CARDNAME.")
|
||||
|| keyword.endsWith("Prevent all damage that would be dealt to and dealt by CARDNAME.")) {
|
||||
return ComputerUtilCombat.canAttackNextTurn(card) && card.getNetCombatDamage() >= 2;
|
||||
} else if (keyword.endsWith("CARDNAME doesn't untap during your untap step.")) {
|
||||
return !card.isUntapped();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
package forge.ai.ability;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Predicates;
|
||||
import forge.ai.ComputerUtilCard;
|
||||
import forge.ai.SpellAbilityAi;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.card.CardPredicates;
|
||||
import forge.game.cost.CostPutCounter;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.zone.ZoneType;
|
||||
|
||||
public class CantUntapTurnAi extends SpellAbilityAi {
|
||||
@Override
|
||||
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
||||
if (sa.usesTargeting()) {
|
||||
CardCollection oppCards = ai.getOpponents().getCardsIn(ZoneType.Battlefield);
|
||||
|
||||
CardCollection relevantToHold = CardLists.filter(oppCards,
|
||||
Predicates.and(CardPredicates.Presets.TAPPED, new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(Card card) {
|
||||
if (card.isCreature()) {
|
||||
return true;
|
||||
}
|
||||
for (final SpellAbility ab : card.getSpellAbilities()) {
|
||||
if (ab.isAbility() && (ab.getPayCosts() != null) && ab.getPayCosts().hasTapCost()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}));
|
||||
|
||||
Card bestToTap = ComputerUtilCard.getBestAI(relevantToHold);
|
||||
Card validTarget = ComputerUtilCard.getBestAI(CardLists.filter(oppCards, CardPredicates.Presets.TAPPED));
|
||||
if (validTarget == null) {
|
||||
validTarget = ComputerUtilCard.getBestAI(oppCards);
|
||||
}
|
||||
|
||||
if (bestToTap != null) {
|
||||
sa.getTargets().add(bestToTap);
|
||||
return true;
|
||||
} else if (sa.hasParam("Planeswalker")
|
||||
&& sa.getPayCosts() != null && sa.getPayCosts().hasSpecificCostType(CostPutCounter.class)) {
|
||||
sa.getTargets().add(validTarget);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean doTriggerAINoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) {
|
||||
return mandatory || canPlayAI(aiPlayer, sa);
|
||||
}
|
||||
}
|
||||
@@ -45,6 +45,8 @@ public class FightAi extends SpellAbilityAi {
|
||||
aiCreatures = ComputerUtil.getSafeTargets(ai, sa, aiCreatures);
|
||||
List<Card> humCreatures = ai.getOpponents().getCreaturesInPlay();
|
||||
humCreatures = CardLists.getTargetableCards(humCreatures, sa);
|
||||
if (humCreatures.isEmpty())
|
||||
return false; //prevent IndexOutOfBoundsException on MOJHOSTO variant
|
||||
|
||||
// assumes the triggered card belongs to the ai
|
||||
if (sa.hasParam("Defined")) {
|
||||
|
||||
@@ -15,6 +15,7 @@ import forge.game.combat.CombatUtil;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.phase.PhaseHandler;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.phase.Untap;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.zone.ZoneType;
|
||||
@@ -129,6 +130,9 @@ public abstract class PumpAiBase extends SpellAbilityAi {
|
||||
// target needs to be a creature, controlled by the player which is attacked
|
||||
return !sa.getHostCard().isTapped() || (combat != null && combat.isAttacking(sa.getHostCard())
|
||||
&& card.getController().equals(combat.getDefenderPlayerByAttacker(sa.getHostCard())));
|
||||
} else if (keyword.endsWith("This card doesn't untap during your next untap step.")) {
|
||||
return !ph.getPhase().isBefore(PhaseType.MAIN2) && !card.isUntapped() && ph.isPlayerTurn(ai)
|
||||
&& Untap.canUntap(card);
|
||||
} else if (keyword.endsWith("Prevent all combat damage that would be dealt by CARDNAME.")
|
||||
|| keyword.endsWith("Prevent all damage that would be dealt by CARDNAME.")) {
|
||||
if (ph.isPlayerTurn(ai) && (!(CombatUtil.canBlock(card) || combat != null && combat.isBlocking(card))
|
||||
@@ -507,7 +511,8 @@ public abstract class PumpAiBase extends SpellAbilityAi {
|
||||
for (final String keyword : keywords) {
|
||||
// since most keywords are combat relevant check for those that are
|
||||
// not
|
||||
if (keyword.endsWith("Shroud") || keyword.endsWith("Hexproof")) {
|
||||
if (keyword.endsWith("This card doesn't untap during your next untap step.")
|
||||
|| keyword.endsWith("Shroud") || keyword.endsWith("Hexproof")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,6 +63,7 @@ public class SacrificeAi extends SpellAbilityAi {
|
||||
final boolean destroy = sa.hasParam("Destroy");
|
||||
|
||||
Player opp = ai.getWeakestOpponent();
|
||||
|
||||
if (tgt != null) {
|
||||
sa.resetTargets();
|
||||
if (!opp.canBeTargetedBy(sa)) {
|
||||
@@ -74,8 +75,16 @@ public class SacrificeAi extends SpellAbilityAi {
|
||||
num = (num == null) ? "1" : num;
|
||||
final int amount = AbilityUtils.calculateAmount(sa.getHostCard(), num, sa);
|
||||
|
||||
List<Card> list =
|
||||
CardLists.getValidCards(opp.getCardsIn(ZoneType.Battlefield), valid.split(","), sa.getActivatingPlayer(), sa.getHostCard(), sa);
|
||||
List<Card> list = null;
|
||||
try {
|
||||
list = CardLists.getValidCards(opp.getCardsIn(ZoneType.Battlefield), valid.split(","), sa.getActivatingPlayer(), sa.getHostCard(), sa);
|
||||
} catch (NullPointerException e) {
|
||||
return false;
|
||||
} finally {
|
||||
if (list == null)
|
||||
return false;
|
||||
}//prevent NPE on MoJhoSto
|
||||
|
||||
for (Card c : list) {
|
||||
if (c.hasSVar("SacMe") && Integer.parseInt(c.getSVar("SacMe")) > 3) {
|
||||
return false;
|
||||
|
||||
@@ -331,7 +331,7 @@ public class UntapAi extends SpellAbilityAi {
|
||||
}
|
||||
|
||||
// See if there's anything to untap that is tapped and that doesn't untap during the next untap step by itself
|
||||
CardCollection noAutoUntap = CardLists.filter(untapList, Predicates.not(CardPredicates.canUntapPhaseController()));
|
||||
CardCollection noAutoUntap = CardLists.filter(untapList, CardPredicates.hasKeyword("CARDNAME doesn't untap during your untap step."));
|
||||
if (!noAutoUntap.isEmpty()) {
|
||||
return ComputerUtilCard.getBestAI(noAutoUntap);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<artifactId>forge</artifactId>
|
||||
<groupId>forge</groupId>
|
||||
<version>1.6.30-SNAPSHOT</version>
|
||||
<version>1.6.30</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>forge-core</artifactId>
|
||||
@@ -21,7 +21,7 @@
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>3.7</version>
|
||||
<version>3.8.1</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import org.apache.commons.lang3.StringUtils;
|
||||
public abstract class LobbyPlayer {
|
||||
protected String name;
|
||||
private int avatarIndex = -1;
|
||||
private int sleeveIndex = -1;
|
||||
private String avatarCardImageKey;
|
||||
|
||||
public LobbyPlayer(String name) {
|
||||
@@ -59,9 +60,15 @@ public abstract class LobbyPlayer {
|
||||
public int getAvatarIndex() {
|
||||
return avatarIndex;
|
||||
}
|
||||
public int getSleeveIndex() {
|
||||
return sleeveIndex;
|
||||
}
|
||||
public void setAvatarIndex(int avatarIndex) {
|
||||
this.avatarIndex = avatarIndex;
|
||||
}
|
||||
public void setSleeveIndex(int sleeveIndex) {
|
||||
this.sleeveIndex = sleeveIndex;
|
||||
}
|
||||
|
||||
public String getAvatarCardImageKey() {
|
||||
return avatarCardImageKey;
|
||||
|
||||
@@ -35,6 +35,7 @@ public class StaticData {
|
||||
|
||||
private Predicate<PaperCard> standardPredicate;
|
||||
private Predicate<PaperCard> brawlPredicate;
|
||||
private Predicate<PaperCard> pioneerPredicate;
|
||||
private Predicate<PaperCard> modernPredicate;
|
||||
private Predicate<PaperCard> commanderPredicate;
|
||||
private Predicate<PaperCard> oathbreakerPredicate;
|
||||
@@ -197,13 +198,13 @@ public class StaticData {
|
||||
|
||||
public TokenDb getAllTokens() { return allTokens; }
|
||||
|
||||
public Predicate<PaperCard> getStandardPredicate() {
|
||||
return standardPredicate;
|
||||
}
|
||||
|
||||
|
||||
public void setStandardPredicate(Predicate<PaperCard> standardPredicate) { this.standardPredicate = standardPredicate; }
|
||||
|
||||
public void setModernPredicate(Predicate<PaperCard> modernPredicate) { this.modernPredicate = standardPredicate; }
|
||||
public void setPioneerPredicate(Predicate<PaperCard> pioneerPredicate) { this.pioneerPredicate = pioneerPredicate; }
|
||||
|
||||
public void setModernPredicate(Predicate<PaperCard> modernPredicate) { this.modernPredicate = modernPredicate; }
|
||||
|
||||
public void setCommanderPredicate(Predicate<PaperCard> commanderPredicate) { this.commanderPredicate = commanderPredicate; }
|
||||
|
||||
@@ -211,9 +212,11 @@ public class StaticData {
|
||||
|
||||
public void setBrawlPredicate(Predicate<PaperCard> brawlPredicate) { this.brawlPredicate = brawlPredicate; }
|
||||
|
||||
public Predicate<PaperCard> getModernPredicate() {
|
||||
return modernPredicate;
|
||||
}
|
||||
public Predicate<PaperCard> getStandardPredicate() { return standardPredicate; }
|
||||
|
||||
public Predicate<PaperCard> getPioneerPredicate() { return pioneerPredicate; }
|
||||
|
||||
public Predicate<PaperCard> getModernPredicate() { return modernPredicate; }
|
||||
|
||||
public Predicate<PaperCard> getCommanderPredicate() { return commanderPredicate; }
|
||||
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
package forge.card;
|
||||
package forge.util;
|
||||
|
||||
import com.esotericsoftware.minlog.Log;
|
||||
import com.google.common.base.Charsets;
|
||||
import forge.properties.ForgeConstants;
|
||||
import forge.util.LineReader;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
@@ -15,12 +12,12 @@ public class CardTranslation {
|
||||
private static Map <String, String> translatednames;
|
||||
private static Map <String, String> translatedtypes;
|
||||
private static Map <String, String> translatedoracles;
|
||||
private static String languageSelected;
|
||||
private static String languageSelected = "en-US";
|
||||
|
||||
private static void readTranslationFile(String language) {
|
||||
private static void readTranslationFile(String language, String languagesDirectory) {
|
||||
String filename = "cardnames-" + language + ".txt";
|
||||
|
||||
try (LineReader translationFile = new LineReader(new FileInputStream(ForgeConstants.LANG_DIR + filename), Charsets.UTF_8)) {
|
||||
try (LineReader translationFile = new LineReader(new FileInputStream(languagesDirectory + filename), Charsets.UTF_8)) {
|
||||
for (String line : translationFile.readLines()) {
|
||||
String[] matches = line.split("\\|");
|
||||
if (matches.length >= 2) {
|
||||
@@ -34,7 +31,7 @@ public class CardTranslation {
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.error("Error reading translation file: cardnames-" + language + ".txt");
|
||||
System.err.println("Error reading translation file: cardnames-" + language + ".txt");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +63,7 @@ public class CardTranslation {
|
||||
}
|
||||
|
||||
public static HashMap<String, String> getTranslationTexts(String cardname, String altcardname) {
|
||||
HashMap<String, String> translations = new HashMap<String, String>();
|
||||
HashMap<String, String> translations = new HashMap<>();
|
||||
translations.put("name", getTranslatedName(cardname));
|
||||
translations.put("oracle", getTranslatedOracle(cardname));
|
||||
translations.put("altname", getTranslatedName(altcardname));
|
||||
@@ -78,14 +75,14 @@ public class CardTranslation {
|
||||
return !languageSelected.equals("en-US");
|
||||
}
|
||||
|
||||
public static void preloadTranslation(String language) {
|
||||
public static void preloadTranslation(String language, String languagesDirectory) {
|
||||
languageSelected = language;
|
||||
|
||||
if (needsTranslation()) {
|
||||
translatednames = new HashMap<>();
|
||||
translatedtypes = new HashMap<>();
|
||||
translatedoracles = new HashMap<>();
|
||||
readTranslationFile(languageSelected);
|
||||
readTranslationFile(languageSelected, languagesDirectory);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<artifactId>forge</artifactId>
|
||||
<groupId>forge</groupId>
|
||||
<version>1.6.30-SNAPSHOT</version>
|
||||
<version>1.6.30</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>forge-game</artifactId>
|
||||
@@ -32,8 +32,8 @@
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.sentry</groupId>
|
||||
<artifactId>sentry-log4j</artifactId>
|
||||
<version>1.7.5</version>
|
||||
<artifactId>sentry-log4j2</artifactId>
|
||||
<version>1.7.27</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
@@ -701,7 +701,9 @@ public class GameAction {
|
||||
// Run triggers
|
||||
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromCard(c);
|
||||
runParams.put(AbilityKey.Cause, cause);
|
||||
runParams.put(AbilityKey.Origin, origin.getZoneType().name());
|
||||
if (origin != null) { // is generally null when adding via dev mode
|
||||
runParams.put(AbilityKey.Origin, origin.getZoneType().name());
|
||||
}
|
||||
if (params != null) {
|
||||
runParams.putAll(params);
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ import java.util.Map.Entry;
|
||||
public class GameFormat implements Comparable<GameFormat> {
|
||||
private final String name;
|
||||
public enum FormatType {Sanctioned, Casual, Historic, Digital, Custom}
|
||||
public enum FormatSubType {Block, Standard, Extended, Modern, Legacy, Vintage, Commander, Planechase, Videogame, MTGO, Custom}
|
||||
public enum FormatSubType {Block, Standard, Extended, Pioneer, Modern, Legacy, Vintage, Commander, Planechase, Videogame, MTGO, Custom}
|
||||
|
||||
// contains allowed sets, when empty allows all sets
|
||||
private FormatType formatType;
|
||||
@@ -290,6 +290,7 @@ public class GameFormat implements Comparable<GameFormat> {
|
||||
private List<String> coreFormats = new ArrayList<>();
|
||||
{
|
||||
coreFormats.add("Standard.txt");
|
||||
coreFormats.add("Pioneer.txt");
|
||||
coreFormats.add("Modern.txt");
|
||||
coreFormats.add("Legacy.txt");
|
||||
coreFormats.add("Vintage.txt");
|
||||
@@ -468,6 +469,10 @@ public class GameFormat implements Comparable<GameFormat> {
|
||||
return this.map.get("Extended");
|
||||
}
|
||||
|
||||
public GameFormat getPioneer() {
|
||||
return this.map.get("Pioneer");
|
||||
}
|
||||
|
||||
public GameFormat getModern() {
|
||||
return this.map.get("Modern");
|
||||
}
|
||||
|
||||
@@ -324,12 +324,6 @@ public class StaticEffect {
|
||||
if (hasParam("CanBlockAmount")) {
|
||||
affectedCard.removeCanBlockAdditional(getTimestamp());
|
||||
}
|
||||
if (hasParam("CantUntap")) {
|
||||
affectedCard.removeCantUntap(getTimestamp());
|
||||
}
|
||||
if (hasParam("CantUntapPlayer")) {
|
||||
affectedCard.removeCantUntapPlayer(getTimestamp());
|
||||
}
|
||||
|
||||
affectedCard.updateAbilityTextForView(); // only update keywords and text for view to avoid flickering
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@ public enum ApiType {
|
||||
Block (BlockEffect.class),
|
||||
Bond (BondEffect.class),
|
||||
Branch (BranchEffect.class),
|
||||
CantUntapTurn (CantUntapTurnEffect.class),
|
||||
ChangeCombatants (ChangeCombatantsEffect.class),
|
||||
ChangeTargets (ChangeTargetsEffect.class),
|
||||
ChangeText (ChangeTextEffect.class),
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
package forge.game.ability.effects;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import forge.game.Game;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
|
||||
public class CantUntapTurnEffect extends SpellAbilityEffect {
|
||||
|
||||
@Override
|
||||
public void resolve(SpellAbility sa) {
|
||||
final Card host = sa.getHostCard();
|
||||
final Game game = host.getGame();
|
||||
final long timestamp = game.getNextTimestamp();
|
||||
|
||||
final int n = AbilityUtils.calculateAmount(host, sa.getParamOrDefault("Turns", "1"), sa);
|
||||
|
||||
List<Card> cards = getTargetCards(sa);
|
||||
|
||||
for (final Card tgtC : cards) {
|
||||
if (sa.usesTargeting() && !tgtC.canBeTargetedBy(sa)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
tgtC.addCantUntapTurn(timestamp, n);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -48,15 +48,6 @@ public class EffectEffect extends SpellAbilityEffect {
|
||||
String effectImprinted = null;
|
||||
List<Player> effectOwner = null;
|
||||
boolean imprintOnHost = false;
|
||||
final String duration = sa.getParam("Duration");
|
||||
|
||||
// special for until lose control or host leaves play
|
||||
if ("UntilLoseControlOfHost".equals(duration) || "UntilHostLeavesPlay".equals(duration)
|
||||
|| "UntilUntaps".equals(duration)) {
|
||||
if (!hostCard.isInPlay()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (sa.hasParam("Abilities")) {
|
||||
effectAbilities = sa.getParam("Abilities").split(",");
|
||||
@@ -247,6 +238,7 @@ public class EffectEffect extends SpellAbilityEffect {
|
||||
}
|
||||
|
||||
// Duration
|
||||
final String duration = sa.getParam("Duration");
|
||||
if ((duration == null) || !duration.equals("Permanent")) {
|
||||
final GameCommand endEffect = new GameCommand() {
|
||||
private static final long serialVersionUID = -5861759814760561373L;
|
||||
@@ -263,18 +255,10 @@ public class EffectEffect extends SpellAbilityEffect {
|
||||
else if (duration.equals("UntilHostLeavesPlay")) {
|
||||
hostCard.addLeavesPlayCommand(endEffect);
|
||||
}
|
||||
else if (duration.equals("UntilLoseControlOfHost")) {
|
||||
hostCard.addLeavesPlayCommand(endEffect);
|
||||
hostCard.addChangeControllerCommand(endEffect);
|
||||
}
|
||||
else if (duration.equals("HostLeavesOrEOT")) {
|
||||
game.getEndOfTurn().addUntil(endEffect);
|
||||
hostCard.addLeavesPlayCommand(endEffect);
|
||||
}
|
||||
else if (duration.equals("UntilUntaps")) {
|
||||
hostCard.addLeavesPlayCommand(endEffect);
|
||||
hostCard.addUntapCommand(endEffect);
|
||||
}
|
||||
else if (duration.equals("UntilYourNextTurn")) {
|
||||
game.getCleanup().addUntil(controller, endEffect);
|
||||
}
|
||||
|
||||
@@ -280,9 +280,6 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
private final Table<SpellAbility, StaticAbility, Integer> numberTurnActivationsStatic = HashBasedTable.create();
|
||||
private final Table<SpellAbility, StaticAbility, Integer> numberGameActivationsStatic = HashBasedTable.create();
|
||||
|
||||
private final Map<Long, Integer> cantUntapTurns = Maps.newTreeMap();
|
||||
private final Set<Long> cantUntap = Sets.newHashSet();
|
||||
private final Map<Long, Player> cantUntapPlayer = Maps.newTreeMap();
|
||||
|
||||
// Enumeration for CMC request types
|
||||
public enum SplitCMCMode {
|
||||
@@ -3609,94 +3606,6 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
getGame().fireEvent(new GameEventCardTapped(this, true));
|
||||
}
|
||||
|
||||
public final boolean canUntapPhase(Player activePlayer) {
|
||||
if (activePlayer.equals(getController())) {
|
||||
return canUntapPhaseController();
|
||||
}
|
||||
|
||||
if (cantUntapPlayer.containsValue(activePlayer)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !isExertedBy(activePlayer);
|
||||
}
|
||||
|
||||
public final boolean canUntapPhaseController() {
|
||||
if (!cantUntap.isEmpty() || !cantUntapTurns.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Player p = getController();
|
||||
if (cantUntapPlayer.containsValue(p)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !isExertedBy(p);
|
||||
}
|
||||
|
||||
public boolean isCantUntap() {
|
||||
return !cantUntap.isEmpty();
|
||||
}
|
||||
|
||||
public final boolean addCantUntap(final long timestamp) {
|
||||
boolean result = cantUntap.add(timestamp);
|
||||
getView().updateCantUntap(this);
|
||||
return result;
|
||||
}
|
||||
|
||||
public final boolean removeCantUntap(final long timestamp) {
|
||||
boolean result = cantUntap.remove(timestamp);
|
||||
getView().updateCantUntap(this);
|
||||
return result;
|
||||
}
|
||||
|
||||
public final void addCantUntapTurn(final long timestamp, final int value) {
|
||||
cantUntapTurns.put(timestamp, value);
|
||||
getView().updateCantUntap(this);
|
||||
}
|
||||
|
||||
public final void removeCantUntapTurn(final long timestamp) {
|
||||
cantUntapTurns.remove(timestamp);
|
||||
getView().updateCantUntap(this);
|
||||
}
|
||||
|
||||
public final void removeCantUntapTurn() {
|
||||
// reduce by one each turn
|
||||
|
||||
List<Long> toRemove = Lists.newArrayList();
|
||||
for (final Map.Entry<Long, Integer> e : cantUntapTurns.entrySet()) {
|
||||
e.setValue(e.getValue() - 1);
|
||||
if (e.getValue() <= 0) {
|
||||
toRemove.add(e.getKey());
|
||||
}
|
||||
}
|
||||
for (final long l : toRemove) {
|
||||
cantUntapTurns.remove(l);
|
||||
}
|
||||
getView().updateCantUntap(this);
|
||||
}
|
||||
|
||||
public final int getCantUntapTurnValue() {
|
||||
if (cantUntapTurns.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
return Collections.max(cantUntapTurns.values());
|
||||
}
|
||||
|
||||
public final void addCantUntapPlayer(final Player p, final long timestamp) {
|
||||
cantUntapPlayer.put(timestamp, p);
|
||||
getView().updateCantUntap(this);
|
||||
}
|
||||
|
||||
public final void removeCantUntapPlayer(final long timestamp) {
|
||||
cantUntapPlayer.remove(timestamp);
|
||||
getView().updateCantUntap(this);
|
||||
}
|
||||
|
||||
public final Collection<Player> getCantUntapPlayer() {
|
||||
return cantUntapPlayer.values();
|
||||
}
|
||||
|
||||
public final void untap() {
|
||||
if (!tapped) { return; }
|
||||
|
||||
|
||||
@@ -67,15 +67,6 @@ public final class CardPredicates {
|
||||
};
|
||||
}
|
||||
|
||||
public static final Predicate<Card> canUntapPhaseController() {
|
||||
return new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(final Card c) {
|
||||
return c.canUntapPhaseController();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static final Predicate<Card> isType(final String cardType) {
|
||||
return new Predicate<Card>() {
|
||||
@Override
|
||||
|
||||
@@ -2,8 +2,6 @@ package forge.game.card;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import forge.ImageKeys;
|
||||
import forge.card.*;
|
||||
import forge.card.mana.ManaCost;
|
||||
@@ -24,7 +22,6 @@ import forge.util.Lang;
|
||||
import forge.util.collect.FCollectionView;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -607,7 +604,7 @@ public class CardView extends GameEntityView {
|
||||
sb.append("\r\n\r\n").append("(").append(taltname).append(") ").append(taltoracle);
|
||||
}
|
||||
|
||||
String nonAbilityText = getNonAbilityText();
|
||||
String nonAbilityText = get(TrackableProperty.NonAbilityText);
|
||||
if (!nonAbilityText.isEmpty()) {
|
||||
sb.append("\r\n \r\nNon ability features: \r\n");
|
||||
sb.append(nonAbilityText.replaceAll("CARDNAME", getName()));
|
||||
@@ -635,6 +632,22 @@ public class CardView extends GameEntityView {
|
||||
sb.append("\r\n");
|
||||
}
|
||||
|
||||
if (getCanBlockAny()) {
|
||||
sb.append("\r\n\r\n");
|
||||
sb.append("CARDNAME can block any number of creatures.".replaceAll("CARDNAME", getName()));
|
||||
sb.append("\r\n");
|
||||
} else {
|
||||
int i = getBlockAdditional();
|
||||
if (i > 0) {
|
||||
sb.append("\r\n\r\n");
|
||||
sb.append("CARDNAME can block an additional ".replaceAll("CARDNAME", getName()));
|
||||
sb.append(i == 1 ? "creature" : Lang.nounWithNumeral(i, "creature"));
|
||||
sb.append(" each combat.");
|
||||
sb.append("\r\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
String cloner = get(TrackableProperty.Cloner);
|
||||
if (!cloner.isEmpty()) {
|
||||
sb.append("\r\nCloned by: ").append(cloner);
|
||||
@@ -643,57 +656,6 @@ public class CardView extends GameEntityView {
|
||||
return sb.toString().trim();
|
||||
}
|
||||
|
||||
public String getNonAbilityText() {
|
||||
StringBuilder sb = new StringBuilder(get(TrackableProperty.NonAbilityText));
|
||||
|
||||
if (getCanBlockAny()) {
|
||||
sb.append("\r\n");
|
||||
sb.append("CARDNAME can block any number of creatures.");
|
||||
sb.append("\r\n");
|
||||
} else {
|
||||
int i = getBlockAdditional();
|
||||
if (i > 0) {
|
||||
sb.append("\r\n");
|
||||
sb.append("CARDNAME can block an additional ");
|
||||
sb.append(i == 1 ? "creature" : Lang.nounWithNumeral(i, "creature"));
|
||||
sb.append(" each combat.");
|
||||
sb.append("\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (getCantUntap()) {
|
||||
String msg = "CARDNAME doesn’t untap during its controller’s untap step.";
|
||||
sb.append("\r\n");
|
||||
sb.append(msg);
|
||||
sb.append("\r\n");
|
||||
} else {
|
||||
int i = this.getCantUntapTurn();
|
||||
if (i == 1) {
|
||||
String msg = "CARDNAME doesn’t untap during its controller’s next untap step.";
|
||||
sb.append("\r\n");
|
||||
sb.append(msg);
|
||||
sb.append("\r\n");
|
||||
} else if (i > 1) {
|
||||
String str = Lang.nounWithNumeral(i, "untap step");
|
||||
String msg = ("CARDNAME doesn’t untap during its controller’s next " + str + ".");
|
||||
sb.append("\r\n");
|
||||
sb.append(msg);
|
||||
sb.append("\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
FCollectionView<PlayerView> untapList = getCantUntapPlayer();
|
||||
if (untapList != null && !untapList.isEmpty()) {
|
||||
String p = Lang.joinHomogenous(Lists.newArrayList(untapList), null, "or");
|
||||
String msg = "CARDNAME doesn’t untap during " + p + " untap step.";
|
||||
sb.append("\r\n");
|
||||
sb.append(msg);
|
||||
sb.append("\r\n");
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public CardStateView getCurrentState() {
|
||||
return get(TrackableProperty.CurrentState);
|
||||
}
|
||||
@@ -805,39 +767,6 @@ public class CardView extends GameEntityView {
|
||||
set(TrackableProperty.BlockAny, c.canBlockAny());
|
||||
}
|
||||
|
||||
boolean getCantUntap() {
|
||||
return get(TrackableProperty.CantUntapAny);
|
||||
}
|
||||
int getCantUntapTurn() {
|
||||
return get(TrackableProperty.CantUntapTurns);
|
||||
}
|
||||
|
||||
FCollectionView<PlayerView> getCantUntapPlayer() {
|
||||
return get(TrackableProperty.CantUntapPlayer);
|
||||
}
|
||||
|
||||
void updateCantUntap(Card c) {
|
||||
set(TrackableProperty.CantUntapAny, c.isCantUntap());
|
||||
set(TrackableProperty.CantUntapTurns, c.getCantUntapTurnValue());
|
||||
|
||||
Collection<Player> list = c.getCantUntapPlayer();
|
||||
if (list.isEmpty()) {
|
||||
set(TrackableProperty.CantUntapPlayer, null);
|
||||
} else {
|
||||
TrackableCollection<PlayerView> prop = get(TrackableProperty.CantUntapPlayer);
|
||||
if (prop == null) {
|
||||
prop = new TrackableCollection<>();
|
||||
} else {
|
||||
prop.clear();
|
||||
}
|
||||
for (Player p : list) {
|
||||
prop.add(p.getView());
|
||||
}
|
||||
|
||||
set(TrackableProperty.CantUntapPlayer, prop);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String name = getName();
|
||||
|
||||
@@ -234,7 +234,7 @@ public class ManaPool extends ManaConversionMatrix implements Iterable<Mana> {
|
||||
final Card host = sa.getHostCard();
|
||||
if (mana.addsKeywords(sa) && mana.addsKeywordsType()
|
||||
&& host.getType().hasStringType(mana.getManaAbility().getAddsKeywordsType())) {
|
||||
final long timestamp = sa.getHostCard().getGame().getNextTimestamp();
|
||||
final long timestamp = host.getGame().getNextTimestamp();
|
||||
final List<String> kws = Arrays.asList(mana.getAddedKeywords().split(" & "));
|
||||
host.addChangedCardKeywords(kws, null, false, false, timestamp);
|
||||
if (mana.addsKeywordsUntil()) {
|
||||
@@ -243,14 +243,7 @@ public class ManaPool extends ManaConversionMatrix implements Iterable<Mana> {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (!kws.isEmpty()) {
|
||||
for (String kw : kws) {
|
||||
if (kw.startsWith("HIDDEN")) {
|
||||
sa.getHostCard().removeHiddenExtrinsicKeyword(kw);
|
||||
}
|
||||
}
|
||||
host.removeChangedCardKeywords(timestamp);
|
||||
}
|
||||
host.removeChangedCardKeywords(timestamp);
|
||||
host.getGame().fireEvent(new GameEventCardStatsChanged(host));
|
||||
}
|
||||
};
|
||||
@@ -261,10 +254,10 @@ public class ManaPool extends ManaConversionMatrix implements Iterable<Mana> {
|
||||
}
|
||||
}
|
||||
if (mana.addsCounters(sa)) {
|
||||
mana.getManaAbility().createETBCounters(sa.getHostCard());
|
||||
mana.getManaAbility().createETBCounters(host);
|
||||
}
|
||||
if (mana.triggersWhenSpent()) {
|
||||
mana.getManaAbility().addTriggersWhenSpent(sa, sa.getHostCard());
|
||||
mana.getManaAbility().addTriggersWhenSpent(sa, host);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -44,7 +44,16 @@ public enum PhaseType {
|
||||
nameForScripts = name_for_scripts;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public final boolean phaseforUpdateField() {
|
||||
boolean result =
|
||||
((ALL_PHASES.indexOf(this) >= ALL_PHASES.indexOf(UNTAP)
|
||||
&& ALL_PHASES.indexOf(this) < ALL_PHASES.indexOf(COMBAT_FIRST_STRIKE_DAMAGE))
|
||||
|| (ALL_PHASES.indexOf(this) >= ALL_PHASES.indexOf(MAIN2)
|
||||
&& ALL_PHASES.indexOf(this) < ALL_PHASES.indexOf(CLEANUP)));
|
||||
return result;
|
||||
}
|
||||
|
||||
public final boolean isAfter(final PhaseType phase) {
|
||||
return ALL_PHASES.indexOf(this) > ALL_PHASES.indexOf(phase);
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ public class Untap extends Phase {
|
||||
*/
|
||||
@Override
|
||||
public void executeAt() {
|
||||
super.executeAt();
|
||||
this.execute(this.at);
|
||||
|
||||
final Player turn = game.getPhaseHandler().getPlayerTurn();
|
||||
Untap.doPhasing(turn);
|
||||
@@ -83,9 +83,17 @@ public class Untap extends Phase {
|
||||
* @return a boolean.
|
||||
*/
|
||||
public static boolean canUntap(final Card c) {
|
||||
|
||||
if (c.hasKeyword("CARDNAME doesn't untap during your untap step.")
|
||||
|| c.hasKeyword("This card doesn't untap during your next untap step.")
|
||||
|| c.hasKeyword("This card doesn't untap during your next two untap steps.")
|
||||
|| c.hasKeyword("This card doesn't untap.")) {
|
||||
return false;
|
||||
}
|
||||
//exerted need current player turn
|
||||
final Player playerTurn = c.getGame().getPhaseHandler().getPlayerTurn();
|
||||
return c.canUntapPhase(playerTurn);
|
||||
|
||||
return !c.isExertedBy(playerTurn);
|
||||
}
|
||||
|
||||
public static final Predicate<Card> CANUNTAP = new Predicate<Card>() {
|
||||
@@ -153,10 +161,11 @@ public class Untap extends Phase {
|
||||
// other players untapping during your untap phase
|
||||
List<Card> cardsWithKW = CardLists.getKeyword(game.getCardsIn(ZoneType.Battlefield),
|
||||
"CARDNAME untaps during each other player's untap step.");
|
||||
cardsWithKW = CardLists.getNotKeyword(cardsWithKW, "This card doesn't untap.");
|
||||
|
||||
cardsWithKW = CardLists.filterControlledBy(cardsWithKW, player.getAllOtherPlayers());
|
||||
for (final Card cardWithKW : cardsWithKW) {
|
||||
if (!cardWithKW.canUntapPhase(player)) {
|
||||
if (cardWithKW.isExertedBy(player)) {
|
||||
continue;
|
||||
}
|
||||
cardWithKW.untap();
|
||||
@@ -193,7 +202,11 @@ public class Untap extends Phase {
|
||||
|
||||
// Remove temporary keywords
|
||||
for (final Card c : player.getCardsIn(ZoneType.Battlefield)) {
|
||||
c.removeCantUntapTurn();
|
||||
c.removeHiddenExtrinsicKeyword("This card doesn't untap during your next untap step.");
|
||||
if (c.hasKeyword("This card doesn't untap during your next two untap steps.")) {
|
||||
c.removeHiddenExtrinsicKeyword("This card doesn't untap during your next two untap steps.");
|
||||
c.addHiddenExtrinsicKeyword("This card doesn't untap during your next untap step.");
|
||||
}
|
||||
}
|
||||
|
||||
// remove exerted flags from all things in play
|
||||
|
||||
@@ -2430,6 +2430,7 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
controllerCreator = ctrlr;
|
||||
controller = ctrlr;
|
||||
updateAvatar();
|
||||
updateSleeve();
|
||||
view.updateIsAI(this);
|
||||
view.updateLobbyPlayerName(this);
|
||||
}
|
||||
@@ -2439,6 +2440,10 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
view.updateAvatarCardImageKey(this);
|
||||
}
|
||||
|
||||
public void updateSleeve() {
|
||||
view.updateSleeveIndex(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a procedure using a different controller
|
||||
*/
|
||||
|
||||
@@ -85,6 +85,13 @@ public class PlayerView extends GameEntityView {
|
||||
set(TrackableProperty.AvatarCardImageKey, p.getLobbyPlayer().getAvatarCardImageKey());
|
||||
}
|
||||
|
||||
public int getSleeveIndex() {
|
||||
return get(TrackableProperty.SleeveIndex);
|
||||
}
|
||||
void updateSleeveIndex(Player p) {
|
||||
set(TrackableProperty.SleeveIndex, p.getLobbyPlayer().getSleeveIndex());
|
||||
}
|
||||
|
||||
public String getCurrentPlaneName() { return get(TrackableProperty.CurrentPlane); }
|
||||
void updateCurrentPlaneName( String plane ) {
|
||||
set(TrackableProperty.CurrentPlane, plane);
|
||||
|
||||
@@ -188,9 +188,7 @@ public class StaticAbility extends CardTraitBase implements IIdentifiable, Clone
|
||||
layers.add(StaticAbilityLayer.RULES);
|
||||
}
|
||||
|
||||
if (hasParam("IgnoreEffectCost") || hasParam("Goad")
|
||||
|| hasParam("CanBlockAny") || hasParam("CanBlockAmount")
|
||||
|| hasParam("CantUntap") || hasParam("CantUntapPlayer")) {
|
||||
if (hasParam("IgnoreEffectCost") || hasParam("Goad") || hasParam("CanBlockAny") || hasParam("CanBlockAmount")) {
|
||||
layers.add(StaticAbilityLayer.RULES);
|
||||
}
|
||||
|
||||
|
||||
@@ -124,7 +124,7 @@ public class StaticAbilityCantTarget {
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return common(st, spellAbility);
|
||||
}
|
||||
|
||||
protected static boolean common(final StaticAbility st, final SpellAbility spellAbility) {
|
||||
|
||||
@@ -677,16 +677,6 @@ public final class StaticAbilityContinuous {
|
||||
int v = AbilityUtils.calculateAmount(hostCard, params.get("CanBlockAmount"), stAb, true);
|
||||
affectedCard.addCanBlockAdditional(v, se.getTimestamp());
|
||||
}
|
||||
if (params.containsKey("CantUntap")) {
|
||||
affectedCard.addCantUntap(se.getTimestamp());
|
||||
}
|
||||
if (params.containsKey("CantUntapPlayer")) {
|
||||
Player p = Iterables.getFirst(
|
||||
AbilityUtils.getDefinedPlayers(hostCard, params.get("CantUntapPlayer"), null), null);
|
||||
if (p != null) {
|
||||
affectedCard.addCantUntapPlayer(p, se.getTimestamp());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mayLookAt != null) {
|
||||
|
||||
@@ -113,9 +113,6 @@ public enum TrackableProperty {
|
||||
OpponentMayLook(TrackableTypes.BooleanType),
|
||||
BlockAdditional(TrackableTypes.IntegerType),
|
||||
BlockAny(TrackableTypes.BooleanType),
|
||||
CantUntapTurns(TrackableTypes.IntegerType),
|
||||
CantUntapPlayer(TrackableTypes.PlayerViewCollectionType),
|
||||
CantUntapAny(TrackableTypes.BooleanType),
|
||||
AbilityText(TrackableTypes.StringType),
|
||||
NonAbilityText(TrackableTypes.StringType),
|
||||
FoilIndex(TrackableTypes.IntegerType),
|
||||
@@ -125,6 +122,7 @@ public enum TrackableProperty {
|
||||
LobbyPlayerName(TrackableTypes.StringType),
|
||||
AvatarIndex(TrackableTypes.IntegerType),
|
||||
AvatarCardImageKey(TrackableTypes.StringType),
|
||||
SleeveIndex(TrackableTypes.IntegerType),
|
||||
Opponents(TrackableTypes.PlayerViewCollectionType),
|
||||
Life(TrackableTypes.IntegerType),
|
||||
PoisonCounters(TrackableTypes.IntegerType),
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<parent>
|
||||
<artifactId>forge</artifactId>
|
||||
<groupId>forge</groupId>
|
||||
<version>1.6.30-SNAPSHOT</version>
|
||||
<version>1.6.30</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>forge-gui-android</artifactId>
|
||||
@@ -81,17 +81,17 @@
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>24.1-android</version>
|
||||
<version>28.1-android</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.thoughtworks.xstream</groupId>
|
||||
<artifactId>xstream</artifactId>
|
||||
<version>1.4.7</version>
|
||||
<version>1.4.9</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>3.7</version>
|
||||
<version>3.8.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>xmlpull</groupId>
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
-dontwarn java.lang.**
|
||||
-dontwarn org.slf4j.**
|
||||
-dontwarn javax.**
|
||||
-dontwarn org.apache.logging.log4j.**
|
||||
-dontwarn module-info
|
||||
|
||||
# mandatory proguard rules for cache2k to keep the core implementation
|
||||
-dontwarn org.cache2k.impl.xmlConfiguration.**
|
||||
@@ -48,6 +50,7 @@
|
||||
-keep class com.google.guava.** { *; }
|
||||
-keep class com.google.common.** { *; }
|
||||
-keep class io.sentry.event.Event { *; }
|
||||
-keep class io.netty.util.internal.logging.** { *; }
|
||||
|
||||
-keepclassmembers class com.badlogic.gdx.backends.android.AndroidInput* {
|
||||
<init>(com.badlogic.gdx.Application, android.content.Context, java.lang.Object, com.badlogic.gdx.backends.android.AndroidApplicationConfiguration);
|
||||
|
||||
@@ -14,6 +14,7 @@ import android.graphics.BitmapFactory;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.os.PowerManager;
|
||||
@@ -71,7 +72,8 @@ public class Main extends AndroidApplication {
|
||||
Main.this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
|
||||
}
|
||||
|
||||
initialize(Forge.getApp(new AndroidClipboard(), adapter, assetsDir));
|
||||
boolean value = Build.VERSION.SDK_INT >= 26;
|
||||
initialize(Forge.getApp(new AndroidClipboard(), adapter, assetsDir, value));
|
||||
}
|
||||
|
||||
/*@Override
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>forge</artifactId>
|
||||
<groupId>forge</groupId>
|
||||
<version>1.6.30-SNAPSHOT</version>
|
||||
<version>1.6.30</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>forge-gui-desktop</artifactId>
|
||||
@@ -142,12 +142,12 @@
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>24.1-android</version>
|
||||
<version>28.1-android</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.thoughtworks.xstream</groupId>
|
||||
<artifactId>xstream</artifactId>
|
||||
<version>1.4.10</version>
|
||||
<version>1.4.11.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.testng</groupId>
|
||||
@@ -168,9 +168,14 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>log4j</groupId>
|
||||
<artifactId>log4j</artifactId>
|
||||
<version>1.2.17</version>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-api</artifactId>
|
||||
<version>2.11.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-core</artifactId>
|
||||
<version>2.11.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.googlecode</groupId>
|
||||
@@ -180,7 +185,7 @@
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>3.7</version>
|
||||
<version>3.8.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.freemarker</groupId>
|
||||
|
||||
@@ -202,6 +202,14 @@ public class GuiDesktop implements IGuiBase {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSleevesCount() {
|
||||
if (FSkin.isLoaded()) {
|
||||
return FSkin.getSleeves().size();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String showFileDialog(final String title, final String defaultDir) {
|
||||
final JFileChooser fc = new JFileChooser(defaultDir);
|
||||
|
||||
@@ -383,6 +383,11 @@ public class FDeckChooser extends JPanel implements IDecksComboBoxListener {
|
||||
updateMatrix(FModel.getFormats().getStandard());
|
||||
}
|
||||
break;
|
||||
case PIONEER_CARDGEN_DECK:
|
||||
if(FModel.isdeckGenMatrixLoaded()) {
|
||||
updateMatrix(FModel.getFormats().getPioneer());
|
||||
}
|
||||
break;
|
||||
case MODERN_CARDGEN_DECK:
|
||||
if(FModel.isdeckGenMatrixLoaded()) {
|
||||
updateMatrix(FModel.getFormats().getModern());
|
||||
|
||||
@@ -14,6 +14,7 @@ import javax.swing.ButtonGroup;
|
||||
import javax.swing.JCheckBoxMenuItem;
|
||||
import javax.swing.JPopupMenu;
|
||||
|
||||
import forge.screens.home.sanctioned.SleeveSelector;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
@@ -66,7 +67,8 @@ public class PlayerPanel extends FPanel {
|
||||
|
||||
private final FLabel nameRandomiser;
|
||||
private final FLabel avatarLabel = new FLabel.Builder().opaque(true).hoverable(true).iconScaleFactor(0.99f).iconInBackground(true).build();
|
||||
private int avatarIndex;
|
||||
private final FLabel sleeveLabel = new FLabel.Builder().opaque(true).hoverable(true).iconScaleFactor(0.99f).iconInBackground(true).build();
|
||||
private int avatarIndex, sleeveIndex;
|
||||
|
||||
private final FTextField txtPlayerName = new FTextField.Builder().build();
|
||||
private FRadioButton radioHuman;
|
||||
@@ -127,6 +129,10 @@ public class PlayerPanel extends FPanel {
|
||||
createAvatar();
|
||||
this.add(avatarLabel, "spany 2, width 80px, height 80px");
|
||||
|
||||
/*TODO Layout and Override for PC*/
|
||||
//createSleeve();
|
||||
//this.add(sleeveLabel, "spany 2, width 60px, height 80px");
|
||||
|
||||
createNameEditor();
|
||||
this.add(lobby.newLabel(localizer.getMessage("lblName") +":"), "w 40px, h 30px, gaptop 5px");
|
||||
this.add(txtPlayerName, "h 30px, pushx, growx");
|
||||
@@ -204,6 +210,10 @@ public class PlayerPanel extends FPanel {
|
||||
avatarLabel.setIcon(FSkin.getAvatars().get(Integer.valueOf(type == LobbySlotType.OPEN ? -1 : avatarIndex)));
|
||||
avatarLabel.repaintSelf();
|
||||
|
||||
sleeveLabel.setEnabled(mayEdit);
|
||||
sleeveLabel.setIcon(FSkin.getSleeves().get(Integer.valueOf(type == LobbySlotType.OPEN ? -1 : sleeveIndex)));
|
||||
sleeveLabel.repaintSelf();
|
||||
|
||||
txtPlayerName.setEnabled(mayEdit);
|
||||
txtPlayerName.setText(type == LobbySlotType.OPEN ? StringUtils.EMPTY : playerName);
|
||||
nameRandomiser.setEnabled(mayEdit);
|
||||
@@ -332,6 +342,62 @@ public class PlayerPanel extends FPanel {
|
||||
}
|
||||
};
|
||||
|
||||
/** Listens to sleeve buttons and gives the appropriate player focus. */
|
||||
private final FocusAdapter sleeveFocusListener = new FocusAdapter() {
|
||||
@Override
|
||||
public void focusGained(final FocusEvent e) {
|
||||
lobby.changePlayerFocus(index);
|
||||
}
|
||||
};
|
||||
|
||||
private final FMouseAdapter sleeveMouseListener = new FMouseAdapter() {
|
||||
@Override public final void onLeftClick(final MouseEvent e) {
|
||||
if (!sleeveLabel.isEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final FLabel sleeve = (FLabel)e.getSource();
|
||||
|
||||
lobby.changePlayerFocus(index);
|
||||
sleeve.requestFocusInWindow();
|
||||
|
||||
final SleeveSelector sSel = new SleeveSelector(playerName, sleeveIndex, lobby.getUsedSleeves());
|
||||
for (final FLabel lbl : sSel.getSelectables()) {
|
||||
lbl.setCommand(new UiCommand() {
|
||||
@Override
|
||||
public void run() {
|
||||
setSleeveIndex(Integer.valueOf(lbl.getName().substring(11)));
|
||||
sSel.setVisible(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
sSel.setVisible(true);
|
||||
sSel.dispose();
|
||||
|
||||
if (index < 2) {
|
||||
lobby.updateSleevePrefs();
|
||||
}
|
||||
|
||||
lobby.firePlayerChangeListener(index);
|
||||
}
|
||||
|
||||
@Override public final void onRightClick(final MouseEvent e) {
|
||||
if (!sleeveLabel.isEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
lobby.changePlayerFocus(index);
|
||||
sleeveLabel.requestFocusInWindow();
|
||||
|
||||
setRandomSleeve();
|
||||
|
||||
if (index < 2) {
|
||||
lobby.updateSleevePrefs();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private void updateVariantControlsVisibility() {
|
||||
final boolean isOathbreaker = lobby.hasVariant(GameType.Oathbreaker);
|
||||
final boolean isTinyLeaders = lobby.hasVariant(GameType.TinyLeaders);
|
||||
@@ -703,6 +769,20 @@ public class PlayerPanel extends FPanel {
|
||||
avatarLabel.addMouseListener(avatarMouseListener);
|
||||
}
|
||||
|
||||
private void createSleeve() {
|
||||
final String[] currentPrefs = FModel.getPreferences().getPref(FPref.UI_SLEEVES).split(",");
|
||||
if (index < currentPrefs.length) {
|
||||
sleeveIndex = Integer.parseInt(currentPrefs[index]);
|
||||
sleeveLabel.setIcon(FSkin.getSleeves().get(sleeveIndex));
|
||||
} else {
|
||||
setRandomSleeve(false);
|
||||
}
|
||||
|
||||
sleeveLabel.setToolTipText("L-click: Select sleeve. R-click: Randomize sleeve.");
|
||||
sleeveLabel.addFocusListener(sleeveFocusListener);
|
||||
sleeveLabel.addMouseListener(sleeveMouseListener);
|
||||
}
|
||||
|
||||
/** Applies a random avatar, avoiding avatars already used. */
|
||||
private void setRandomAvatar() {
|
||||
setRandomAvatar(true);
|
||||
@@ -721,6 +801,24 @@ public class PlayerPanel extends FPanel {
|
||||
}
|
||||
}
|
||||
|
||||
/** Applies a random sleeve, avoiding sleeve already used. */
|
||||
private void setRandomSleeve() {
|
||||
setRandomSleeve(true);
|
||||
}
|
||||
private void setRandomSleeve(final boolean fireListeners) {
|
||||
int random = 0;
|
||||
|
||||
final List<Integer> usedSleeves = lobby.getUsedSleeves();
|
||||
do {
|
||||
random = MyRandom.getRandom().nextInt(FSkin.getSleeves().size());
|
||||
} while (usedSleeves.contains(random));
|
||||
setSleeveIndex(random);
|
||||
|
||||
if (fireListeners) {
|
||||
lobby.firePlayerChangeListener(index);
|
||||
}
|
||||
}
|
||||
|
||||
private final FSkin.LineSkinBorder focusedBorder = new FSkin.LineSkinBorder(FSkin.getColor(FSkin.Colors.CLR_BORDERS).alphaColor(255), 3);
|
||||
private final FSkin.LineSkinBorder defaultBorder = new FSkin.LineSkinBorder(FSkin.getColor(FSkin.Colors.CLR_THEME).alphaColor(200), 2);
|
||||
|
||||
@@ -746,6 +844,16 @@ public class PlayerPanel extends FPanel {
|
||||
avatarLabel.repaintSelf();
|
||||
}
|
||||
|
||||
public int getSleeveIndex() {
|
||||
return sleeveIndex;
|
||||
}
|
||||
public void setSleeveIndex(final int sleeveIndex0) {
|
||||
sleeveIndex = sleeveIndex0;
|
||||
final SkinImage icon = FSkin.getSleeves().get(sleeveIndex);
|
||||
sleeveLabel.setIcon(icon);
|
||||
sleeveLabel.repaintSelf();
|
||||
}
|
||||
|
||||
public int getTeam() {
|
||||
return teamComboBox.getSelectedIndex();
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package forge.screens.home;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import forge.GuiBase;
|
||||
import forge.UiCommand;
|
||||
import forge.ai.AIOption;
|
||||
import forge.deck.*;
|
||||
@@ -222,6 +223,7 @@ public class VLobby implements ILobbyView {
|
||||
DeckType selectedDeckType = deckChooser.getSelectedDeckType();
|
||||
switch (selectedDeckType){
|
||||
case STANDARD_CARDGEN_DECK:
|
||||
case PIONEER_CARDGEN_DECK:
|
||||
case MODERN_CARDGEN_DECK:
|
||||
case LEGACY_CARDGEN_DECK:
|
||||
case VINTAGE_CARDGEN_DECK:
|
||||
@@ -258,6 +260,9 @@ public class VLobby implements ILobbyView {
|
||||
addPlayerBtn.setEnabled(activePlayersNum < MAX_PLAYERS);
|
||||
|
||||
final boolean allowNetworking = lobby.isAllowNetworking();
|
||||
|
||||
GuiBase.setNetworkplay(allowNetworking);
|
||||
|
||||
ImmutableList<VariantCheckBox> vntBoxes = null;
|
||||
if (allowNetworking) {
|
||||
vntBoxes = vntBoxesNetwork;
|
||||
@@ -397,7 +402,7 @@ public class VLobby implements ILobbyView {
|
||||
|
||||
private UpdateLobbyPlayerEvent getSlot(final int index) {
|
||||
final PlayerPanel panel = playerPanels.get(index);
|
||||
return UpdateLobbyPlayerEvent.create(panel.getType(), panel.getPlayerName(), panel.getAvatarIndex(), panel.getTeam(), panel.isArchenemy(), panel.isReady(), panel.isDevMode(), panel.getAiOptions());
|
||||
return UpdateLobbyPlayerEvent.create(panel.getType(), panel.getPlayerName(), panel.getAvatarIndex(), -1/*TODO panel.getSleeveIndex()*/, panel.getTeam(), panel.isArchenemy(), panel.isReady(), panel.isDevMode(), panel.getAiOptions());
|
||||
}
|
||||
|
||||
/** Builds the actual deck panel layouts for each player.
|
||||
@@ -858,6 +863,15 @@ public class VLobby implements ILobbyView {
|
||||
prefs.save();
|
||||
}
|
||||
|
||||
/** Saves sleeve prefs for players one and two. */
|
||||
void updateSleevePrefs() {
|
||||
final int pOneIndex = playerPanels.get(0).getSleeveIndex();
|
||||
final int pTwoIndex = playerPanels.get(1).getSleeveIndex();
|
||||
|
||||
prefs.setPref(FPref.UI_SLEEVES, pOneIndex + "," + pTwoIndex);
|
||||
prefs.save();
|
||||
}
|
||||
|
||||
/** Adds a pre-styled FLabel component with the specified title. */
|
||||
FLabel newLabel(final String title) {
|
||||
return new FLabel.Builder().text(title).fontSize(14).fontStyle(Font.ITALIC).build();
|
||||
@@ -871,6 +885,14 @@ public class VLobby implements ILobbyView {
|
||||
return usedAvatars;
|
||||
}
|
||||
|
||||
List<Integer> getUsedSleeves() {
|
||||
final List<Integer> usedSleeves = Lists.newArrayListWithCapacity(MAX_PLAYERS);
|
||||
for (final PlayerPanel pp : playerPanels) {
|
||||
usedSleeves.add(pp.getSleeveIndex());
|
||||
}
|
||||
return usedSleeves;
|
||||
}
|
||||
|
||||
|
||||
private static final ImmutableList<String> genderOptions = ImmutableList.of("Male", "Female", "Any"),
|
||||
typeOptions = ImmutableList.of("Fantasy", "Generic", "Any");
|
||||
|
||||
@@ -72,6 +72,7 @@ public enum CSubmenuGauntletQuick implements ICDoc {
|
||||
if (view.getBoxColorDecks().isSelected()) { allowedDeckTypes.add(DeckType.COLOR_DECK); }
|
||||
if (view.getBoxStandardColorDecks().isSelected()) { allowedDeckTypes.add(DeckType.STANDARD_COLOR_DECK); }
|
||||
if (view.getBoxStandardGenDecks().isSelected()) { allowedDeckTypes.add(DeckType.STANDARD_CARDGEN_DECK); }
|
||||
if (view.getBoxPioneerGenDecks().isSelected()) { allowedDeckTypes.add(DeckType.PIONEER_CARDGEN_DECK); }
|
||||
if (view.getBoxModernGenDecks().isSelected()) { allowedDeckTypes.add(DeckType.MODERN_CARDGEN_DECK); }
|
||||
if (view.getBoxLegacyGenDecks().isSelected()) { allowedDeckTypes.add(DeckType.LEGACY_CARDGEN_DECK); }
|
||||
if (view.getBoxVintageGenDecks().isSelected()) { allowedDeckTypes.add(DeckType.VINTAGE_CARDGEN_DECK); }
|
||||
|
||||
@@ -56,6 +56,7 @@ public enum VSubmenuGauntletQuick implements IVSubmenu<CSubmenuGauntletQuick> {
|
||||
private final JCheckBox boxColorDecks = new FCheckBox(DeckType.COLOR_DECK.toString());
|
||||
private final JCheckBox boxStandardColorDecks = new FCheckBox(DeckType.STANDARD_COLOR_DECK.toString());
|
||||
private final JCheckBox boxStandardCardgenDecks = new FCheckBox(DeckType.STANDARD_CARDGEN_DECK.toString());
|
||||
private final JCheckBox boxPioneerCardgenDecks = new FCheckBox(DeckType.PIONEER_CARDGEN_DECK.toString());
|
||||
private final JCheckBox boxModernCardgenDecks = new FCheckBox(DeckType.MODERN_CARDGEN_DECK.toString());
|
||||
private final JCheckBox boxLegacyCardgenDecks = new FCheckBox(DeckType.LEGACY_CARDGEN_DECK.toString());
|
||||
private final JCheckBox boxVintageCardgenDecks = new FCheckBox(DeckType.VINTAGE_CARDGEN_DECK.toString());
|
||||
@@ -88,11 +89,13 @@ public enum VSubmenuGauntletQuick implements IVSubmenu<CSubmenuGauntletQuick> {
|
||||
boxStandardColorDecks.setSelected(true);
|
||||
if(FModel.isdeckGenMatrixLoaded()) {
|
||||
boxStandardCardgenDecks.setSelected(true);
|
||||
boxPioneerCardgenDecks.setSelected(true);
|
||||
boxModernCardgenDecks.setSelected(true);
|
||||
boxLegacyCardgenDecks.setSelected(true);
|
||||
boxVintageCardgenDecks.setSelected(true);
|
||||
}else{
|
||||
boxStandardCardgenDecks.setSelected(false);
|
||||
boxPioneerCardgenDecks.setSelected(false);
|
||||
boxModernCardgenDecks.setSelected(false);
|
||||
boxLegacyCardgenDecks.setSelected(false);
|
||||
boxVintageCardgenDecks.setSelected(false);
|
||||
@@ -121,6 +124,7 @@ public enum VSubmenuGauntletQuick implements IVSubmenu<CSubmenuGauntletQuick> {
|
||||
pnlOptions.add(boxColorDecks, "w 96%!, h 30px!, gap 2% 0 0 5px");
|
||||
if(FModel.isdeckGenMatrixLoaded()) {
|
||||
pnlOptions.add(boxStandardCardgenDecks, "w 96%!, h 30px!, gap 2% 0 0 5px");
|
||||
pnlOptions.add(boxPioneerCardgenDecks, "w 96%!, h 30px!, gap 2% 0 0 5px");
|
||||
pnlOptions.add(boxModernCardgenDecks, "w 96%!, h 30px!, gap 2% 0 0 5px");
|
||||
pnlOptions.add(boxLegacyCardgenDecks, "w 96%!, h 30px!, gap 2% 0 0 5px");
|
||||
pnlOptions.add(boxVintageCardgenDecks, "w 96%!, h 30px!, gap 2% 0 0 5px");
|
||||
@@ -221,6 +225,9 @@ public enum VSubmenuGauntletQuick implements IVSubmenu<CSubmenuGauntletQuick> {
|
||||
public JCheckBox getBoxModernGenDecks() {
|
||||
return boxModernCardgenDecks;
|
||||
}
|
||||
public JCheckBox getBoxPioneerGenDecks() {
|
||||
return boxPioneerCardgenDecks;
|
||||
}
|
||||
public JCheckBox getBoxLegacyGenDecks() {
|
||||
return boxLegacyCardgenDecks;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
package forge.screens.home.sanctioned;
|
||||
|
||||
import forge.gui.WrapLayout;
|
||||
import forge.toolbox.FLabel;
|
||||
import forge.toolbox.FScrollPane;
|
||||
import forge.toolbox.FSkin;
|
||||
import forge.view.FDialog;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class SleeveSelector extends FDialog {
|
||||
private final List<FLabel> selectables = new ArrayList<>();
|
||||
private final Map<Integer, FSkin.SkinImage> sleeveMap = FSkin.getSleeves();
|
||||
|
||||
public SleeveSelector(final String playerName, final int currentIndex, final Collection<Integer> usedIndices) {
|
||||
this.setTitle("Select Sleeve for " + playerName);
|
||||
|
||||
final JPanel pnlSleevePics = new JPanel(new WrapLayout());
|
||||
|
||||
pnlSleevePics.setOpaque(false);
|
||||
pnlSleevePics.setOpaque(false);
|
||||
|
||||
final FLabel initialSelection = makeSleeveLabel(sleeveMap.get(currentIndex), currentIndex, currentIndex);
|
||||
pnlSleevePics.add(initialSelection);
|
||||
for (final Integer i : sleeveMap.keySet()) {
|
||||
if (currentIndex != i) {
|
||||
pnlSleevePics.add(makeSleeveLabel(sleeveMap.get(i), i, currentIndex));
|
||||
}
|
||||
}
|
||||
|
||||
final int width = this.getOwner().getWidth() * 3 / 4;
|
||||
final int height = this.getOwner().getHeight() * 3 / 4;
|
||||
this.setPreferredSize(new Dimension(width, height));
|
||||
this.setSize(width, height);
|
||||
|
||||
final FScrollPane scroller = new FScrollPane(pnlSleevePics, false);
|
||||
scroller.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
|
||||
this.add(scroller, "w 100%-24px!, pushy, growy, gap 12px 0 0 0");
|
||||
this.setDefaultFocus(initialSelection);
|
||||
}
|
||||
|
||||
private FLabel makeSleeveLabel(final FSkin.SkinImage img0, final int index0, final int oldIndex) {
|
||||
final FLabel lbl = new FLabel.Builder().icon(img0).iconScaleFactor(0.95).iconAlignX(SwingConstants.CENTER)
|
||||
.iconInBackground(true).hoverable(true).selectable(true).selected(oldIndex == index0)
|
||||
.unhoveredAlpha(oldIndex == index0 ? 0.9f : 0.7f).build();
|
||||
|
||||
final Dimension size = new Dimension(60, 80);
|
||||
lbl.setPreferredSize(size);
|
||||
lbl.setMaximumSize(size);
|
||||
lbl.setMinimumSize(size);
|
||||
lbl.setName("SleeveLabel" + index0);
|
||||
|
||||
if (oldIndex == index0) {
|
||||
lbl.setBorder(new FSkin.LineSkinBorder(FSkin.getColor(FSkin.Colors.CLR_BORDERS).alphaColor(255), 3));
|
||||
}
|
||||
|
||||
selectables.add(lbl);
|
||||
|
||||
return lbl;
|
||||
}
|
||||
|
||||
public List<FLabel> getSelectables() {
|
||||
return this.selectables;
|
||||
}
|
||||
}
|
||||
@@ -859,6 +859,7 @@ public class FSkin {
|
||||
}
|
||||
|
||||
private static Map<Integer, SkinImage> avatars;
|
||||
private static Map<Integer, SkinImage> sleeves;
|
||||
private static Map<Integer, Font> fixedFonts = new HashMap<>();
|
||||
|
||||
public static Font getFixedFont() {
|
||||
@@ -1039,7 +1040,7 @@ public class FSkin {
|
||||
private static String preferredDir;
|
||||
private static String preferredName;
|
||||
private static BufferedImage bimDefaultSprite, bimFavIcon, bimPreferredSprite, bimFoils, bimQuestDraftDeck,
|
||||
bimOldFoils, bimDefaultAvatars, bimPreferredAvatars, bimTrophies, bimAbilities, bimManaIcons;
|
||||
bimOldFoils, bimDefaultAvatars, bimPreferredAvatars, bimTrophies, bimAbilities, bimManaIcons, bimDefaultSleeve, bimDefaultSleeve2;
|
||||
private static int x0, y0, w0, h0, newW, newH, preferredW, preferredH;
|
||||
private static int[] tempCoords;
|
||||
private static int defaultFontSize = 12;
|
||||
@@ -1173,6 +1174,8 @@ public class FSkin {
|
||||
final File f9 = new File(defaultDir + ForgeConstants.SPRITE_FAVICONS_FILE);
|
||||
final File f10 = new File(defaultDir + ForgeConstants.SPRITE_ABILITY_FILE);
|
||||
final File f11 = new File(defaultDir + ForgeConstants.SPRITE_MANAICONS_FILE);
|
||||
final File f12 = new File(defaultDir + ForgeConstants.SPRITE_SLEEVES_FILE);
|
||||
final File f13 = new File(defaultDir + ForgeConstants.SPRITE_SLEEVES2_FILE);
|
||||
|
||||
try {
|
||||
int p = 0;
|
||||
@@ -1190,6 +1193,10 @@ public class FSkin {
|
||||
FView.SINGLETON_INSTANCE.incrementSplashProgessBar(++p);
|
||||
bimDefaultAvatars = ImageIO.read(f4);
|
||||
FView.SINGLETON_INSTANCE.incrementSplashProgessBar(++p);
|
||||
bimDefaultSleeve = ImageIO.read(f12);
|
||||
FView.SINGLETON_INSTANCE.incrementSplashProgessBar(++p);
|
||||
bimDefaultSleeve2 = ImageIO.read(f13);
|
||||
FView.SINGLETON_INSTANCE.incrementSplashProgessBar(++p);
|
||||
bimTrophies = ImageIO.read(f7);
|
||||
FView.SINGLETON_INSTANCE.incrementSplashProgessBar(++p);
|
||||
bimQuestDraftDeck = ImageIO.read(f8);
|
||||
@@ -1255,6 +1262,8 @@ public class FSkin {
|
||||
|
||||
// Assemble avatar images
|
||||
assembleAvatars();
|
||||
// Sleeves
|
||||
assembleSleeves();
|
||||
|
||||
// Images loaded; can start UI init.
|
||||
FView.SINGLETON_INSTANCE.setSplashProgessBarMessage("Creating display components.");
|
||||
@@ -1266,6 +1275,8 @@ public class FSkin {
|
||||
bimOldFoils.flush();
|
||||
bimPreferredSprite.flush();
|
||||
bimDefaultAvatars.flush();
|
||||
bimDefaultSleeve.flush();
|
||||
bimDefaultSleeve2.flush();
|
||||
bimQuestDraftDeck.flush();
|
||||
bimTrophies.flush();
|
||||
bimAbilities.flush();
|
||||
@@ -1278,6 +1289,8 @@ public class FSkin {
|
||||
bimOldFoils = null;
|
||||
bimPreferredSprite = null;
|
||||
bimDefaultAvatars = null;
|
||||
bimDefaultSleeve = null;
|
||||
bimDefaultSleeve2 = null;
|
||||
bimPreferredAvatars = null;
|
||||
bimQuestDraftDeck = null;
|
||||
bimTrophies = null;
|
||||
@@ -1379,6 +1392,10 @@ public class FSkin {
|
||||
return avatars;
|
||||
}
|
||||
|
||||
public static Map<Integer, SkinImage> getSleeves() {
|
||||
return sleeves;
|
||||
}
|
||||
|
||||
public static boolean isLoaded() { return loaded; }
|
||||
|
||||
/**
|
||||
@@ -1482,6 +1499,34 @@ public class FSkin {
|
||||
}
|
||||
}
|
||||
|
||||
private static void assembleSleeves() {
|
||||
sleeves = new HashMap<>();
|
||||
int counter = 0;
|
||||
Color pxTest;
|
||||
|
||||
final int pw = bimDefaultSleeve.getWidth();
|
||||
final int ph = bimDefaultSleeve.getHeight();
|
||||
|
||||
for (int j = 0; j < ph; j += 500) {
|
||||
for (int i = 0; i < pw; i += 360) {
|
||||
pxTest = getColorFromPixel(bimDefaultSleeve.getRGB(i + 180, j + 250));
|
||||
if (pxTest.getAlpha() == 0) { continue; }
|
||||
sleeves.put(counter++, new SkinImage(bimDefaultSleeve.getSubimage(i, j, 360, 500)));
|
||||
}
|
||||
}
|
||||
//2nd set
|
||||
final int aw = bimDefaultSleeve2.getWidth();
|
||||
final int ah = bimDefaultSleeve2.getHeight();
|
||||
|
||||
for (int j = 0; j < ah; j += 500) {
|
||||
for (int i = 0; i < aw; i += 360) {
|
||||
pxTest = getColorFromPixel(bimDefaultSleeve2.getRGB(i + 180, j + 250));
|
||||
if (pxTest.getAlpha() == 0) { continue; }
|
||||
sleeves.put(counter++, new SkinImage(bimDefaultSleeve2.getSubimage(i, j, 360, 500)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void setImage(final FSkinProp s0, final BufferedImage bim) {
|
||||
tempCoords = s0.getCoords();
|
||||
x0 = tempCoords[0];
|
||||
|
||||
@@ -49,6 +49,9 @@ public final class Main {
|
||||
//setup GUI interface
|
||||
GuiBase.setInterface(new GuiDesktop());
|
||||
|
||||
//set PropertyConfig log4j to true
|
||||
GuiBase.enablePropertyConfig(true);
|
||||
|
||||
//install our error handler
|
||||
ExceptionHandler.registerErrorHandling();
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@ import forge.CachedCardImage;
|
||||
import forge.FThreads;
|
||||
import forge.StaticData;
|
||||
import forge.card.CardEdition;
|
||||
import forge.card.CardTranslation;
|
||||
import forge.card.mana.ManaCost;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardView;
|
||||
@@ -39,6 +38,7 @@ import forge.screens.match.CMatchUI;
|
||||
import forge.toolbox.CardFaceSymbols;
|
||||
import forge.toolbox.FSkin.SkinnedPanel;
|
||||
import forge.toolbox.IDisposable;
|
||||
import forge.util.CardTranslation;
|
||||
import forge.view.arcane.util.OutlinedLabel;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<parent>
|
||||
<artifactId>forge</artifactId>
|
||||
<groupId>forge</groupId>
|
||||
<version>1.6.30-SNAPSHOT</version>
|
||||
<version>1.6.30</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>forge-gui-ios</artifactId>
|
||||
|
||||
@@ -29,7 +29,7 @@ public class Main extends IOSApplication.Delegate {
|
||||
final IOSApplicationConfiguration config = new IOSApplicationConfiguration();
|
||||
config.useAccelerometer = false;
|
||||
config.useCompass = false;
|
||||
final ApplicationListener app = Forge.getApp(new IOSClipboard(), new IOSAdapter(), assetsDir);
|
||||
final ApplicationListener app = Forge.getApp(new IOSClipboard(), new IOSAdapter(), assetsDir, false);
|
||||
final IOSApplication iosApp = new IOSApplication(app, config);
|
||||
return iosApp;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>forge</artifactId>
|
||||
<groupId>forge</groupId>
|
||||
<version>1.6.30-SNAPSHOT</version>
|
||||
<version>1.6.30</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>forge-gui-mobile-dev</artifactId>
|
||||
|
||||
@@ -93,7 +93,7 @@ public class Main {
|
||||
config.useHDPI = desktopMode; // enable HiDPI on Mac OS
|
||||
|
||||
new LwjglApplication(Forge.getApp(new LwjglClipboard(), new DesktopAdapter(switchOrientationFile),
|
||||
desktopMode ? desktopModeAssetsDir : assetsDir), config);
|
||||
desktopMode ? desktopModeAssetsDir : assetsDir, true), config);
|
||||
}
|
||||
|
||||
private static class DesktopAdapter implements IDeviceAdapter {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>forge</artifactId>
|
||||
<groupId>forge</groupId>
|
||||
<version>1.6.30-SNAPSHOT</version>
|
||||
<version>1.6.30</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>forge-gui-mobile</artifactId>
|
||||
@@ -48,17 +48,17 @@
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>24.1-android</version>
|
||||
<version>28.1-android</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.thoughtworks.xstream</groupId>
|
||||
<artifactId>xstream</artifactId>
|
||||
<version>1.4.7</version>
|
||||
<version>1.4.9</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>3.7</version>
|
||||
<version>3.8.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.badlogicgames.gdx</groupId>
|
||||
|
||||
@@ -10,7 +10,6 @@ import forge.assets.AssetsDownloader;
|
||||
import forge.assets.FSkin;
|
||||
import forge.assets.FSkinFont;
|
||||
import forge.assets.ImageCache;
|
||||
import forge.card.CardTranslation;
|
||||
import forge.error.BugReporter;
|
||||
import forge.error.ExceptionHandler;
|
||||
import forge.interfaces.IDeviceAdapter;
|
||||
@@ -31,6 +30,7 @@ import forge.toolbox.FGestureAdapter;
|
||||
import forge.toolbox.FOptionPane;
|
||||
import forge.toolbox.FOverlay;
|
||||
import forge.util.Callback;
|
||||
import forge.util.CardTranslation;
|
||||
import forge.util.FileUtil;
|
||||
import forge.util.Localizer;
|
||||
import forge.util.Utils;
|
||||
@@ -67,11 +67,12 @@ public class Forge implements ApplicationListener {
|
||||
public static boolean enablePreloadExtendedArt = false;
|
||||
public static String locale = "en-US";
|
||||
|
||||
public static ApplicationListener getApp(Clipboard clipboard0, IDeviceAdapter deviceAdapter0, String assetDir0) {
|
||||
public static ApplicationListener getApp(Clipboard clipboard0, IDeviceAdapter deviceAdapter0, String assetDir0, boolean value) {
|
||||
if (GuiBase.getInterface() == null) {
|
||||
clipboard = clipboard0;
|
||||
deviceAdapter = deviceAdapter0;
|
||||
GuiBase.setInterface(new GuiMobile(assetDir0));
|
||||
GuiBase.enablePropertyConfig(value);
|
||||
}
|
||||
return app;
|
||||
}
|
||||
@@ -129,13 +130,13 @@ public class Forge implements ApplicationListener {
|
||||
FSkinFont.preloadAll(locale);
|
||||
|
||||
splashScreen.getProgressBar().setDescription(localizer.getMessage("lblLoadingCardTranslations"));
|
||||
CardTranslation.preloadTranslation(locale);
|
||||
CardTranslation.preloadTranslation(locale, ForgeConstants.LANG_DIR);
|
||||
|
||||
splashScreen.getProgressBar().setDescription(localizer.getMessage("lblFinishingStartup"));
|
||||
|
||||
//add reminder to preload
|
||||
if (enablePreloadExtendedArt)
|
||||
splashScreen.getProgressBar().setDescription("Preload Extended Art...");
|
||||
splashScreen.getProgressBar().setDescription(localizer.getMessage("lblPreloadExtendedArt"));
|
||||
Gdx.app.postRunnable(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
@@ -244,6 +244,14 @@ public class GuiMobile implements IGuiBase {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSleevesCount() {
|
||||
if (FSkin.isLoaded()) {
|
||||
return FSkin.getSleeves().size();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String showFileDialog(final String title, final String defaultDir) {
|
||||
return ForgeConstants.USER_GAMES_DIR + "Test.fgs"; //TODO: Show dialog
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package forge.assets;
|
||||
|
||||
|
||||
import com.badlogic.gdx.files.FileHandle;
|
||||
import com.badlogic.gdx.graphics.Pixmap;
|
||||
import com.badlogic.gdx.graphics.PixmapIO;
|
||||
@@ -25,29 +24,30 @@ import com.badlogic.gdx.graphics.g2d.BitmapFont.Glyph;
|
||||
import com.badlogic.gdx.graphics.g2d.PixmapPacker.Page;
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
|
||||
/** A utility to output BitmapFontData to a FNT file. This can be useful for caching the result from TrueTypeFont, for faster load
|
||||
* times.
|
||||
*
|
||||
* The font format is from the AngelCodeFont BMFont tool.
|
||||
*
|
||||
* @author mattdesl AKA davedes */
|
||||
|
||||
|
||||
/**
|
||||
* This file is 'borrowed' from gdx-tools in the libgdx source
|
||||
*/
|
||||
|
||||
public class BitmapFontWriter {
|
||||
/** A utility to output BitmapFontData to a FNT file. This can be useful for caching the result from TrueTypeFont, for faster load
|
||||
* times.
|
||||
* <p>
|
||||
* The font file format is from the AngelCodeFont BMFont tool.
|
||||
* <p>
|
||||
* Output is nearly identical to the FreeType settting in the Hiero tool {@Link com.badlogic.gdx.tools.hiero.Hiero}. BitmapFontWriter gives more flexibility, eg
|
||||
* borders and shadows can be used. Hiero is able to avoid outputting the same glyph image more than once if multiple character
|
||||
* codes have the exact same glyph.
|
||||
* @author mattdesl AKA davedes */
|
||||
public class BitmapFontWriter {
|
||||
|
||||
/** The output format. */
|
||||
public enum OutputFormat {
|
||||
|
||||
public static enum OutputFormat {
|
||||
|
||||
/** AngelCodeFont text format */
|
||||
Text,
|
||||
/** AngelCodeFont XML format */
|
||||
XML
|
||||
XML;
|
||||
}
|
||||
|
||||
|
||||
/** The output format */
|
||||
private static OutputFormat format = OutputFormat.Text;
|
||||
|
||||
@@ -55,26 +55,25 @@ import com.badlogic.gdx.utils.Array;
|
||||
* Pixi.js).
|
||||
*
|
||||
* @param fmt the output format to use */
|
||||
public static void setOutputFormat(OutputFormat fmt) {
|
||||
if (fmt==null)
|
||||
throw new NullPointerException("format cannot be null");
|
||||
public static void setOutputFormat (OutputFormat fmt) {
|
||||
if (fmt == null) throw new NullPointerException("format cannot be null");
|
||||
format = fmt;
|
||||
}
|
||||
|
||||
/** Returns the currently used output format.
|
||||
* @return the output format */
|
||||
public static OutputFormat getOutputFormat() {
|
||||
public static OutputFormat getOutputFormat () {
|
||||
return format;
|
||||
}
|
||||
|
||||
|
||||
/** The Padding parameter for FontInfo. */
|
||||
public static class Padding {
|
||||
public int up, down, left, right;
|
||||
|
||||
public Padding() {
|
||||
public Padding () {
|
||||
}
|
||||
|
||||
public Padding(int up, int down, int left, int right) {
|
||||
|
||||
public Padding (int up, int down, int left, int right) {
|
||||
this.up = up;
|
||||
this.down = down;
|
||||
this.left = left;
|
||||
@@ -87,8 +86,8 @@ import com.badlogic.gdx.utils.Array;
|
||||
public int horizontal, vertical;
|
||||
}
|
||||
|
||||
/** The font "info" line; this will be ignored by LibGDX's BitmapFont reader,
|
||||
* but useful for clean and organized output. */
|
||||
/** The font "info" line; everything except padding and override metrics are ignored by LibGDX's BitmapFont reader, it is otherwise just useful for
|
||||
* clean and organized output. */
|
||||
public static class FontInfo {
|
||||
/** Face name */
|
||||
public String face;
|
||||
@@ -113,32 +112,55 @@ import com.badlogic.gdx.utils.Array;
|
||||
/** Horizontal/vertical spacing that was applied to font */
|
||||
public Spacing spacing = new Spacing();
|
||||
public int outline = 0;
|
||||
|
||||
public FontInfo() {
|
||||
|
||||
/** Override metrics */
|
||||
public boolean hasOverrideMetrics;
|
||||
public float ascent;
|
||||
public float descent;
|
||||
public float down;
|
||||
public float capHeight;
|
||||
public float lineHeight;
|
||||
public float spaceXAdvance;
|
||||
public float xHeight;
|
||||
|
||||
public FontInfo () {
|
||||
}
|
||||
|
||||
public FontInfo(String face, int size) {
|
||||
|
||||
public FontInfo (String face, int size) {
|
||||
this.face = face;
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
public void overrideMetrics (BitmapFontData data) {
|
||||
hasOverrideMetrics = true;
|
||||
ascent = data.ascent;
|
||||
descent = data.descent;
|
||||
down = data.down;
|
||||
capHeight = data.capHeight;
|
||||
lineHeight = data.lineHeight;
|
||||
spaceXAdvance = data.spaceXadvance;
|
||||
xHeight = data.xHeight;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static String quote(Object params) {
|
||||
|
||||
private static String quote (Object params) {
|
||||
return quote(params, false);
|
||||
}
|
||||
|
||||
private static String quote(Object params, boolean spaceAfter) {
|
||||
|
||||
private static String quote (Object params, boolean spaceAfter) {
|
||||
if (BitmapFontWriter.getOutputFormat() == OutputFormat.XML)
|
||||
return "\"" + params.toString().trim() + "\"" + (spaceAfter ? " " : "");
|
||||
else
|
||||
return params.toString();
|
||||
}
|
||||
|
||||
/** Writes the given BitmapFontData to a file, using the specified <tt>pageRefs</tt> strings as the image paths for each texture
|
||||
* page. The glyphs in BitmapFontData have a "page" id, which references the index of the pageRef you specify here.
|
||||
/** Writes the given BitmapFontData to a file, using the specified <tt>pageRefs</tt> strings as the image paths for each
|
||||
* texture page. The glyphs in BitmapFontData have a "page" id, which references the index of the pageRef you specify here.
|
||||
*
|
||||
* The FontInfo parameter is useful for cleaner output; such as including a size and font face name hint. However, it can be
|
||||
* null to use default values. Ultimately, LibGDX ignores the "info" line when reading back fonts.
|
||||
* null to use default values. LibGDX ignores most of the "info" line when reading back fonts, only padding is used. Padding
|
||||
* also affects the size, location, and offset of the glyphs that are output.
|
||||
*
|
||||
* Likewise, the scaleW and scaleH are only for cleaner output. They are currently ignored by LibGDX's reader. For maximum
|
||||
* compatibility with other BMFont tools, you should use the width and height of your texture pages (each page should be the
|
||||
@@ -150,21 +172,22 @@ import com.badlogic.gdx.utils.Array;
|
||||
* @param info the optional info for the file header; can be null
|
||||
* @param scaleW the width of your texture pages
|
||||
* @param scaleH the height of your texture pages */
|
||||
public static void writeFont (BitmapFontData fontData, String[] pageRefs, FileHandle outFntFile, FontInfo info, int scaleW, int scaleH) {
|
||||
if (info==null) {
|
||||
public static void writeFont (BitmapFontData fontData, String[] pageRefs, FileHandle outFntFile, FontInfo info, int scaleW,
|
||||
int scaleH) {
|
||||
if (info == null) {
|
||||
info = new FontInfo();
|
||||
info.face = outFntFile.nameWithoutExtension();
|
||||
}
|
||||
|
||||
|
||||
int lineHeight = (int)fontData.lineHeight;
|
||||
int pages = pageRefs.length;
|
||||
int packed = 0;
|
||||
int base = (int)((fontData.capHeight) + (fontData.flipped ? -fontData.ascent : fontData.ascent));
|
||||
OutputFormat fmt = BitmapFontWriter.getOutputFormat();
|
||||
boolean xml = fmt == OutputFormat.XML;
|
||||
|
||||
boolean xml = fmt == OutputFormat.XML;
|
||||
|
||||
StringBuilder buf = new StringBuilder();
|
||||
|
||||
|
||||
if (xml) {
|
||||
buf.append("<font>\n");
|
||||
}
|
||||
@@ -172,152 +195,129 @@ import com.badlogic.gdx.utils.Array;
|
||||
String xmlCloseSelf = xml ? "/>" : "";
|
||||
String xmlTab = xml ? "\t" : "";
|
||||
String xmlClose = xml ? ">" : "";
|
||||
|
||||
|
||||
String xmlQuote = xml ? "\"" : "";
|
||||
String alphaChnlParams =
|
||||
xml ? " alphaChnl=\"0\" redChnl=\"0\" greenChnl=\"0\" blueChnl=\"0\""
|
||||
: " alphaChnl=0 redChnl=0 greenChnl=0 blueChnl=0";
|
||||
//INFO LINE
|
||||
|
||||
buf.append(xmlOpen)
|
||||
.append("info face=\"")
|
||||
.append(info.face==null ? "" : info.face.replaceAll("\"", "'"))
|
||||
.append("\" size=").append( quote(info.size) )
|
||||
.append(" bold=").append( quote(info.bold ? 1 : 0) )
|
||||
.append(" italic=").append( quote(info.italic ? 1 : 0) )
|
||||
.append(" charset=\"").append(info.charset==null ? "" : info.charset)
|
||||
.append("\" unicode=").append( quote(info.unicode ? 1 : 0) )
|
||||
.append(" stretchH=").append( quote(info.stretchH) )
|
||||
.append(" smooth=").append( quote(info.smooth ? 1 : 0) )
|
||||
.append(" aa=").append( quote(info.aa) )
|
||||
.append(" padding=")
|
||||
.append(xmlQuote)
|
||||
.append(info.padding.up).append(",")
|
||||
.append(info.padding.down).append(",")
|
||||
.append(info.padding.left).append(",")
|
||||
.append(info.padding.right)
|
||||
.append(xmlQuote)
|
||||
.append(" spacing=")
|
||||
.append(xmlQuote)
|
||||
.append(info.spacing.horizontal).append(",")
|
||||
.append(info.spacing.vertical)
|
||||
.append(xmlQuote)
|
||||
.append(xmlCloseSelf)
|
||||
String alphaChnlParams = xml ? " alphaChnl=\"0\" redChnl=\"0\" greenChnl=\"0\" blueChnl=\"0\""
|
||||
: " alphaChnl=0 redChnl=0 greenChnl=0 blueChnl=0";
|
||||
|
||||
// INFO LINE
|
||||
buf.append(xmlOpen).append("info face=\"").append(info.face == null ? "" : info.face.replaceAll("\"", "'"))
|
||||
.append("\" size=").append(quote(info.size)).append(" bold=").append(quote(info.bold ? 1 : 0)).append(" italic=")
|
||||
.append(quote(info.italic ? 1 : 0)).append(" charset=\"").append(info.charset == null ? "" : info.charset)
|
||||
.append("\" unicode=").append(quote(info.unicode ? 1 : 0)).append(" stretchH=").append(quote(info.stretchH))
|
||||
.append(" smooth=").append(quote(info.smooth ? 1 : 0)).append(" aa=").append(quote(info.aa)).append(" padding=")
|
||||
.append(xmlQuote).append(info.padding.up).append(",").append(info.padding.right).append(",").append(info.padding.down)
|
||||
.append(",").append(info.padding.left).append(xmlQuote).append(" spacing=").append(xmlQuote)
|
||||
.append(info.spacing.horizontal).append(",").append(info.spacing.vertical).append(xmlQuote).append(xmlCloseSelf)
|
||||
.append("\n");
|
||||
|
||||
//COMMON line
|
||||
buf.append(xmlOpen)
|
||||
.append("common lineHeight=").append( quote(lineHeight) )
|
||||
.append(" base=").append( quote(base) )
|
||||
.append(" scaleW=").append( quote(scaleW) )
|
||||
.append(" scaleH=").append( quote(scaleH) )
|
||||
.append(" pages=").append( quote(pages) )
|
||||
.append(" packed=").append( quote(packed) )
|
||||
.append(alphaChnlParams)
|
||||
.append(xmlCloseSelf)
|
||||
.append("\n");
|
||||
|
||||
if (xml)
|
||||
buf.append("\t<pages>\n");
|
||||
|
||||
//PAGES
|
||||
for (int i=0; i<pageRefs.length; i++) {
|
||||
buf.append(xmlTab)
|
||||
.append(xmlOpen)
|
||||
.append("page id=")
|
||||
.append( quote(i) )
|
||||
.append(" file=\"")
|
||||
.append(pageRefs[i])
|
||||
.append("\"")
|
||||
.append(xmlCloseSelf)
|
||||
.append("\n");
|
||||
|
||||
// COMMON line
|
||||
buf.append(xmlOpen).append("common lineHeight=").append(quote(lineHeight)).append(" base=").append(quote(base))
|
||||
.append(" scaleW=").append(quote(scaleW)).append(" scaleH=").append(quote(scaleH)).append(" pages=").append(quote(pages))
|
||||
.append(" packed=").append(quote(packed)).append(alphaChnlParams).append(xmlCloseSelf).append("\n");
|
||||
|
||||
if (xml) buf.append("\t<pages>\n");
|
||||
|
||||
// PAGES
|
||||
for (int i = 0; i < pageRefs.length; i++) {
|
||||
buf.append(xmlTab).append(xmlOpen).append("page id=").append(quote(i)).append(" file=\"").append(pageRefs[i])
|
||||
.append("\"").append(xmlCloseSelf).append("\n");
|
||||
}
|
||||
|
||||
if (xml)
|
||||
buf.append("\t</pages>\n");
|
||||
|
||||
//CHARS
|
||||
Array<Glyph> glyphs = new Array<>(256);
|
||||
for (int i=0; i<fontData.glyphs.length; i++) {
|
||||
if (fontData.glyphs[i]==null)
|
||||
continue;
|
||||
|
||||
for (int j=0; j<fontData.glyphs[i].length; j++) {
|
||||
if (fontData.glyphs[i][j]!=null) {
|
||||
|
||||
if (xml) buf.append("\t</pages>\n");
|
||||
|
||||
// CHARS
|
||||
Array<Glyph> glyphs = new Array<Glyph>(256);
|
||||
for (int i = 0; i < fontData.glyphs.length; i++) {
|
||||
if (fontData.glyphs[i] == null) continue;
|
||||
|
||||
for (int j = 0; j < fontData.glyphs[i].length; j++) {
|
||||
if (fontData.glyphs[i][j] != null) {
|
||||
glyphs.add(fontData.glyphs[i][j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buf.append(xmlOpen)
|
||||
.append("chars count=").append(quote(glyphs.size))
|
||||
.append(xmlClose)
|
||||
.append("\n");
|
||||
|
||||
//CHAR definitions
|
||||
for (int i=0; i<glyphs.size; i++) {
|
||||
|
||||
buf.append(xmlOpen).append("chars count=").append(quote(glyphs.size)).append(xmlClose).append("\n");
|
||||
|
||||
int padLeft = 0, padRight = 0, padTop = 0, padX = 0, padY = 0;
|
||||
if (info != null) {
|
||||
padTop = info.padding.up;
|
||||
padLeft = info.padding.left;
|
||||
padRight = info.padding.right;
|
||||
padX = padLeft + padRight;
|
||||
padY = info.padding.up + info.padding.down;
|
||||
}
|
||||
|
||||
// CHAR definitions
|
||||
for (int i = 0; i < glyphs.size; i++) {
|
||||
Glyph g = glyphs.get(i);
|
||||
buf.append(xmlTab)
|
||||
.append(xmlOpen)
|
||||
.append("char id=")
|
||||
.append(quote( String.format("%-5s", g.id), true ))
|
||||
.append("x=").append(quote( String.format("%-5s", g.srcX), true ) )
|
||||
.append("y=").append(quote( String.format("%-5s", g.srcY), true ) )
|
||||
.append("width=").append(quote( String.format("%-5s", g.width), true ) )
|
||||
.append("height=").append(quote( String.format("%-5s", g.height), true ) )
|
||||
.append("xoffset=").append(quote( String.format("%-5s", g.xoffset), true ) )
|
||||
.append("yoffset=").append(quote( String.format("%-5s", fontData.flipped ? g.yoffset : -(g.height + g.yoffset) ), true ) )
|
||||
.append("xadvance=").append(quote( String.format("%-5s", g.xadvance), true ) )
|
||||
.append("page=").append(quote( String.format("%-5s", g.page), true ) )
|
||||
.append("chnl=").append(quote(0, true))
|
||||
.append(xmlCloseSelf)
|
||||
boolean empty = g.width == 0 || g.height == 0;
|
||||
buf.append(xmlTab).append(xmlOpen).append("char id=").append(quote(String.format("%-6s", g.id), true)).append("x=")
|
||||
.append(quote(String.format("%-5s", empty ? 0 : g.srcX), true)).append("y=")
|
||||
.append(quote(String.format("%-5s", empty ? 0 : g.srcY), true)).append("width=")
|
||||
.append(quote(String.format("%-5s", empty ? 0 : g.width), true)).append("height=")
|
||||
.append(quote(String.format("%-5s", empty ? 0 : g.height), true)).append("xoffset=")
|
||||
.append(quote(String.format("%-5s", g.xoffset - padLeft), true)).append("yoffset=")
|
||||
.append(quote(String.format("%-5s", fontData.flipped ? g.yoffset + padTop : -(g.height + (g.yoffset + padTop))), true))
|
||||
.append("xadvance=").append(quote(String.format("%-5s", g.xadvance), true)).append("page=")
|
||||
.append(quote(String.format("%-5s", g.page), true)).append("chnl=").append(quote(0, true)).append(xmlCloseSelf)
|
||||
.append("\n");
|
||||
}
|
||||
|
||||
if (xml)
|
||||
buf.append("\t</chars>\n");
|
||||
|
||||
//KERNINGS
|
||||
|
||||
if (xml) buf.append("\t</chars>\n");
|
||||
|
||||
// KERNINGS
|
||||
int kernCount = 0;
|
||||
StringBuilder kernBuf = new StringBuilder();
|
||||
StringBuilder kernBuf = new StringBuilder();
|
||||
for (int i = 0; i < glyphs.size; i++) {
|
||||
for (int j = 0; j < glyphs.size; j++) {
|
||||
Glyph first = glyphs.get(i);
|
||||
Glyph second = glyphs.get(j);
|
||||
int kern = first.getKerning((char)second.id);
|
||||
if (kern!=0) {
|
||||
if (kern != 0) {
|
||||
kernCount++;
|
||||
kernBuf.append(xmlTab)
|
||||
.append(xmlOpen)
|
||||
.append("kerning first=").append(quote(first.id))
|
||||
.append(" second=").append(quote(second.id))
|
||||
.append(" amount=").append(quote(kern, true))
|
||||
.append(xmlCloseSelf)
|
||||
.append("\n");
|
||||
kernBuf.append(xmlTab).append(xmlOpen).append("kerning first=").append(quote(first.id)).append(" second=")
|
||||
.append(quote(second.id)).append(" amount=").append(quote(kern, true)).append(xmlCloseSelf).append("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//KERN info
|
||||
buf.append(xmlOpen)
|
||||
.append("kernings count=").append(quote(kernCount))
|
||||
.append(xmlClose)
|
||||
.append("\n");
|
||||
// KERN info
|
||||
buf.append(xmlOpen).append("kernings count=").append(quote(kernCount)).append(xmlClose).append("\n");
|
||||
buf.append(kernBuf);
|
||||
|
||||
|
||||
if (xml) {
|
||||
buf.append("\t</kernings>\n");
|
||||
}
|
||||
|
||||
// Override metrics
|
||||
if (info.hasOverrideMetrics) {
|
||||
if (xml) buf.append("\t<metrics>\n");
|
||||
|
||||
buf.append(xmlTab).append(xmlOpen)
|
||||
.append("metrics ascent=").append(quote(info.ascent, true))
|
||||
.append(" descent=").append(quote(info.descent, true))
|
||||
.append(" down=").append(quote(info.down, true))
|
||||
.append(" capHeight=").append(quote(info.capHeight, true))
|
||||
.append(" lineHeight=").append(quote(info.lineHeight, true))
|
||||
.append(" spaceXAdvance=").append(quote(info.spaceXAdvance, true))
|
||||
.append(" xHeight=").append(quote(info.xHeight, true))
|
||||
.append(xmlCloseSelf).append("\n");
|
||||
|
||||
if (xml) buf.append("\t</metrics>\n");
|
||||
}
|
||||
|
||||
if (xml) {
|
||||
buf.append("</font>");
|
||||
}
|
||||
|
||||
|
||||
String charset = info.charset;
|
||||
if (charset!=null&&charset.length()==0)
|
||||
charset = null;
|
||||
|
||||
if (charset != null && charset.length() == 0) charset = null;
|
||||
|
||||
outFntFile.writeString(buf.toString(), false, charset);
|
||||
}
|
||||
|
||||
|
||||
/** A utility method which writes the given font data to a file.
|
||||
*
|
||||
* The specified pixmaps are written to the parent directory of <tt>outFntFile</tt>, using that file's name without an
|
||||
@@ -337,8 +337,8 @@ import com.badlogic.gdx.utils.Array;
|
||||
* @param info the optional font info for the header file, can be null */
|
||||
public static void writeFont (BitmapFontData fontData, Pixmap[] pages, FileHandle outFntFile, FontInfo info) {
|
||||
String[] pageRefs = writePixmaps(pages, outFntFile.parent(), outFntFile.nameWithoutExtension());
|
||||
|
||||
//write the font data
|
||||
|
||||
// write the font data
|
||||
writeFont(fontData, pageRefs, outFntFile, info, pages[0].getWidth(), pages[0].getHeight());
|
||||
}
|
||||
|
||||
@@ -357,18 +357,17 @@ import com.badlogic.gdx.utils.Array;
|
||||
* @param fileName the file names for the output images
|
||||
* @return the array of string references to be used with <tt>writeFont</tt> */
|
||||
public static String[] writePixmaps (Pixmap[] pages, FileHandle outputDir, String fileName) {
|
||||
if (pages==null || pages.length==0)
|
||||
throw new IllegalArgumentException("no pixmaps supplied to BitmapFontWriter.write");
|
||||
|
||||
if (pages == null || pages.length == 0) throw new IllegalArgumentException("no pixmaps supplied to BitmapFontWriter.write");
|
||||
|
||||
String[] pageRefs = new String[pages.length];
|
||||
|
||||
for (int i=0; i<pages.length; i++) {
|
||||
String ref = pages.length==1 ? (fileName+".png") : (fileName+"_"+i+".png");
|
||||
|
||||
//the ref for this image
|
||||
|
||||
for (int i = 0; i < pages.length; i++) {
|
||||
String ref = pages.length == 1 ? (fileName + ".png") : (fileName + "_" + i + ".png");
|
||||
|
||||
// the ref for this image
|
||||
pageRefs[i] = ref;
|
||||
|
||||
//write the PNG in that directory
|
||||
|
||||
// write the PNG in that directory
|
||||
PixmapIO.writePNG(outputDir.child(ref), pages[i]);
|
||||
}
|
||||
return pageRefs;
|
||||
@@ -383,9 +382,9 @@ import com.badlogic.gdx.utils.Array;
|
||||
* @return the file refs */
|
||||
public static String[] writePixmaps (Array<Page> pages, FileHandle outputDir, String fileName) {
|
||||
Pixmap[] pix = new Pixmap[pages.size];
|
||||
for (int i=0; i<pages.size; i++) {
|
||||
for (int i = 0; i < pages.size; i++) {
|
||||
pix[i] = pages.get(i).getPixmap();
|
||||
}
|
||||
return writePixmaps(pix, outputDir, fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,10 @@
|
||||
package forge.assets;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import forge.Forge;
|
||||
import org.apache.commons.lang3.text.WordUtils;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
@@ -30,8 +29,10 @@ import forge.toolbox.FProgressBar;
|
||||
public class FSkin {
|
||||
private static final Map<FSkinProp, FSkinImage> images = new HashMap<>();
|
||||
private static final Map<Integer, TextureRegion> avatars = new HashMap<>();
|
||||
private static final Map<Integer, TextureRegion> sleeves = new HashMap<>();
|
||||
private static final Map<Integer, TextureRegion> borders = new HashMap<>();
|
||||
|
||||
private static List<String> allSkins;
|
||||
private static Array<String> allSkins;
|
||||
private static FileHandle preferredDir;
|
||||
private static String preferredName;
|
||||
private static boolean loaded = false;
|
||||
@@ -98,12 +99,12 @@ public class FSkin {
|
||||
else {
|
||||
if (splashScreen != null) {
|
||||
if (allSkins == null) { //initialize
|
||||
allSkins = new ArrayList<>();
|
||||
final List<String> skinDirectoryNames = getSkinDirectoryNames();
|
||||
allSkins = new Array<>();
|
||||
final Array<String> skinDirectoryNames = getSkinDirectoryNames();
|
||||
for (final String skinDirectoryName : skinDirectoryNames) {
|
||||
allSkins.add(WordUtils.capitalize(skinDirectoryName.replace('_', ' ')));
|
||||
}
|
||||
Collections.sort(allSkins);
|
||||
allSkins.sort();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,6 +173,9 @@ public class FSkin {
|
||||
}
|
||||
|
||||
avatars.clear();
|
||||
sleeves.clear();
|
||||
|
||||
boolean textureFilter = Forge.isTextureFilteringEnabled();
|
||||
|
||||
final Map<String, Texture> textures = new HashMap<>();
|
||||
|
||||
@@ -183,6 +187,9 @@ public class FSkin {
|
||||
final FileHandle f5 = getSkinFile(ForgeConstants.SPRITE_AVATARS_FILE);
|
||||
final FileHandle f6 = getDefaultSkinFile(SourceFile.OLD_FOILS.getFilename());
|
||||
final FileHandle f7 = getDefaultSkinFile(ForgeConstants.SPRITE_MANAICONS_FILE);
|
||||
final FileHandle f8 = getDefaultSkinFile(ForgeConstants.SPRITE_SLEEVES_FILE);
|
||||
final FileHandle f9 = getDefaultSkinFile(ForgeConstants.SPRITE_SLEEVES2_FILE);
|
||||
final FileHandle f10 = getDefaultSkinFile(ForgeConstants.SPRITE_BORDER_FILE);
|
||||
|
||||
try {
|
||||
textures.put(f1.path(), new Texture(f1));
|
||||
@@ -218,16 +225,25 @@ public class FSkin {
|
||||
|
||||
//assemble avatar textures
|
||||
int counter = 0;
|
||||
int scount = 0;
|
||||
Color pxTest;
|
||||
Pixmap pxDefaultAvatars, pxPreferredAvatars;
|
||||
Texture txDefaultAvatars, txPreferredAvatars;
|
||||
Pixmap pxDefaultAvatars, pxPreferredAvatars, pxDefaultSleeves;
|
||||
Texture txDefaultAvatars, txPreferredAvatars, txDefaultSleeves;
|
||||
|
||||
pxDefaultAvatars = new Pixmap(f4);
|
||||
txDefaultAvatars = new Texture(f4);
|
||||
pxDefaultSleeves = new Pixmap(f8);
|
||||
txDefaultAvatars = new Texture(f4, textureFilter);
|
||||
if (textureFilter)
|
||||
txDefaultAvatars.setFilter(Texture.TextureFilter.MipMapLinearLinear, Texture.TextureFilter.Linear);
|
||||
txDefaultSleeves = new Texture(f8, textureFilter);
|
||||
if (textureFilter)
|
||||
txDefaultSleeves.setFilter(Texture.TextureFilter.MipMapLinearLinear, Texture.TextureFilter.Linear);
|
||||
|
||||
if (f5.exists()) {
|
||||
pxPreferredAvatars = new Pixmap(f5);
|
||||
txPreferredAvatars = new Texture(f5);
|
||||
txPreferredAvatars = new Texture(f5, textureFilter);
|
||||
if (textureFilter)
|
||||
txPreferredAvatars.setFilter(Texture.TextureFilter.MipMapLinearLinear, Texture.TextureFilter.Linear);
|
||||
|
||||
final int pw = pxPreferredAvatars.getWidth();
|
||||
final int ph = pxPreferredAvatars.getHeight();
|
||||
@@ -255,8 +271,42 @@ public class FSkin {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
final int sw = pxDefaultSleeves.getWidth();
|
||||
final int sh = pxDefaultSleeves.getHeight();
|
||||
|
||||
for (int j = 0; j < sh; j += 500) {
|
||||
for (int i = 0; i < sw; i += 360) {
|
||||
pxTest = new Color(pxDefaultSleeves.getPixel(i + 180, j + 250));
|
||||
if (pxTest.a == 0) { continue; }
|
||||
FSkin.sleeves.put(scount++, new TextureRegion(txDefaultSleeves, i, j, 360, 500));
|
||||
}
|
||||
}
|
||||
|
||||
//re init second set of sleeves
|
||||
pxDefaultSleeves = new Pixmap(f9);
|
||||
txDefaultSleeves = new Texture(f9, textureFilter);
|
||||
if (textureFilter)
|
||||
txDefaultSleeves.setFilter(Texture.TextureFilter.MipMapLinearLinear, Texture.TextureFilter.Linear);
|
||||
|
||||
final int sw2 = pxDefaultSleeves.getWidth();
|
||||
final int sh2 = pxDefaultSleeves.getHeight();
|
||||
|
||||
for (int j = 0; j < sh2; j += 500) {
|
||||
for (int i = 0; i < sw2; i += 360) {
|
||||
pxTest = new Color(pxDefaultSleeves.getPixel(i + 180, j + 250));
|
||||
if (pxTest.a == 0) { continue; }
|
||||
FSkin.sleeves.put(scount++, new TextureRegion(txDefaultSleeves, i, j, 360, 500));
|
||||
}
|
||||
}
|
||||
|
||||
Texture bordersBW = new Texture(f10);
|
||||
FSkin.borders.put(0, new TextureRegion(bordersBW, 2, 2, 672, 936));
|
||||
FSkin.borders.put(1, new TextureRegion(bordersBW, 676, 2, 672, 936));
|
||||
|
||||
preferredIcons.dispose();
|
||||
pxDefaultAvatars.dispose();
|
||||
pxDefaultSleeves.dispose();;
|
||||
}
|
||||
catch (final Exception e) {
|
||||
System.err.println("FSkin$loadFull: Missing a sprite (default icons, "
|
||||
@@ -314,8 +364,8 @@ public class FSkin {
|
||||
*
|
||||
* @return the skins
|
||||
*/
|
||||
public static List<String> getSkinDirectoryNames() {
|
||||
final List<String> mySkins = new ArrayList<>();
|
||||
public static Array<String> getSkinDirectoryNames() {
|
||||
final Array<String> mySkins = new Array<>();
|
||||
|
||||
final FileHandle dir = Gdx.files.absolute(ForgeConstants.SKINS_DIR);
|
||||
for (FileHandle skinFile : dir.list()) {
|
||||
@@ -340,5 +390,13 @@ public class FSkin {
|
||||
return avatars;
|
||||
}
|
||||
|
||||
public static Map<Integer, TextureRegion> getSleeves() {
|
||||
return sleeves;
|
||||
}
|
||||
|
||||
public static Map<Integer, TextureRegion> getBorders() {
|
||||
return borders;
|
||||
}
|
||||
|
||||
public static boolean isLoaded() { return loaded; }
|
||||
}
|
||||
|
||||
@@ -463,7 +463,7 @@ public class FSkinFont {
|
||||
+ "驽驾驿骁骂骄骆骇验骏骐骑骗骚骤骨骰骷骸骼髅髓高鬃鬓鬣鬼魁魂魄"
|
||||
+ "魅魇魈魏魔鰴鱼鲁鲜鲤鲨鲮鲸鲽鳃鳄鳍鳐鳗鳝鳞鸟鸠鸡鸢鸣鸦鸽鹅鹉"
|
||||
+ "鹊鹏鹗鹞鹤鹦鹫鹭鹰鹿麋麒麟麦麻黄黎黏黑默黛黜點黠黯鼎鼓鼠鼬鼹"
|
||||
+ "鼻齐齑齿龇龙龟!(),/:;?~";
|
||||
+ "鼻齐齑齿龇龙龟伸!(),/:;?~";
|
||||
|
||||
final PixmapPacker packer = new PixmapPacker(pageSize, pageSize, Pixmap.Format.RGBA8888, 2, false);
|
||||
final FreeTypeFontParameter parameter = new FreeTypeFontParameter();
|
||||
|
||||
@@ -214,6 +214,16 @@ public class ImageCache {
|
||||
return Color.valueOf("#fffffd");
|
||||
return Color.valueOf("#171717");
|
||||
}
|
||||
public static int getFSkinBorders(CardView c) {
|
||||
if (c == null)
|
||||
return 0;
|
||||
|
||||
CardView.CardStateView state = c.getCurrentState();
|
||||
CardEdition ed = FModel.getMagicDb().getEditions().get(state.getSetCode());
|
||||
if (ed != null && ed.isWhiteBorder() && state.getFoilIndex() == 0)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
public static boolean isExtendedArt(CardView c) {
|
||||
if (c == null)
|
||||
return false;
|
||||
|
||||
@@ -21,9 +21,10 @@ final class ImageLoader extends CacheLoader<String, Texture> {
|
||||
boolean extendedArt = false;
|
||||
boolean textureFilter = Forge.isTextureFilteringEnabled();
|
||||
if (key.length() > 4){
|
||||
if ((key.substring(0,4).contains("MPS_"))) //TODO add sets to get all extended art???
|
||||
if ((key.substring(0,4).contains("MPS_"))) //MPS_ sets
|
||||
extendedArt = true;
|
||||
else if ((key.substring(0,3).contains("UST"))) //Unstable Set
|
||||
extendedArt = true;
|
||||
//use generated extended art... it will preload the cache at startup so... yeah! :)
|
||||
}
|
||||
File file = ImageKeys.getImageFile(key);
|
||||
if (file != null) {
|
||||
|
||||
@@ -6,7 +6,10 @@ import com.badlogic.gdx.utils.Align;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import forge.Forge;
|
||||
import forge.Graphics;
|
||||
import forge.ImageKeys;
|
||||
import forge.assets.FBufferedImage;
|
||||
import forge.assets.FImage;
|
||||
import forge.assets.FSkin;
|
||||
import forge.assets.FSkinColor;
|
||||
import forge.assets.FSkinFont;
|
||||
import forge.assets.FSkinImage;
|
||||
@@ -25,6 +28,7 @@ import forge.properties.ForgeConstants;
|
||||
import forge.properties.ForgePreferences;
|
||||
import forge.screens.FScreen;
|
||||
import forge.screens.match.MatchController;
|
||||
import forge.util.CardTranslation;
|
||||
import forge.util.Utils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@@ -326,7 +330,9 @@ public class CardImageRenderer {
|
||||
}
|
||||
|
||||
public static void drawZoom(Graphics g, CardView card, GameView gameView, boolean altState, float x, float y, float w, float h, float dispW, float dispH, boolean isCurrentCard) {
|
||||
boolean canshow = MatchController.instance.mayView(card);
|
||||
final Texture image = ImageCache.getImage(card.getState(altState).getImageKey(MatchController.instance.getLocalPlayers()), true);
|
||||
FImage sleeves = MatchController.getPlayerSleeve(card.getOwner());
|
||||
if (image == null) { //draw details if can't draw zoom
|
||||
drawDetails(g, card, gameView, altState, x, y, w, h);
|
||||
return;
|
||||
@@ -337,8 +343,6 @@ public class CardImageRenderer {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean canLook = MatchController.instance.mayView(card);
|
||||
|
||||
if (image == ImageCache.defaultImage) { //support drawing card image manually if card image not found
|
||||
drawCardImage(g, card, altState, x, y, w, h, CardStackPosition.Top);
|
||||
}
|
||||
@@ -358,47 +362,43 @@ public class CardImageRenderer {
|
||||
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 {
|
||||
if (rotatePlane)
|
||||
g.drawfillBorder(3, ImageCache.borderColor(card), new_xRotate, new_yRotate, new_h, new_w, radius);
|
||||
else
|
||||
g.drawfillBorder(3, ImageCache.borderColor(card), x, y, w, h, radius);
|
||||
|
||||
g.drawRotatedImage(ImageCache.croppedBorderImage(image), new_x+radius/2.3f, new_y+radius/2, new_w*0.96f, new_h*0.96f, (new_x+radius/2.3f) + (new_w*0.96f) / 2, (new_y+radius/2) + (new_h*0.96f) / 2, -90);
|
||||
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), 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);
|
||||
}
|
||||
}
|
||||
else
|
||||
g.drawRotatedImage(image, new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, -90);
|
||||
} else if (rotateSplit && isCurrentCard && card.isSplitCard() && canLook) {
|
||||
} else if (rotateSplit && isCurrentCard && card.isSplitCard() && canshow) {
|
||||
boolean isAftermath = card.getText().contains("Aftermath") || card.getAlternateState().getOracleText().contains("Aftermath");
|
||||
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, isAftermath ? 90 : -90);
|
||||
else {
|
||||
if (rotateSplit)
|
||||
g.drawfillBorder(3, ImageCache.borderColor(card), new_xRotate, new_yRotate, new_h, new_w, radius);
|
||||
else
|
||||
g.drawfillBorder(3, ImageCache.borderColor(card), x, y, w, h, radius);
|
||||
|
||||
g.drawRotatedImage(ImageCache.croppedBorderImage(image), new_x + radius / 2.3f, new_y + radius / 2, new_w * 0.96f, new_h * 0.96f, (new_x + radius / 2.3f) + (new_w * 0.96f) / 2, (new_y + radius / 2) + (new_h * 0.96f) / 2, isAftermath ? 90 : -90);
|
||||
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), 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);
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
else {
|
||||
if (Forge.enableUIMask) {
|
||||
if (Forge.enableUIMask && canshow && !ImageKeys.getTokenKey(ImageKeys.MORPH_IMAGE).equals(card.getState(altState).getImageKey())) {
|
||||
if (ImageCache.isExtendedArt(card))
|
||||
g.drawImage(image, x, y, w, h);
|
||||
else {
|
||||
g.drawImage(ImageCache.getBorderImage(card, canLook), x, y, w, h);
|
||||
g.drawImage(ImageCache.getBorderImage(card, canshow), x, y, w, h);
|
||||
g.drawImage(ImageCache.croppedBorderImage(image), x + radius / 2.4f, y + radius / 2, w * 0.96f, h * 0.96f);
|
||||
}
|
||||
}
|
||||
else
|
||||
g.drawImage(image, x, y, w, h);
|
||||
else {
|
||||
if (canshow && !ImageKeys.getTokenKey(ImageKeys.MORPH_IMAGE).equals(card.getState(altState).getImageKey()))
|
||||
g.drawImage(image, x, y, w, h);
|
||||
else // sleeve
|
||||
g.drawImage(sleeves, x, y, w, h);
|
||||
}
|
||||
}
|
||||
}
|
||||
CardRenderer.drawFoilEffect(g, card, x, y, w, h, isCurrentCard && canLook && image != ImageCache.defaultImage);
|
||||
CardRenderer.drawFoilEffect(g, card, x, y, w, h, isCurrentCard && canshow && image != ImageCache.defaultImage);
|
||||
}
|
||||
|
||||
public static void drawDetails(Graphics g, CardView card, GameView gameView, boolean altState, float x, float y, float w, float h) {
|
||||
|
||||
@@ -19,9 +19,12 @@ import forge.CachedCardImage;
|
||||
import forge.Forge;
|
||||
import forge.FThreads;
|
||||
import forge.Graphics;
|
||||
import forge.ImageKeys;
|
||||
import forge.StaticData;
|
||||
import forge.assets.FImage;
|
||||
import forge.assets.FImageComplex;
|
||||
import forge.assets.FRotatedImage;
|
||||
import forge.assets.FSkin;
|
||||
import forge.assets.FSkinColor;
|
||||
import forge.assets.FSkinFont;
|
||||
import forge.assets.FSkinImage;
|
||||
@@ -46,6 +49,7 @@ import forge.properties.ForgePreferences;
|
||||
import forge.properties.ForgePreferences.FPref;
|
||||
import forge.screens.match.MatchController;
|
||||
import forge.toolbox.FList;
|
||||
import forge.util.CardTranslation;
|
||||
import forge.util.Utils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import forge.util.TextBounds;
|
||||
@@ -430,7 +434,9 @@ public class CardRenderer {
|
||||
}
|
||||
}
|
||||
public static void drawCard(Graphics g, CardView card, float x, float y, float w, float h, CardStackPosition pos, boolean rotate) {
|
||||
boolean canshow = MatchController.instance.mayView(card) && !ImageKeys.getTokenKey(ImageKeys.MORPH_IMAGE).equals(card.getCurrentState().getImageKey());
|
||||
Texture image = new RendererCachedCardImage(card, false).getImage();
|
||||
FImage sleeves = MatchController.getPlayerSleeve(card.getOwner());
|
||||
float radius = (h - w)/8;
|
||||
|
||||
if (image != null) {
|
||||
@@ -444,7 +450,7 @@ public class CardRenderer {
|
||||
if (ImageCache.isExtendedArt(card))
|
||||
g.drawRotatedImage(image, x, y, w, h, x + w / 2, y + h / 2, -90);
|
||||
else {
|
||||
g.drawfillBorder(3, ImageCache.borderColor(card), x, y, w, h, radius);
|
||||
g.drawRotatedImage(FSkin.getBorders().get(0), x, y, w, h, x + w / 2, y + h / 2, -90);
|
||||
g.drawRotatedImage(ImageCache.croppedBorderImage(image), 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);
|
||||
}
|
||||
}
|
||||
@@ -452,17 +458,21 @@ public class CardRenderer {
|
||||
g.drawRotatedImage(image, x, y, w, h, x + w / 2, y + h / 2, -90);
|
||||
}
|
||||
else {
|
||||
if (Forge.enableUIMask) {
|
||||
if (Forge.enableUIMask && canshow) {
|
||||
if (ImageCache.isExtendedArt(card))
|
||||
g.drawImage(image, x, y, w, h);
|
||||
else {
|
||||
boolean t = (card.getCurrentState().getOriginalColors() != card.getCurrentState().getColors()) || card.getCurrentState().hasChangeColors();
|
||||
g.drawBorderImage(ImageCache.getBorderImage(card, MatchController.instance.mayView(card)), ImageCache.getTint(card), x, y, w, h, t); //tint check for changed colors
|
||||
g.drawBorderImage(ImageCache.getBorderImage(card, canshow), ImageCache.getTint(card), x, y, w, h, t); //tint check for changed colors
|
||||
g.drawImage(ImageCache.croppedBorderImage(image), x + radius / 2.4f, y + radius / 2, w * 0.96f, h * 0.96f);
|
||||
}
|
||||
}
|
||||
else
|
||||
g.drawImage(image, x, y, w, h);
|
||||
else {
|
||||
if (canshow)
|
||||
g.drawImage(image, x, y, w, h);
|
||||
else // draw card back sleeves
|
||||
g.drawImage(sleeves, x, y, w, h);
|
||||
}
|
||||
}
|
||||
}
|
||||
drawFoilEffect(g, card, x, y, w, h, false);
|
||||
@@ -1113,11 +1123,15 @@ 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;
|
||||
if (Forge.enableUIMask) {
|
||||
new_x += radius/2.4f; new_y += radius/2; new_w = w * 0.96f; new_h = h * 0.96f;
|
||||
}
|
||||
if (isPreferenceEnabled(FPref.UI_OVERLAY_FOIL_EFFECT) && MatchController.instance.mayView(card)) {
|
||||
boolean rotateSplit = isPreferenceEnabled(FPref.UI_ROTATE_SPLIT_CARDS) && card.isSplitCard() && inZoomer;
|
||||
int foil = card.getCurrentState().getFoilIndex();
|
||||
if (foil > 0) {
|
||||
CardFaceSymbols.drawOther(g, String.format("foil%02d", foil), x, y, w, h, rotateSplit);
|
||||
CardFaceSymbols.drawOther(g, String.format("foil%02d", foil), new_x, new_y, new_w, new_h, rotateSplit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import forge.toolbox.FOptionPane;
|
||||
import forge.toolbox.FTextField;
|
||||
import forge.toolbox.FEvent.FEventHandler;
|
||||
import forge.util.Callback;
|
||||
import forge.util.Localizer;
|
||||
|
||||
public class GameEntityPicker extends TabPageScreen<GameEntityPicker> {
|
||||
private final FOptionPane optionPane;
|
||||
@@ -73,7 +74,7 @@ public class GameEntityPicker extends TabPageScreen<GameEntityPicker> {
|
||||
super(caption0 + " (" + items.size() + ")", icon0);
|
||||
txtSearch = add(new FTextField());
|
||||
txtSearch.setFont(FSkinFont.get(12));
|
||||
txtSearch.setGhostText("Search");
|
||||
txtSearch.setGhostText(Localizer.getInstance().getMessage("lblSearch"));
|
||||
txtSearch.setChangedHandler(new FEventHandler() {
|
||||
@Override
|
||||
public void handleEvent(FEvent e) {
|
||||
|
||||
@@ -149,6 +149,7 @@ public class FDeckChooser extends FScreen {
|
||||
@Override
|
||||
public void handleEvent(FEvent e) {
|
||||
if (selectedDeckType != DeckType.STANDARD_COLOR_DECK && selectedDeckType != DeckType.STANDARD_CARDGEN_DECK
|
||||
&& selectedDeckType != DeckType.PIONEER_CARDGEN_DECK
|
||||
&& selectedDeckType != DeckType.MODERN_CARDGEN_DECK && selectedDeckType != DeckType.LEGACY_CARDGEN_DECK
|
||||
&& selectedDeckType != DeckType.VINTAGE_CARDGEN_DECK && selectedDeckType != DeckType.MODERN_COLOR_DECK &&
|
||||
selectedDeckType != DeckType.COLOR_DECK && selectedDeckType != DeckType.THEME_DECK
|
||||
@@ -172,6 +173,9 @@ public class FDeckChooser extends FScreen {
|
||||
else if (selectedDeckType == DeckType.STANDARD_CARDGEN_DECK){
|
||||
DeckgenUtil.randomSelect(lstDecks);
|
||||
}
|
||||
else if (selectedDeckType == DeckType.PIONEER_CARDGEN_DECK){
|
||||
DeckgenUtil.randomSelect(lstDecks);
|
||||
}
|
||||
else if (selectedDeckType == DeckType.MODERN_CARDGEN_DECK){
|
||||
DeckgenUtil.randomSelect(lstDecks);
|
||||
}
|
||||
@@ -296,6 +300,7 @@ public class FDeckChooser extends FScreen {
|
||||
case RANDOM_CARDGEN_COMMANDER_DECK:
|
||||
case RANDOM_COMMANDER_DECK:
|
||||
case MODERN_CARDGEN_DECK:
|
||||
case PIONEER_CARDGEN_DECK:
|
||||
case LEGACY_CARDGEN_DECK:
|
||||
case VINTAGE_CARDGEN_DECK:
|
||||
case MODERN_COLOR_DECK:
|
||||
@@ -486,6 +491,7 @@ public class FDeckChooser extends FScreen {
|
||||
cmbDeckTypes.addItem(DeckType.STANDARD_COLOR_DECK);
|
||||
if(FModel.isdeckGenMatrixLoaded()) {
|
||||
cmbDeckTypes.addItem(DeckType.STANDARD_CARDGEN_DECK);
|
||||
cmbDeckTypes.addItem(DeckType.PIONEER_CARDGEN_DECK);
|
||||
cmbDeckTypes.addItem(DeckType.MODERN_CARDGEN_DECK);
|
||||
cmbDeckTypes.addItem(DeckType.LEGACY_CARDGEN_DECK);
|
||||
cmbDeckTypes.addItem(DeckType.VINTAGE_CARDGEN_DECK);
|
||||
@@ -698,6 +704,14 @@ public class FDeckChooser extends FScreen {
|
||||
}
|
||||
config = ItemManagerConfig.STRING_ONLY;
|
||||
break;
|
||||
case PIONEER_CARDGEN_DECK:
|
||||
maxSelections = 1;
|
||||
pool= new ArrayList<>();
|
||||
if(FModel.isdeckGenMatrixLoaded()) {
|
||||
pool = ArchetypeDeckGenerator.getMatrixDecks(FModel.getFormats().getPioneer(), isAi);
|
||||
}
|
||||
config = ItemManagerConfig.STRING_ONLY;
|
||||
break;
|
||||
case MODERN_CARDGEN_DECK:
|
||||
maxSelections = 1;
|
||||
pool= new ArrayList<>();
|
||||
@@ -1077,6 +1091,7 @@ public class FDeckChooser extends FScreen {
|
||||
DeckType.STANDARD_COLOR_DECK,
|
||||
DeckType.STANDARD_CARDGEN_DECK,
|
||||
DeckType.MODERN_COLOR_DECK,
|
||||
DeckType.PIONEER_CARDGEN_DECK,
|
||||
DeckType.MODERN_CARDGEN_DECK,
|
||||
DeckType.LEGACY_CARDGEN_DECK,
|
||||
DeckType.VINTAGE_CARDGEN_DECK,
|
||||
@@ -1085,6 +1100,7 @@ public class FDeckChooser extends FScreen {
|
||||
);
|
||||
if (!FModel.isdeckGenMatrixLoaded()) {
|
||||
deckTypes.remove(DeckType.STANDARD_CARDGEN_DECK);
|
||||
deckTypes.remove(DeckType.PIONEER_CARDGEN_DECK);
|
||||
deckTypes.remove(DeckType.MODERN_CARDGEN_DECK);
|
||||
deckTypes.remove(DeckType.LEGACY_CARDGEN_DECK);
|
||||
deckTypes.remove(DeckType.VINTAGE_CARDGEN_DECK);
|
||||
|
||||
@@ -13,6 +13,7 @@ import forge.toolbox.FDisplayObject;
|
||||
import forge.toolbox.FEvent;
|
||||
import forge.toolbox.FEvent.FEventHandler;
|
||||
import forge.toolbox.FTextField;
|
||||
import forge.util.Localizer;
|
||||
|
||||
|
||||
public class TextSearchFilter<T extends InventoryItem> extends ItemFilter<T> {
|
||||
@@ -78,10 +79,10 @@ public class TextSearchFilter<T extends InventoryItem> extends ItemFilter<T> {
|
||||
}
|
||||
|
||||
public String getCaption() {
|
||||
return txtSearch.getGhostText().substring("Search ".length());
|
||||
return txtSearch.getGhostText().substring((Localizer.getInstance().getMessage("lblSearch") + " ").length());
|
||||
}
|
||||
public void setCaption(String caption0) {
|
||||
txtSearch.setGhostText("Search " + caption0);
|
||||
txtSearch.setGhostText(Localizer.getInstance().getMessage("lblSearch") + " " + caption0);
|
||||
}
|
||||
|
||||
protected class SearchField extends FTextField {
|
||||
@@ -89,7 +90,7 @@ public class TextSearchFilter<T extends InventoryItem> extends ItemFilter<T> {
|
||||
|
||||
private SearchField() {
|
||||
setFont(FONT);
|
||||
setGhostText("Search");
|
||||
setGhostText(Localizer.getInstance().getMessage("lblSearch"));
|
||||
setHeight(getDefaultHeight(DEFAULT_FONT)); //set height based on default filter font
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ public class AvatarSelector extends FScreen {
|
||||
}
|
||||
|
||||
private static final float PADDING = Utils.scale(5);
|
||||
private static final int COLUMNS = 4;
|
||||
private static final int COLUMNS = 5;
|
||||
|
||||
private final int currentIndex;
|
||||
private final List<Integer> usedAvatars;
|
||||
|
||||
@@ -4,6 +4,7 @@ import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import forge.GuiBase;
|
||||
import forge.deck.CardPool;
|
||||
import forge.deck.Deck;
|
||||
import forge.deck.DeckSection;
|
||||
@@ -301,13 +302,20 @@ public abstract class LobbyScreen extends LaunchScreen implements ILobbyView {
|
||||
}
|
||||
|
||||
/** Saves avatar prefs for players one and two. */
|
||||
void updateAvatarPrefs() {
|
||||
void updateAvatarPrefs() {
|
||||
int pOneIndex = playerPanels.get(0).getAvatarIndex();
|
||||
int pTwoIndex = playerPanels.get(1).getAvatarIndex();
|
||||
|
||||
prefs.setPref(FPref.UI_AVATARS, pOneIndex + "," + pTwoIndex);
|
||||
prefs.save();
|
||||
}
|
||||
void updateSleevePrefs() {
|
||||
int pOneIndex = playerPanels.get(0).getSleeveIndex();
|
||||
int pTwoIndex = playerPanels.get(1).getSleeveIndex();
|
||||
|
||||
prefs.setPref(FPref.UI_SLEEVES, pOneIndex + "," + pTwoIndex);
|
||||
prefs.save();
|
||||
}
|
||||
|
||||
/** Updates the avatars from preferences on update. */
|
||||
private void updatePlayersFromPrefs() {
|
||||
@@ -320,6 +328,13 @@ public abstract class LobbyScreen extends LaunchScreen implements ILobbyView {
|
||||
playerPanels.get(i).setAvatarIndex(avatarIndex);
|
||||
}
|
||||
|
||||
// Sleeves
|
||||
String[] sleevePrefs = prefs.getPref(FPref.UI_SLEEVES).split(",");
|
||||
for (int i = 0; i < sleevePrefs.length; i++) {
|
||||
int sleeveIndex = Integer.parseInt(sleevePrefs[i]);
|
||||
playerPanels.get(i).setSleeveIndex(sleeveIndex);
|
||||
}
|
||||
|
||||
// Name
|
||||
String prefName = prefs.getPref(FPref.PLAYER_NAME);
|
||||
playerPanels.get(0).setPlayerName(StringUtils.isBlank(prefName) ? Localizer.getInstance().getMessage("lblHuman") : prefName);
|
||||
@@ -334,6 +349,15 @@ public abstract class LobbyScreen extends LaunchScreen implements ILobbyView {
|
||||
return usedAvatars;
|
||||
}
|
||||
|
||||
List<Integer> getUsedSleeves() {
|
||||
List<Integer> usedSleeves = Arrays.asList(-1,-1,-1,-1,-1,-1,-1,-1);
|
||||
int i = 0;
|
||||
for (PlayerPanel pp : playerPanels) {
|
||||
usedSleeves.set(i++, pp.getSleeveIndex());
|
||||
}
|
||||
return usedSleeves;
|
||||
}
|
||||
|
||||
List<String> getPlayerNames() {
|
||||
List<String> names = new ArrayList<>();
|
||||
for (PlayerPanel pp : playerPanels) {
|
||||
@@ -350,6 +374,10 @@ public abstract class LobbyScreen extends LaunchScreen implements ILobbyView {
|
||||
return playerPanels.get(i).getAvatarIndex();
|
||||
}
|
||||
|
||||
public int getPlayerSleeve(int i) {
|
||||
return playerPanels.get(i).getSleeveIndex();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////
|
||||
//========== Various listeners in build order
|
||||
|
||||
@@ -448,6 +476,7 @@ public abstract class LobbyScreen extends LaunchScreen implements ILobbyView {
|
||||
DeckType selectedDeckType = deckChooser.getSelectedDeckType();
|
||||
switch (selectedDeckType){
|
||||
case STANDARD_CARDGEN_DECK:
|
||||
case PIONEER_CARDGEN_DECK:
|
||||
case MODERN_CARDGEN_DECK:
|
||||
case LEGACY_CARDGEN_DECK:
|
||||
case VINTAGE_CARDGEN_DECK:
|
||||
@@ -498,6 +527,9 @@ public abstract class LobbyScreen extends LaunchScreen implements ILobbyView {
|
||||
updateVariantSelection();
|
||||
|
||||
final boolean allowNetworking = lobby.isAllowNetworking();
|
||||
|
||||
GuiBase.setNetworkplay(allowNetworking);
|
||||
|
||||
for (int i = 0; i < cbPlayerCount.getSelectedItem(); i++) {
|
||||
final boolean hasPanel = i < playerPanels.size();
|
||||
if (i < playerCount) {
|
||||
@@ -527,8 +559,18 @@ public abstract class LobbyScreen extends LaunchScreen implements ILobbyView {
|
||||
|
||||
final LobbySlotType type = slot.getType();
|
||||
panel.setType(type);
|
||||
panel.setPlayerName(slot.getName());
|
||||
panel.setAvatarIndex(slot.getAvatarIndex());
|
||||
if (type != LobbySlotType.AI) {
|
||||
panel.setPlayerName(slot.getName());
|
||||
panel.setAvatarIndex(slot.getAvatarIndex());
|
||||
panel.setSleeveIndex(slot.getSleeveIndex());
|
||||
} else {
|
||||
//AI: this one overrides the setplayername if blank
|
||||
if (panel.getPlayerName().isEmpty())
|
||||
panel.setPlayerName(slot.getName());
|
||||
//AI: override settings if somehow player changes it for AI
|
||||
slot.setAvatarIndex(panel.getAvatarIndex());
|
||||
slot.setSleeveIndex(panel.getSleeveIndex());
|
||||
}
|
||||
panel.setTeam(slot.getTeam());
|
||||
panel.setIsReady(slot.isReady());
|
||||
panel.setIsDevMode(slot.isDevMode());
|
||||
@@ -631,6 +673,18 @@ public abstract class LobbyScreen extends LaunchScreen implements ILobbyView {
|
||||
}
|
||||
}
|
||||
|
||||
void updateAvatar(final int index, final int avatarIndex) {
|
||||
if (playerChangeListener != null) {
|
||||
playerChangeListener.update(index, UpdateLobbyPlayerEvent.avatarUpdate(avatarIndex));
|
||||
}
|
||||
}
|
||||
|
||||
void updateSleeve(final int index, final int sleeveIndex) {
|
||||
if (playerChangeListener != null) {
|
||||
playerChangeListener.update(index, UpdateLobbyPlayerEvent.sleeveUpdate(sleeveIndex));
|
||||
}
|
||||
}
|
||||
|
||||
void setReady(final int index, final boolean ready) {
|
||||
if (ready) {
|
||||
updateDeck(index);
|
||||
@@ -667,7 +721,7 @@ public abstract class LobbyScreen extends LaunchScreen implements ILobbyView {
|
||||
|
||||
private UpdateLobbyPlayerEvent getSlot(final int index) {
|
||||
final PlayerPanel panel = playerPanels.get(index);
|
||||
return UpdateLobbyPlayerEvent.create(panel.getType(), panel.getPlayerName(), panel.getAvatarIndex(), panel.getTeam(), panel.isArchenemy(), panel.isReady(), panel.isDevMode(), panel.getAiOptions());
|
||||
return UpdateLobbyPlayerEvent.create(panel.getType(), panel.getPlayerName(), panel.getAvatarIndex(), panel.getSleeveIndex(), panel.getTeam(), panel.isArchenemy(), panel.isReady(), panel.isDevMode(), panel.getAiOptions());
|
||||
}
|
||||
|
||||
public List<PlayerPanel> getPlayerPanels() {
|
||||
|
||||
@@ -59,7 +59,8 @@ public class PlayerPanel extends FContainer {
|
||||
|
||||
private final FLabel nameRandomiser;
|
||||
private final FLabel avatarLabel = new FLabel.Builder().opaque(true).iconScaleFactor(0.99f).alphaComposite(1).iconInBackground(true).build();
|
||||
private int avatarIndex;
|
||||
private final FLabel sleeveLabel = new FLabel.Builder().opaque(true).iconScaleFactor(0.99f).alphaComposite(1).iconInBackground(true).build();
|
||||
private int avatarIndex, sleeveIndex;
|
||||
|
||||
final Localizer localizer = Localizer.getInstance();
|
||||
private final FTextField txtPlayerName = new FTextField(localizer.getMessage("lblPlayerName"));
|
||||
@@ -98,6 +99,7 @@ public class PlayerPanel extends FContainer {
|
||||
setType(slot.getType());
|
||||
setPlayerName(slot.getName());
|
||||
setAvatarIndex(slot.getAvatarIndex());
|
||||
setSleeveIndex(slot.getSleeveIndex());
|
||||
|
||||
devModeSwitch = new FToggleSwitch(localizer.getMessage("lblNormal"), localizer.getMessage("lblDevMode"));
|
||||
devModeSwitch.setVisible(isNetworkHost());
|
||||
@@ -189,6 +191,9 @@ public class PlayerPanel extends FContainer {
|
||||
createAvatar();
|
||||
add(avatarLabel);
|
||||
|
||||
createSleeve();
|
||||
add(sleeveLabel);
|
||||
|
||||
createNameEditor();
|
||||
add(newLabel(localizer.getMessage("lblName") + ":"));
|
||||
add(txtPlayerName);
|
||||
@@ -299,12 +304,16 @@ public class PlayerPanel extends FContainer {
|
||||
float y = PADDING;
|
||||
float fieldHeight = txtPlayerName.getHeight();
|
||||
float avatarSize = 2 * fieldHeight + PADDING;
|
||||
float sleeveSize = 2 * fieldHeight + PADDING;
|
||||
float sleeveSizeW = (sleeveSize/4)*3;
|
||||
float dy = fieldHeight + PADDING;
|
||||
|
||||
avatarLabel.setBounds(x, y, avatarSize, avatarSize);
|
||||
x += avatarSize + PADDING;
|
||||
sleeveLabel.setBounds(x, y, sleeveSizeW, sleeveSize);
|
||||
x += sleeveSizeW + PADDING;
|
||||
float w = width - x - fieldHeight - 2 * PADDING;
|
||||
txtPlayerName.setBounds(x, y, w, fieldHeight);
|
||||
txtPlayerName.setBounds(x, y, w, fieldHeight); //add space for card back
|
||||
x += w + PADDING;
|
||||
nameRandomiser.setBounds(x, y, fieldHeight, fieldHeight);
|
||||
|
||||
@@ -312,8 +321,8 @@ public class PlayerPanel extends FContainer {
|
||||
humanAiSwitch.setSize(humanAiSwitch.getAutoSizeWidth(fieldHeight), fieldHeight);
|
||||
x = width - humanAiSwitch.getWidth() - PADDING;
|
||||
humanAiSwitch.setPosition(x, y);
|
||||
w = x - avatarSize - 3 * PADDING;
|
||||
x = avatarSize + 2 * PADDING;
|
||||
w = x - (avatarSize+sleeveSizeW+PADDING) - 3 * PADDING;
|
||||
x = (avatarSize+sleeveSizeW+PADDING) + 2 * PADDING;
|
||||
if (cbArchenemyTeam.isVisible()) {
|
||||
cbArchenemyTeam.setBounds(x, y, w, fieldHeight);
|
||||
}
|
||||
@@ -411,6 +420,7 @@ public class PlayerPanel extends FContainer {
|
||||
//update may edit in-case it changed as a result of the AI change
|
||||
setMayEdit(screen.getLobby().mayEdit(index));
|
||||
setAvatarIndex(slot.getAvatarIndex());
|
||||
setSleeveIndex(slot.getSleeveIndex());
|
||||
setPlayerName(slot.getName());
|
||||
}
|
||||
}
|
||||
@@ -470,6 +480,7 @@ public class PlayerPanel extends FContainer {
|
||||
setAvatarIndex(result);
|
||||
|
||||
if (index < 2) {
|
||||
screen.updateAvatar(index, result);
|
||||
screen.updateAvatarPrefs();
|
||||
}
|
||||
if (allowNetworking) {
|
||||
@@ -480,6 +491,26 @@ public class PlayerPanel extends FContainer {
|
||||
}
|
||||
};
|
||||
|
||||
private FEventHandler sleeveCommand = new FEventHandler() {
|
||||
@Override
|
||||
public void handleEvent(FEvent e) {
|
||||
SleevesSelector.show(getPlayerName(), sleeveIndex, screen.getUsedSleeves(), new Callback<Integer>() {
|
||||
@Override
|
||||
public void run(Integer result) {
|
||||
setSleeveIndex(result);
|
||||
|
||||
if (index < 2) {
|
||||
screen.updateSleeve(index, result);
|
||||
screen.updateSleevePrefs();
|
||||
}
|
||||
if (allowNetworking) {
|
||||
screen.firePlayerChangeListener(index);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
public void setDeckSelectorButtonText(String text) {
|
||||
btnDeck.setText(text);
|
||||
}
|
||||
@@ -664,6 +695,17 @@ public class PlayerPanel extends FContainer {
|
||||
avatarLabel.setCommand(avatarCommand);
|
||||
}
|
||||
|
||||
private void createSleeve() {
|
||||
String[] currentPrefs = prefs.getPref(FPref.UI_SLEEVES).split(",");
|
||||
if (index < currentPrefs.length) {
|
||||
setSleeveIndex(Integer.parseInt(currentPrefs[index]));
|
||||
}
|
||||
else {
|
||||
setSleeveIndex(SleevesSelector.getRandomSleeves(screen.getUsedSleeves()));
|
||||
}
|
||||
sleeveLabel.setCommand(sleeveCommand);
|
||||
}
|
||||
|
||||
public void setAvatarIndex(int newAvatarIndex) {
|
||||
avatarIndex = newAvatarIndex;
|
||||
if (avatarIndex != -1) {
|
||||
@@ -674,10 +716,24 @@ public class PlayerPanel extends FContainer {
|
||||
}
|
||||
}
|
||||
|
||||
public void setSleeveIndex(int newSleeveIndex) {
|
||||
sleeveIndex = newSleeveIndex;
|
||||
if (sleeveIndex != -1) {
|
||||
sleeveLabel.setIcon(new FTextureRegionImage(FSkin.getSleeves().get(newSleeveIndex)));
|
||||
}
|
||||
else {
|
||||
sleeveLabel.setIcon(null);
|
||||
}
|
||||
}
|
||||
|
||||
public int getAvatarIndex() {
|
||||
return avatarIndex;
|
||||
}
|
||||
|
||||
public int getSleeveIndex() {
|
||||
return sleeveIndex;
|
||||
}
|
||||
|
||||
public void setPlayerName(String string) {
|
||||
txtPlayerName.setText(string);
|
||||
}
|
||||
@@ -759,6 +815,7 @@ public class PlayerPanel extends FContainer {
|
||||
if (mayEdit == mayEdit0) { return; }
|
||||
mayEdit = mayEdit0;
|
||||
avatarLabel.setEnabled(mayEdit);
|
||||
sleeveLabel.setEnabled(mayEdit);
|
||||
txtPlayerName.setEnabled(mayEdit);
|
||||
nameRandomiser.setEnabled(mayEdit);
|
||||
humanAiSwitch.setEnabled(mayEdit);
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
package forge.screens.constructed;
|
||||
|
||||
import forge.Forge;
|
||||
import forge.assets.FImage;
|
||||
import forge.assets.FSkin;
|
||||
import forge.assets.FSkinImage;
|
||||
import forge.assets.FTextureRegionImage;
|
||||
import forge.screens.FScreen;
|
||||
import forge.toolbox.FDisplayObject;
|
||||
import forge.toolbox.FEvent;
|
||||
import forge.toolbox.FEvent.FEventHandler;
|
||||
import forge.toolbox.FLabel;
|
||||
import forge.toolbox.FScrollPane;
|
||||
import forge.util.Callback;
|
||||
import forge.util.MyRandom;
|
||||
import forge.util.Utils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.badlogic.gdx.graphics.g2d.TextureRegion;
|
||||
import com.badlogic.gdx.utils.Align;
|
||||
|
||||
public class SleevesSelector extends FScreen {
|
||||
public static int getRandomSleeves(List<Integer> usedSleeves) {
|
||||
int random = 0;
|
||||
do {
|
||||
random = MyRandom.getRandom().nextInt(FSkin.getSleeves().size());
|
||||
} while (usedSleeves.contains(random));
|
||||
return random;
|
||||
}
|
||||
|
||||
public static void show(final String playerName, final int currentIndex0, final List<Integer> usedSleeves0, final Callback<Integer> callback0) {
|
||||
SleevesSelector selector = new SleevesSelector(playerName, currentIndex0, usedSleeves0, callback0);
|
||||
Forge.openScreen(selector);
|
||||
}
|
||||
|
||||
private static final float PADDING = Utils.scale(5);
|
||||
private static final int COLUMNS = 5;
|
||||
|
||||
private final int currentIndex;
|
||||
private final List<Integer> usedSleeves;
|
||||
private final Callback<Integer> callback;
|
||||
private final FScrollPane scroller = new FScrollPane() {
|
||||
@Override
|
||||
protected ScrollBounds layoutAndGetScrollBounds(float visibleWidth, float visibleHeight) {
|
||||
int rowCount = 0;
|
||||
float x = PADDING;
|
||||
float y = PADDING;
|
||||
float labelSize = (visibleWidth - (COLUMNS + 1) * PADDING) / COLUMNS;
|
||||
for (FDisplayObject lbl : scroller.getChildren()) {
|
||||
if (rowCount == COLUMNS) { //wrap to next line
|
||||
x = PADDING;
|
||||
y += labelSize + PADDING;
|
||||
rowCount = 0;
|
||||
}
|
||||
lbl.setBounds(x, y, labelSize, labelSize);
|
||||
x += labelSize + PADDING;
|
||||
rowCount++;
|
||||
}
|
||||
return new ScrollBounds(visibleWidth, y + labelSize + PADDING);
|
||||
}
|
||||
};
|
||||
|
||||
private SleevesSelector(final String playerName, final int currentIndex0, final List<Integer> usedSleeves0, final Callback<Integer> callback0) {
|
||||
super("Select Sleeves for " + playerName);
|
||||
|
||||
currentIndex = currentIndex0;
|
||||
usedSleeves = usedSleeves0;
|
||||
callback = callback0;
|
||||
|
||||
//add label for selecting random sleeves first
|
||||
addSleevesLabel(FSkinImage.UNKNOWN, -1);
|
||||
|
||||
//add label for currently selected sleeves next
|
||||
final Map<Integer, TextureRegion> sleeveMap = FSkin.getSleeves();
|
||||
addSleevesLabel(new FTextureRegionImage(sleeveMap.get(currentIndex)), currentIndex);
|
||||
|
||||
//add label for remaining sleeves
|
||||
for (final Integer i : sleeveMap.keySet()) {
|
||||
if (currentIndex != i) {
|
||||
addSleevesLabel(new FTextureRegionImage(sleeveMap.get(i)), i);
|
||||
}
|
||||
}
|
||||
|
||||
add(scroller);
|
||||
}
|
||||
|
||||
private void addSleevesLabel(final FImage img, final int index) {
|
||||
final FLabel lbl = new FLabel.Builder().icon(img).iconScaleFactor(0.99f).align(Align.center)
|
||||
.iconInBackground(true).selectable(true).selected(currentIndex == index)
|
||||
.build();
|
||||
|
||||
if (index == -1) {
|
||||
lbl.setCommand(new FEventHandler() {
|
||||
@Override
|
||||
public void handleEvent(FEvent e) {
|
||||
callback.run(getRandomSleeves(usedSleeves));
|
||||
Forge.back();
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
lbl.setCommand(new FEventHandler() {
|
||||
@Override
|
||||
public void handleEvent(FEvent e) {
|
||||
callback.run(index);
|
||||
Forge.back();
|
||||
}
|
||||
});
|
||||
}
|
||||
scroller.add(lbl);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doLayout(float startY, float width, float height) {
|
||||
scroller.setBounds(0, startY, width, height - startY);
|
||||
}
|
||||
}
|
||||
@@ -23,15 +23,18 @@ import forge.toolbox.FTextArea;
|
||||
import forge.toolbox.GuiChoose;
|
||||
import forge.toolbox.ListChooser;
|
||||
import forge.util.Callback;
|
||||
import forge.util.Localizer;
|
||||
import forge.util.Utils;
|
||||
|
||||
public class NewGauntletScreen extends LaunchScreen {
|
||||
private static final float PADDING = Utils.scale(10);
|
||||
|
||||
private final Localizer localizer = Localizer.getInstance();
|
||||
|
||||
private final FTextArea lblDesc = add(new FTextArea(false,
|
||||
"In Gauntlet mode, you select a deck and play against multiple opponents.\n\n" +
|
||||
"Configure how many opponents you wish to face and what decks or types of decks they will play.\n\n" +
|
||||
"Then, try to beat all AI opponents without losing a match."));
|
||||
localizer.getMessage("lblGauntletText1") + "\n\n" +
|
||||
localizer.getMessage("lblGauntletText2") + "\n\n" +
|
||||
localizer.getMessage("lblGauntletText3")));
|
||||
|
||||
public NewGauntletScreen() {
|
||||
super(null, NewGameMenu.getMenu());
|
||||
@@ -51,44 +54,41 @@ public class NewGauntletScreen extends LaunchScreen {
|
||||
|
||||
@Override
|
||||
protected void startMatch() {
|
||||
GuiChoose.oneOrNone("Select a Gauntlet Type", new String[] {
|
||||
"Quick Gauntlet",
|
||||
"Custom Gauntlet",
|
||||
"Gauntlet Contest",
|
||||
GuiChoose.oneOrNone(localizer.getMessage("lblSelectGauntletType"), new String[] {
|
||||
localizer.getMessage("lblQuickGauntlet"),
|
||||
localizer.getMessage("lblCustomGauntlet"),
|
||||
localizer.getMessage("lblGauntletContest"),
|
||||
}, new Callback<String>() {
|
||||
@Override
|
||||
public void run(String result) {
|
||||
if (result == null) { return; }
|
||||
|
||||
switch (result) {
|
||||
case "Quick Gauntlet":
|
||||
if (localizer.getMessage("lblQuickGauntlet").equals(result)) {
|
||||
createQuickGauntlet();
|
||||
break;
|
||||
case "Custom Gauntlet":
|
||||
} else if(localizer.getMessage("lblCustomGauntlet").equals(result)) {
|
||||
createCustomGauntlet();
|
||||
break;
|
||||
default:
|
||||
} else {
|
||||
createGauntletContest();
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void createQuickGauntlet() {
|
||||
GuiChoose.getInteger("How many opponents are you willing to face?", 3, 50, new Callback<Integer>() {
|
||||
GuiChoose.getInteger(localizer.getMessage("lblHowManyOpponents"), 3, 50, new Callback<Integer>() {
|
||||
@Override
|
||||
public void run(final Integer numOpponents) {
|
||||
if (numOpponents == null) { return; }
|
||||
|
||||
ListChooser<DeckType> chooser = new ListChooser<>(
|
||||
"Choose allowed deck types for opponents", 0, 11, Arrays.asList(DeckType.CUSTOM_DECK,
|
||||
localizer.getMessage("lblChooseAllowedDeckTypeOpponents"), 0, 11, Arrays.asList(DeckType.CUSTOM_DECK,
|
||||
DeckType.PRECONSTRUCTED_DECK,
|
||||
DeckType.QUEST_OPPONENT_DECK,
|
||||
DeckType.COLOR_DECK,
|
||||
DeckType.STANDARD_COLOR_DECK,
|
||||
DeckType.STANDARD_CARDGEN_DECK,
|
||||
DeckType.MODERN_COLOR_DECK,
|
||||
DeckType.PIONEER_CARDGEN_DECK,
|
||||
DeckType.MODERN_CARDGEN_DECK,
|
||||
DeckType.LEGACY_CARDGEN_DECK,
|
||||
DeckType.VINTAGE_CARDGEN_DECK,
|
||||
@@ -99,7 +99,7 @@ public class NewGauntletScreen extends LaunchScreen {
|
||||
return;
|
||||
}
|
||||
|
||||
FDeckChooser.promptForDeck("Select Your Deck", GameType.Gauntlet, false, new Callback<Deck>() {
|
||||
FDeckChooser.promptForDeck(localizer.getMessage("lblSelectYourDeck"), GameType.Gauntlet, false, new Callback<Deck>() {
|
||||
@Override
|
||||
public void run(Deck userDeck) {
|
||||
if (userDeck == null) {
|
||||
@@ -118,7 +118,7 @@ public class NewGauntletScreen extends LaunchScreen {
|
||||
}
|
||||
|
||||
private void createCustomGauntlet() {
|
||||
GuiChoose.getInteger("How many opponents are you willing to face?", 3, 50, new Callback<Integer>() {
|
||||
GuiChoose.getInteger(localizer.getMessage("lblHowManyOpponents"), 3, 50, new Callback<Integer>() {
|
||||
@Override
|
||||
public void run(final Integer numOpponents) {
|
||||
if (numOpponents == null) { return; }
|
||||
@@ -132,7 +132,7 @@ public class NewGauntletScreen extends LaunchScreen {
|
||||
|
||||
private void promptForAiDeck(final GauntletData gauntlet, final int numOpponents) {
|
||||
final int opponentNum = gauntlet.getDecks().size() + 1;
|
||||
FDeckChooser.promptForDeck("Select Deck for Opponent " + opponentNum + " / " + numOpponents, GameType.Gauntlet, true, new Callback<Deck>() {
|
||||
FDeckChooser.promptForDeck(localizer.getMessage("lblSelectDeckForOpponent") + " " + opponentNum + " / " + numOpponents, GameType.Gauntlet, true, new Callback<Deck>() {
|
||||
@Override
|
||||
public void run(Deck aiDeck) {
|
||||
if (aiDeck == null) { return; }
|
||||
@@ -145,7 +145,7 @@ public class NewGauntletScreen extends LaunchScreen {
|
||||
}
|
||||
else {
|
||||
//once all ai decks have been selected, prompt for user deck
|
||||
FDeckChooser.promptForDeck("Select Your Deck", GameType.Gauntlet, false, new Callback<Deck>() {
|
||||
FDeckChooser.promptForDeck(localizer.getMessage("lblSelectYourDeck"), GameType.Gauntlet, false, new Callback<Deck>() {
|
||||
@Override
|
||||
public void run(Deck userDeck) {
|
||||
if (userDeck == null) { return; }
|
||||
@@ -170,12 +170,12 @@ public class NewGauntletScreen extends LaunchScreen {
|
||||
}
|
||||
}
|
||||
|
||||
GuiChoose.oneOrNone("Select Gauntlet Contest", contests, new Callback<GauntletData>() {
|
||||
GuiChoose.oneOrNone(localizer.getMessage("lblSelectGauntletContest"), contests, new Callback<GauntletData>() {
|
||||
@Override
|
||||
public void run(final GauntletData contest) {
|
||||
if (contest == null) { return; }
|
||||
|
||||
FDeckChooser.promptForDeck("Select Your Deck", GameType.Gauntlet, false, new Callback<Deck>() {
|
||||
FDeckChooser.promptForDeck(localizer.getMessage("lblSelectYourDeck"), GameType.Gauntlet, false, new Callback<Deck>() {
|
||||
@Override
|
||||
public void run(final Deck userDeck) {
|
||||
if (userDeck == null) { return; }
|
||||
|
||||
@@ -19,15 +19,13 @@ import forge.toolbox.FEvent.FEventHandler;
|
||||
import forge.util.Localizer;
|
||||
|
||||
public class LoadGameMenu extends FPopupMenu {
|
||||
final static Localizer localizer = Localizer.getInstance();
|
||||
|
||||
public enum LoadGameScreen {
|
||||
BoosterDraft(localizer.getMessage("lblBoosterDraft"), FSkinImage.HAND, LoadDraftScreen.class),
|
||||
SealedDeck(localizer.getMessage("lblSealedDeck"), FSkinImage.PACK, LoadSealedScreen.class),
|
||||
QuestMode(localizer.getMessage("lblQuestMode"), FSkinImage.QUEST_ZEP, LoadQuestScreen.class),
|
||||
PlanarConquest(localizer.getMessage("lblPlanarConquest"), FSkinImage.MULTIVERSE, LoadConquestScreen.class),
|
||||
Gauntlet(localizer.getMessage("lblGauntlet"), FSkinImage.ALPHASTRIKE, LoadGauntletScreen.class);
|
||||
|
||||
BoosterDraft("Booster Draft", FSkinImage.HAND, LoadDraftScreen.class),
|
||||
SealedDeck("Sealed Deck", FSkinImage.PACK, LoadSealedScreen.class),
|
||||
QuestMode("Quest Mode", FSkinImage.QUEST_ZEP, LoadQuestScreen.class),
|
||||
PlanarConquest("Planar Conquest", FSkinImage.MULTIVERSE, LoadConquestScreen.class),
|
||||
Gauntlet("Gauntlet", FSkinImage.ALPHASTRIKE, LoadGauntletScreen.class);
|
||||
|
||||
private final FMenuItem item;
|
||||
private final Class<? extends FScreen> screenClass;
|
||||
private FScreen screen;
|
||||
@@ -47,7 +45,7 @@ public class LoadGameMenu extends FPopupMenu {
|
||||
if (screen == null) { //don't initialize screen until it's opened the first time
|
||||
try {
|
||||
screen = screenClass.newInstance();
|
||||
screen.setHeaderCaption(localizer.getMessage("lblLoadGame") + " - " + item.getText());
|
||||
screen.setHeaderCaption(Localizer.getInstance().getMessage("lblLoadGame") + " - " + item.getText());
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
|
||||
@@ -18,6 +18,7 @@ import forge.toolbox.FOptionPane;
|
||||
import forge.toolbox.FTextArea;
|
||||
import forge.toolbox.GuiChoose;
|
||||
import forge.util.Callback;
|
||||
import forge.util.Localizer;
|
||||
import forge.util.Utils;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@@ -28,10 +29,9 @@ public class PuzzleScreen extends LaunchScreen {
|
||||
private static final float PADDING = Utils.scale(10);
|
||||
|
||||
private final FTextArea lblDesc = add(new FTextArea(false,
|
||||
"Puzzle Mode loads in a puzzle that you have to win in a predetermined time/way.\n\n" +
|
||||
"To begin, press the Start button below, then select a puzzle from a list.\n\n" +
|
||||
"Your objective will be displayed in a pop-up window when the puzzle starts and also " +
|
||||
"specified on a special effect card which will be placed in your command zone."));
|
||||
Localizer.getInstance().getMessage("lblPuzzleText1") + "\n\n" +
|
||||
Localizer.getInstance().getMessage("lblPuzzleText2") + "\n\n" +
|
||||
Localizer.getInstance().getMessage("lblPuzzleText3")));
|
||||
|
||||
public PuzzleScreen() {
|
||||
super(null, NewGameMenu.getMenu());
|
||||
@@ -54,10 +54,10 @@ public class PuzzleScreen extends LaunchScreen {
|
||||
final ArrayList<Puzzle> puzzles = PuzzleIO.loadPuzzles();
|
||||
Collections.sort(puzzles);
|
||||
|
||||
GuiChoose.one("Choose a puzzle", puzzles, new Callback<Puzzle>() {
|
||||
GuiChoose.one(Localizer.getInstance().getMessage("lblChooseAPuzzle"), puzzles, new Callback<Puzzle>() {
|
||||
@Override
|
||||
public void run(final Puzzle chosen) {
|
||||
LoadingOverlay.show("Loading the puzzle...", new Runnable() {
|
||||
LoadingOverlay.show(Localizer.getInstance().getMessage("lblLoadingThePuzzle"), new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// Load selected puzzle
|
||||
|
||||
@@ -30,6 +30,7 @@ import forge.match.HostedMatch;
|
||||
import forge.model.FModel;
|
||||
import forge.player.GamePlayerUtil;
|
||||
import forge.toolbox.FComboBox;
|
||||
import forge.util.Localizer;
|
||||
import forge.util.gui.SGuiChoose;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -37,20 +38,20 @@ import java.util.List;
|
||||
public class LoadDraftScreen extends LaunchScreen {
|
||||
private final DeckManager lstDecks = add(new DeckManager(GameType.Draft));
|
||||
private final FLabel lblTip = add(new FLabel.Builder()
|
||||
.text("Double-tap to edit deck (Long-press to view)")
|
||||
.text(Localizer.getInstance().getMessage("lblDoubleTapToEditDeck"))
|
||||
.textColor(FLabel.INLINE_LABEL_COLOR)
|
||||
.align(Align.center).font(FSkinFont.get(12)).build());
|
||||
|
||||
private final FSkinFont GAME_MODE_FONT= FSkinFont.get(12);
|
||||
private final FLabel lblMode = add(new FLabel.Builder().text("Mode:").font(GAME_MODE_FONT).build());
|
||||
private final FLabel lblMode = add(new FLabel.Builder().text(Localizer.getInstance().getMessage("lblMode")).font(GAME_MODE_FONT).build());
|
||||
private final FComboBox<String> cbMode = add(new FComboBox<>());
|
||||
|
||||
public LoadDraftScreen() {
|
||||
super(null, LoadGameMenu.getMenu());
|
||||
|
||||
cbMode.setFont(GAME_MODE_FONT);
|
||||
cbMode.addItem("Gauntlet");
|
||||
cbMode.addItem("Single Match");
|
||||
cbMode.addItem(Localizer.getInstance().getMessage("lblGauntlet"));
|
||||
cbMode.addItem(Localizer.getInstance().getMessage("lblSingleMatch"));
|
||||
|
||||
lstDecks.setup(ItemManagerConfig.DRAFT_DECKS);
|
||||
lstDecks.setItemActivateHandler(new FEventHandler() {
|
||||
@@ -98,17 +99,18 @@ public class LoadDraftScreen extends LaunchScreen {
|
||||
FThreads.invokeInBackgroundThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Localizer localizer = Localizer.getInstance();
|
||||
final DeckProxy humanDeck = lstDecks.getSelectedItem();
|
||||
if (humanDeck == null) {
|
||||
FOptionPane.showErrorDialog("You must select an existing deck or build a deck from a new booster draft game.", "No Deck");
|
||||
FOptionPane.showErrorDialog(localizer.getMessage("lblYouMustSelectExistingDeck"), localizer.getMessage("lblNoDeck"));
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: if booster draft tournaments are supported in the future, add the possibility to choose them here
|
||||
final boolean gauntlet = cbMode.getSelectedItem().equals("Gauntlet");
|
||||
final boolean gauntlet = cbMode.getSelectedItem().equals(localizer.getMessage("lblGauntlet"));
|
||||
|
||||
if (gauntlet) {
|
||||
final Integer rounds = SGuiChoose.getInteger("How many opponents are you willing to face?",
|
||||
final Integer rounds = SGuiChoose.getInteger(localizer.getMessage("lblHowManyOpponents"),
|
||||
1, FModel.getDecks().getDraft().get(humanDeck.getName()).getAiDecks().size());
|
||||
if (rounds == null) {
|
||||
return;
|
||||
@@ -121,7 +123,7 @@ public class LoadDraftScreen extends LaunchScreen {
|
||||
return;
|
||||
}
|
||||
|
||||
LoadingOverlay.show("Loading new game...", new Runnable() {
|
||||
LoadingOverlay.show(localizer.getMessage("lblLoadingNewGame"), new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
FModel.getGauntletMini().resetGauntletDraft();
|
||||
@@ -131,7 +133,7 @@ public class LoadDraftScreen extends LaunchScreen {
|
||||
}
|
||||
});
|
||||
} else {
|
||||
final Integer aiIndex = SGuiChoose.getInteger("Which opponent would you like to face?",
|
||||
final Integer aiIndex = SGuiChoose.getInteger(localizer.getMessage("lblWhichOpponentWouldYouLikeToFace"),
|
||||
1, FModel.getDecks().getDraft().get(humanDeck.getName()).getAiDecks().size());
|
||||
if (aiIndex == null) {
|
||||
return; // Cancel was pressed
|
||||
@@ -146,7 +148,7 @@ public class LoadDraftScreen extends LaunchScreen {
|
||||
FThreads.invokeInEdtLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
LoadingOverlay.show("Loading new game...", new Runnable() {
|
||||
LoadingOverlay.show(localizer.getMessage("lblLoadingNewGame"), new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!checkDeckLegality(humanDeck)) {
|
||||
@@ -177,7 +179,7 @@ public class LoadDraftScreen extends LaunchScreen {
|
||||
if (FModel.getPreferences().getPrefBoolean(FPref.ENFORCE_DECK_LEGALITY)) {
|
||||
String errorMessage = GameType.Draft.getDeckFormat().getDeckConformanceProblem(humanDeck.getDeck());
|
||||
if (errorMessage != null) {
|
||||
FOptionPane.showErrorDialog("Your deck " + errorMessage + "\nPlease edit or choose a different deck.", "Invalid Deck");
|
||||
FOptionPane.showErrorDialog(Localizer.getInstance().getMessage("lblInvalidDeckDesc").replace("%n", errorMessage), Localizer.getInstance().getMessage("lblInvalidDeck"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import forge.screens.LoadingOverlay;
|
||||
import forge.screens.home.NewGameMenu;
|
||||
import forge.toolbox.FLabel;
|
||||
import forge.toolbox.FTextArea;
|
||||
import forge.util.Localizer;
|
||||
import forge.util.ThreadUtil;
|
||||
import forge.util.Utils;
|
||||
import forge.util.gui.SGuiChoose;
|
||||
@@ -19,9 +20,9 @@ public class NewDraftScreen extends LaunchScreen {
|
||||
private static final float PADDING = Utils.scale(10);
|
||||
|
||||
private final FTextArea lblDesc = add(new FTextArea(false,
|
||||
"In Draft mode, three booster packs are rotated around eight players.\n\n" +
|
||||
"Build a deck from the cards you choose. The AI will do the same.\n\n" +
|
||||
"Then, play against any number of the AI opponents."));
|
||||
Localizer.getInstance().getMessage("lblDraftText1") + "\n\n" +
|
||||
Localizer.getInstance().getMessage("lblDraftText2") + "\n\n" +
|
||||
Localizer.getInstance().getMessage("lblDraftText3")));
|
||||
|
||||
public NewDraftScreen() {
|
||||
super(null, NewGameMenu.getMenu());
|
||||
@@ -44,7 +45,7 @@ public class NewDraftScreen extends LaunchScreen {
|
||||
ThreadUtil.invokeInGameThread(new Runnable() { //must run in game thread to prevent blocking UI thread
|
||||
@Override
|
||||
public void run() {
|
||||
final LimitedPoolType poolType = SGuiChoose.oneOrNone("Choose Draft Format", LimitedPoolType.values());
|
||||
final LimitedPoolType poolType = SGuiChoose.oneOrNone(Localizer.getInstance().getMessage("lblChooseDraftFormat"), LimitedPoolType.values());
|
||||
if (poolType == null) { return; }
|
||||
|
||||
final BoosterDraft draft = BoosterDraft.createDraft(poolType);
|
||||
@@ -53,7 +54,7 @@ public class NewDraftScreen extends LaunchScreen {
|
||||
FThreads.invokeInEdtLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
LoadingOverlay.show("Loading new draft...", new Runnable() {
|
||||
LoadingOverlay.show(Localizer.getInstance().getMessage("lblLoadingNewDraft"), new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Forge.openScreen(new DraftingProcessScreen(draft, EditorType.Draft, null));
|
||||
|
||||
@@ -12,6 +12,7 @@ import forge.screens.LaunchScreen;
|
||||
import forge.screens.home.NewGameMenu;
|
||||
import forge.toolbox.FLabel;
|
||||
import forge.toolbox.FTextArea;
|
||||
import forge.util.Localizer;
|
||||
import forge.util.ThreadUtil;
|
||||
import forge.util.Utils;
|
||||
|
||||
@@ -19,9 +20,9 @@ public class NewSealedScreen extends LaunchScreen {
|
||||
private static final float PADDING = Utils.scale(10);
|
||||
|
||||
private final FTextArea lblDesc = add(new FTextArea(false,
|
||||
"In Sealed mode, you build a deck from booster packs (maximum 10).\n\n" +
|
||||
"Build a deck from the cards you receive. A number of AI opponents will do the same.\n\n" +
|
||||
"Then, play against each of the AI opponents."));
|
||||
Localizer.getInstance().getMessage("lblSealedText2") + "\n\n" +
|
||||
Localizer.getInstance().getMessage("lblSealedText3") + "\n\n" +
|
||||
Localizer.getInstance().getMessage("lblSealedText4")));
|
||||
|
||||
public NewSealedScreen() {
|
||||
super(null, NewGameMenu.getMenu());
|
||||
|
||||
@@ -7,6 +7,8 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import forge.FThreads;
|
||||
import forge.assets.FSkinImage;
|
||||
import forge.util.Localizer;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@@ -68,6 +70,8 @@ public class MatchController extends AbstractGuiGame {
|
||||
|
||||
private static final Map<String, FImage> avatarImages = new HashMap<>();
|
||||
|
||||
private static final Map<String, FImage> sleeveImages = new HashMap<>();
|
||||
|
||||
private static HostedMatch hostedMatch;
|
||||
private static MatchScreen view;
|
||||
|
||||
@@ -100,12 +104,34 @@ public class MatchController extends AbstractGuiGame {
|
||||
return avatar;
|
||||
}
|
||||
|
||||
public static FImage getPlayerSleeve(final PlayerView p) {
|
||||
if (p == null)
|
||||
return FSkinImage.UNKNOWN;
|
||||
final String lp = p.getLobbyPlayerName();
|
||||
FImage sleeve = sleeveImages.get(lp);
|
||||
if (sleeve == null) {
|
||||
sleeve = new FTextureRegionImage(FSkin.getSleeves().get(p.getSleeveIndex()));
|
||||
}
|
||||
return sleeve;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshCardDetails(final Iterable<CardView> cards) {
|
||||
//ensure cards appear in the correct row of the field
|
||||
/*//ensure cards appear in the correct row of the field
|
||||
for (final VPlayerPanel pnl : view.getPlayerPanels().values()) {
|
||||
pnl.getField().update();
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshField() {
|
||||
if(getGameView() == null)
|
||||
return;
|
||||
if(getGameView().getPhase() == null)
|
||||
return;
|
||||
if (getGameView().getPhase().phaseforUpdateField())
|
||||
for (final VPlayerPanel pnl : view.getPlayerPanels().values())
|
||||
pnl.getField().update();
|
||||
}
|
||||
|
||||
public boolean hotSeatMode() {
|
||||
@@ -363,6 +389,42 @@ public class MatchController extends AbstractGuiGame {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSelectables(final Iterable<CardView> cards) {
|
||||
super.setSelectables(cards);
|
||||
// update zones on tabletop and floating zones - non-selectable cards may be rendered differently
|
||||
FThreads.invokeInEdtNowOrLater(new Runnable() {
|
||||
@Override public final void run() {
|
||||
for (final PlayerView p : getGameView().getPlayers()) {
|
||||
if ( p.getCards(ZoneType.Battlefield) != null ) {
|
||||
updateCards(p.getCards(ZoneType.Battlefield));
|
||||
}
|
||||
if ( p.getCards(ZoneType.Hand) != null ) {
|
||||
updateCards(p.getCards(ZoneType.Hand));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearSelectables() {
|
||||
super.clearSelectables();
|
||||
// update zones on tabletop and floating zones - non-selectable cards may be rendered differently
|
||||
FThreads.invokeInEdtNowOrLater(new Runnable() {
|
||||
@Override public final void run() {
|
||||
for (final PlayerView p : getGameView().getPlayers()) {
|
||||
if ( p.getCards(ZoneType.Battlefield) != null ) {
|
||||
updateCards(p.getCards(ZoneType.Battlefield));
|
||||
}
|
||||
if ( p.getCards(ZoneType.Hand) != null ) {
|
||||
updateCards(p.getCards(ZoneType.Hand));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterGameEnd() {
|
||||
Forge.back();
|
||||
|
||||
@@ -376,6 +376,14 @@ public class MatchScreen extends FScreen {
|
||||
TargetingOverlay.drawArrow(g, blocker, attacker);
|
||||
}
|
||||
}
|
||||
//player
|
||||
if (is4Player() || is3Player()) {
|
||||
int numplayers = is3Player() ? 3 : 4;
|
||||
for (final PlayerView p : game.getPlayers()) {
|
||||
if (combat.getAttackersOf(p).contains(attacker))
|
||||
TargetingOverlay.drawArrow(g, attacker, p, numplayers);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -395,7 +403,7 @@ public class MatchScreen extends FScreen {
|
||||
return getActivePrompt().getBtnCancel().trigger(); //trigger Cancel if can't trigger OK
|
||||
case Keys.ESCAPE:
|
||||
if (!FModel.getPreferences().getPrefBoolean(FPref.UI_ALLOW_ESC_TO_END_TURN)) {
|
||||
if (getActivePrompt().getBtnCancel().getText().equals("End Turn")) {
|
||||
if (getActivePrompt().getBtnCancel().getText().equals(Localizer.getInstance().getMessage("lblEndTurn"))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,6 +79,11 @@ public class TargetingOverlay {
|
||||
CardAreaPanel.get(endCard).getTargetingArrowOrigin(),
|
||||
connects);
|
||||
}
|
||||
public static void drawArrow(Graphics g, CardView startCard, PlayerView targetPlayer, int numplayers) {
|
||||
drawArrow(g, CardAreaPanel.get(startCard).getTargetingArrowOrigin(),
|
||||
MatchController.getView().getPlayerPanel(targetPlayer).getAvatar().getTargetingArrowOrigin(numplayers),
|
||||
ArcConnection.FoesAttacking);
|
||||
}
|
||||
public static void drawArrow(Graphics g, Vector2 start, CardView targetCard, ArcConnection connects) {
|
||||
drawArrow(g, start,
|
||||
CardAreaPanel.get(targetCard).getTargetingArrowOrigin(),
|
||||
|
||||
@@ -45,10 +45,15 @@ public class VAvatar extends FDisplayObject {
|
||||
}
|
||||
|
||||
public Vector2 getTargetingArrowOrigin() {
|
||||
return getTargetingArrowOrigin(2);
|
||||
}
|
||||
public Vector2 getTargetingArrowOrigin(int numplayers) {
|
||||
Vector2 origin = new Vector2(screenPos.x, screenPos.y);
|
||||
|
||||
origin.x += WIDTH * 0.75f;
|
||||
if (origin.y < MatchController.getView().getHeight() / 2) {
|
||||
float modx = numplayers > 2 ? 0.25f : 0.75f;
|
||||
|
||||
origin.x += WIDTH * modx;
|
||||
if (origin.y < MatchController.getView().getHeight() / numplayers) {
|
||||
origin.y += HEIGHT * 0.75f; //target bottom right corner if on top half of screen
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -53,21 +53,24 @@ public class ControlWinLose {
|
||||
view.hide();
|
||||
saveOptions();
|
||||
|
||||
MatchController.getHostedMatch().continueMatch();
|
||||
try { MatchController.getHostedMatch().continueMatch();
|
||||
} catch (NullPointerException e) {}
|
||||
}
|
||||
|
||||
/** Action performed when "restart" button is pressed in default win/lose UI. */
|
||||
public void actionOnRestart() {
|
||||
view.hide();
|
||||
saveOptions();
|
||||
MatchController.getHostedMatch().restartMatch();
|
||||
try { MatchController.getHostedMatch().restartMatch();
|
||||
} catch (NullPointerException e) {}
|
||||
}
|
||||
|
||||
/** Action performed when "quit" button is pressed in default win/lose UI. */
|
||||
public void actionOnQuit() {
|
||||
// Reset other stuff
|
||||
saveOptions();
|
||||
MatchController.getHostedMatch().endCurrentGame();
|
||||
try { MatchController.getHostedMatch().endCurrentGame();
|
||||
} catch (NullPointerException e) {}
|
||||
view.hide();
|
||||
}
|
||||
|
||||
|
||||
@@ -302,8 +302,8 @@ public class SettingsPage extends TabPage<SettingsScreen> {
|
||||
localizer.getMessage("nlDisableCardEffect")),
|
||||
4);
|
||||
lstSettings.addItem(new BooleanSetting(FPref.UI_ENABLE_BORDER_MASKING,
|
||||
"Enable Round Border Mask",
|
||||
"When enabled, the card corners are rounded (Preferably Card with Full Borders)."){
|
||||
localizer.getMessage("lblEnableRoundBorder"),
|
||||
localizer.getMessage("nlEnableRoundBorder")){
|
||||
@Override
|
||||
public void select() {
|
||||
super.select();
|
||||
@@ -312,8 +312,8 @@ public class SettingsPage extends TabPage<SettingsScreen> {
|
||||
}
|
||||
},4);
|
||||
lstSettings.addItem(new BooleanSetting(FPref.UI_ENABLE_PRELOAD_EXTENDED_ART,
|
||||
"Preload Extended Art Cards",
|
||||
"When enabled, Preloads Extended Art Cards to Cache on Startup."){
|
||||
localizer.getMessage("lblPreloadExtendedArtCards"),
|
||||
localizer.getMessage("nlPreloadExtendedArtCards")){
|
||||
@Override
|
||||
public void select() {
|
||||
super.select();
|
||||
@@ -322,8 +322,8 @@ public class SettingsPage extends TabPage<SettingsScreen> {
|
||||
}
|
||||
},4);
|
||||
lstSettings.addItem(new BooleanSetting(FPref.UI_SHOW_FPS,
|
||||
"Show FPS Display",
|
||||
"When enabled, show the FPS Display (Experimental)."){
|
||||
localizer.getMessage("lblShowFPSDisplay"),
|
||||
localizer.getMessage("nlShowFPSDisplay")){
|
||||
@Override
|
||||
public void select() {
|
||||
super.select();
|
||||
|
||||
@@ -1 +1,84 @@
|
||||
This file is automatically updated by our release bot on Discord, Blacksmith. It is created from the files present in the 'release-files' directory. Please do not hand-edit this file if using the bot to perform a release, as your changes will be overwritten.
|
||||
Forge: 11/11/2019 ver 1.6.30
|
||||
|
||||
19439 cards in total.
|
||||
|
||||
|
||||
--------------
|
||||
Release Notes:
|
||||
--------------
|
||||
|
||||
- Bug fixes -
|
||||
As always, this release of Forge features an assortment of bug fixes and improvements based on user feedback during the previous release run.
|
||||
|
||||
-------------
|
||||
Known Issues:
|
||||
-------------
|
||||
|
||||
Known issues are here: https://git.cardforge.org/core-developers/forge/issues
|
||||
|
||||
Feel free to report your own there if you have any.
|
||||
|
||||
-------------
|
||||
Installation:
|
||||
-------------
|
||||
|
||||
The Forge archive includes a MANUAL.txt file and we ask that you spend a few minutes reading this file as it contains some information that may prove useful. We do tend to update this file at times and you should quickly read this file and look for new information for each and every new release. Thank you.
|
||||
|
||||
The archive format used for the Forge distribution is ".tar.bz2". There are utilities for Windows, Mac OS and the various *nix's that can be used to extract/decompress these ".tar.bz2" archives. We recommend that you extract/decompress the Forge archive into a new and unused folder.
|
||||
|
||||
Some people use the Windows application 7zip. This utility can be found at http://www.7-zip.org/download.html. Mac users can double click on the archive and the application Archive Utility will launch and extract the archive. Mac users do not need to download a separate utility.
|
||||
|
||||
Once the Forge archive has been decompressed you should then be able to launch Forge by using the included launcher. Launching Forge by double clicking on the forge jar file in the past caused a java heap space error. Forge's memory requirements have increased over time and the launchers increase the java heap space available to Forge. Currently you can launch Forge by double clicking on the forge jar file without a java heap space error but this is likely to change as we add in more sounds, icons, etc.
|
||||
|
||||
- The Mac OS application version -
|
||||
We provide separate macOS builds of desktop and mobile (backported) Forge packaged as native Mac applications. Please check the relevant thread for details and download links.
|
||||
|
||||
|
||||
- Online Multiplayer -
|
||||
For local network play you should only need two systems running Forge. One to host and one to join and play. For remote (over the Internet) play you will need to ensure that the port used (36743 by default) is forwarded to the hosting machine.
|
||||
|
||||
--------------------
|
||||
Active Contributors:
|
||||
--------------------
|
||||
|
||||
Agetian
|
||||
apantel
|
||||
Austinio7116
|
||||
Churrufli
|
||||
DrDev
|
||||
excessum
|
||||
Flair
|
||||
Gos
|
||||
Hanmac
|
||||
Indigo Dragon
|
||||
Jamin Collins
|
||||
kevlahnota
|
||||
klaxnek
|
||||
KrazyTheFox
|
||||
leriomaggio
|
||||
Luke
|
||||
Marek14
|
||||
mcrawford620
|
||||
Meerkov
|
||||
Myrd
|
||||
nefigah
|
||||
OgreBattlecruiser
|
||||
pfps
|
||||
Ryan1729
|
||||
Seravy
|
||||
Sirspud
|
||||
Sloth
|
||||
slyfox7777777
|
||||
Sol
|
||||
Swordshine
|
||||
tjtillman
|
||||
tojammot
|
||||
torridus
|
||||
Xyx
|
||||
Zuchinni
|
||||
|
||||
(If you think your name should be on this list, add it with your next contribution)
|
||||
|
||||
(Quest icons used created by Teekatas, from his Legendora set http://raindropmemory.deviantart.com)
|
||||
(Thanks to the XMage team for permission to use their targeting arrows.)
|
||||
(Thanks to http://www.freesound.org/browse/ for providing some sound files.)
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>forge</artifactId>
|
||||
<groupId>forge</groupId>
|
||||
<version>1.6.30-SNAPSHOT</version>
|
||||
<version>1.6.30</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>forge-gui</artifactId>
|
||||
@@ -39,28 +39,33 @@
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>24.1-android</version>
|
||||
<version>28.1-android</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.thoughtworks.xstream</groupId>
|
||||
<artifactId>xstream</artifactId>
|
||||
<version>1.4.10</version>
|
||||
<version>1.4.11.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>3.7</version>
|
||||
<version>3.8.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-all</artifactId>
|
||||
<version>4.0.25.Final</version>
|
||||
<version>4.1.43.Final</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>log4j</groupId>
|
||||
<artifactId>log4j</artifactId>
|
||||
<version>1.2.17</version>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-api</artifactId>
|
||||
<version>2.11.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-core</artifactId>
|
||||
<version>2.11.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.fourthline.cling</groupId>
|
||||
@@ -72,6 +77,11 @@
|
||||
<artifactId>slf4j-simple</artifactId>
|
||||
<version>1.7.22</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mapdb</groupId>
|
||||
<artifactId>elsa</artifactId>
|
||||
<version>3.0.0-M7</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#Add one announcement per line
|
||||
Throne of Eldraine pre-release!
|
||||
Updated Libgdx from 1.5.5 to 1.9.10 ([url=https://github.com/libgdx/libgdx/blob/master/CHANGES]detailed changes are here[/url]).
|
||||
Throne of Eldraine fixes
|
||||
Pioneer is here!
|
||||
Some fancy looking UI stuff was added. Card Sleeves and Keyword Icons
|
||||
Continued work on Translations
|
||||
[b]Forge now requires Java 8 (or newer). You will not be able to start the game if you are not yet running Java 8.[/b]
|
||||
For some reason Oracle hates Forge and version 1.8.0_211 does bad things with Forge for unknown reasons. Downgrade to 202 for a beter time.
|
||||
https://www.oracle.com/technetwork/java/javase/downloads/java-archive-javase8-2177648.html
|
||||
|
||||
@@ -10,6 +10,7 @@ Hanmac
|
||||
Indigo Dragon
|
||||
Jamin Collins
|
||||
kevlahnota
|
||||
klaxnek
|
||||
KrazyTheFox
|
||||
leriomaggio
|
||||
Luke
|
||||
|
||||
@@ -9,6 +9,6 @@ SVar:BuffedBy:Permanent.Snow
|
||||
SVar:NoZeroToughnessAI:True
|
||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigTap | TriggerDescription$ When CARDNAME enters the battlefield, tap target creature an opponent controls. That creature doesn't untap during its controller's next untap step.
|
||||
SVar:TrigTap:DB$ Tap | ValidTgts$ Creature.OppCtrl | TgtPrompt$ Choose target creature an opponent controls. | SubAbility$ DBPump
|
||||
SVar:DBPump:DB$ CantUntapTurn | Defined$ Targeted
|
||||
SVar:DBPump:DB$ Pump | Defined$ Targeted | KW$ HIDDEN This card doesn't untap during your next untap step. | Permanent$ True
|
||||
SVar:PlayMain1:TRUE
|
||||
Oracle:Trample\nAbominable Treefolk's power and toughness are each equal to the number of snow permanents you control.\nWhen Abominable Treefolk enters the battlefield, tap target creature an opponent controls. That creature doesn't untap during its controller's next untap step.
|
||||
|
||||
@@ -3,7 +3,7 @@ ManaCost:3 U
|
||||
Types:Instant
|
||||
K:Devoid
|
||||
A:SP$ Tap | Cost$ 3 U | TargetMin$ 0 | TargetMax$ 2 | TgtPrompt$ Choose target creature | ValidTgts$ Creature | SubAbility$ TrigPump | SpellDescription$ Tap up to two target creatures.
|
||||
SVar:TrigPump:DB$ CantUntapTurn | Defined$ Targeted | SubAbility$ DBToken | SpellDescription$ Those creatures don't untap during their controller's next untap step.
|
||||
SVar:TrigPump:DB$Pump | Defined$ Targeted | KW$ HIDDEN This card doesn't untap during your next untap step. | Permanent$ True | SubAbility$ DBToken | SpellDescription$ Those creatures don't untap during their controller's next untap step.
|
||||
SVar:DBToken:DB$ Token | TokenAmount$ 1 | TokenScript$ c_1_1_eldrazi_scion_sac | TokenOwner$ You | LegacyImage$ c 1 1 eldrazi scion sac bfz | SpellDescription$ Create a 1/1 colorless Eldrazi Scion creature token. It has "Sacrifice this creature: Add {C}."
|
||||
DeckHints:Type$Eldrazi
|
||||
DeckHas:Ability$Mana.Colorless & Ability$Token
|
||||
|
||||
@@ -2,7 +2,7 @@ Name:Ajani Vengeant
|
||||
ManaCost:2 R W
|
||||
Types:Legendary Planeswalker Ajani
|
||||
Loyalty:3
|
||||
A:AB$ CantUntapTurn | Cost$ AddCounter<1/LOYALTY> | Planeswalker$ True | ValidTgts$ Permanent | SpellDescription$ Target permanent doesn't untap during its controller's next untap step.
|
||||
A:AB$ Pump | Cost$ AddCounter<1/LOYALTY> | Planeswalker$ True | KW$ HIDDEN This card doesn't untap during your next untap step. | ValidTgts$ Permanent | Permanent$ True | IsCurse$ True | SpellDescription$ Target permanent doesn't untap during its controller's next untap step.
|
||||
A:AB$ DealDamage | Cost$ SubCounter<2/LOYALTY> | Planeswalker$ True | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | NumDmg$ 3 | SubAbility$ DBGainLife | SpellDescription$ CARDNAME deals 3 damage to any target and you gain 3 life.
|
||||
SVar:DBGainLife:DB$GainLife | LifeAmount$ 3
|
||||
A:AB$ DestroyAll | Cost$ SubCounter<7/LOYALTY> | Planeswalker$ True | Ultimate$ True | ValidTgts$ Player | TgtPrompt$ Select target player | ValidCards$ Land | SpellDescription$ Destroy all lands target player controls.
|
||||
|
||||
@@ -3,7 +3,7 @@ ManaCost:7
|
||||
Types:Artifact Creature Golem
|
||||
PT:*/*
|
||||
K:Trample
|
||||
S:Mode$ Continuous | AffectedZone$ Battlefield | Affected$ Card.Self | CantUntap$ True | Description$ CARDNAME doesn't untap during your untap step.
|
||||
K:CARDNAME doesn't untap during your untap step.
|
||||
S:Mode$ Continuous | EffectZone$ All | CharacteristicDefining$ True | SetPower$ X | SetToughness$ X | Description$ CARDNAME's power and toughness are each equal to the number of creatures on the battlefield.
|
||||
SVar:X:Count$Valid Creature
|
||||
A:AB$ Untap | Cost$ tapXType<5/Creature> | SpellDescription$ Untap CARDNAME.
|
||||
|
||||
@@ -2,9 +2,10 @@ Name:Amber Prison
|
||||
ManaCost:4
|
||||
Types:Artifact
|
||||
K:You may choose not to untap CARDNAME during your untap step.
|
||||
A:AB$ Tap | Cost$ 4 T | ValidTgts$ Artifact,Creature,Land | TgtPrompt$ Select target artifact, creature, or land | SubAbility$ DBEffect | SpellDescription$ Tap target artifact, creature, or land. That permanent doesn't untap during its controller's untap step for as long as CARDNAME remains tapped. | StackDescription$ SpellDescription
|
||||
SVar:DBEffect:DB$ Effect | RememberObjects$ Targeted | StaticAbilities$ DontUntap | Duration$ UntilUntaps | ForgetOnMoved$ Battlefield
|
||||
SVar:DontUntap:Mode$ Continuous | EffectZone$ Command | AffectedZone$ Battlefield | Affected$ Card.IsRemembered | CantUntap$ True | Description$ Remembered doesn't untap during its controller's untap step for as long as EFFECTSOURCE remains tapped.
|
||||
A:AB$ Tap | Cost$ 4 T | ValidTgts$ Artifact,Creature,Land | TgtPrompt$ Select target artifact, creature, or land | RememberTapped$ True | AlwaysRemember$ True | SpellDescription$ Tap target artifact, creature, or land. That permanent doesn't untap during its controller's untap step for as long as CARDNAME remains tapped. | StackDescription$ SpellDescription
|
||||
S:Mode$ Continuous | Affected$ Card.IsRemembered | AddHiddenKeyword$ CARDNAME doesn't untap during your untap step.
|
||||
T:Mode$ Untaps | ValidCard$ Card.Self | TriggerZones$ Battlefield | Execute$ ClearRemembered | Static$ True
|
||||
SVar:ClearRemembered:DB$ Cleanup | ClearRemembered$ True
|
||||
AI:RemoveDeck:All
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/amber_prison.jpg
|
||||
Oracle:You may choose not to untap Amber Prison during your untap step.\n{4}, {T}: Tap target artifact, creature, or land. That permanent doesn't untap during its controller's untap step for as long as Amber Prison remains tapped.
|
||||
|
||||
@@ -3,7 +3,7 @@ ManaCost:2 R R
|
||||
Types:Enchantment
|
||||
K:ETBReplacement:Other:ChooseCT
|
||||
SVar:ChooseCT:DB$ ChooseType | Defined$ You | Type$ Creature | AILogic$ MostProminentOppControls | SpellDescription$ As CARDNAME enters the battlefield, choose a creature type.
|
||||
S:Mode$ Continuous | Affected$ Creature.ChosenType | CantUntap$ True | Description$ Creatures of the chosen type don't untap during their controllers' untap steps.
|
||||
S:Mode$ Continuous | Affected$ Creature.ChosenType | AddHiddenKeyword$ CARDNAME doesn't untap during your untap step. | Description$ Creatures of the chosen type don't untap during their controllers' untap steps.
|
||||
AI:RemoveDeck:Random
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/an_zerrin_ruins.jpg
|
||||
Oracle:As An-Zerrin Ruins enters the battlefield, choose a creature type.\nCreatures of the chosen type don't untap during their controllers' untap steps.
|
||||
|
||||
@@ -3,7 +3,7 @@ ManaCost:U
|
||||
Types:Enchantment Aura
|
||||
K:Enchant creature
|
||||
A:SP$ Attach | Cost$ U | ValidTgts$ Creature | AILogic$ Curse
|
||||
S:Mode$ Continuous | Affected$ Creature.EnchantedBy | CantUntap$ True | Description$ Enchanted creature doesn't untap during its controller's untap step. At the beginning of the upkeep of enchanted creature's controller, that player may discard a card at random. If the player does, untap that creature.
|
||||
S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddHiddenKeyword$ CARDNAME doesn't untap during your untap step. | Description$ Enchanted creature doesn't untap during its controller's untap step. At the beginning of the upkeep of enchanted creature's controller, that player may discard a card at random. If the player does, untap that creature.
|
||||
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ EnchantedController | TriggerZones$ Battlefield | OptionalDecider$ EnchantedController | Execute$ ApathyDiscard | TriggerDescription$ At the beginning of the upkeep of enchanted creature's controller, that player may discard a card at random. If the player does, untap that creature.
|
||||
SVar:ApathyDiscard:DB$ Discard | Defined$ TriggeredPlayer | NumCards$ 1 | Mode$ Random | RememberDiscarded$ True | SubAbility$ ApathyUntap | References$ X
|
||||
SVar:ApathyUntap:DB$ Untap | Defined$ Enchanted | SpellDescription$ Untap enchanted creature | ConditionCheckSVar$ X | ConditionSVarCompare$ EQ1 | References$ X | SubAbility$ DBCleanup
|
||||
|
||||
@@ -2,7 +2,7 @@ Name:Apes of Rath
|
||||
ManaCost:2 G G
|
||||
Types:Creature Ape
|
||||
PT:5/4
|
||||
T:Mode$ Attacks | ValidCard$ Card.Self | TriggerZones$ Battlefield | Execute$ DBPump | TriggerDescription$ Whenever CARDNAME attacks, it doesn't untap during its controller's next untap step.
|
||||
SVar:DBPump:DB$ CantUntapTurn | Defined$ Self
|
||||
T:Mode$ Attacks | ValidCard$ Card.Self | TriggerZones$ Battlefield | Execute$ StayTapped | TriggerDescription$ Whenever CARDNAME attacks, it doesn't untap during its controller's next untap step.
|
||||
SVar:StayTapped:DB$Pump | KW$ HIDDEN This card doesn't untap during your next untap step. | Defined$ Self | Permanent$ True
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/apes_of_rath.jpg
|
||||
Oracle:Whenever Apes of Rath attacks, it doesn't untap during its controller's next untap step.
|
||||
|
||||
@@ -2,10 +2,7 @@ Name:Arbalest Elite
|
||||
ManaCost:2 W W
|
||||
Types:Creature Human Archer
|
||||
PT:2/3
|
||||
A:AB$ DealDamage | Cost$ 2 W T | ValidTgts$ Creature.attacking,Creature.blocking | TgtPrompt$ Select target attacking or blocking creature | NumDmg$ 3 | SubAbility$ DBEffect | SpellDescription$ CARDNAME deals 3 damage to target attacking or blocking creature. CARDNAME doesn't untap during your next untap step.
|
||||
SVar:DBEffect:DB$ Effect | RememberObjects$ Self | StaticAbilities$ DontUntap | Duration$ Permanent | ForgetOnMoved$ Battlefield | Triggers$ RemoveEffect | SVars$ ExileEffect
|
||||
SVar:DontUntap:Mode$ Continuous | EffectZone$ Command | AffectedZone$ Battlefield | Affected$ Card.IsRemembered | CantUntap$ True | Description$ EFFECTSOURCE don't untap during your next untap step.
|
||||
SVar:RemoveEffect:Mode$ Phase | Phase$ Untap | ValidPlayer$ You | TriggerZones$ Command | Static$ True | Execute$ ExileEffect
|
||||
SVar:ExileEffect:DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile
|
||||
A:AB$ DealDamage | Cost$ 2 W T | ValidTgts$ Creature.attacking,Creature.blocking | TgtPrompt$ Select target attacking or blocking creature | NumDmg$ 3 | SubAbility$ DBStayTapped | SpellDescription$ CARDNAME deals 3 damage to target attacking or blocking creature. CARDNAME doesn't untap during your next untap step.
|
||||
SVar:DBStayTapped:DB$Pump | KW$ HIDDEN This card doesn't untap during your next untap step. | Defined$ Self | Permanent$ True
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/arbalest_elite.jpg
|
||||
Oracle:{2}{W}, {T}: Arbalest Elite deals 3 damage to target attacking or blocking creature. Arbalest Elite doesn't untap during your next untap step.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Name:Arena of the Ancients
|
||||
ManaCost:3
|
||||
Types:Artifact
|
||||
S:Mode$ Continuous | Affected$ Creature.Legendary | CantUntap$ True | Description$ Legendary creatures don't untap during their controllers' untap steps.
|
||||
S:Mode$ Continuous | Affected$ Creature.Legendary | AddHiddenKeyword$ CARDNAME doesn't untap during your untap step. | Description$ Legendary creatures don't untap during their controllers' untap steps.
|
||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigTapAll | TriggerDescription$ When CARDNAME enters the battlefield, tap all legendary creatures.
|
||||
SVar:TrigTapAll:DB$TapAll | ValidCards$ Creature.Legendary
|
||||
SVar:NonStackingEffect:True
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Name:Back to Basics
|
||||
ManaCost:2 U
|
||||
Types:Enchantment
|
||||
S:Mode$ Continuous | Affected$ Land.nonBasic | CantUntap$ True | Description$ Nonbasic lands don't untap during their controllers' untap steps.
|
||||
S:Mode$ Continuous | Affected$ Land.nonBasic | AddHiddenKeyword$ CARDNAME doesn't untap during your untap step. | Description$ Nonbasic lands don't untap during their controllers' untap steps.
|
||||
SVar:NonStackingEffect:True
|
||||
AI:RemoveDeck:Random
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/back_to_basics.jpg
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Name:Barl's Cage
|
||||
ManaCost:4
|
||||
Types:Artifact
|
||||
A:AB$ CantUntapTurn | Cost$ 3 | ValidTgts$ Creature | TgtPrompt$ Select target creature | SpellDescription$ Target creature doesn't untap during its controller's next untap step.
|
||||
A:AB$ Pump | Cost$ 3 | ValidTgts$ Creature | TgtPrompt$ Select target creature | KW$ HIDDEN This card doesn't untap during your next untap step. | Permanent$ True | IsCurse$ True | SpellDescription$ Target creature doesn't untap during its controller's next untap step.
|
||||
SVar:NonStackingEffect:True
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/barls_cage.jpg
|
||||
Oracle:{3}: Target creature doesn't untap during its controller's next untap step.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user