mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-15 18:28:00 +00:00
Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git
This commit is contained in:
@@ -616,7 +616,8 @@ public class AiController {
|
|||||||
}
|
}
|
||||||
catch (IllegalArgumentException ex) {
|
catch (IllegalArgumentException ex) {
|
||||||
System.err.println(ex.getMessage());
|
System.err.println(ex.getMessage());
|
||||||
ComparatorUtil.verifyTransitivity(saComparator, all);
|
String assertex = ComparatorUtil.verifyTransitivity(saComparator, all);
|
||||||
|
Sentry.capture(ex.getMessage() + "\nAssertionError [verifyTransitivity]: " + assertex);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (final SpellAbility sa : ComputerUtilAbility.getOriginalAndAltCostAbilities(all, player)) {
|
for (final SpellAbility sa : ComputerUtilAbility.getOriginalAndAltCostAbilities(all, player)) {
|
||||||
@@ -1585,7 +1586,8 @@ public class AiController {
|
|||||||
}
|
}
|
||||||
catch (IllegalArgumentException ex) {
|
catch (IllegalArgumentException ex) {
|
||||||
System.err.println(ex.getMessage());
|
System.err.println(ex.getMessage());
|
||||||
ComparatorUtil.verifyTransitivity(saComparator, all);
|
String assertex = ComparatorUtil.verifyTransitivity(saComparator, all);
|
||||||
|
Sentry.capture(ex.getMessage() + "\nAssertionError [verifyTransitivity]: " + assertex);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (final SpellAbility sa : ComputerUtilAbility.getOriginalAndAltCostAbilities(all, player)) {
|
for (final SpellAbility sa : ComputerUtilAbility.getOriginalAndAltCostAbilities(all, player)) {
|
||||||
@@ -1844,7 +1846,7 @@ public class AiController {
|
|||||||
// Special case for Bow to My Command which simulates a complex tap cost via ChooseCard
|
// Special case for Bow to My Command which simulates a complex tap cost via ChooseCard
|
||||||
// TODO: consider enhancing support for tapXType<Any/...> in UnlessCost to get rid of this hack
|
// TODO: consider enhancing support for tapXType<Any/...> in UnlessCost to get rid of this hack
|
||||||
if ("BowToMyCommand".equals(sa.getParam("AILogic"))) {
|
if ("BowToMyCommand".equals(sa.getParam("AILogic"))) {
|
||||||
if (!sa.getHostCard().getZone().is(ZoneType.Command)) {
|
if (!sa.getHostCard().isInZone(ZoneType.Command)) {
|
||||||
// Make sure that other opponents do not tap for an already abandoned scheme
|
// Make sure that other opponents do not tap for an already abandoned scheme
|
||||||
result.clear();
|
result.clear();
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -2403,7 +2403,7 @@ public class ComputerUtil {
|
|||||||
}
|
}
|
||||||
// if source is not on the battlefield anymore, choose +1/+1
|
// if source is not on the battlefield anymore, choose +1/+1
|
||||||
// ones
|
// ones
|
||||||
if (!game.getCardState(source).getZone().is(ZoneType.Battlefield)) {
|
if (!game.getCardState(source).isInZone(ZoneType.Battlefield)) {
|
||||||
return opponent ? "Feather" : "Quill";
|
return opponent ? "Feather" : "Quill";
|
||||||
}
|
}
|
||||||
// if no hand cards, try to mill opponent
|
// if no hand cards, try to mill opponent
|
||||||
@@ -2435,7 +2435,7 @@ public class ComputerUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if source is not on the battlefield anymore
|
// if source is not on the battlefield anymore
|
||||||
if (!game.getCardState(source).getZone().is(ZoneType.Battlefield)) {
|
if (!game.getCardState(source).isInZone(ZoneType.Battlefield)) {
|
||||||
return opponent ? "Strength" : "Numbers";
|
return opponent ? "Strength" : "Numbers";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2484,7 +2484,7 @@ public class ComputerUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if source is not on the battlefield anymore
|
// if source is not on the battlefield anymore
|
||||||
if (!game.getCardState(source).getZone().is(ZoneType.Battlefield)) {
|
if (!game.getCardState(source).isInZone(ZoneType.Battlefield)) {
|
||||||
return opponent ? "Sprout" : "Harvest";
|
return opponent ? "Sprout" : "Harvest";
|
||||||
}
|
}
|
||||||
// TODO add Lifegain to +1/+1 counters trigger
|
// TODO add Lifegain to +1/+1 counters trigger
|
||||||
|
|||||||
@@ -1294,7 +1294,7 @@ public class ComputerUtilCard {
|
|||||||
// cast it during Declare Blockers, thus ruining its attacker
|
// cast it during Declare Blockers, thus ruining its attacker
|
||||||
if (holdCombatTricks && sa.getApi() == ApiType.Pump
|
if (holdCombatTricks && sa.getApi() == ApiType.Pump
|
||||||
&& sa.hasParam("NumAtt") && sa.getHostCard() != null
|
&& sa.hasParam("NumAtt") && sa.getHostCard() != null
|
||||||
&& sa.getHostCard().getZone() != null && sa.getHostCard().getZone().is(ZoneType.Hand)
|
&& sa.getHostCard().isInZone(ZoneType.Hand)
|
||||||
&& c.getNetPower() > 0 // too obvious if attacking with a 0-power creature
|
&& c.getNetPower() > 0 // too obvious if attacking with a 0-power creature
|
||||||
&& sa.getHostCard().isInstant() // only do it for instant speed spells in hand
|
&& sa.getHostCard().isInstant() // only do it for instant speed spells in hand
|
||||||
&& ComputerUtilMana.hasEnoughManaSourcesToCast(sa, ai)) {
|
&& ComputerUtilMana.hasEnoughManaSourcesToCast(sa, ai)) {
|
||||||
|
|||||||
@@ -729,7 +729,7 @@ public class PlayerControllerAi extends PlayerController {
|
|||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
Card rem = (Card) source.getFirstRemembered();
|
Card rem = (Card) source.getFirstRemembered();
|
||||||
if (!rem.getZone().is(ZoneType.Battlefield)) {
|
if (!rem.isInZone(ZoneType.Battlefield)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -737,7 +737,7 @@ public class PlayerControllerAi extends PlayerController {
|
|||||||
case "BetterTgtThanRemembered":
|
case "BetterTgtThanRemembered":
|
||||||
if (source.getRememberedCount() > 0) {
|
if (source.getRememberedCount() > 0) {
|
||||||
Card rem = (Card) source.getFirstRemembered();
|
Card rem = (Card) source.getFirstRemembered();
|
||||||
if (!rem.getZone().is(ZoneType.Battlefield)) {
|
if (!rem.isInZone(ZoneType.Battlefield)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
for (Card c : source.getController().getCreaturesInPlay()) {
|
for (Card c : source.getController().getCreaturesInPlay()) {
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ public class DamageDealAi extends DamageAiBase {
|
|||||||
// Set PayX here to maximum value.
|
// Set PayX here to maximum value.
|
||||||
dmg = ComputerUtilMana.determineLeftoverMana(sa, ai);
|
dmg = ComputerUtilMana.determineLeftoverMana(sa, ai);
|
||||||
source.setSVar("PayX", Integer.toString(dmg));
|
source.setSVar("PayX", Integer.toString(dmg));
|
||||||
} else if (sa.getSVar(damage).equals("Count$CardsInYourHand") && source.getZone().is(ZoneType.Hand)) {
|
} else if (sa.getSVar(damage).equals("Count$CardsInYourHand") && source.isInZone(ZoneType.Hand)) {
|
||||||
dmg--; // the card will be spent casting the spell, so actual damage is 1 less
|
dmg--; // the card will be spent casting the spell, so actual damage is 1 less
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -113,7 +113,7 @@ public class DamageDealAi extends DamageAiBase {
|
|||||||
|
|
||||||
// Set PayX here to maximum value. It will be adjusted later depending on the target.
|
// Set PayX here to maximum value. It will be adjusted later depending on the target.
|
||||||
source.setSVar("PayX", Integer.toString(dmg));
|
source.setSVar("PayX", Integer.toString(dmg));
|
||||||
} else if (sa.getSVar(damage).contains("InYourHand") && source.getZone().is(ZoneType.Hand)) {
|
} else if (sa.getSVar(damage).contains("InYourHand") && source.isInZone(ZoneType.Hand)) {
|
||||||
dmg = CardFactoryUtil.xCount(source, sa.getSVar(damage)) - 1; // the card will be spent casting the spell, so actual damage is 1 less
|
dmg = CardFactoryUtil.xCount(source, sa.getSVar(damage)) - 1; // the card will be spent casting the spell, so actual damage is 1 less
|
||||||
} else if (sa.getSVar(damage).equals("TargetedPlayer$CardsInHand")) {
|
} else if (sa.getSVar(damage).equals("TargetedPlayer$CardsInHand")) {
|
||||||
// cards that deal damage by the number of cards in target player's hand, e.g. Sudden Impact
|
// cards that deal damage by the number of cards in target player's hand, e.g. Sudden Impact
|
||||||
|
|||||||
@@ -409,7 +409,7 @@ public class DrawAi extends SpellAbilityAi {
|
|||||||
if (computerHandSize + numCards > computerMaxHandSize && game.getPhaseHandler().isPlayerTurn(ai)) {
|
if (computerHandSize + numCards > computerMaxHandSize && game.getPhaseHandler().isPlayerTurn(ai)) {
|
||||||
if (xPaid) {
|
if (xPaid) {
|
||||||
numCards = computerMaxHandSize - computerHandSize;
|
numCards = computerMaxHandSize - computerHandSize;
|
||||||
if (sa.getHostCard().getZone().is(ZoneType.Hand)) {
|
if (sa.getHostCard().isInZone(ZoneType.Hand)) {
|
||||||
numCards++; // the card will be spent
|
numCards++; // the card will be spent
|
||||||
}
|
}
|
||||||
source.setSVar("PayX", Integer.toString(numCards));
|
source.setSVar("PayX", Integer.toString(numCards));
|
||||||
|
|||||||
@@ -309,7 +309,7 @@ public class PumpAi extends PumpAiBase {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
defense = AbilityUtils.calculateAmount(sa.getHostCard(), numDefense, sa);
|
defense = AbilityUtils.calculateAmount(sa.getHostCard(), numDefense, sa);
|
||||||
if (numDefense.contains("X") && sa.getSVar("X").equals("Count$CardsInYourHand") && source.getZone().is(ZoneType.Hand)) {
|
if (numDefense.contains("X") && sa.getSVar("X").equals("Count$CardsInYourHand") && source.isInZone(ZoneType.Hand)) {
|
||||||
defense--; // the card will be spent casting the spell, so actual toughness is 1 less
|
defense--; // the card will be spent casting the spell, so actual toughness is 1 less
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -328,7 +328,7 @@ public class PumpAi extends PumpAiBase {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
attack = AbilityUtils.calculateAmount(sa.getHostCard(), numAttack, sa);
|
attack = AbilityUtils.calculateAmount(sa.getHostCard(), numAttack, sa);
|
||||||
if (numAttack.contains("X") && sa.getSVar("X").equals("Count$CardsInYourHand") && source.getZone().is(ZoneType.Hand)) {
|
if (numAttack.contains("X") && sa.getSVar("X").equals("Count$CardsInYourHand") && source.isInZone(ZoneType.Hand)) {
|
||||||
attack--; // the card will be spent casting the spell, so actual power is 1 less
|
attack--; // the card will be spent casting the spell, so actual power is 1 less
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ public class SetStateAi extends SpellAbilityAi {
|
|||||||
if (card.isFaceDown()) {
|
if (card.isFaceDown()) {
|
||||||
// hidden agenda
|
// hidden agenda
|
||||||
if (card.getState(CardStateName.Original).hasIntrinsicKeyword("Hidden agenda")
|
if (card.getState(CardStateName.Original).hasIntrinsicKeyword("Hidden agenda")
|
||||||
&& card.getZone().is(ZoneType.Command)) {
|
&& card.isInZone(ZoneType.Command)) {
|
||||||
String chosenName = card.getNamedCard();
|
String chosenName = card.getNamedCard();
|
||||||
for (Card cast : ai.getGame().getStack().getSpellsCastThisTurn()) {
|
for (Card cast : ai.getGame().getStack().getSpellsCastThisTurn()) {
|
||||||
if (cast.getController() == ai && cast.getName().equals(chosenName)) {
|
if (cast.getController() == ai && cast.getName().equals(chosenName)) {
|
||||||
|
|||||||
@@ -228,7 +228,7 @@ public class GameCopier {
|
|||||||
CardFactory.copyCopiableCharacteristics(c, result);
|
CardFactory.copyCopiableCharacteristics(c, result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
if (USE_FROM_PAPER_CARD && !c.isEmblem()) {
|
if (USE_FROM_PAPER_CARD && !c.isEmblem() && c.getPaperCard() != null) {
|
||||||
Card newCard = Card.fromPaperCard(c.getPaperCard(), newOwner);
|
Card newCard = Card.fromPaperCard(c.getPaperCard(), newOwner);
|
||||||
newCard.setCommander(c.isCommander());
|
newCard.setCommander(c.isCommander());
|
||||||
return newCard;
|
return newCard;
|
||||||
|
|||||||
@@ -15,8 +15,9 @@ public final class ComparatorUtil
|
|||||||
* @param elements the elements to test against
|
* @param elements the elements to test against
|
||||||
* @throws AssertionError if the comparator is not transitive
|
* @throws AssertionError if the comparator is not transitive
|
||||||
*/
|
*/
|
||||||
public static <T> void verifyTransitivity(Comparator<T> comparator, Collection<T> elements)
|
public static <T> String verifyTransitivity(Comparator<T> comparator, Collection<T> elements)
|
||||||
{
|
{
|
||||||
|
String exception = "";
|
||||||
for (T first: elements)
|
for (T first: elements)
|
||||||
{
|
{
|
||||||
for (T second: elements)
|
for (T second: elements)
|
||||||
@@ -27,8 +28,12 @@ public final class ComparatorUtil
|
|||||||
{
|
{
|
||||||
// Uncomment the following line to step through the failed case
|
// Uncomment the following line to step through the failed case
|
||||||
//comparator.compare(first, second);
|
//comparator.compare(first, second);
|
||||||
throw new AssertionError("compare(" + first + ", " + second + ") == " + result1 +
|
/*throw new AssertionError("compare(" + first + ", " + second + ") == " + result1 +
|
||||||
" but swapping the parameters returns " + result2);
|
" but swapping the parameters returns " + result2);*/
|
||||||
|
exception = "compare(" + first + ", " + second + ") == " + result1 +
|
||||||
|
" but swapping the parameters returns " + result2;
|
||||||
|
System.err.println(exception);
|
||||||
|
return exception;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -49,13 +54,19 @@ public final class ComparatorUtil
|
|||||||
{
|
{
|
||||||
// Uncomment the following line to step through the failed case
|
// Uncomment the following line to step through the failed case
|
||||||
//comparator.compare(first, third);
|
//comparator.compare(first, third);
|
||||||
throw new AssertionError("compare(" + first + ", " + second + ") > 0, " +
|
/*throw new AssertionError("compare(" + first + ", " + second + ") > 0, " +
|
||||||
"compare(" + second + ", " + third + ") > 0, but compare(" + first + ", " + third + ") == " +
|
"compare(" + second + ", " + third + ") > 0, but compare(" + first + ", " + third + ") == " +
|
||||||
firstGreaterThanThird);
|
firstGreaterThanThird);*/
|
||||||
|
exception = "compare(" + first + ", " + second + ") > 0, " +
|
||||||
|
"compare(" + second + ", " + third + ") > 0, but compare(" + first + ", " + third + ") == " +
|
||||||
|
firstGreaterThanThird;
|
||||||
|
System.err.println(exception);
|
||||||
|
return exception;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return exception;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -145,11 +145,6 @@ public class GameAction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if an adventureCard is put from Stack somewhere else, need to reset to Original State
|
|
||||||
if (c.isAdventureCard() && ((zoneFrom != null && zoneFrom.is(ZoneType.Stack)) || !zoneTo.is(ZoneType.Stack))) {
|
|
||||||
c.setState(CardStateName.Original, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clean up the temporary Dash SVar when the Dashed card leaves the battlefield
|
// Clean up the temporary Dash SVar when the Dashed card leaves the battlefield
|
||||||
// Clean up the temporary AtEOT SVar
|
// Clean up the temporary AtEOT SVar
|
||||||
String endofTurn = c.getSVar("EndOfTurnLeavePlay");
|
String endofTurn = c.getSVar("EndOfTurnLeavePlay");
|
||||||
@@ -256,7 +251,7 @@ public class GameAction {
|
|||||||
}
|
}
|
||||||
if(noLandLKI.isLand()) {
|
if(noLandLKI.isLand()) {
|
||||||
// if it isn't on the Stack, it stays in that Zone
|
// if it isn't on the Stack, it stays in that Zone
|
||||||
if (!c.getZone().is(ZoneType.Stack)) {
|
if (!c.isInZone(ZoneType.Stack)) {
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
// if something would only be a land when entering the battlefield and not before
|
// if something would only be a land when entering the battlefield and not before
|
||||||
@@ -352,6 +347,11 @@ public class GameAction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if an adventureCard is put from Stack somewhere else, need to reset to Original State
|
||||||
|
if (copied.isAdventureCard() && ((zoneFrom != null && zoneFrom.is(ZoneType.Stack)) || !zoneTo.is(ZoneType.Stack))) {
|
||||||
|
copied.setState(CardStateName.Original, false);
|
||||||
|
}
|
||||||
|
|
||||||
GameEntityCounterTable table = new GameEntityCounterTable();
|
GameEntityCounterTable table = new GameEntityCounterTable();
|
||||||
|
|
||||||
// need to suspend cards own replacement effects
|
// need to suspend cards own replacement effects
|
||||||
|
|||||||
@@ -300,12 +300,12 @@ public final class GameActionUtil {
|
|||||||
costs.add(new OptionalCostValue(type, cost));
|
costs.add(new OptionalCostValue(type, cost));
|
||||||
}
|
}
|
||||||
} else if (keyword.equals("Retrace")) {
|
} else if (keyword.equals("Retrace")) {
|
||||||
if (source.getZone().is(ZoneType.Graveyard)) {
|
if (source.isInZone(ZoneType.Graveyard)) {
|
||||||
final Cost cost = new Cost("Discard<1/Land>", false);
|
final Cost cost = new Cost("Discard<1/Land>", false);
|
||||||
costs.add(new OptionalCostValue(OptionalCost.Retrace, cost));
|
costs.add(new OptionalCostValue(OptionalCost.Retrace, cost));
|
||||||
}
|
}
|
||||||
} else if (keyword.equals("Jump-start")) {
|
} else if (keyword.equals("Jump-start")) {
|
||||||
if (source.getZone().is(ZoneType.Graveyard)) {
|
if (source.isInZone(ZoneType.Graveyard)) {
|
||||||
final Cost cost = new Cost("Discard<1/Card>", false);
|
final Cost cost = new Cost("Discard<1/Card>", false);
|
||||||
costs.add(new OptionalCostValue(OptionalCost.Jumpstart, cost));
|
costs.add(new OptionalCostValue(OptionalCost.Jumpstart, cost));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ public class CloneEffect extends SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (sa.hasParam("CloneZone")) {
|
if (sa.hasParam("CloneZone")) {
|
||||||
if (!tgtCard.getZone().is(ZoneType.smartValueOf(sa.getParam("CloneZone")))) {
|
if (!tgtCard.isInZone(ZoneType.smartValueOf(sa.getParam("CloneZone")))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -134,7 +134,7 @@ public class CloneEffect extends SpellAbilityEffect {
|
|||||||
tgtCard.clearImprintedCards();
|
tgtCard.clearImprintedCards();
|
||||||
|
|
||||||
// check if clone is now an Aura that needs to be attached
|
// check if clone is now an Aura that needs to be attached
|
||||||
if (tgtCard.isAura() && !tgtCard.getZone().is(ZoneType.Battlefield)) {
|
if (tgtCard.isAura() && !tgtCard.isInZone(ZoneType.Battlefield)) {
|
||||||
AttachEffect.attachAuraOnIndirectEnterBattlefield(tgtCard);
|
AttachEffect.attachAuraOnIndirectEnterBattlefield(tgtCard);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -120,7 +120,10 @@ public class PumpEffect extends SpellAbilityEffect {
|
|||||||
&& !(host.isInPlay() || host.isInZone(ZoneType.Stack))) {
|
&& !(host.isInPlay() || host.isInZone(ZoneType.Stack))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!keywords.isEmpty()) {
|
||||||
p.addChangedKeywords(keywords, ImmutableList.of(), timestamp);
|
p.addChangedKeywords(keywords, ImmutableList.of(), timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
if (!sa.hasParam("Permanent")) {
|
if (!sa.hasParam("Permanent")) {
|
||||||
// If not Permanent, remove Pumped at EOT
|
// If not Permanent, remove Pumped at EOT
|
||||||
@@ -129,12 +132,7 @@ public class PumpEffect extends SpellAbilityEffect {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
p.removeChangedKeywords(timestamp);
|
||||||
if (keywords.size() > 0) {
|
|
||||||
for (int i = 0; i < keywords.size(); i++) {
|
|
||||||
p.removeKeyword(keywords.get(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
addUntilCommand(sa, untilEOT);
|
addUntilCommand(sa, untilEOT);
|
||||||
|
|||||||
@@ -3155,6 +3155,7 @@ public class Card extends GameEntity implements Comparable<Card> {
|
|||||||
public final void addColor(final String s, final boolean addToColors, final long timestamp) {
|
public final void addColor(final String s, final boolean addToColors, final long timestamp) {
|
||||||
changedCardColors.put(timestamp, new CardColor(s, addToColors, timestamp));
|
changedCardColors.put(timestamp, new CardColor(s, addToColors, timestamp));
|
||||||
currentState.getView().updateColors(this);
|
currentState.getView().updateColors(this);
|
||||||
|
currentState.getView().updateHasChangeColors(!getChangedCardColors().isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void removeColor(final long timestampIn) {
|
public final void removeColor(final long timestampIn) {
|
||||||
@@ -3162,6 +3163,7 @@ public class Card extends GameEntity implements Comparable<Card> {
|
|||||||
|
|
||||||
if (removeCol != null) {
|
if (removeCol != null) {
|
||||||
currentState.getView().updateColors(this);
|
currentState.getView().updateColors(this);
|
||||||
|
currentState.getView().updateHasChangeColors(!getChangedCardColors().isEmpty());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5616,34 +5618,7 @@ public class Card extends GameEntity implements Comparable<Card> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
final MutableBoolean result = new MutableBoolean(true);
|
|
||||||
visitKeywords(currentState, new Visitor<KeywordInterface>() {
|
|
||||||
@Override
|
|
||||||
public boolean visit(KeywordInterface kw) {
|
|
||||||
switch (kw.getOriginal()) {
|
|
||||||
case "Shroud":
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
sb.append("Can target CardUID_").append(getId());
|
|
||||||
sb.append(" with spells and abilities as though it didn't have shroud.");
|
|
||||||
if (sa.getActivatingPlayer() == null) {
|
|
||||||
System.err.println("Unexpected behavior: SA activator was null when trying to determine if the activating player could target a card with Shroud. SA host card = " + source + ", SA = " + sa);
|
|
||||||
result.setFalse(); // FIXME: maybe this should check by SA host card controller at this point instead?
|
|
||||||
} else if (!sa.getActivatingPlayer().hasKeyword(sb.toString())) {
|
|
||||||
result.setFalse();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "CARDNAME can't be the target of spells.":
|
|
||||||
if (sa.isSpell()) {
|
|
||||||
result.setFalse();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return result.isTrue();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (result.isFalse()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (sa.isSpell()) {
|
if (sa.isSpell()) {
|
||||||
for(KeywordInterface inst : source.getKeywords()) {
|
for(KeywordInterface inst : source.getKeywords()) {
|
||||||
String kw = inst.getOriginal();
|
String kw = inst.getOriginal();
|
||||||
|
|||||||
@@ -4501,7 +4501,9 @@ public class CardFactoryUtil {
|
|||||||
effect = "Mode$ CantTarget | Hexproof$ True | ValidCard$ Card.Self | Secondary$ True"
|
effect = "Mode$ CantTarget | Hexproof$ True | ValidCard$ Card.Self | Secondary$ True"
|
||||||
+ sbValid.toString() + " | Activator$ Opponent | Description$ "
|
+ sbValid.toString() + " | Activator$ Opponent | Description$ "
|
||||||
+ sbDesc.toString() + " (" + inst.getReminderText() + ")";
|
+ sbDesc.toString() + " (" + inst.getReminderText() + ")";
|
||||||
|
} else if (keyword.equals("Shroud")) {
|
||||||
|
effect = "Mode$ CantTarget | Shroud$ True | ValidCard$ Card.Self | Secondary$ True"
|
||||||
|
+ " | Description$ Shroud (" + inst.getReminderText() + ")";
|
||||||
} else if (keyword.startsWith("Strive")) {
|
} else if (keyword.startsWith("Strive")) {
|
||||||
final String[] k = keyword.split(":");
|
final String[] k = keyword.split(":");
|
||||||
final String manacost = k[1];
|
final String manacost = k[1];
|
||||||
@@ -4693,19 +4695,30 @@ public class CardFactoryUtil {
|
|||||||
}
|
}
|
||||||
sa.setAdventure(true);
|
sa.setAdventure(true);
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("Event$ Moved | ValidCard$ Card.Self | Origin$ Stack | ExcludeDestination$ Exile ");
|
||||||
|
sb.append("| ValidStackSa$ Spell.Adventure | Fizzle$ False | Secondary$ True | Description$ Adventure");
|
||||||
|
|
||||||
|
String repeffstr = sb.toString();
|
||||||
|
|
||||||
String abExile = "DB$ ChangeZone | Defined$ Self | Origin$ Stack | Destination$ Exile | StackDescription$ None";
|
String abExile = "DB$ ChangeZone | Defined$ Self | Origin$ Stack | Destination$ Exile | StackDescription$ None";
|
||||||
|
|
||||||
AbilitySub saExile = (AbilitySub)AbilityFactory.getAbility(abExile, card);
|
SpellAbility saExile = AbilityFactory.getAbility(abExile, card);
|
||||||
|
|
||||||
String abEffect = "DB$ Effect | RememberObjects$ Self | StaticAbilities$ Play | ExileOnMoved$ Exile | Duration$ Permanent | ConditionDefined$ Self | ConditionPresent$ Card.nonCopiedSpell";
|
String abEffect = "DB$ Effect | RememberObjects$ Self | StaticAbilities$ Play | ExileOnMoved$ Exile | Duration$ Permanent | ConditionDefined$ Self | ConditionPresent$ Card.nonCopiedSpell";
|
||||||
AbilitySub saEffect = (AbilitySub)AbilityFactory.getAbility(abEffect, card);
|
AbilitySub saEffect = (AbilitySub)AbilityFactory.getAbility(abEffect, card);
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sbPlay = new StringBuilder();
|
||||||
sb.append("Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered+nonAdventure");
|
sbPlay.append("Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered+nonAdventure");
|
||||||
sb.append(" | AffectedZone$ Exile | Description$ You may cast the card.");
|
sbPlay.append(" | AffectedZone$ Exile | Description$ You may cast the card.");
|
||||||
saEffect.setSVar("Play", sb.toString());
|
saEffect.setSVar("Play", sbPlay.toString());
|
||||||
|
|
||||||
saExile.setSubAbility(saEffect);
|
saExile.setSubAbility(saEffect);
|
||||||
sa.appendSubAbility(saExile);
|
|
||||||
|
ReplacementEffect re = ReplacementHandler.parseReplacement(repeffstr, card, true);
|
||||||
|
re.setLayer(ReplacementLayer.Other);
|
||||||
|
|
||||||
|
re.setOverridingAbility(saExile);
|
||||||
|
card.addReplacementEffect(re);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -523,7 +523,6 @@ public class CardView extends GameEntityView {
|
|||||||
void updateChangedColorWords(Card c) {
|
void updateChangedColorWords(Card c) {
|
||||||
set(TrackableProperty.ChangedColorWords, c.getChangedTextColorWords());
|
set(TrackableProperty.ChangedColorWords, c.getChangedTextColorWords());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, String> getChangedTypes() {
|
public Map<String, String> getChangedTypes() {
|
||||||
return get(TrackableProperty.ChangedTypes);
|
return get(TrackableProperty.ChangedTypes);
|
||||||
}
|
}
|
||||||
@@ -706,11 +705,13 @@ public class CardView extends GameEntityView {
|
|||||||
// update the color only while in Game
|
// update the color only while in Game
|
||||||
if (c.getGame() != null) {
|
if (c.getGame() != null) {
|
||||||
currentStateView.updateColors(currentState);
|
currentStateView.updateColors(currentState);
|
||||||
|
currentStateView.updateHasChangeColors(!c.getChangedCardColors().isEmpty());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
currentStateView.updateLoyalty(currentState);
|
currentStateView.updateLoyalty(currentState);
|
||||||
}
|
}
|
||||||
currentState.getView().updateKeywords(c, currentState); //update keywords even if state doesn't change
|
currentState.getView().updateKeywords(c, currentState); //update keywords even if state doesn't change
|
||||||
|
currentState.getView().setOriginalColors(c); //set original Colors
|
||||||
|
|
||||||
CardState alternateState = isSplitCard && isFaceDown() ? c.getState(CardStateName.RightSplit) : c.getAlternateState();
|
CardState alternateState = isSplitCard && isFaceDown() ? c.getState(CardStateName.RightSplit) : c.getAlternateState();
|
||||||
|
|
||||||
@@ -840,13 +841,22 @@ public class CardView extends GameEntityView {
|
|||||||
public ColorSet getColors() {
|
public ColorSet getColors() {
|
||||||
return get(TrackableProperty.Colors);
|
return get(TrackableProperty.Colors);
|
||||||
}
|
}
|
||||||
|
public ColorSet getOriginalColors() {
|
||||||
|
return get(TrackableProperty.OriginalColors);
|
||||||
|
}
|
||||||
void updateColors(Card c) {
|
void updateColors(Card c) {
|
||||||
set(TrackableProperty.Colors, c.determineColor());
|
set(TrackableProperty.Colors, c.determineColor());
|
||||||
}
|
}
|
||||||
void updateColors(CardState c) {
|
void updateColors(CardState c) {
|
||||||
set(TrackableProperty.Colors, ColorSet.fromMask(c.getColor()));
|
set(TrackableProperty.Colors, ColorSet.fromMask(c.getColor()));
|
||||||
}
|
}
|
||||||
|
void setOriginalColors(Card c) {
|
||||||
|
set(TrackableProperty.OriginalColors, c.determineColor());
|
||||||
|
}
|
||||||
|
void updateHasChangeColors(boolean hasChangeColor) {
|
||||||
|
set(TrackableProperty.HasChangedColors, hasChangeColor);
|
||||||
|
}
|
||||||
|
public boolean hasChangeColors() { return get(TrackableProperty.HasChangedColors); }
|
||||||
public String getImageKey() {
|
public String getImageKey() {
|
||||||
return getImageKey(null);
|
return getImageKey(null);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -192,7 +192,7 @@ public class CostAdjustment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (final StaticAbility stAb : reduceAbilities) {
|
for (final StaticAbility stAb : reduceAbilities) {
|
||||||
sumGeneric += applyReduceCostAbility(stAb, sa, cost);
|
sumGeneric += applyReduceCostAbility(stAb, sa, cost, sumGeneric);
|
||||||
}
|
}
|
||||||
// need to reduce generic extra because of 2 hybrid mana
|
// need to reduce generic extra because of 2 hybrid mana
|
||||||
cost.decreaseGenericMana(sumGeneric);
|
cost.decreaseGenericMana(sumGeneric);
|
||||||
@@ -360,7 +360,7 @@ public class CostAdjustment {
|
|||||||
* @param manaCost
|
* @param manaCost
|
||||||
* a ManaCost
|
* a ManaCost
|
||||||
*/
|
*/
|
||||||
private static int applyReduceCostAbility(final StaticAbility staticAbility, final SpellAbility sa, final ManaCostBeingPaid manaCost) {
|
private static int applyReduceCostAbility(final StaticAbility staticAbility, final SpellAbility sa, final ManaCostBeingPaid manaCost, int sumReduced) {
|
||||||
//Can't reduce zero cost
|
//Can't reduce zero cost
|
||||||
if (manaCost.toString().equals("{0}")) {
|
if (manaCost.toString().equals("{0}")) {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -393,7 +393,7 @@ public class CostAdjustment {
|
|||||||
minMana = Integer.valueOf(staticAbility.getParam("MinMana"));
|
minMana = Integer.valueOf(staticAbility.getParam("MinMana"));
|
||||||
}
|
}
|
||||||
|
|
||||||
final int maxReduction = Math.max(0, manaCost.getConvertedManaCost() - minMana);
|
final int maxReduction = Math.max(0, manaCost.getConvertedManaCost() - minMana - sumReduced);
|
||||||
if (maxReduction > 0) {
|
if (maxReduction > 0) {
|
||||||
return Math.min(value, maxReduction);
|
return Math.min(value, maxReduction);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import com.google.common.collect.Lists;
|
|||||||
|
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardFactoryUtil;
|
import forge.game.card.CardFactoryUtil;
|
||||||
|
import forge.game.player.Player;
|
||||||
|
import forge.game.player.PlayerFactoryUtil;
|
||||||
import forge.game.replacement.ReplacementEffect;
|
import forge.game.replacement.ReplacementEffect;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.staticability.StaticAbility;
|
import forge.game.staticability.StaticAbility;
|
||||||
@@ -125,6 +127,53 @@ public abstract class KeywordInstance<T extends KeywordInstance<?>> implements K
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* (non-Javadoc)
|
||||||
|
* @see forge.game.keyword.KeywordInterface#createTraits(forge.game.player.Player)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void createTraits(Player player) {
|
||||||
|
createTraits(player, false);
|
||||||
|
}
|
||||||
|
/* (non-Javadoc)
|
||||||
|
* @see forge.game.keyword.KeywordInterface#createTraits(forge.game.player.Player, boolean)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void createTraits(Player player, boolean clear) {
|
||||||
|
if (clear) {
|
||||||
|
triggers.clear();
|
||||||
|
replacements.clear();
|
||||||
|
abilities.clear();
|
||||||
|
staticAbilities.clear();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
String msg = "KeywordInstance:createTraits: make Traits for Keyword";
|
||||||
|
Sentry.getContext().recordBreadcrumb(
|
||||||
|
new BreadcrumbBuilder().setMessage(msg)
|
||||||
|
.withData("Player", player.getName()).withData("Keyword", this.original).build()
|
||||||
|
);
|
||||||
|
|
||||||
|
// add Extra for debugging
|
||||||
|
Sentry.getContext().addExtra("Player", player);
|
||||||
|
Sentry.getContext().addExtra("Keyword", this.original);
|
||||||
|
|
||||||
|
PlayerFactoryUtil.addTriggerAbility(this, player);
|
||||||
|
PlayerFactoryUtil.addReplacementEffect(this, player);
|
||||||
|
PlayerFactoryUtil.addSpellAbility(this, player);
|
||||||
|
PlayerFactoryUtil.addStaticAbility(this, player);
|
||||||
|
} catch (Exception e) {
|
||||||
|
String msg = "KeywordInstance:createTraits: failed Traits for Keyword";
|
||||||
|
Sentry.getContext().recordBreadcrumb(
|
||||||
|
new BreadcrumbBuilder().setMessage(msg)
|
||||||
|
.withData("Player", player.getName()).withData("Keyword", this.original).build()
|
||||||
|
);
|
||||||
|
//rethrow
|
||||||
|
throw new RuntimeException("Error in Keyword " + this.original + " for player " + player.getName(), e);
|
||||||
|
} finally {
|
||||||
|
// remove added extra
|
||||||
|
Sentry.getContext().removeExtra("Player");
|
||||||
|
Sentry.getContext().removeExtra("Keyword");
|
||||||
|
}
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
* @see forge.game.keyword.KeywordInterface#addTrigger(forge.game.trigger.Trigger)
|
* @see forge.game.keyword.KeywordInterface#addTrigger(forge.game.trigger.Trigger)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package forge.game.keyword;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
|
import forge.game.player.Player;
|
||||||
import forge.game.replacement.ReplacementEffect;
|
import forge.game.replacement.ReplacementEffect;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.staticability.StaticAbility;
|
import forge.game.staticability.StaticAbility;
|
||||||
@@ -24,6 +25,9 @@ public interface KeywordInterface extends Cloneable {
|
|||||||
void createTraits(final Card host, final boolean intrinsic);
|
void createTraits(final Card host, final boolean intrinsic);
|
||||||
void createTraits(final Card host, final boolean intrinsic, final boolean clear);
|
void createTraits(final Card host, final boolean intrinsic, final boolean clear);
|
||||||
|
|
||||||
|
void createTraits(final Player player);
|
||||||
|
void createTraits(final Player player, final boolean clear);
|
||||||
|
|
||||||
void addTrigger(final Trigger trg);
|
void addTrigger(final Trigger trg);
|
||||||
|
|
||||||
void addReplacement(final ReplacementEffect trg);
|
void addReplacement(final ReplacementEffect trg);
|
||||||
|
|||||||
@@ -23,6 +23,11 @@ import java.util.List;
|
|||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
|
import forge.game.player.Player;
|
||||||
|
import forge.game.replacement.ReplacementEffect;
|
||||||
|
import forge.game.spellability.SpellAbility;
|
||||||
|
import forge.game.staticability.StaticAbility;
|
||||||
|
import forge.game.trigger.Trigger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
@@ -132,6 +137,12 @@ public class KeywordsChange implements Cloneable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final void addKeywordsToPlayer(final Player player) {
|
||||||
|
for (KeywordInterface inst : keywords.getValues()) {
|
||||||
|
inst.createTraits(player, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public final boolean removeKeywordfromAdd(final String keyword) {
|
public final boolean removeKeywordfromAdd(final String keyword) {
|
||||||
return keywords.remove(keyword);
|
return keywords.remove(keyword);
|
||||||
}
|
}
|
||||||
@@ -201,6 +212,47 @@ public class KeywordsChange implements Cloneable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the triggers
|
||||||
|
*/
|
||||||
|
public Collection<Trigger> getTriggers() {
|
||||||
|
List<Trigger> result = Lists.newArrayList();
|
||||||
|
for (KeywordInterface k : this.keywords.getValues()) {
|
||||||
|
result.addAll(k.getTriggers());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @return the replacements
|
||||||
|
*/
|
||||||
|
public Collection<ReplacementEffect> getReplacements() {
|
||||||
|
List<ReplacementEffect> result = Lists.newArrayList();
|
||||||
|
for (KeywordInterface k : this.keywords.getValues()) {
|
||||||
|
result.addAll(k.getReplacements());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @return the abilities
|
||||||
|
*/
|
||||||
|
public Collection<SpellAbility> getAbilities() {
|
||||||
|
List<SpellAbility> result = Lists.newArrayList();
|
||||||
|
for (KeywordInterface k : this.keywords.getValues()) {
|
||||||
|
result.addAll(k.getAbilities());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @return the staticAbilities
|
||||||
|
*/
|
||||||
|
public Collection<StaticAbility> getStaticAbilities() {
|
||||||
|
List<StaticAbility> result = Lists.newArrayList();
|
||||||
|
for (KeywordInterface k : this.keywords.getValues()) {
|
||||||
|
result.addAll(k.getStaticAbilities());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see java.lang.Object#toString()
|
* @see java.lang.Object#toString()
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ package forge.game.player;
|
|||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
import com.google.common.base.Predicates;
|
import com.google.common.base.Predicates;
|
||||||
import com.google.common.collect.*;
|
import com.google.common.collect.*;
|
||||||
|
|
||||||
|
import forge.ImageKeys;
|
||||||
import forge.LobbyPlayer;
|
import forge.LobbyPlayer;
|
||||||
import forge.card.MagicColor;
|
import forge.card.MagicColor;
|
||||||
import forge.game.*;
|
import forge.game.*;
|
||||||
@@ -41,7 +43,6 @@ import forge.game.phase.PhaseType;
|
|||||||
import forge.game.replacement.ReplacementHandler;
|
import forge.game.replacement.ReplacementHandler;
|
||||||
import forge.game.replacement.ReplacementResult;
|
import forge.game.replacement.ReplacementResult;
|
||||||
import forge.game.replacement.ReplacementType;
|
import forge.game.replacement.ReplacementType;
|
||||||
import forge.game.spellability.AbilityActivated;
|
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.staticability.StaticAbility;
|
import forge.game.staticability.StaticAbility;
|
||||||
import forge.game.trigger.Trigger;
|
import forge.game.trigger.Trigger;
|
||||||
@@ -155,6 +156,7 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
|
|
||||||
private Card monarchEffect = null;
|
private Card monarchEffect = null;
|
||||||
private Card blessingEffect = null;
|
private Card blessingEffect = null;
|
||||||
|
private Card keywordEffect = null;
|
||||||
|
|
||||||
private final AchievementTracker achievementTracker = new AchievementTracker();
|
private final AchievementTracker achievementTracker = new AchievementTracker();
|
||||||
private final PlayerView view;
|
private final PlayerView view;
|
||||||
@@ -1025,23 +1027,25 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
|
|
||||||
public final void addChangedKeywords(final List<String> addKeywords, final List<String> removeKeywords, final Long timestamp) {
|
public final void addChangedKeywords(final List<String> addKeywords, final List<String> removeKeywords, final Long timestamp) {
|
||||||
// if the key already exists - merge entries
|
// if the key already exists - merge entries
|
||||||
|
KeywordsChange cks = null;
|
||||||
if (changedKeywords.containsKey(timestamp)) {
|
if (changedKeywords.containsKey(timestamp)) {
|
||||||
final KeywordsChange cks = changedKeywords.get(timestamp);
|
getKeywordCard().removeChangedCardTraits(timestamp);
|
||||||
|
|
||||||
changedKeywords.put(timestamp, cks.merge(addKeywords, removeKeywords,
|
cks = changedKeywords.get(timestamp).merge(addKeywords, removeKeywords, false, false);
|
||||||
cks.isRemoveAllKeywords(), cks.isRemoveIntrinsicKeywords()));
|
} else {
|
||||||
updateKeywords();
|
cks = new KeywordsChange(addKeywords, removeKeywords, false, false);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
cks.addKeywordsToPlayer(this);
|
||||||
changedKeywords.put(timestamp, new KeywordsChange(addKeywords, removeKeywords, false, false));
|
getKeywordCard().addChangedCardTraits(cks.getAbilities(), null, cks.getTriggers(), cks.getReplacements(), cks.getStaticAbilities(), false, false, false, timestamp);
|
||||||
|
changedKeywords.put(timestamp, cks);
|
||||||
updateKeywords();
|
updateKeywords();
|
||||||
game.fireEvent(new GameEventPlayerStatsChanged(this));
|
game.fireEvent(new GameEventPlayerStatsChanged(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
public final KeywordsChange removeChangedKeywords(final Long timestamp) {
|
public final KeywordsChange removeChangedKeywords(final Long timestamp) {
|
||||||
KeywordsChange change = changedKeywords.remove(Long.valueOf(timestamp));
|
KeywordsChange change = changedKeywords.remove(timestamp);
|
||||||
if (change != null) {
|
if (change != null) {
|
||||||
|
getKeywordCard().removeChangedCardTraits(timestamp);
|
||||||
updateKeywords();
|
updateKeywords();
|
||||||
game.fireEvent(new GameEventPlayerStatsChanged(this));
|
game.fireEvent(new GameEventPlayerStatsChanged(this));
|
||||||
}
|
}
|
||||||
@@ -1100,6 +1104,7 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
for (final Entry<Long, KeywordsChange> ck : ImmutableList.copyOf(changedKeywords.entrySet())) {
|
for (final Entry<Long, KeywordsChange> ck : ImmutableList.copyOf(changedKeywords.entrySet())) {
|
||||||
if (ck.getValue().isEmpty() && changedKeywords.remove(ck.getKey()) != null) {
|
if (ck.getValue().isEmpty() && changedKeywords.remove(ck.getKey()) != null) {
|
||||||
keywordRemoved = true;
|
keywordRemoved = true;
|
||||||
|
getKeywordCard().removeChangedCardTraits(ck.getKey());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1178,33 +1183,17 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final boolean canBeTargetedBy(final SpellAbility sa) {
|
public final boolean canBeTargetedBy(final SpellAbility sa) {
|
||||||
if (hasKeyword("Shroud")) {
|
|
||||||
return false;
|
// CantTarget static abilities
|
||||||
}
|
for (final Card ca : getGame().getCardsIn(ZoneType.listValueOf("Battlefield,Command"))) {
|
||||||
if (hasKeyword("Hexproof")) {
|
for (final StaticAbility stAb : ca.getStaticAbilities()) {
|
||||||
final Player a = sa.getActivatingPlayer();
|
if (stAb.applyAbility("CantTarget", this, sa)) {
|
||||||
if (isOpponentOf(a)) {
|
|
||||||
boolean cancelHexproof = false;
|
|
||||||
for (String k : a.getKeywords()) {
|
|
||||||
if (k.startsWith("IgnoreHexproof")) {
|
|
||||||
String[] m = k.split(":");
|
|
||||||
if (isValid(m[1].split(","), a, sa.getHostCard(), sa)) {
|
|
||||||
cancelHexproof = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!cancelHexproof) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasProtectionFrom(sa.getHostCard())) {
|
return !hasProtectionFrom(sa.getHostCard());
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (!hasKeyword("You can't be the targets of spells or activated abilities") || (!sa.isSpell() && (!(sa instanceof AbilityActivated))));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -2971,4 +2960,26 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
|| !hasKeyword("Spells and abilities you control can't cause you to search your library.");
|
|| !hasKeyword("Spells and abilities you control can't cause you to search your library.");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Card getKeywordCard() {
|
||||||
|
if (keywordEffect != null) {
|
||||||
|
return keywordEffect;
|
||||||
|
}
|
||||||
|
|
||||||
|
final PlayerZone com = getZone(ZoneType.Command);
|
||||||
|
|
||||||
|
keywordEffect = new Card(game.nextCardId(), null, false, game);
|
||||||
|
keywordEffect.setImmutable(true);
|
||||||
|
keywordEffect.setOwner(this);
|
||||||
|
keywordEffect.setName("Keyword Effects");
|
||||||
|
keywordEffect.setImageKey(ImageKeys.HIDDEN_CARD);
|
||||||
|
keywordEffect.addType("Effect");
|
||||||
|
|
||||||
|
keywordEffect.updateStateForView();
|
||||||
|
|
||||||
|
com.add(keywordEffect);
|
||||||
|
|
||||||
|
this.updateZoneForView(com);
|
||||||
|
return keywordEffect;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package forge.game.player;
|
||||||
|
|
||||||
|
import forge.game.card.Card;
|
||||||
|
import forge.game.keyword.KeywordInterface;
|
||||||
|
import forge.game.staticability.StaticAbility;
|
||||||
|
|
||||||
|
public class PlayerFactoryUtil {
|
||||||
|
|
||||||
|
public static void addStaticAbility(final KeywordInterface inst, final Player player) {
|
||||||
|
final Card card = player.getKeywordCard();
|
||||||
|
String keyword = inst.getOriginal();
|
||||||
|
String effect = null;
|
||||||
|
if (keyword.startsWith("Hexproof")) {
|
||||||
|
final StringBuilder sbDesc = new StringBuilder("Hexproof");
|
||||||
|
final StringBuilder sbValid = new StringBuilder();
|
||||||
|
|
||||||
|
if (!keyword.equals("Hexproof")) {
|
||||||
|
final String[] k = keyword.split(":");
|
||||||
|
|
||||||
|
sbDesc.append(" from ").append(k[2]);
|
||||||
|
sbValid.append("| ValidSource$ ").append(k[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
effect = "Mode$ CantTarget | Hexproof$ True | ValidPlayer$ Player.You | Secondary$ True "
|
||||||
|
+ sbValid.toString() + " | Activator$ Opponent | EffectZone$ Command | Description$ "
|
||||||
|
+ sbDesc.toString() + " (" + inst.getReminderText() + ")";
|
||||||
|
} else if (keyword.equals("Shroud")) {
|
||||||
|
effect = "Mode$ CantTarget | Shroud$ True | ValidPlayer$ Player.You | Secondary$ True "
|
||||||
|
+ "| EffectZone$ Command | Description$ Shroud (" + inst.getReminderText() + ")";
|
||||||
|
}
|
||||||
|
if (effect != null) {
|
||||||
|
StaticAbility st = new StaticAbility(effect, card);
|
||||||
|
st.setIntrinsic(false);
|
||||||
|
inst.addStaticAbility(st);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addTriggerAbility(final KeywordInterface inst, Player player) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addReplacementEffect(final KeywordInterface inst, Player player) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addSpellAbility(final KeywordInterface inst, Player player) {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -391,6 +391,23 @@ public class StaticAbility extends CardTraitBase implements IIdentifiable, Clone
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final boolean applyAbility(final String mode, final Player player, final SpellAbility spellAbility) {
|
||||||
|
// don't apply the ability if it hasn't got the right mode
|
||||||
|
if (!getParam("Mode").equals(mode)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.isSuppressed() || !this.checkConditions()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode.equals("CantTarget")) {
|
||||||
|
return StaticAbilityCantTarget.applyCantTargetAbility(this, player, spellAbility);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public final boolean applyAbility(String mode, Card card, CounterType type) {
|
public final boolean applyAbility(String mode, Card card, CounterType type) {
|
||||||
|
|
||||||
// don't apply the ability if it hasn't got the right mode
|
// don't apply the ability if it hasn't got the right mode
|
||||||
|
|||||||
@@ -22,8 +22,6 @@ import forge.game.player.Player;
|
|||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Class StaticAbilityCantTarget.
|
* The Class StaticAbilityCantTarget.
|
||||||
*/
|
*/
|
||||||
@@ -32,7 +30,7 @@ public class StaticAbilityCantTarget {
|
|||||||
/**
|
/**
|
||||||
* Apply can't target ability.
|
* Apply can't target ability.
|
||||||
*
|
*
|
||||||
* @param staticAbility
|
* @param st
|
||||||
* the static ability
|
* the static ability
|
||||||
* @param card
|
* @param card
|
||||||
* the card
|
* the card
|
||||||
@@ -40,16 +38,19 @@ public class StaticAbilityCantTarget {
|
|||||||
* the spell/ability
|
* the spell/ability
|
||||||
* @return true, if successful
|
* @return true, if successful
|
||||||
*/
|
*/
|
||||||
public static boolean applyCantTargetAbility(final StaticAbility staticAbility, final Card card,
|
public static boolean applyCantTargetAbility(final StaticAbility st, final Card card,
|
||||||
final SpellAbility spellAbility) {
|
final SpellAbility spellAbility) {
|
||||||
final Map<String, String> params = staticAbility.getMapParams();
|
final Card hostCard = st.getHostCard();
|
||||||
final Card hostCard = staticAbility.getHostCard();
|
|
||||||
final Card source = spellAbility.getHostCard();
|
final Card source = spellAbility.getHostCard();
|
||||||
final Player activator = spellAbility.getActivatingPlayer();
|
final Player activator = spellAbility.getActivatingPlayer();
|
||||||
|
|
||||||
if (params.containsKey("AffectedZone")) {
|
if (st.hasParam("ValidPlayer")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (st.hasParam("AffectedZone")) {
|
||||||
boolean inZone = false;
|
boolean inZone = false;
|
||||||
for (final ZoneType zt : ZoneType.listValueOf(params.get("AffectedZone"))) {
|
for (final ZoneType zt : ZoneType.listValueOf(st.getParam("AffectedZone"))) {
|
||||||
if (card.isInZone(zt)) {
|
if (card.isInZone(zt)) {
|
||||||
inZone = true;
|
inZone = true;
|
||||||
break;
|
break;
|
||||||
@@ -65,38 +66,14 @@ public class StaticAbilityCantTarget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params.containsKey("ValidSA")
|
|
||||||
&& !spellAbility.isValid(params.get("ValidSA").split(","), hostCard.getController(), hostCard, spellAbility)) {
|
if (st.hasParam("ValidCard")
|
||||||
|
&& !card.isValid(st.getParam("ValidCard").split(","), hostCard.getController(), hostCard, null)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params.containsKey("ValidCard")
|
|
||||||
&& !card.isValid(params.get("ValidCard").split(","), hostCard.getController(), hostCard, null)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (params.containsKey("ValidSource")
|
if (st.hasParam("Hexproof") && (activator != null)) {
|
||||||
&& !source.isValid(params.get("ValidSource").split(","), hostCard.getController(), hostCard, null)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (params.containsKey("Activator") && (activator != null)
|
|
||||||
&& !activator.isValid(params.get("Activator"), hostCard.getController(), hostCard, spellAbility)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (spellAbility.getParam("ValidTgts")!=null &&
|
|
||||||
(params.containsKey("SourceCanOnlyTarget")
|
|
||||||
&& (!spellAbility.getParam("ValidTgts").contains(params.get("SourceCanOnlyTarget"))
|
|
||||||
|| spellAbility.getParam("ValidTgts").contains(","))
|
|
||||||
|| spellAbility.getParam("ValidTgts").contains("non" + params.get("SourceCanOnlyTarget")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (params.containsKey("Hexproof") && (activator != null)) {
|
|
||||||
for (String k : activator.getKeywords()) {
|
for (String k : activator.getKeywords()) {
|
||||||
if (k.startsWith("IgnoreHexproof")) {
|
if (k.startsWith("IgnoreHexproof")) {
|
||||||
String[] m = k.split(":");
|
String[] m = k.split(":");
|
||||||
@@ -106,8 +83,81 @@ public class StaticAbilityCantTarget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (st.hasParam("Shroud") && (activator != null)) {
|
||||||
|
for (String k : activator.getKeywords()) {
|
||||||
|
if (k.startsWith("IgnoreShroud")) {
|
||||||
|
String[] m = k.split(":");
|
||||||
|
if (card.isValid(m[1].split(","), activator, source, spellAbility)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return common(st, spellAbility);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean applyCantTargetAbility(final StaticAbility st, final Player player,
|
||||||
|
final SpellAbility spellAbility) {
|
||||||
|
final Card hostCard = st.getHostCard();
|
||||||
|
final Card source = spellAbility.getHostCard();
|
||||||
|
final Player activator = spellAbility.getActivatingPlayer();
|
||||||
|
|
||||||
|
if (st.hasParam("ValidCard") || st.hasParam("AffectedZone")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (st.hasParam("ValidPlayer")
|
||||||
|
&& !player.isValid(st.getParam("ValidPlayer").split(","), hostCard.getController(), hostCard, null)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (st.hasParam("Hexproof") && (activator != null)) {
|
||||||
|
for (String k : activator.getKeywords()) {
|
||||||
|
if (k.startsWith("IgnoreHexproof")) {
|
||||||
|
String[] m = k.split(":");
|
||||||
|
if (player.isValid(m[1].split(","), activator, source, spellAbility)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected static boolean common(final StaticAbility st, final SpellAbility spellAbility) {
|
||||||
|
final Card hostCard = st.getHostCard();
|
||||||
|
final Card source = spellAbility.getHostCard();
|
||||||
|
final Player activator = spellAbility.getActivatingPlayer();
|
||||||
|
|
||||||
|
if (st.hasParam("ValidSA")
|
||||||
|
&& !spellAbility.isValid(st.getParam("ValidSA").split(","), hostCard.getController(), hostCard, spellAbility)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (st.hasParam("ValidSource")
|
||||||
|
&& !source.isValid(st.getParam("ValidSource").split(","), hostCard.getController(), hostCard, null)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (st.hasParam("Activator") && (activator != null)
|
||||||
|
&& !activator.isValid(st.getParam("Activator"), hostCard.getController(), hostCard, spellAbility)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spellAbility.hasParam("ValidTgts") &&
|
||||||
|
(st.hasParam("SourceCanOnlyTarget")
|
||||||
|
&& (!spellAbility.getParam("ValidTgts").contains(st.getParam("SourceCanOnlyTarget"))
|
||||||
|
|| spellAbility.getParam("ValidTgts").contains(","))
|
||||||
|
|| spellAbility.getParam("ValidTgts").contains("non" + st.getParam("SourceCanOnlyTarget")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ public enum TrackableProperty {
|
|||||||
//Card State
|
//Card State
|
||||||
Name(TrackableTypes.StringType),
|
Name(TrackableTypes.StringType),
|
||||||
Colors(TrackableTypes.ColorSetType),
|
Colors(TrackableTypes.ColorSetType),
|
||||||
|
OriginalColors(TrackableTypes.ColorSetType),
|
||||||
ImageKey(TrackableTypes.StringType),
|
ImageKey(TrackableTypes.StringType),
|
||||||
Type(TrackableTypes.CardTypeViewType),
|
Type(TrackableTypes.CardTypeViewType),
|
||||||
ManaCost(TrackableTypes.ManaCostType),
|
ManaCost(TrackableTypes.ManaCostType),
|
||||||
@@ -80,6 +81,7 @@ public enum TrackableProperty {
|
|||||||
Toughness(TrackableTypes.IntegerType),
|
Toughness(TrackableTypes.IntegerType),
|
||||||
Loyalty(TrackableTypes.StringType),
|
Loyalty(TrackableTypes.StringType),
|
||||||
ChangedColorWords(TrackableTypes.StringMapType),
|
ChangedColorWords(TrackableTypes.StringMapType),
|
||||||
|
HasChangedColors(TrackableTypes.BooleanType),
|
||||||
ChangedTypes(TrackableTypes.StringMapType),
|
ChangedTypes(TrackableTypes.StringMapType),
|
||||||
|
|
||||||
KeywordKey(TrackableTypes.StringType),
|
KeywordKey(TrackableTypes.StringType),
|
||||||
|
|||||||
BIN
forge-gui-android/libs/arm64-v8a/libgdx-freetype.so
Normal file
BIN
forge-gui-android/libs/arm64-v8a/libgdx-freetype.so
Normal file
Binary file not shown.
BIN
forge-gui-android/libs/arm64-v8a/libgdx.so
Normal file
BIN
forge-gui-android/libs/arm64-v8a/libgdx.so
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
forge-gui-android/libs/x86_64/libgdx-freetype.so
Normal file
BIN
forge-gui-android/libs/x86_64/libgdx-freetype.so
Normal file
Binary file not shown.
BIN
forge-gui-android/libs/x86_64/libgdx.so
Normal file
BIN
forge-gui-android/libs/x86_64/libgdx.so
Normal file
Binary file not shown.
@@ -1850,7 +1850,7 @@ public class GameSimulatorTest extends SimulationTestCase {
|
|||||||
Card simSpark = (Card)sim.getGameCopier().find(sparkDouble);
|
Card simSpark = (Card)sim.getGameCopier().find(sparkDouble);
|
||||||
|
|
||||||
assertNotNull(simSpark);
|
assertNotNull(simSpark);
|
||||||
assertTrue(simSpark.getZone().is(ZoneType.Battlefield));
|
assertTrue(simSpark.isInZone(ZoneType.Battlefield));
|
||||||
assertEquals(1, simSpark.getCounters(CounterType.P1P1));
|
assertEquals(1, simSpark.getCounters(CounterType.P1P1));
|
||||||
assertEquals(5, simSpark.getCounters(CounterType.LOYALTY));
|
assertEquals(5, simSpark.getCounters(CounterType.LOYALTY));
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -63,6 +63,9 @@ public class Forge implements ApplicationListener {
|
|||||||
public static float heigtModifier = 0.0f;
|
public static float heigtModifier = 0.0f;
|
||||||
private static boolean isloadingaMatch = false;
|
private static boolean isloadingaMatch = false;
|
||||||
public static boolean showFPS = false;
|
public static boolean showFPS = false;
|
||||||
|
public static boolean enableUIMask = false;
|
||||||
|
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) {
|
||||||
if (GuiBase.getInterface() == null) {
|
if (GuiBase.getInterface() == null) {
|
||||||
@@ -105,8 +108,10 @@ public class Forge implements ApplicationListener {
|
|||||||
FSkin.loadLight(skinName, splashScreen);
|
FSkin.loadLight(skinName, splashScreen);
|
||||||
|
|
||||||
textureFiltering = prefs.getPrefBoolean(FPref.UI_LIBGDX_TEXTURE_FILTERING);
|
textureFiltering = prefs.getPrefBoolean(FPref.UI_LIBGDX_TEXTURE_FILTERING);
|
||||||
|
|
||||||
showFPS = prefs.getPrefBoolean(FPref.UI_SHOW_FPS);
|
showFPS = prefs.getPrefBoolean(FPref.UI_SHOW_FPS);
|
||||||
|
enableUIMask = prefs.getPrefBoolean(FPref.UI_ENABLE_BORDER_MASKING);
|
||||||
|
enablePreloadExtendedArt = prefs.getPrefBoolean(FPref.UI_ENABLE_PRELOAD_EXTENDED_ART);
|
||||||
|
locale = prefs.getPref(FPref.UI_LANGUAGE);
|
||||||
|
|
||||||
final Localizer localizer = Localizer.getInstance();
|
final Localizer localizer = Localizer.getInstance();
|
||||||
|
|
||||||
@@ -121,15 +126,15 @@ public class Forge implements ApplicationListener {
|
|||||||
FModel.initialize(splashScreen.getProgressBar(), null);
|
FModel.initialize(splashScreen.getProgressBar(), null);
|
||||||
|
|
||||||
splashScreen.getProgressBar().setDescription(localizer.getMessage("lblLoadingFonts"));
|
splashScreen.getProgressBar().setDescription(localizer.getMessage("lblLoadingFonts"));
|
||||||
FSkinFont.preloadAll(prefs.getPref(FPref.UI_LANGUAGE));
|
FSkinFont.preloadAll(locale);
|
||||||
|
|
||||||
splashScreen.getProgressBar().setDescription(localizer.getMessage("lblLoadingCardTranslations"));
|
splashScreen.getProgressBar().setDescription(localizer.getMessage("lblLoadingCardTranslations"));
|
||||||
CardTranslation.preloadTranslation(prefs.getPref(FPref.UI_LANGUAGE));
|
CardTranslation.preloadTranslation(locale);
|
||||||
|
|
||||||
splashScreen.getProgressBar().setDescription(localizer.getMessage("lblFinishingStartup"));
|
splashScreen.getProgressBar().setDescription(localizer.getMessage("lblFinishingStartup"));
|
||||||
|
|
||||||
//add reminder to preload
|
//add reminder to preload
|
||||||
if (prefs.getPrefBoolean(FPref.UI_ENABLE_PRELOAD_EXTENDED_ART))
|
if (enablePreloadExtendedArt)
|
||||||
splashScreen.getProgressBar().setDescription("Preload Extended Art...");
|
splashScreen.getProgressBar().setDescription("Preload Extended Art...");
|
||||||
Gdx.app.postRunnable(new Runnable() {
|
Gdx.app.postRunnable(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
@@ -145,7 +150,7 @@ public class Forge implements ApplicationListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void preloadExtendedArt() {
|
private void preloadExtendedArt() {
|
||||||
if (!FModel.getPreferences().getPrefBoolean(FPref.UI_ENABLE_PRELOAD_EXTENDED_ART))
|
if (!enablePreloadExtendedArt)
|
||||||
return;
|
return;
|
||||||
List<String> keys = new ArrayList<>();
|
List<String> keys = new ArrayList<>();
|
||||||
File[] directories = new File(ForgeConstants.CACHE_CARD_PICS_DIR).listFiles(new FileFilter() {
|
File[] directories = new File(ForgeConstants.CACHE_CARD_PICS_DIR).listFiles(new FileFilter() {
|
||||||
|
|||||||
@@ -563,6 +563,18 @@ public class Graphics {
|
|||||||
}
|
}
|
||||||
public float getfloatAlphaComposite() { return alphaComposite; }
|
public float getfloatAlphaComposite() { return alphaComposite; }
|
||||||
|
|
||||||
|
|
||||||
|
public void drawBorderImage(FImage image, Color color, float x, float y, float w, float h, boolean tint) {
|
||||||
|
image.draw(this, x, y, w, h);
|
||||||
|
if(tint){
|
||||||
|
float oldalpha = alphaComposite;
|
||||||
|
setAlphaComposite(0.8f);
|
||||||
|
drawRoundRect(2f, Color.WHITE, x, y, w, h, (h-w)/12);
|
||||||
|
setAlphaComposite(1f);
|
||||||
|
fillRoundRect(color, x, y, w, h, (h-w)/12);
|
||||||
|
setAlphaComposite(oldalpha);
|
||||||
|
}
|
||||||
|
}
|
||||||
public void drawImage(FImage image, float x, float y, float w, float h) {
|
public void drawImage(FImage image, float x, float y, float w, float h) {
|
||||||
drawImage(image, x, y, w, h, false);
|
drawImage(image, x, y, w, h, false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator.FreeTypeFont
|
|||||||
import com.badlogic.gdx.graphics.glutils.PixmapTextureData;
|
import com.badlogic.gdx.graphics.glutils.PixmapTextureData;
|
||||||
import com.badlogic.gdx.utils.Array;
|
import com.badlogic.gdx.utils.Array;
|
||||||
import forge.FThreads;
|
import forge.FThreads;
|
||||||
|
import forge.Forge;
|
||||||
import forge.properties.ForgeConstants;
|
import forge.properties.ForgeConstants;
|
||||||
import forge.util.FileUtil;
|
import forge.util.FileUtil;
|
||||||
import forge.util.TextBounds;
|
import forge.util.TextBounds;
|
||||||
@@ -26,8 +27,10 @@ import java.util.Map;
|
|||||||
|
|
||||||
public class FSkinFont {
|
public class FSkinFont {
|
||||||
private static final int MIN_FONT_SIZE = 8;
|
private static final int MIN_FONT_SIZE = 8;
|
||||||
private static final int MAX_FONT_SIZE = 72;
|
private static int MAX_FONT_SIZE = 72;
|
||||||
private static final int MAX_FONT_SIZE_ZH_CN = 28;
|
|
||||||
|
private static final int MAX_FONT_SIZE_LESS_GLYPHS = 72;
|
||||||
|
private static final int MAX_FONT_SIZE_MANY_GLYPHS = 36;
|
||||||
|
|
||||||
private static final String TTF_FILE = "font1.ttf";
|
private static final String TTF_FILE = "font1.ttf";
|
||||||
private static final Map<Integer, FSkinFont> fonts = new HashMap<>();
|
private static final Map<Integer, FSkinFont> fonts = new HashMap<>();
|
||||||
@@ -60,8 +63,9 @@ public class FSkinFont {
|
|||||||
|
|
||||||
//pre-load all supported font sizes
|
//pre-load all supported font sizes
|
||||||
public static void preloadAll(String language) {
|
public static void preloadAll(String language) {
|
||||||
int maxfontSize = (language.equals("zh-CN")) ? MAX_FONT_SIZE_ZH_CN : MAX_FONT_SIZE;
|
//todo:really check the language glyph is a lot
|
||||||
for (int size = MIN_FONT_SIZE; size <= maxfontSize; size++) {
|
MAX_FONT_SIZE = (language.equals("zh-CN")) ? MAX_FONT_SIZE_MANY_GLYPHS : MAX_FONT_SIZE_LESS_GLYPHS;
|
||||||
|
for (int size = MIN_FONT_SIZE; size <= MAX_FONT_SIZE; size++) {
|
||||||
_get(size);
|
_get(size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -357,6 +361,7 @@ public class FSkinFont {
|
|||||||
//generate from zh-CN.properties,and cardnames-zh-CN.txt
|
//generate from zh-CN.properties,and cardnames-zh-CN.txt
|
||||||
//forge generate 3000+ characters cache need Take some time(MIN_FONT_SIZE - MAX_FONT_SIZE all size)
|
//forge generate 3000+ characters cache need Take some time(MIN_FONT_SIZE - MAX_FONT_SIZE all size)
|
||||||
//maybe using libgdx-hiero generate font cache is better
|
//maybe using libgdx-hiero generate font cache is better
|
||||||
|
if (Forge.locale.equals("zh-CN"))
|
||||||
chars += "●、。「」『』一丁七万三上下不与丑专且世丘业丛东丝两严丧个中"
|
chars += "●、。「」『』一丁七万三上下不与丑专且世丘业丛东丝两严丧个中"
|
||||||
+ "丰临丸丹为主丽举乃久么义之乌乍乐乔乖乘乙九也乡书乱乳乾了予争"
|
+ "丰临丸丹为主丽举乃久么义之乌乍乐乔乖乘乙九也乡书乱乳乾了予争"
|
||||||
+ "事二于云互五井亘亚些亡交亥亦产享京亮亲亵人亿什仁仅仆仇今介仍"
|
+ "事二于云互五井亘亚些亡交亥亦产享京亮亲亵人亿什仁仅仆仇今介仍"
|
||||||
|
|||||||
@@ -231,7 +231,9 @@ public class ImageCache {
|
|||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
public static FImage getBorderImage(CardView c) {
|
public static FImage getBorderImage(CardView c, boolean canshow) {
|
||||||
|
if (!canshow)
|
||||||
|
return BlackBorder;
|
||||||
if (isWhiteBordered(c))
|
if (isWhiteBordered(c))
|
||||||
return WhiteBorder;
|
return WhiteBorder;
|
||||||
return BlackBorder;
|
return BlackBorder;
|
||||||
@@ -241,4 +243,29 @@ public class ImageCache {
|
|||||||
return WhiteBorder;
|
return WhiteBorder;
|
||||||
return BlackBorder;
|
return BlackBorder;
|
||||||
}
|
}
|
||||||
|
public static Color getTint(CardView c) {
|
||||||
|
if (c == null)
|
||||||
|
return Color.CLEAR;
|
||||||
|
if (c.isFaceDown())
|
||||||
|
return Color.CLEAR;
|
||||||
|
|
||||||
|
CardView.CardStateView state = c.getCurrentState();
|
||||||
|
if (state.getColors().isColorless()) //Moonlace -> target spell or permanent becomes colorless.
|
||||||
|
return Color.valueOf("#A0A6A4");
|
||||||
|
else if (state.getColors().isMonoColor()) {
|
||||||
|
if (state.getColors().hasBlack())
|
||||||
|
return Color.valueOf("#48494a");
|
||||||
|
else if (state.getColors().hasBlue())
|
||||||
|
return Color.valueOf("#62b5f8");
|
||||||
|
else if (state.getColors().hasRed())
|
||||||
|
return Color.valueOf("#f6532d");
|
||||||
|
else if (state.getColors().hasGreen())
|
||||||
|
return Color.valueOf("#66cb35");
|
||||||
|
else if (state.getColors().hasWhite())
|
||||||
|
return Color.valueOf("#EEEBE1");
|
||||||
|
}
|
||||||
|
else if (state.getColors().isMulticolor())
|
||||||
|
return Color.valueOf("#F9E084");
|
||||||
|
return Color.CLEAR;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,12 +8,14 @@ import com.badlogic.gdx.graphics.Pixmap;
|
|||||||
import com.badlogic.gdx.graphics.Texture;
|
import com.badlogic.gdx.graphics.Texture;
|
||||||
import com.badlogic.gdx.graphics.TextureData;
|
import com.badlogic.gdx.graphics.TextureData;
|
||||||
import com.badlogic.gdx.graphics.glutils.PixmapTextureData;
|
import com.badlogic.gdx.graphics.glutils.PixmapTextureData;
|
||||||
|
import forge.FThreads;
|
||||||
import org.cache2k.integration.CacheLoader;
|
import org.cache2k.integration.CacheLoader;
|
||||||
|
|
||||||
import forge.Forge;
|
import forge.Forge;
|
||||||
import forge.ImageKeys;
|
import forge.ImageKeys;
|
||||||
|
|
||||||
final class ImageLoader extends CacheLoader<String, Texture> {
|
final class ImageLoader extends CacheLoader<String, Texture> {
|
||||||
|
Texture n;
|
||||||
@Override
|
@Override
|
||||||
public Texture load(String key) {
|
public Texture load(String key) {
|
||||||
boolean extendedArt = false;
|
boolean extendedArt = false;
|
||||||
@@ -44,6 +46,9 @@ final class ImageLoader extends CacheLoader<String, Texture> {
|
|||||||
public Texture generateTexture(FileHandle fh, Texture t, boolean textureFilter) {
|
public Texture generateTexture(FileHandle fh, Texture t, boolean textureFilter) {
|
||||||
if (t == null || fh == null)
|
if (t == null || fh == null)
|
||||||
return t;
|
return t;
|
||||||
|
FThreads.invokeInEdtNowOrLater(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
Pixmap pImage = new Pixmap(fh);
|
Pixmap pImage = new Pixmap(fh);
|
||||||
int w = pImage.getWidth();
|
int w = pImage.getWidth();
|
||||||
int h = pImage.getHeight();
|
int h = pImage.getHeight();
|
||||||
@@ -55,12 +60,14 @@ final class ImageLoader extends CacheLoader<String, Texture> {
|
|||||||
Pixmap.Format.RGBA8888,
|
Pixmap.Format.RGBA8888,
|
||||||
textureFilter, //use mipmaps
|
textureFilter, //use mipmaps
|
||||||
false, true);
|
false, true);
|
||||||
t = new Texture(textureData);
|
n = new Texture(textureData);
|
||||||
if (textureFilter)
|
if (textureFilter)
|
||||||
t.setFilter(Texture.TextureFilter.MipMapLinearLinear, Texture.TextureFilter.Linear);
|
n.setFilter(Texture.TextureFilter.MipMapLinearLinear, Texture.TextureFilter.Linear);
|
||||||
pImage.dispose();
|
pImage.dispose();
|
||||||
pMask.dispose();
|
pMask.dispose();
|
||||||
return t;
|
}
|
||||||
|
});
|
||||||
|
return n;
|
||||||
}
|
}
|
||||||
public Pixmap createRoundedRectangle(int width, int height, int cornerRadius, Color color) {
|
public Pixmap createRoundedRectangle(int width, int height, int cornerRadius, Color color) {
|
||||||
Pixmap pixmap = new Pixmap(width, height, Pixmap.Format.RGBA8888);
|
Pixmap pixmap = new Pixmap(width, height, Pixmap.Format.RGBA8888);
|
||||||
|
|||||||
@@ -2,14 +2,13 @@ package forge.card;
|
|||||||
|
|
||||||
import com.badlogic.gdx.graphics.Texture;
|
import com.badlogic.gdx.graphics.Texture;
|
||||||
|
|
||||||
|
import forge.Forge;
|
||||||
import forge.Graphics;
|
import forge.Graphics;
|
||||||
import forge.assets.FImage;
|
import forge.assets.FImage;
|
||||||
import forge.assets.ImageCache;
|
import forge.assets.ImageCache;
|
||||||
import forge.card.CardRenderer.CardStackPosition;
|
import forge.card.CardRenderer.CardStackPosition;
|
||||||
import forge.game.card.CardView;
|
import forge.game.card.CardView;
|
||||||
import forge.item.PaperCard;
|
import forge.item.PaperCard;
|
||||||
import forge.model.FModel;
|
|
||||||
import forge.properties.ForgePreferences;
|
|
||||||
import forge.toolbox.FCardPanel;
|
import forge.toolbox.FCardPanel;
|
||||||
|
|
||||||
public class CardImage implements FImage {
|
public class CardImage implements FImage {
|
||||||
@@ -19,9 +18,6 @@ public class CardImage implements FImage {
|
|||||||
public CardImage(PaperCard card0) {
|
public CardImage(PaperCard card0) {
|
||||||
card = card0;
|
card = card0;
|
||||||
}
|
}
|
||||||
private static boolean isPreferenceEnabled(ForgePreferences.FPref preferenceName) {
|
|
||||||
return FModel.getPreferences().getPrefBoolean(preferenceName);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public float getWidth() {
|
public float getWidth() {
|
||||||
@@ -38,11 +34,10 @@ public class CardImage implements FImage {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void draw(Graphics g, float x, float y, float w, float h) {
|
public void draw(Graphics g, float x, float y, float w, float h) {
|
||||||
boolean mask = isPreferenceEnabled(ForgePreferences.FPref.UI_ENABLE_BORDER_MASKING);
|
|
||||||
if (image == null) { //attempt to retrieve card image if needed
|
if (image == null) { //attempt to retrieve card image if needed
|
||||||
image = ImageCache.getImage(card);
|
image = ImageCache.getImage(card);
|
||||||
if (image == null) {
|
if (image == null) {
|
||||||
if (mask) //render this if mask is still loading
|
if (Forge.enableUIMask) //render this if mask is still loading
|
||||||
CardImageRenderer.drawCardImage(g, CardView.getCardForUi(card), false, x, y, w, h, CardStackPosition.Top);
|
CardImageRenderer.drawCardImage(g, CardView.getCardForUi(card), false, x, y, w, h, CardStackPosition.Top);
|
||||||
|
|
||||||
return; //can't draw anything if can't be loaded yet
|
return; //can't draw anything if can't be loaded yet
|
||||||
@@ -53,7 +48,7 @@ public class CardImage implements FImage {
|
|||||||
CardImageRenderer.drawCardImage(g, CardView.getCardForUi(card), false, x, y, w, h, CardStackPosition.Top);
|
CardImageRenderer.drawCardImage(g, CardView.getCardForUi(card), false, x, y, w, h, CardStackPosition.Top);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (mask) {
|
if (Forge.enableUIMask) {
|
||||||
if (ImageCache.isExtendedArt(card))
|
if (ImageCache.isExtendedArt(card))
|
||||||
g.drawImage(image, x, y, w, h);
|
g.drawImage(image, x, y, w, h);
|
||||||
else {
|
else {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import com.badlogic.gdx.graphics.Color;
|
|||||||
import com.badlogic.gdx.graphics.Texture;
|
import com.badlogic.gdx.graphics.Texture;
|
||||||
import com.badlogic.gdx.utils.Align;
|
import com.badlogic.gdx.utils.Align;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import forge.Forge;
|
||||||
import forge.Graphics;
|
import forge.Graphics;
|
||||||
import forge.assets.FBufferedImage;
|
import forge.assets.FBufferedImage;
|
||||||
import forge.assets.FSkinColor;
|
import forge.assets.FSkinColor;
|
||||||
@@ -325,8 +326,6 @@ 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) {
|
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 mask = isPreferenceEnabled(ForgePreferences.FPref.UI_ENABLE_BORDER_MASKING);
|
|
||||||
//this one is currently using the mask, others are cropped and use generated borders from shaperenderer ...
|
|
||||||
final Texture image = ImageCache.getImage(card.getState(altState).getImageKey(MatchController.instance.getLocalPlayers()), true);
|
final Texture image = ImageCache.getImage(card.getState(altState).getImageKey(MatchController.instance.getLocalPlayers()), true);
|
||||||
if (image == null) { //draw details if can't draw zoom
|
if (image == null) { //draw details if can't draw zoom
|
||||||
drawDetails(g, card, gameView, altState, x, y, w, h);
|
drawDetails(g, card, gameView, altState, x, y, w, h);
|
||||||
@@ -355,7 +354,7 @@ public class CardImageRenderer {
|
|||||||
boolean rotateSplit = FModel.getPreferences().getPrefBoolean(ForgePreferences.FPref.UI_ROTATE_SPLIT_CARDS);
|
boolean rotateSplit = FModel.getPreferences().getPrefBoolean(ForgePreferences.FPref.UI_ROTATE_SPLIT_CARDS);
|
||||||
boolean rotatePlane = FModel.getPreferences().getPrefBoolean(ForgePreferences.FPref.UI_ROTATE_PLANE_OR_PHENOMENON);
|
boolean rotatePlane = FModel.getPreferences().getPrefBoolean(ForgePreferences.FPref.UI_ROTATE_PLANE_OR_PHENOMENON);
|
||||||
if (rotatePlane && (card.getCurrentState().isPhenomenon() || card.getCurrentState().isPlane())) {
|
if (rotatePlane && (card.getCurrentState().isPhenomenon() || card.getCurrentState().isPlane())) {
|
||||||
if (mask){
|
if (Forge.enableUIMask){
|
||||||
if (ImageCache.isExtendedArt(card))
|
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);
|
g.drawRotatedImage(image, new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, -90);
|
||||||
else {
|
else {
|
||||||
@@ -371,7 +370,7 @@ public class CardImageRenderer {
|
|||||||
g.drawRotatedImage(image, new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, -90);
|
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() && canLook) {
|
||||||
boolean isAftermath = card.getText().contains("Aftermath") || card.getAlternateState().getOracleText().contains("Aftermath");
|
boolean isAftermath = card.getText().contains("Aftermath") || card.getAlternateState().getOracleText().contains("Aftermath");
|
||||||
if (mask) {
|
if (Forge.enableUIMask) {
|
||||||
if (ImageCache.isExtendedArt(card))
|
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);
|
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 {
|
else {
|
||||||
@@ -387,11 +386,11 @@ public class CardImageRenderer {
|
|||||||
g.drawRotatedImage(image, new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, isAftermath ? 90 : -90);
|
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 {
|
else {
|
||||||
if (mask) {
|
if (Forge.enableUIMask) {
|
||||||
if (ImageCache.isExtendedArt(card))
|
if (ImageCache.isExtendedArt(card))
|
||||||
g.drawImage(image, x, y, w, h);
|
g.drawImage(image, x, y, w, h);
|
||||||
else {
|
else {
|
||||||
g.drawImage(ImageCache.getBorderImage(card), x, y, w, h);
|
g.drawImage(ImageCache.getBorderImage(card, canLook), x, y, w, h);
|
||||||
g.drawImage(ImageCache.croppedBorderImage(image), x + radius / 2.4f, y + radius / 2, w * 0.96f, h * 0.96f);
|
g.drawImage(ImageCache.croppedBorderImage(image), x + radius / 2.4f, y + radius / 2, w * 0.96f, h * 0.96f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -561,8 +560,4 @@ public class CardImageRenderer {
|
|||||||
g.drawRect(BORDER_THICKNESS, Color.BLACK, x, y, w, h);
|
g.drawRect(BORDER_THICKNESS, Color.BLACK, x, y, w, h);
|
||||||
g.drawText(ptText, PT_FONT, Color.BLACK, x, y, w, h, false, Align.center, true);
|
g.drawText(ptText, PT_FONT, Color.BLACK, x, y, w, h, false, Align.center, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isPreferenceEnabled(ForgePreferences.FPref preferenceName) {
|
|
||||||
return FModel.getPreferences().getPrefBoolean(preferenceName);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import com.badlogic.gdx.graphics.glutils.PixmapTextureData;
|
|||||||
import com.badlogic.gdx.utils.Align;
|
import com.badlogic.gdx.utils.Align;
|
||||||
import com.badlogic.gdx.utils.Array;
|
import com.badlogic.gdx.utils.Array;
|
||||||
import forge.CachedCardImage;
|
import forge.CachedCardImage;
|
||||||
|
import forge.Forge;
|
||||||
import forge.FThreads;
|
import forge.FThreads;
|
||||||
import forge.Graphics;
|
import forge.Graphics;
|
||||||
import forge.StaticData;
|
import forge.StaticData;
|
||||||
@@ -120,6 +121,8 @@ public class CardRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static Color getRarityColor(CardRarity rarity) {
|
public static Color getRarityColor(CardRarity rarity) {
|
||||||
|
if (rarity == null)// NPE from Rarity weird...
|
||||||
|
return Color.CLEAR;
|
||||||
switch(rarity) {
|
switch(rarity) {
|
||||||
case Uncommon:
|
case Uncommon:
|
||||||
return fromDetailColor(DetailColors.UNCOMMON);
|
return fromDetailColor(DetailColors.UNCOMMON);
|
||||||
@@ -392,7 +395,6 @@ public class CardRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void drawCard(Graphics g, IPaperCard pc, float x, float y, float w, float h, CardStackPosition pos) {
|
public static void drawCard(Graphics g, IPaperCard pc, float x, float y, float w, float h, CardStackPosition pos) {
|
||||||
boolean mask = isPreferenceEnabled(FPref.UI_ENABLE_BORDER_MASKING);
|
|
||||||
Texture image = new RendererCachedCardImage(pc, false).getImage();
|
Texture image = new RendererCachedCardImage(pc, false).getImage();
|
||||||
float radius = (h - w)/8;
|
float radius = (h - w)/8;
|
||||||
|
|
||||||
@@ -401,7 +403,7 @@ public class CardRenderer {
|
|||||||
CardImageRenderer.drawCardImage(g, CardView.getCardForUi(pc), false, x, y, w, h, pos);
|
CardImageRenderer.drawCardImage(g, CardView.getCardForUi(pc), false, x, y, w, h, pos);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (mask) {
|
if (Forge.enableUIMask) {
|
||||||
if (ImageCache.isExtendedArt(pc))
|
if (ImageCache.isExtendedArt(pc))
|
||||||
g.drawImage(image, x, y, w, h);
|
g.drawImage(image, x, y, w, h);
|
||||||
else {
|
else {
|
||||||
@@ -421,15 +423,13 @@ public class CardRenderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (mask) //render this if mask is still loading
|
if (Forge.enableUIMask) //render this if mask is still loading
|
||||||
CardImageRenderer.drawCardImage(g, CardView.getCardForUi(pc), false, x, y, w, h, pos);
|
CardImageRenderer.drawCardImage(g, CardView.getCardForUi(pc), false, x, y, w, h, pos);
|
||||||
else //draw cards without textures as just a black rectangle
|
else //draw cards without textures as just a black rectangle
|
||||||
g.fillRect(Color.BLACK, x, y, w, h);
|
g.fillRect(Color.BLACK, x, y, w, h);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void drawCard(Graphics g, CardView card, float x, float y, float w, float h, CardStackPosition pos, boolean rotate) {
|
public static void drawCard(Graphics g, CardView card, float x, float y, float w, float h, CardStackPosition pos, boolean rotate) {
|
||||||
boolean mask = isPreferenceEnabled(FPref.UI_ENABLE_BORDER_MASKING);
|
|
||||||
Texture image = new RendererCachedCardImage(card, false).getImage();
|
Texture image = new RendererCachedCardImage(card, false).getImage();
|
||||||
float radius = (h - w)/8;
|
float radius = (h - w)/8;
|
||||||
|
|
||||||
@@ -440,7 +440,7 @@ public class CardRenderer {
|
|||||||
else {
|
else {
|
||||||
if(FModel.getPreferences().getPrefBoolean(ForgePreferences.FPref.UI_ROTATE_PLANE_OR_PHENOMENON)
|
if(FModel.getPreferences().getPrefBoolean(ForgePreferences.FPref.UI_ROTATE_PLANE_OR_PHENOMENON)
|
||||||
&& (card.getCurrentState().isPhenomenon() || card.getCurrentState().isPlane()) && rotate){
|
&& (card.getCurrentState().isPhenomenon() || card.getCurrentState().isPlane()) && rotate){
|
||||||
if (mask) {
|
if (Forge.enableUIMask) {
|
||||||
if (ImageCache.isExtendedArt(card))
|
if (ImageCache.isExtendedArt(card))
|
||||||
g.drawRotatedImage(image, x, y, w, h, x + w / 2, y + h / 2, -90);
|
g.drawRotatedImage(image, x, y, w, h, x + w / 2, y + h / 2, -90);
|
||||||
else {
|
else {
|
||||||
@@ -452,11 +452,12 @@ public class CardRenderer {
|
|||||||
g.drawRotatedImage(image, x, y, w, h, x + w / 2, y + h / 2, -90);
|
g.drawRotatedImage(image, x, y, w, h, x + w / 2, y + h / 2, -90);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (mask) {
|
if (Forge.enableUIMask) {
|
||||||
if (ImageCache.isExtendedArt(card))
|
if (ImageCache.isExtendedArt(card))
|
||||||
g.drawImage(image, x, y, w, h);
|
g.drawImage(image, x, y, w, h);
|
||||||
else {
|
else {
|
||||||
g.drawImage(ImageCache.getBorderImage(card), x, y, w, h);
|
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.drawImage(ImageCache.croppedBorderImage(image), x + radius / 2.4f, y + radius / 2, w * 0.96f, h * 0.96f);
|
g.drawImage(ImageCache.croppedBorderImage(image), x + radius / 2.4f, y + radius / 2, w * 0.96f, h * 0.96f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -467,7 +468,7 @@ public class CardRenderer {
|
|||||||
drawFoilEffect(g, card, x, y, w, h, false);
|
drawFoilEffect(g, card, x, y, w, h, false);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (mask) //render this if mask is still loading
|
if (Forge.enableUIMask) //render this if mask is still loading
|
||||||
CardImageRenderer.drawCardImage(g, card, false, x, y, w, h, pos);
|
CardImageRenderer.drawCardImage(g, card, false, x, y, w, h, pos);
|
||||||
else //draw cards without textures as just a black rectangle
|
else //draw cards without textures as just a black rectangle
|
||||||
g.fillRect(Color.BLACK, x, y, w, h);
|
g.fillRect(Color.BLACK, x, y, w, h);
|
||||||
@@ -475,6 +476,9 @@ public class CardRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void drawCardWithOverlays(Graphics g, CardView card, float x, float y, float w, float h, CardStackPosition pos) {
|
public static void drawCardWithOverlays(Graphics g, CardView card, float x, float y, float w, float h, CardStackPosition pos) {
|
||||||
|
boolean canShow = MatchController.instance.mayView(card);
|
||||||
|
float oldAlpha = g.getfloatAlphaComposite();
|
||||||
|
boolean unselectable = !MatchController.instance.isSelectable(card) && MatchController.instance.isSelecting();
|
||||||
float cx, cy, cw, ch;
|
float cx, cy, cw, ch;
|
||||||
cx = x; cy = y; cw = w; ch = h;
|
cx = x; cy = y; cw = w; ch = h;
|
||||||
drawCard(g, card, x, y, w, h, pos, false);
|
drawCard(g, card, x, y, w, h, pos, false);
|
||||||
@@ -485,10 +489,6 @@ public class CardRenderer {
|
|||||||
w -= 2 * padding;
|
w -= 2 * padding;
|
||||||
h -= 2 * padding;
|
h -= 2 * padding;
|
||||||
|
|
||||||
boolean canShow = MatchController.instance.mayView(card);
|
|
||||||
float oldAlpha = g.getfloatAlphaComposite();
|
|
||||||
boolean unselectable = !MatchController.instance.isSelectable(card) && MatchController.instance.isSelecting();
|
|
||||||
|
|
||||||
// TODO: A hacky workaround is currently used to make the game not leak the color information for Morph cards.
|
// TODO: A hacky workaround is currently used to make the game not leak the color information for Morph cards.
|
||||||
final CardStateView details = card.getCurrentState();
|
final CardStateView details = card.getCurrentState();
|
||||||
final boolean isFaceDown = card.isFaceDown();
|
final boolean isFaceDown = card.isFaceDown();
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package forge.itemmanager.views;
|
package forge.itemmanager.views;
|
||||||
|
|
||||||
|
import forge.Forge;
|
||||||
import forge.Forge.KeyInputAdapter;
|
import forge.Forge.KeyInputAdapter;
|
||||||
import forge.Graphics;
|
import forge.Graphics;
|
||||||
import forge.assets.FImage;
|
import forge.assets.FImage;
|
||||||
@@ -954,7 +955,16 @@ public class ImageView<T extends InventoryItem> extends ItemView<T> {
|
|||||||
final float w = getWidth();
|
final float w = getWidth();
|
||||||
final float h = getHeight();
|
final float h = getHeight();
|
||||||
|
|
||||||
if (selected) {
|
if (selected) { //if round border is enabled, the select highlight is also rounded..
|
||||||
|
if (Forge.enableUIMask) {
|
||||||
|
//fillroundrect has rough/aliased corner
|
||||||
|
g.fillRoundRect(Color.GREEN, x - SEL_BORDER_SIZE, y - SEL_BORDER_SIZE,
|
||||||
|
w + 2 * SEL_BORDER_SIZE, h + 2 * SEL_BORDER_SIZE, (h - w) / 10);
|
||||||
|
//drawroundrect has GL_SMOOTH to `smoothen/faux` the aliased corner
|
||||||
|
g.drawRoundRect(1f, Color.GREEN, x - SEL_BORDER_SIZE, y - SEL_BORDER_SIZE,
|
||||||
|
w + 1.5f * SEL_BORDER_SIZE, h + 1.5f * SEL_BORDER_SIZE, (h - w) / 10);
|
||||||
|
}
|
||||||
|
else //default rectangle highlight
|
||||||
g.fillRect(Color.GREEN, x - SEL_BORDER_SIZE, y - SEL_BORDER_SIZE,
|
g.fillRect(Color.GREEN, x - SEL_BORDER_SIZE, y - SEL_BORDER_SIZE,
|
||||||
w + 2 * SEL_BORDER_SIZE, h + 2 * SEL_BORDER_SIZE);
|
w + 2 * SEL_BORDER_SIZE, h + 2 * SEL_BORDER_SIZE);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -129,6 +129,7 @@ public class VField extends FContainer {
|
|||||||
cardName.equals(c.getCurrentState().getName()) &&
|
cardName.equals(c.getCurrentState().getName()) &&
|
||||||
card.hasSameCounters(c) &&
|
card.hasSameCounters(c) &&
|
||||||
card.getCurrentState().getKeywordKey().equals(c.getCurrentState().getKeywordKey()) &&
|
card.getCurrentState().getKeywordKey().equals(c.getCurrentState().getKeywordKey()) &&
|
||||||
|
card.getCurrentState().getColors() == c.getCurrentState().getColors() &&
|
||||||
card.isSick() == c.isSick() && //don't stack sick tokens on non sick
|
card.isSick() == c.isSick() && //don't stack sick tokens on non sick
|
||||||
card.isToken() == c.isToken()) { //don't stack tokens on top of non-tokens
|
card.isToken() == c.isToken()) { //don't stack tokens on top of non-tokens
|
||||||
CardAreaPanel cPanel = CardAreaPanel.get(c);
|
CardAreaPanel cPanel = CardAreaPanel.get(c);
|
||||||
|
|||||||
@@ -303,12 +303,24 @@ public class SettingsPage extends TabPage<SettingsScreen> {
|
|||||||
4);
|
4);
|
||||||
lstSettings.addItem(new BooleanSetting(FPref.UI_ENABLE_BORDER_MASKING,
|
lstSettings.addItem(new BooleanSetting(FPref.UI_ENABLE_BORDER_MASKING,
|
||||||
"Enable Round Border Mask",
|
"Enable Round Border Mask",
|
||||||
"When enabled, the card corners are rounded (Preferably Card with Full Borders)."),
|
"When enabled, the card corners are rounded (Preferably Card with Full Borders)."){
|
||||||
4);
|
@Override
|
||||||
|
public void select() {
|
||||||
|
super.select();
|
||||||
|
//update
|
||||||
|
Forge.enableUIMask = FModel.getPreferences().getPrefBoolean(FPref.UI_ENABLE_BORDER_MASKING);
|
||||||
|
}
|
||||||
|
},4);
|
||||||
lstSettings.addItem(new BooleanSetting(FPref.UI_ENABLE_PRELOAD_EXTENDED_ART,
|
lstSettings.addItem(new BooleanSetting(FPref.UI_ENABLE_PRELOAD_EXTENDED_ART,
|
||||||
"Preload Extended Art Cards",
|
"Preload Extended Art Cards",
|
||||||
"When enabled, Preloads Extended Art Cards to Cache on Startup."),
|
"When enabled, Preloads Extended Art Cards to Cache on Startup."){
|
||||||
4);
|
@Override
|
||||||
|
public void select() {
|
||||||
|
super.select();
|
||||||
|
//update
|
||||||
|
Forge.enablePreloadExtendedArt = FModel.getPreferences().getPrefBoolean(FPref.UI_ENABLE_PRELOAD_EXTENDED_ART);
|
||||||
|
}
|
||||||
|
},4);
|
||||||
lstSettings.addItem(new BooleanSetting(FPref.UI_SHOW_FPS,
|
lstSettings.addItem(new BooleanSetting(FPref.UI_SHOW_FPS,
|
||||||
"Show FPS Display",
|
"Show FPS Display",
|
||||||
"When enabled, show the FPS Display (Experimental)."){
|
"When enabled, show the FPS Display (Experimental)."){
|
||||||
@@ -319,7 +331,6 @@ public class SettingsPage extends TabPage<SettingsScreen> {
|
|||||||
Forge.showFPS = FModel.getPreferences().getPrefBoolean(FPref.UI_SHOW_FPS);
|
Forge.showFPS = FModel.getPreferences().getPrefBoolean(FPref.UI_SHOW_FPS);
|
||||||
}
|
}
|
||||||
},4);
|
},4);
|
||||||
|
|
||||||
lstSettings.addItem(new CustomSelectSetting(FPref.UI_CARD_COUNTER_DISPLAY_TYPE,
|
lstSettings.addItem(new CustomSelectSetting(FPref.UI_CARD_COUNTER_DISPLAY_TYPE,
|
||||||
localizer.getMessage("cbpCounterDisplayType"),
|
localizer.getMessage("cbpCounterDisplayType"),
|
||||||
localizer.getMessage("nlCounterDisplayType"),
|
localizer.getMessage("nlCounterDisplayType"),
|
||||||
@@ -327,7 +338,6 @@ public class SettingsPage extends TabPage<SettingsScreen> {
|
|||||||
ForgeConstants.CounterDisplayType.TEXT.getName(), ForgeConstants.CounterDisplayType.IMAGE.getName(),
|
ForgeConstants.CounterDisplayType.TEXT.getName(), ForgeConstants.CounterDisplayType.IMAGE.getName(),
|
||||||
ForgeConstants.CounterDisplayType.HYBRID.getName(), ForgeConstants.CounterDisplayType.OLD_WHEN_SMALL.getName()}),
|
ForgeConstants.CounterDisplayType.HYBRID.getName(), ForgeConstants.CounterDisplayType.OLD_WHEN_SMALL.getName()}),
|
||||||
4);
|
4);
|
||||||
|
|
||||||
//Card Overlays
|
//Card Overlays
|
||||||
lstSettings.addItem(new BooleanSetting(FPref.UI_SHOW_CARD_OVERLAYS,
|
lstSettings.addItem(new BooleanSetting(FPref.UI_SHOW_CARD_OVERLAYS,
|
||||||
localizer.getMessage("lblShowCardOverlays"),
|
localizer.getMessage("lblShowCardOverlays"),
|
||||||
@@ -353,7 +363,6 @@ public class SettingsPage extends TabPage<SettingsScreen> {
|
|||||||
localizer.getMessage("lblShowAbilityIconsOverlays"),
|
localizer.getMessage("lblShowAbilityIconsOverlays"),
|
||||||
localizer.getMessage("nlShowAbilityIconsOverlays")),
|
localizer.getMessage("nlShowAbilityIconsOverlays")),
|
||||||
5);
|
5);
|
||||||
|
|
||||||
//Vibration Options
|
//Vibration Options
|
||||||
lstSettings.addItem(new BooleanSetting(FPref.UI_VIBRATE_ON_LIFE_LOSS,
|
lstSettings.addItem(new BooleanSetting(FPref.UI_VIBRATE_ON_LIFE_LOSS,
|
||||||
localizer.getMessage("lblVibrateWhenLosingLife"),
|
localizer.getMessage("lblVibrateWhenLosingLife"),
|
||||||
@@ -363,7 +372,6 @@ public class SettingsPage extends TabPage<SettingsScreen> {
|
|||||||
localizer.getMessage("lblVibrateAfterLongPress"),
|
localizer.getMessage("lblVibrateAfterLongPress"),
|
||||||
localizer.getMessage("nlVibrateAfterLongPress")),
|
localizer.getMessage("nlVibrateAfterLongPress")),
|
||||||
6);
|
6);
|
||||||
|
|
||||||
//Sound Options
|
//Sound Options
|
||||||
lstSettings.addItem(new BooleanSetting(FPref.UI_ENABLE_SOUNDS,
|
lstSettings.addItem(new BooleanSetting(FPref.UI_ENABLE_SOUNDS,
|
||||||
localizer.getMessage("cbEnableSounds"),
|
localizer.getMessage("cbEnableSounds"),
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ ManaCost:4 G G
|
|||||||
Types:Legendary Creature Avatar
|
Types:Legendary Creature Avatar
|
||||||
PT:4/4
|
PT:4/4
|
||||||
K:Shroud
|
K:Shroud
|
||||||
A:AB$ Pump | Cost$ G | ValidTgts$ Player | Defined$ Targeted | TgtPrompt$ Select target player to be able to target Autumn Willow | KW$ Can target CardUIDSource with spells and abilities as though it didn't have shroud. | DefinedKW$ CardUIDSource | StackDescription$ Until end of turn, {p:Targeted} can target CARDNAME as though it didn't have shroud. | SpellDescription$ Until end of turn, Autumn Willow can be the target of spells and abilities controlled by target player as though it didn't have shroud.
|
A:AB$ Pump | Cost$ G | ValidTgts$ Player | Defined$ Targeted | TgtPrompt$ Select target player to be able to target Autumn Willow | KW$ IgnoreShroud:Card.CardUIDSource | DefinedKW$ CardUIDSource | UntilHostLeavesPlayOrEOT$ True | StackDescription$ Until end of turn, {p:Targeted} can target CARDNAME as though it didn't have shroud. | SpellDescription$ Until end of turn, Autumn Willow can be the target of spells and abilities controlled by target player as though it didn't have shroud.
|
||||||
AI:RemoveDeck:All
|
AI:RemoveDeck:All
|
||||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/autumn_willow.jpg
|
SVar:Picture:http://www.wizards.com/global/images/magic/general/autumn_willow.jpg
|
||||||
Oracle:Shroud (This creature can't be the target of spells or abilities.)\n{G}: Until end of turn, Autumn Willow can be the target of spells and abilities controlled by target player as though it didn't have shroud.
|
Oracle:Shroud (This creature can't be the target of spells or abilities.)\n{G}: Until end of turn, Autumn Willow can be the target of spells and abilities controlled by target player as though it didn't have shroud.
|
||||||
|
|||||||
@@ -4,6 +4,6 @@ Types:Sorcery
|
|||||||
A:SP$ Effect | Cost$ 1 W | Name$ Peace Talks Effect | StaticAbilities$ STCantAttack,STCantTarget,STCantTargetPlayer | Duration$ ThisTurnAndNextTurn | SpellDescription$ This turn and next turn, creatures can't attack, and players and permanents can't be the targets of spells or activated abilities.
|
A:SP$ Effect | Cost$ 1 W | Name$ Peace Talks Effect | StaticAbilities$ STCantAttack,STCantTarget,STCantTargetPlayer | Duration$ ThisTurnAndNextTurn | SpellDescription$ This turn and next turn, creatures can't attack, and players and permanents can't be the targets of spells or activated abilities.
|
||||||
SVar:STCantAttack:Mode$ CantAttack | EffectZone$ Command | ValidCard$ Creature | Description$ Creatures can't attack.
|
SVar:STCantAttack:Mode$ CantAttack | EffectZone$ Command | ValidCard$ Creature | Description$ Creatures can't attack.
|
||||||
SVar:STCantTarget:Mode$ CantTarget | ValidCard$ Permanent | EffectZone$ Command | ValidSA$ Spell,Activated | Description$ Permanents can't be the targets of spells or activated abilities.
|
SVar:STCantTarget:Mode$ CantTarget | ValidCard$ Permanent | EffectZone$ Command | ValidSA$ Spell,Activated | Description$ Permanents can't be the targets of spells or activated abilities.
|
||||||
SVar:STCantTargetPlayer:Mode$ Continuous | Affected$ Player | EffectZone$ Command | AddKeyword$ You can't be the targets of spells or activated abilities | Description$ Players can't be the targets of spells or activated abilities.
|
SVar:STCantTargetPlayer:Mode$ CantTarget | ValidPlayer$ Player | EffectZone$ Command | ValidSA$ Spell,Activated | Description$Players can't be the targets of spells or activated abilities.
|
||||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/peace_talks.jpg
|
SVar:Picture:http://www.wizards.com/global/images/magic/general/peace_talks.jpg
|
||||||
Oracle:This turn and next turn, creatures can't attack, and players and permanents can't be the targets of spells or activated abilities.
|
Oracle:This turn and next turn, creatures can't attack, and players and permanents can't be the targets of spells or activated abilities.
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ ManaCost:1 W U
|
|||||||
Types:Enchantment Aura
|
Types:Enchantment Aura
|
||||||
K:Enchant creature
|
K:Enchant creature
|
||||||
A:SP$ Attach | Cost$ 1 W U | ValidTgts$ Creature | AILogic$ Pump
|
A:SP$ Attach | Cost$ 1 W U | ValidTgts$ Creature | AILogic$ Pump
|
||||||
S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddToughness$ 2 | AddHiddenKeyword$ CARDNAME can't be the target of spells. | Description$ Enchanted creature gets +0/+2 and can't be the target of spells.
|
S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddToughness$ 2 | Description$ Enchanted creature gets +0/+2.
|
||||||
|
S:Mode$ CantTarget | ValidCard$ Creature.EnchantedBy | ValidSA$ Spell | Description$ Enchanted creature can't be the target of spells.
|
||||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/spectral_shield.jpg
|
SVar:Picture:http://www.wizards.com/global/images/magic/general/spectral_shield.jpg
|
||||||
Oracle:Enchant creature\nEnchanted creature gets +0/+2 and can't be the target of spells.
|
Oracle:Enchant creature\nEnchanted creature gets +0/+2 and can't be the target of spells.
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
Name:Veil of Summer
|
Name:Veil of Summer
|
||||||
ManaCost:G
|
ManaCost:G
|
||||||
Types:Instant
|
Types:Instant
|
||||||
A:SP$ Draw | Cost$ G | Defined$ You | ConditionCheckSVar$ X | ConditionSVarCompare$ GE1 | SubAbility$ DBEffect | SpellDescription$ Draw a card if an opponent has cast a blue or black spell this turn. Spells you control can't be countered this turn. You and permanents you control gain hexproof from blue and from black until end of turn. (You and they can't be the targets of blue or black spells or abilities your opponents control.)
|
A:SP$ Draw | Cost$ G | Defined$ You | ConditionCheckSVar$ X | ConditionSVarCompare$ GE1 |References$ X | SubAbility$ DBEffect | StackDescription$ SpellDescription | SpellDescription$ Draw a card if an opponent has cast a blue or black spell this turn. Spells you control can't be countered this turn. You and permanents you control gain hexproof from blue and from black until end of turn. (You and they can't be the targets of blue or black spells or abilities your opponents control.)
|
||||||
SVar:DBEffect:DB$ Effect | Name$ CARDNAME Effect | StaticAbilities$ AntiMagic | SubAbility$ DBPump
|
SVar:DBEffect:DB$ Effect | StaticAbilities$ AntiMagic | SubAbility$ DBPump
|
||||||
SVar:AntiMagic:Mode$ Continuous | Affected$ Card.YouCtrl | AffectedZone$ Stack | EffectZone$ Command | AddHiddenKeyword$ CARDNAME can't be countered. | Description$ Spells you control can't be countered this turn.
|
SVar:AntiMagic:Mode$ Continuous | Affected$ Card.YouCtrl | AffectedZone$ Stack | EffectZone$ Command | AddHiddenKeyword$ CARDNAME can't be countered. | Description$ Spells you control can't be countered this turn.
|
||||||
SVar:DBPump:DB$ Pump | Defined$ You | KW$ Hexproof:Card.Black:black & Hexproof:Card.Blue:blue | SubAbility$ DBPumpAll
|
SVar:DBPump:DB$ Pump | Defined$ You | KW$ Hexproof:Card.Black:black & Hexproof:Card.Blue:blue | SubAbility$ DBPumpAll
|
||||||
SVar:DBPumpAll:DB$ PumpAll | ValidCards$ Permanent.YouCtrl | KW$ Hexproof:Card.Black:black & Hexproof:Card.Blue:blue
|
SVar:DBPumpAll:DB$ PumpAll | ValidCards$ Permanent.YouCtrl | KW$ Hexproof:Card.Black:black & Hexproof:Card.Blue:blue
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
19
forge-gui/res/puzzle/PS_ELD1.pzl
Normal file
19
forge-gui/res/puzzle/PS_ELD1.pzl
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
[metadata]
|
||||||
|
Name:Possibility Storm - Throne of Eldraine #01
|
||||||
|
URL:http://www.possibilitystorm.com/wp-content/uploads/2019/10/132.-ELD1.jpg
|
||||||
|
Goal:Win
|
||||||
|
Turns:1
|
||||||
|
Difficulty:Rare
|
||||||
|
Description:Win this turn. You have three cards in your graveyard.
|
||||||
|
[state]
|
||||||
|
humanlife=20
|
||||||
|
ailife=8
|
||||||
|
turn=1
|
||||||
|
activeplayer=human
|
||||||
|
activephase=MAIN1
|
||||||
|
humanhand=Irencrag Feat;Charnel Troll;Steelbane Hydra;Festive Funeral;Trollbred Guardian
|
||||||
|
humangraveyard=Molderhulk;Stomping Ground;Deathless Knight
|
||||||
|
humanbattlefield=Fires of Invention;Goblin Smuggler;Yorvo, Lord of Garenbrig|Counters:P1P1=8;Vraska, Golgari Queen|Counters:LOYALTY=9;Blood Crypt|NoETBTrigs;Blood Crypt|NoETBTrigs;Blood Crypt|NoETBTrigs;Forest|Set:ELD;Forest|Set:ELD
|
||||||
|
aibattlefield=Inspiring Veteran
|
||||||
|
aiprecast=The Circle of Loyalty:TrigToken;The Circle of Loyalty:TrigToken;The Circle of Loyalty:TrigToken;The Circle of Loyalty:TrigToken;The Circle of Loyalty:TrigToken;The Circle of Loyalty:TrigToken
|
||||||
|
removesummoningsickness=true
|
||||||
@@ -58,7 +58,7 @@ public abstract class AbstractGuiGame implements IGuiGame, IMayViewCards {
|
|||||||
public final void setCurrentPlayer(PlayerView player) {
|
public final void setCurrentPlayer(PlayerView player) {
|
||||||
player = TrackableTypes.PlayerViewType.lookup(player); //ensure we use the correct player
|
player = TrackableTypes.PlayerViewType.lookup(player); //ensure we use the correct player
|
||||||
|
|
||||||
if (!gameControllers.containsKey(player)) {
|
if (hasLocalPlayers() && !isLocalPlayer(player)) { //add check if gameControllers is not empty
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user