mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-15 10:18:01 +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) {
|
||||
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)) {
|
||||
@@ -1585,7 +1586,8 @@ public class AiController {
|
||||
}
|
||||
catch (IllegalArgumentException ex) {
|
||||
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)) {
|
||||
@@ -1844,7 +1846,7 @@ public class AiController {
|
||||
// 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
|
||||
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
|
||||
result.clear();
|
||||
break;
|
||||
|
||||
@@ -2403,7 +2403,7 @@ public class ComputerUtil {
|
||||
}
|
||||
// if source is not on the battlefield anymore, choose +1/+1
|
||||
// ones
|
||||
if (!game.getCardState(source).getZone().is(ZoneType.Battlefield)) {
|
||||
if (!game.getCardState(source).isInZone(ZoneType.Battlefield)) {
|
||||
return opponent ? "Feather" : "Quill";
|
||||
}
|
||||
// if no hand cards, try to mill opponent
|
||||
@@ -2435,7 +2435,7 @@ public class ComputerUtil {
|
||||
}
|
||||
|
||||
// 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";
|
||||
}
|
||||
|
||||
@@ -2484,7 +2484,7 @@ public class ComputerUtil {
|
||||
}
|
||||
|
||||
// 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";
|
||||
}
|
||||
// TODO add Lifegain to +1/+1 counters trigger
|
||||
|
||||
@@ -1294,7 +1294,7 @@ public class ComputerUtilCard {
|
||||
// cast it during Declare Blockers, thus ruining its attacker
|
||||
if (holdCombatTricks && sa.getApi() == ApiType.Pump
|
||||
&& 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
|
||||
&& sa.getHostCard().isInstant() // only do it for instant speed spells in hand
|
||||
&& ComputerUtilMana.hasEnoughManaSourcesToCast(sa, ai)) {
|
||||
|
||||
@@ -729,7 +729,7 @@ public class PlayerControllerAi extends PlayerController {
|
||||
return true;
|
||||
} else {
|
||||
Card rem = (Card) source.getFirstRemembered();
|
||||
if (!rem.getZone().is(ZoneType.Battlefield)) {
|
||||
if (!rem.isInZone(ZoneType.Battlefield)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -737,7 +737,7 @@ public class PlayerControllerAi extends PlayerController {
|
||||
case "BetterTgtThanRemembered":
|
||||
if (source.getRememberedCount() > 0) {
|
||||
Card rem = (Card) source.getFirstRemembered();
|
||||
if (!rem.getZone().is(ZoneType.Battlefield)) {
|
||||
if (!rem.isInZone(ZoneType.Battlefield)) {
|
||||
return true;
|
||||
}
|
||||
for (Card c : source.getController().getCreaturesInPlay()) {
|
||||
|
||||
@@ -76,7 +76,7 @@ public class DamageDealAi extends DamageAiBase {
|
||||
// Set PayX here to maximum value.
|
||||
dmg = ComputerUtilMana.determineLeftoverMana(sa, ai);
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -113,7 +113,7 @@ public class DamageDealAi extends DamageAiBase {
|
||||
|
||||
// Set PayX here to maximum value. It will be adjusted later depending on the target.
|
||||
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
|
||||
} 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
|
||||
|
||||
@@ -409,7 +409,7 @@ public class DrawAi extends SpellAbilityAi {
|
||||
if (computerHandSize + numCards > computerMaxHandSize && game.getPhaseHandler().isPlayerTurn(ai)) {
|
||||
if (xPaid) {
|
||||
numCards = computerMaxHandSize - computerHandSize;
|
||||
if (sa.getHostCard().getZone().is(ZoneType.Hand)) {
|
||||
if (sa.getHostCard().isInZone(ZoneType.Hand)) {
|
||||
numCards++; // the card will be spent
|
||||
}
|
||||
source.setSVar("PayX", Integer.toString(numCards));
|
||||
|
||||
@@ -309,7 +309,7 @@ public class PumpAi extends PumpAiBase {
|
||||
}
|
||||
} else {
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -328,7 +328,7 @@ public class PumpAi extends PumpAiBase {
|
||||
}
|
||||
} else {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,7 +148,7 @@ public class SetStateAi extends SpellAbilityAi {
|
||||
if (card.isFaceDown()) {
|
||||
// hidden agenda
|
||||
if (card.getState(CardStateName.Original).hasIntrinsicKeyword("Hidden agenda")
|
||||
&& card.getZone().is(ZoneType.Command)) {
|
||||
&& card.isInZone(ZoneType.Command)) {
|
||||
String chosenName = card.getNamedCard();
|
||||
for (Card cast : ai.getGame().getStack().getSpellsCastThisTurn()) {
|
||||
if (cast.getController() == ai && cast.getName().equals(chosenName)) {
|
||||
|
||||
@@ -228,7 +228,7 @@ public class GameCopier {
|
||||
CardFactory.copyCopiableCharacteristics(c, 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);
|
||||
newCard.setCommander(c.isCommander());
|
||||
return newCard;
|
||||
|
||||
@@ -15,8 +15,9 @@ public final class ComparatorUtil
|
||||
* @param elements the elements to test against
|
||||
* @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 second: elements)
|
||||
@@ -27,8 +28,12 @@ public final class ComparatorUtil
|
||||
{
|
||||
// Uncomment the following line to step through the failed case
|
||||
//comparator.compare(first, second);
|
||||
throw new AssertionError("compare(" + first + ", " + second + ") == " + result1 +
|
||||
" but swapping the parameters returns " + result2);
|
||||
/*throw new AssertionError("compare(" + first + ", " + second + ") == " + result1 +
|
||||
" 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
|
||||
//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 + ") == " +
|
||||
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 AtEOT SVar
|
||||
String endofTurn = c.getSVar("EndOfTurnLeavePlay");
|
||||
@@ -256,7 +251,7 @@ public class GameAction {
|
||||
}
|
||||
if(noLandLKI.isLand()) {
|
||||
// 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;
|
||||
}
|
||||
// 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();
|
||||
|
||||
// need to suspend cards own replacement effects
|
||||
|
||||
@@ -300,12 +300,12 @@ public final class GameActionUtil {
|
||||
costs.add(new OptionalCostValue(type, cost));
|
||||
}
|
||||
} 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);
|
||||
costs.add(new OptionalCostValue(OptionalCost.Retrace, cost));
|
||||
}
|
||||
} 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);
|
||||
costs.add(new OptionalCostValue(OptionalCost.Jumpstart, cost));
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ public class CloneEffect extends SpellAbilityEffect {
|
||||
}
|
||||
|
||||
if (sa.hasParam("CloneZone")) {
|
||||
if (!tgtCard.getZone().is(ZoneType.smartValueOf(sa.getParam("CloneZone")))) {
|
||||
if (!tgtCard.isInZone(ZoneType.smartValueOf(sa.getParam("CloneZone")))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -134,7 +134,7 @@ public class CloneEffect extends SpellAbilityEffect {
|
||||
tgtCard.clearImprintedCards();
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
||||
@@ -120,7 +120,10 @@ public class PumpEffect extends SpellAbilityEffect {
|
||||
&& !(host.isInPlay() || host.isInZone(ZoneType.Stack))) {
|
||||
return;
|
||||
}
|
||||
p.addChangedKeywords(keywords, ImmutableList.of(), timestamp);
|
||||
|
||||
if (!keywords.isEmpty()) {
|
||||
p.addChangedKeywords(keywords, ImmutableList.of(), timestamp);
|
||||
}
|
||||
|
||||
if (!sa.hasParam("Permanent")) {
|
||||
// If not Permanent, remove Pumped at EOT
|
||||
@@ -129,12 +132,7 @@ public class PumpEffect extends SpellAbilityEffect {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
if (keywords.size() > 0) {
|
||||
for (int i = 0; i < keywords.size(); i++) {
|
||||
p.removeKeyword(keywords.get(i));
|
||||
}
|
||||
}
|
||||
p.removeChangedKeywords(timestamp);
|
||||
}
|
||||
};
|
||||
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) {
|
||||
changedCardColors.put(timestamp, new CardColor(s, addToColors, timestamp));
|
||||
currentState.getView().updateColors(this);
|
||||
currentState.getView().updateHasChangeColors(!getChangedCardColors().isEmpty());
|
||||
}
|
||||
|
||||
public final void removeColor(final long timestampIn) {
|
||||
@@ -3162,6 +3163,7 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
|
||||
if (removeCol != null) {
|
||||
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 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()) {
|
||||
for(KeywordInterface inst : source.getKeywords()) {
|
||||
String kw = inst.getOriginal();
|
||||
|
||||
@@ -4501,7 +4501,9 @@ public class CardFactoryUtil {
|
||||
effect = "Mode$ CantTarget | Hexproof$ True | ValidCard$ Card.Self | Secondary$ True"
|
||||
+ sbValid.toString() + " | Activator$ Opponent | Description$ "
|
||||
+ 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")) {
|
||||
final String[] k = keyword.split(":");
|
||||
final String manacost = k[1];
|
||||
@@ -4693,19 +4695,30 @@ public class CardFactoryUtil {
|
||||
}
|
||||
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";
|
||||
|
||||
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";
|
||||
AbilitySub saEffect = (AbilitySub)AbilityFactory.getAbility(abEffect, card);
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered+nonAdventure");
|
||||
sb.append(" | AffectedZone$ Exile | Description$ You may cast the card.");
|
||||
saEffect.setSVar("Play", sb.toString());
|
||||
StringBuilder sbPlay = new StringBuilder();
|
||||
sbPlay.append("Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered+nonAdventure");
|
||||
sbPlay.append(" | AffectedZone$ Exile | Description$ You may cast the card.");
|
||||
saEffect.setSVar("Play", sbPlay.toString());
|
||||
|
||||
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) {
|
||||
set(TrackableProperty.ChangedColorWords, c.getChangedTextColorWords());
|
||||
}
|
||||
|
||||
public Map<String, String> getChangedTypes() {
|
||||
return get(TrackableProperty.ChangedTypes);
|
||||
}
|
||||
@@ -706,11 +705,13 @@ public class CardView extends GameEntityView {
|
||||
// update the color only while in Game
|
||||
if (c.getGame() != null) {
|
||||
currentStateView.updateColors(currentState);
|
||||
currentStateView.updateHasChangeColors(!c.getChangedCardColors().isEmpty());
|
||||
}
|
||||
} else {
|
||||
currentStateView.updateLoyalty(currentState);
|
||||
}
|
||||
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();
|
||||
|
||||
@@ -840,13 +841,22 @@ public class CardView extends GameEntityView {
|
||||
public ColorSet getColors() {
|
||||
return get(TrackableProperty.Colors);
|
||||
}
|
||||
public ColorSet getOriginalColors() {
|
||||
return get(TrackableProperty.OriginalColors);
|
||||
}
|
||||
void updateColors(Card c) {
|
||||
set(TrackableProperty.Colors, c.determineColor());
|
||||
}
|
||||
void updateColors(CardState c) {
|
||||
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() {
|
||||
return getImageKey(null);
|
||||
}
|
||||
|
||||
@@ -192,7 +192,7 @@ public class CostAdjustment {
|
||||
}
|
||||
|
||||
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
|
||||
cost.decreaseGenericMana(sumGeneric);
|
||||
@@ -360,7 +360,7 @@ public class CostAdjustment {
|
||||
* @param 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
|
||||
if (manaCost.toString().equals("{0}")) {
|
||||
return 0;
|
||||
@@ -393,7 +393,7 @@ public class CostAdjustment {
|
||||
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) {
|
||||
return Math.min(value, maxReduction);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ import com.google.common.collect.Lists;
|
||||
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardFactoryUtil;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.player.PlayerFactoryUtil;
|
||||
import forge.game.replacement.ReplacementEffect;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.staticability.StaticAbility;
|
||||
@@ -82,7 +84,7 @@ public abstract class KeywordInstance<T extends KeywordInstance<?>> implements K
|
||||
public final void createTraits(final Card host, final boolean intrinsic) {
|
||||
createTraits(host, intrinsic, false);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see forge.game.keyword.KeywordInterface#createTraits(forge.game.card.Card, boolean, boolean)
|
||||
@@ -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)
|
||||
* @see forge.game.keyword.KeywordInterface#addTrigger(forge.game.trigger.Trigger)
|
||||
|
||||
@@ -3,6 +3,7 @@ package forge.game.keyword;
|
||||
import java.util.Collection;
|
||||
|
||||
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;
|
||||
@@ -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, final boolean clear);
|
||||
|
||||
void createTraits(final Player player);
|
||||
void createTraits(final Player player, final boolean clear);
|
||||
|
||||
void addTrigger(final Trigger trg);
|
||||
|
||||
void addReplacement(final ReplacementEffect trg);
|
||||
|
||||
@@ -23,6 +23,11 @@ import java.util.List;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
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>
|
||||
@@ -131,7 +136,13 @@ public class KeywordsChange implements Cloneable {
|
||||
inst.createTraits(host, false, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public final void addKeywordsToPlayer(final Player player) {
|
||||
for (KeywordInterface inst : keywords.getValues()) {
|
||||
inst.createTraits(player, true);
|
||||
}
|
||||
}
|
||||
|
||||
public final boolean removeKeywordfromAdd(final String 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)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
|
||||
@@ -20,6 +20,8 @@ package forge.game.player;
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Predicates;
|
||||
import com.google.common.collect.*;
|
||||
|
||||
import forge.ImageKeys;
|
||||
import forge.LobbyPlayer;
|
||||
import forge.card.MagicColor;
|
||||
import forge.game.*;
|
||||
@@ -41,7 +43,6 @@ import forge.game.phase.PhaseType;
|
||||
import forge.game.replacement.ReplacementHandler;
|
||||
import forge.game.replacement.ReplacementResult;
|
||||
import forge.game.replacement.ReplacementType;
|
||||
import forge.game.spellability.AbilityActivated;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.staticability.StaticAbility;
|
||||
import forge.game.trigger.Trigger;
|
||||
@@ -155,6 +156,7 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
|
||||
private Card monarchEffect = null;
|
||||
private Card blessingEffect = null;
|
||||
private Card keywordEffect = null;
|
||||
|
||||
private final AchievementTracker achievementTracker = new AchievementTracker();
|
||||
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) {
|
||||
// if the key already exists - merge entries
|
||||
KeywordsChange cks = null;
|
||||
if (changedKeywords.containsKey(timestamp)) {
|
||||
final KeywordsChange cks = changedKeywords.get(timestamp);
|
||||
getKeywordCard().removeChangedCardTraits(timestamp);
|
||||
|
||||
changedKeywords.put(timestamp, cks.merge(addKeywords, removeKeywords,
|
||||
cks.isRemoveAllKeywords(), cks.isRemoveIntrinsicKeywords()));
|
||||
updateKeywords();
|
||||
return;
|
||||
cks = changedKeywords.get(timestamp).merge(addKeywords, removeKeywords, false, false);
|
||||
} else {
|
||||
cks = new KeywordsChange(addKeywords, removeKeywords, false, false);
|
||||
}
|
||||
|
||||
changedKeywords.put(timestamp, new KeywordsChange(addKeywords, removeKeywords, false, false));
|
||||
cks.addKeywordsToPlayer(this);
|
||||
getKeywordCard().addChangedCardTraits(cks.getAbilities(), null, cks.getTriggers(), cks.getReplacements(), cks.getStaticAbilities(), false, false, false, timestamp);
|
||||
changedKeywords.put(timestamp, cks);
|
||||
updateKeywords();
|
||||
game.fireEvent(new GameEventPlayerStatsChanged(this));
|
||||
}
|
||||
|
||||
public final KeywordsChange removeChangedKeywords(final Long timestamp) {
|
||||
KeywordsChange change = changedKeywords.remove(Long.valueOf(timestamp));
|
||||
KeywordsChange change = changedKeywords.remove(timestamp);
|
||||
if (change != null) {
|
||||
getKeywordCard().removeChangedCardTraits(timestamp);
|
||||
updateKeywords();
|
||||
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())) {
|
||||
if (ck.getValue().isEmpty() && changedKeywords.remove(ck.getKey()) != null) {
|
||||
keywordRemoved = true;
|
||||
getKeywordCard().removeChangedCardTraits(ck.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1178,33 +1183,17 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
|
||||
@Override
|
||||
public final boolean canBeTargetedBy(final SpellAbility sa) {
|
||||
if (hasKeyword("Shroud")) {
|
||||
return false;
|
||||
}
|
||||
if (hasKeyword("Hexproof")) {
|
||||
final Player a = sa.getActivatingPlayer();
|
||||
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) {
|
||||
|
||||
// CantTarget static abilities
|
||||
for (final Card ca : getGame().getCardsIn(ZoneType.listValueOf("Battlefield,Command"))) {
|
||||
for (final StaticAbility stAb : ca.getStaticAbilities()) {
|
||||
if (stAb.applyAbility("CantTarget", this, sa)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasProtectionFrom(sa.getHostCard())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (!hasKeyword("You can't be the targets of spells or activated abilities") || (!sa.isSpell() && (!(sa instanceof AbilityActivated))));
|
||||
return !hasProtectionFrom(sa.getHostCard());
|
||||
}
|
||||
|
||||
|
||||
@@ -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.");
|
||||
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
// don't apply the ability if it hasn't got the right mode
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
@@ -22,8 +22,6 @@ import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.zone.ZoneType;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* The Class StaticAbilityCantTarget.
|
||||
*/
|
||||
@@ -31,8 +29,8 @@ public class StaticAbilityCantTarget {
|
||||
|
||||
/**
|
||||
* Apply can't target ability.
|
||||
*
|
||||
* @param staticAbility
|
||||
*
|
||||
* @param st
|
||||
* the static ability
|
||||
* @param card
|
||||
* the card
|
||||
@@ -40,16 +38,19 @@ public class StaticAbilityCantTarget {
|
||||
* the spell/ability
|
||||
* @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 Map<String, String> params = staticAbility.getMapParams();
|
||||
final Card hostCard = staticAbility.getHostCard();
|
||||
final Card hostCard = st.getHostCard();
|
||||
final Card source = spellAbility.getHostCard();
|
||||
final Player activator = spellAbility.getActivatingPlayer();
|
||||
|
||||
if (params.containsKey("AffectedZone")) {
|
||||
if (st.hasParam("ValidPlayer")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (st.hasParam("AffectedZone")) {
|
||||
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)) {
|
||||
inZone = true;
|
||||
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;
|
||||
}
|
||||
|
||||
if (params.containsKey("ValidCard")
|
||||
&& !card.isValid(params.get("ValidCard").split(","), hostCard.getController(), hostCard, null)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (params.containsKey("ValidSource")
|
||||
&& !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)) {
|
||||
if (st.hasParam("Hexproof") && (activator != null)) {
|
||||
for (String k : activator.getKeywords()) {
|
||||
if (k.startsWith("IgnoreHexproof")) {
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
Name(TrackableTypes.StringType),
|
||||
Colors(TrackableTypes.ColorSetType),
|
||||
OriginalColors(TrackableTypes.ColorSetType),
|
||||
ImageKey(TrackableTypes.StringType),
|
||||
Type(TrackableTypes.CardTypeViewType),
|
||||
ManaCost(TrackableTypes.ManaCostType),
|
||||
@@ -80,6 +81,7 @@ public enum TrackableProperty {
|
||||
Toughness(TrackableTypes.IntegerType),
|
||||
Loyalty(TrackableTypes.StringType),
|
||||
ChangedColorWords(TrackableTypes.StringMapType),
|
||||
HasChangedColors(TrackableTypes.BooleanType),
|
||||
ChangedTypes(TrackableTypes.StringMapType),
|
||||
|
||||
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);
|
||||
|
||||
assertNotNull(simSpark);
|
||||
assertTrue(simSpark.getZone().is(ZoneType.Battlefield));
|
||||
assertTrue(simSpark.isInZone(ZoneType.Battlefield));
|
||||
assertEquals(1, simSpark.getCounters(CounterType.P1P1));
|
||||
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;
|
||||
private static boolean isloadingaMatch = 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) {
|
||||
if (GuiBase.getInterface() == null) {
|
||||
@@ -105,8 +108,10 @@ public class Forge implements ApplicationListener {
|
||||
FSkin.loadLight(skinName, splashScreen);
|
||||
|
||||
textureFiltering = prefs.getPrefBoolean(FPref.UI_LIBGDX_TEXTURE_FILTERING);
|
||||
|
||||
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();
|
||||
|
||||
@@ -121,15 +126,15 @@ public class Forge implements ApplicationListener {
|
||||
FModel.initialize(splashScreen.getProgressBar(), null);
|
||||
|
||||
splashScreen.getProgressBar().setDescription(localizer.getMessage("lblLoadingFonts"));
|
||||
FSkinFont.preloadAll(prefs.getPref(FPref.UI_LANGUAGE));
|
||||
FSkinFont.preloadAll(locale);
|
||||
|
||||
splashScreen.getProgressBar().setDescription(localizer.getMessage("lblLoadingCardTranslations"));
|
||||
CardTranslation.preloadTranslation(prefs.getPref(FPref.UI_LANGUAGE));
|
||||
CardTranslation.preloadTranslation(locale);
|
||||
|
||||
splashScreen.getProgressBar().setDescription(localizer.getMessage("lblFinishingStartup"));
|
||||
|
||||
//add reminder to preload
|
||||
if (prefs.getPrefBoolean(FPref.UI_ENABLE_PRELOAD_EXTENDED_ART))
|
||||
if (enablePreloadExtendedArt)
|
||||
splashScreen.getProgressBar().setDescription("Preload Extended Art...");
|
||||
Gdx.app.postRunnable(new Runnable() {
|
||||
@Override
|
||||
@@ -145,7 +150,7 @@ public class Forge implements ApplicationListener {
|
||||
}
|
||||
|
||||
private void preloadExtendedArt() {
|
||||
if (!FModel.getPreferences().getPrefBoolean(FPref.UI_ENABLE_PRELOAD_EXTENDED_ART))
|
||||
if (!enablePreloadExtendedArt)
|
||||
return;
|
||||
List<String> keys = new ArrayList<>();
|
||||
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 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) {
|
||||
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.utils.Array;
|
||||
import forge.FThreads;
|
||||
import forge.Forge;
|
||||
import forge.properties.ForgeConstants;
|
||||
import forge.util.FileUtil;
|
||||
import forge.util.TextBounds;
|
||||
@@ -26,8 +27,10 @@ import java.util.Map;
|
||||
|
||||
public class FSkinFont {
|
||||
private static final int MIN_FONT_SIZE = 8;
|
||||
private static final int MAX_FONT_SIZE = 72;
|
||||
private static final int MAX_FONT_SIZE_ZH_CN = 28;
|
||||
private static int MAX_FONT_SIZE = 72;
|
||||
|
||||
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 Map<Integer, FSkinFont> fonts = new HashMap<>();
|
||||
@@ -60,8 +63,9 @@ public class FSkinFont {
|
||||
|
||||
//pre-load all supported font sizes
|
||||
public static void preloadAll(String language) {
|
||||
int maxfontSize = (language.equals("zh-CN")) ? MAX_FONT_SIZE_ZH_CN : MAX_FONT_SIZE;
|
||||
for (int size = MIN_FONT_SIZE; size <= maxfontSize; size++) {
|
||||
//todo:really check the language glyph is a lot
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -357,7 +361,8 @@ public class FSkinFont {
|
||||
//generate from zh-CN.properties,and cardnames-zh-CN.txt
|
||||
//forge generate 3000+ characters cache need Take some time(MIN_FONT_SIZE - MAX_FONT_SIZE all size)
|
||||
//maybe using libgdx-hiero generate font cache is better
|
||||
chars += "●、。「」『』一丁七万三上下不与丑专且世丘业丛东丝两严丧个中"
|
||||
if (Forge.locale.equals("zh-CN"))
|
||||
chars += "●、。「」『』一丁七万三上下不与丑专且世丘业丛东丝两严丧个中"
|
||||
+ "丰临丸丹为主丽举乃久么义之乌乍乐乔乖乘乙九也乡书乱乳乾了予争"
|
||||
+ "事二于云互五井亘亚些亡交亥亦产享京亮亲亵人亿什仁仅仆仇今介仍"
|
||||
+ "从仑仓仕他仗付仙代令以仪们仰仲件价任份仿伊伍伏伐休众优伙会伟"
|
||||
|
||||
@@ -231,7 +231,9 @@ public class ImageCache {
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
public static FImage getBorderImage(CardView c) {
|
||||
public static FImage getBorderImage(CardView c, boolean canshow) {
|
||||
if (!canshow)
|
||||
return BlackBorder;
|
||||
if (isWhiteBordered(c))
|
||||
return WhiteBorder;
|
||||
return BlackBorder;
|
||||
@@ -241,4 +243,29 @@ public class ImageCache {
|
||||
return WhiteBorder;
|
||||
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.TextureData;
|
||||
import com.badlogic.gdx.graphics.glutils.PixmapTextureData;
|
||||
import forge.FThreads;
|
||||
import org.cache2k.integration.CacheLoader;
|
||||
|
||||
import forge.Forge;
|
||||
import forge.ImageKeys;
|
||||
|
||||
final class ImageLoader extends CacheLoader<String, Texture> {
|
||||
Texture n;
|
||||
@Override
|
||||
public Texture load(String key) {
|
||||
boolean extendedArt = false;
|
||||
@@ -44,23 +46,28 @@ final class ImageLoader extends CacheLoader<String, Texture> {
|
||||
public Texture generateTexture(FileHandle fh, Texture t, boolean textureFilter) {
|
||||
if (t == null || fh == null)
|
||||
return t;
|
||||
Pixmap pImage = new Pixmap(fh);
|
||||
int w = pImage.getWidth();
|
||||
int h = pImage.getHeight();
|
||||
int radius = (h - w) / 8;
|
||||
Pixmap pMask = createRoundedRectangle(w, h, radius, Color.RED);
|
||||
drawPixelstoMask(pImage, pMask);
|
||||
TextureData textureData = new PixmapTextureData(
|
||||
pMask, //pixmap to use
|
||||
Pixmap.Format.RGBA8888,
|
||||
textureFilter, //use mipmaps
|
||||
false, true);
|
||||
t = new Texture(textureData);
|
||||
if (textureFilter)
|
||||
t.setFilter(Texture.TextureFilter.MipMapLinearLinear, Texture.TextureFilter.Linear);
|
||||
pImage.dispose();
|
||||
pMask.dispose();
|
||||
return t;
|
||||
FThreads.invokeInEdtNowOrLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Pixmap pImage = new Pixmap(fh);
|
||||
int w = pImage.getWidth();
|
||||
int h = pImage.getHeight();
|
||||
int radius = (h - w) / 8;
|
||||
Pixmap pMask = createRoundedRectangle(w, h, radius, Color.RED);
|
||||
drawPixelstoMask(pImage, pMask);
|
||||
TextureData textureData = new PixmapTextureData(
|
||||
pMask, //pixmap to use
|
||||
Pixmap.Format.RGBA8888,
|
||||
textureFilter, //use mipmaps
|
||||
false, true);
|
||||
n = new Texture(textureData);
|
||||
if (textureFilter)
|
||||
n.setFilter(Texture.TextureFilter.MipMapLinearLinear, Texture.TextureFilter.Linear);
|
||||
pImage.dispose();
|
||||
pMask.dispose();
|
||||
}
|
||||
});
|
||||
return n;
|
||||
}
|
||||
public Pixmap createRoundedRectangle(int width, int height, int cornerRadius, Color color) {
|
||||
Pixmap pixmap = new Pixmap(width, height, Pixmap.Format.RGBA8888);
|
||||
|
||||
@@ -2,14 +2,13 @@ package forge.card;
|
||||
|
||||
import com.badlogic.gdx.graphics.Texture;
|
||||
|
||||
import forge.Forge;
|
||||
import forge.Graphics;
|
||||
import forge.assets.FImage;
|
||||
import forge.assets.ImageCache;
|
||||
import forge.card.CardRenderer.CardStackPosition;
|
||||
import forge.game.card.CardView;
|
||||
import forge.item.PaperCard;
|
||||
import forge.model.FModel;
|
||||
import forge.properties.ForgePreferences;
|
||||
import forge.toolbox.FCardPanel;
|
||||
|
||||
public class CardImage implements FImage {
|
||||
@@ -19,9 +18,6 @@ public class CardImage implements FImage {
|
||||
public CardImage(PaperCard card0) {
|
||||
card = card0;
|
||||
}
|
||||
private static boolean isPreferenceEnabled(ForgePreferences.FPref preferenceName) {
|
||||
return FModel.getPreferences().getPrefBoolean(preferenceName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getWidth() {
|
||||
@@ -38,11 +34,10 @@ public class CardImage implements FImage {
|
||||
|
||||
@Override
|
||||
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
|
||||
image = ImageCache.getImage(card);
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
else {
|
||||
if (mask) {
|
||||
if (Forge.enableUIMask) {
|
||||
if (ImageCache.isExtendedArt(card))
|
||||
g.drawImage(image, x, y, w, h);
|
||||
else {
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.badlogic.gdx.graphics.Color;
|
||||
import com.badlogic.gdx.graphics.Texture;
|
||||
import com.badlogic.gdx.utils.Align;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import forge.Forge;
|
||||
import forge.Graphics;
|
||||
import forge.assets.FBufferedImage;
|
||||
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) {
|
||||
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);
|
||||
if (image == null) { //draw details if can't draw zoom
|
||||
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 rotatePlane = FModel.getPreferences().getPrefBoolean(ForgePreferences.FPref.UI_ROTATE_PLANE_OR_PHENOMENON);
|
||||
if (rotatePlane && (card.getCurrentState().isPhenomenon() || card.getCurrentState().isPlane())) {
|
||||
if (mask){
|
||||
if (Forge.enableUIMask){
|
||||
if (ImageCache.isExtendedArt(card))
|
||||
g.drawRotatedImage(image, new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, -90);
|
||||
else {
|
||||
@@ -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);
|
||||
} else if (rotateSplit && isCurrentCard && card.isSplitCard() && canLook) {
|
||||
boolean isAftermath = card.getText().contains("Aftermath") || card.getAlternateState().getOracleText().contains("Aftermath");
|
||||
if (mask) {
|
||||
if (Forge.enableUIMask) {
|
||||
if (ImageCache.isExtendedArt(card))
|
||||
g.drawRotatedImage(image, new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, isAftermath ? 90 : -90);
|
||||
else {
|
||||
@@ -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);
|
||||
}
|
||||
else {
|
||||
if (mask) {
|
||||
if (Forge.enableUIMask) {
|
||||
if (ImageCache.isExtendedArt(card))
|
||||
g.drawImage(image, x, y, w, h);
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -561,8 +560,4 @@ public class CardImageRenderer {
|
||||
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);
|
||||
}
|
||||
|
||||
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.Array;
|
||||
import forge.CachedCardImage;
|
||||
import forge.Forge;
|
||||
import forge.FThreads;
|
||||
import forge.Graphics;
|
||||
import forge.StaticData;
|
||||
@@ -120,6 +121,8 @@ public class CardRenderer {
|
||||
}
|
||||
|
||||
public static Color getRarityColor(CardRarity rarity) {
|
||||
if (rarity == null)// NPE from Rarity weird...
|
||||
return Color.CLEAR;
|
||||
switch(rarity) {
|
||||
case 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) {
|
||||
boolean mask = isPreferenceEnabled(FPref.UI_ENABLE_BORDER_MASKING);
|
||||
Texture image = new RendererCachedCardImage(pc, false).getImage();
|
||||
float radius = (h - w)/8;
|
||||
|
||||
@@ -401,7 +403,7 @@ public class CardRenderer {
|
||||
CardImageRenderer.drawCardImage(g, CardView.getCardForUi(pc), false, x, y, w, h, pos);
|
||||
}
|
||||
else {
|
||||
if (mask) {
|
||||
if (Forge.enableUIMask) {
|
||||
if (ImageCache.isExtendedArt(pc))
|
||||
g.drawImage(image, x, y, w, h);
|
||||
else {
|
||||
@@ -421,15 +423,13 @@ public class CardRenderer {
|
||||
}
|
||||
}
|
||||
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);
|
||||
else //draw cards without textures as just a black rectangle
|
||||
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) {
|
||||
boolean mask = isPreferenceEnabled(FPref.UI_ENABLE_BORDER_MASKING);
|
||||
Texture image = new RendererCachedCardImage(card, false).getImage();
|
||||
float radius = (h - w)/8;
|
||||
|
||||
@@ -440,7 +440,7 @@ public class CardRenderer {
|
||||
else {
|
||||
if(FModel.getPreferences().getPrefBoolean(ForgePreferences.FPref.UI_ROTATE_PLANE_OR_PHENOMENON)
|
||||
&& (card.getCurrentState().isPhenomenon() || card.getCurrentState().isPlane()) && rotate){
|
||||
if (mask) {
|
||||
if (Forge.enableUIMask) {
|
||||
if (ImageCache.isExtendedArt(card))
|
||||
g.drawRotatedImage(image, x, y, w, h, x + w / 2, y + h / 2, -90);
|
||||
else {
|
||||
@@ -452,11 +452,12 @@ public class CardRenderer {
|
||||
g.drawRotatedImage(image, x, y, w, h, x + w / 2, y + h / 2, -90);
|
||||
}
|
||||
else {
|
||||
if (mask) {
|
||||
if (Forge.enableUIMask) {
|
||||
if (ImageCache.isExtendedArt(card))
|
||||
g.drawImage(image, x, y, w, h);
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -467,7 +468,7 @@ public class CardRenderer {
|
||||
drawFoilEffect(g, card, x, y, w, h, false);
|
||||
}
|
||||
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);
|
||||
else //draw cards without textures as just a black rectangle
|
||||
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) {
|
||||
boolean canShow = MatchController.instance.mayView(card);
|
||||
float oldAlpha = g.getfloatAlphaComposite();
|
||||
boolean unselectable = !MatchController.instance.isSelectable(card) && MatchController.instance.isSelecting();
|
||||
float cx, cy, cw, ch;
|
||||
cx = x; cy = y; cw = w; ch = h;
|
||||
drawCard(g, card, x, y, w, h, pos, false);
|
||||
@@ -485,10 +489,6 @@ public class CardRenderer {
|
||||
w -= 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.
|
||||
final CardStateView details = card.getCurrentState();
|
||||
final boolean isFaceDown = card.isFaceDown();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package forge.itemmanager.views;
|
||||
|
||||
import forge.Forge;
|
||||
import forge.Forge.KeyInputAdapter;
|
||||
import forge.Graphics;
|
||||
import forge.assets.FImage;
|
||||
@@ -181,7 +182,7 @@ public class ImageView<T extends InventoryItem> extends ItemView<T> {
|
||||
getPnlOptions().add(cbPileByOptions);
|
||||
|
||||
Group group = new Group(""); //add default group
|
||||
groups.add(group);
|
||||
groups.add(group);
|
||||
getScroller().add(group);
|
||||
}
|
||||
|
||||
@@ -300,7 +301,7 @@ public class ImageView<T extends InventoryItem> extends ItemView<T> {
|
||||
updateLayout(false);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
float offsetTop = focalItem0.getTop() - getScrollValue();
|
||||
updateLayout(false);
|
||||
setScrollValue(focalItem0.getTop() - offsetTop);
|
||||
@@ -891,7 +892,7 @@ public class ImageView<T extends InventoryItem> extends ItemView<T> {
|
||||
public void draw(Graphics g) {
|
||||
final float visibleTop = getScrollValue();
|
||||
final float visibleBottom = visibleTop + getScroller().getHeight();
|
||||
|
||||
|
||||
ItemInfo skippedItem = null;
|
||||
for (ItemInfo itemInfo : items) {
|
||||
if (itemInfo.getBottom() < visibleTop) {
|
||||
@@ -954,9 +955,18 @@ public class ImageView<T extends InventoryItem> extends ItemView<T> {
|
||||
final float w = getWidth();
|
||||
final float h = getHeight();
|
||||
|
||||
if (selected) {
|
||||
g.fillRect(Color.GREEN, x - SEL_BORDER_SIZE, y - SEL_BORDER_SIZE,
|
||||
w + 2 * SEL_BORDER_SIZE, h + 2 * SEL_BORDER_SIZE);
|
||||
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,
|
||||
w + 2 * SEL_BORDER_SIZE, h + 2 * SEL_BORDER_SIZE);
|
||||
}
|
||||
|
||||
if (item instanceof PaperCard) {
|
||||
|
||||
@@ -129,6 +129,7 @@ public class VField extends FContainer {
|
||||
cardName.equals(c.getCurrentState().getName()) &&
|
||||
card.hasSameCounters(c) &&
|
||||
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.isToken() == c.isToken()) { //don't stack tokens on top of non-tokens
|
||||
CardAreaPanel cPanel = CardAreaPanel.get(c);
|
||||
|
||||
@@ -260,7 +260,7 @@ public class SettingsPage extends TabPage<SettingsScreen> {
|
||||
TabPageScreen.COMPACT_TABS = FModel.getPreferences().getPrefBoolean(FPref.UI_COMPACT_TABS);
|
||||
parentScreen.revalidate();
|
||||
}
|
||||
}, 4);
|
||||
},4);
|
||||
lstSettings.addItem(new BooleanSetting(FPref.UI_COMPACT_LIST_ITEMS,
|
||||
localizer.getMessage("lblCompactListItems"),
|
||||
localizer.getMessage("nlCompactListItems")),
|
||||
@@ -302,32 +302,42 @@ public class SettingsPage extends TabPage<SettingsScreen> {
|
||||
localizer.getMessage("nlDisableCardEffect")),
|
||||
4);
|
||||
lstSettings.addItem(new BooleanSetting(FPref.UI_ENABLE_BORDER_MASKING,
|
||||
"Enable Round Border Mask",
|
||||
"When enabled, the card corners are rounded (Preferably Card with Full Borders)."),
|
||||
4);
|
||||
"Enable Round Border Mask",
|
||||
"When enabled, the card corners are rounded (Preferably Card with Full Borders)."){
|
||||
@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,
|
||||
"Preload Extended Art Cards",
|
||||
"When enabled, Preloads Extended Art Cards to Cache on Startup."),
|
||||
4);
|
||||
"Preload Extended Art Cards",
|
||||
"When enabled, Preloads Extended Art Cards to Cache on Startup."){
|
||||
@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,
|
||||
"Show FPS Display",
|
||||
"When enabled, show the FPS Display (Experimental)."){
|
||||
@Override
|
||||
public void select() {
|
||||
super.select();
|
||||
//update
|
||||
Forge.showFPS = FModel.getPreferences().getPrefBoolean(FPref.UI_SHOW_FPS);
|
||||
}
|
||||
},4);
|
||||
|
||||
"Show FPS Display",
|
||||
"When enabled, show the FPS Display (Experimental)."){
|
||||
@Override
|
||||
public void select() {
|
||||
super.select();
|
||||
//update
|
||||
Forge.showFPS = FModel.getPreferences().getPrefBoolean(FPref.UI_SHOW_FPS);
|
||||
}
|
||||
},4);
|
||||
lstSettings.addItem(new CustomSelectSetting(FPref.UI_CARD_COUNTER_DISPLAY_TYPE,
|
||||
localizer.getMessage("cbpCounterDisplayType"),
|
||||
localizer.getMessage("nlCounterDisplayType"),
|
||||
new String[]{
|
||||
ForgeConstants.CounterDisplayType.TEXT.getName(), ForgeConstants.CounterDisplayType.IMAGE.getName(),
|
||||
ForgeConstants.CounterDisplayType.HYBRID.getName(), ForgeConstants.CounterDisplayType.OLD_WHEN_SMALL.getName()}),
|
||||
localizer.getMessage("cbpCounterDisplayType"),
|
||||
localizer.getMessage("nlCounterDisplayType"),
|
||||
new String[]{
|
||||
ForgeConstants.CounterDisplayType.TEXT.getName(), ForgeConstants.CounterDisplayType.IMAGE.getName(),
|
||||
ForgeConstants.CounterDisplayType.HYBRID.getName(), ForgeConstants.CounterDisplayType.OLD_WHEN_SMALL.getName()}),
|
||||
4);
|
||||
|
||||
//Card Overlays
|
||||
lstSettings.addItem(new BooleanSetting(FPref.UI_SHOW_CARD_OVERLAYS,
|
||||
localizer.getMessage("lblShowCardOverlays"),
|
||||
@@ -353,7 +363,6 @@ public class SettingsPage extends TabPage<SettingsScreen> {
|
||||
localizer.getMessage("lblShowAbilityIconsOverlays"),
|
||||
localizer.getMessage("nlShowAbilityIconsOverlays")),
|
||||
5);
|
||||
|
||||
//Vibration Options
|
||||
lstSettings.addItem(new BooleanSetting(FPref.UI_VIBRATE_ON_LIFE_LOSS,
|
||||
localizer.getMessage("lblVibrateWhenLosingLife"),
|
||||
@@ -363,7 +372,6 @@ public class SettingsPage extends TabPage<SettingsScreen> {
|
||||
localizer.getMessage("lblVibrateAfterLongPress"),
|
||||
localizer.getMessage("nlVibrateAfterLongPress")),
|
||||
6);
|
||||
|
||||
//Sound Options
|
||||
lstSettings.addItem(new BooleanSetting(FPref.UI_ENABLE_SOUNDS,
|
||||
localizer.getMessage("cbEnableSounds"),
|
||||
@@ -378,7 +386,7 @@ public class SettingsPage extends TabPage<SettingsScreen> {
|
||||
//update background music when this setting changes
|
||||
SoundSystem.instance.changeBackgroundTrack();
|
||||
}
|
||||
}, 7);
|
||||
},7);
|
||||
/*lstSettings.addItem(new BooleanSetting(FPref.UI_ALT_SOUND_SYSTEM,
|
||||
"Use Alternate Sound System",
|
||||
"Use the alternate sound system (only use if you have issues with sound not playing or disappearing)."),
|
||||
|
||||
@@ -3,7 +3,7 @@ ManaCost:4 G G
|
||||
Types:Legendary Creature Avatar
|
||||
PT:4/4
|
||||
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
|
||||
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.
|
||||
|
||||
@@ -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.
|
||||
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: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
|
||||
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
|
||||
K:Enchant creature
|
||||
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
|
||||
Oracle:Enchant creature\nEnchanted creature gets +0/+2 and can't be the target of spells.
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
Name:Veil of Summer
|
||||
ManaCost:G
|
||||
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.)
|
||||
SVar:DBEffect:DB$ Effect | Name$ CARDNAME Effect | StaticAbilities$ AntiMagic | SubAbility$ DBPump
|
||||
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 | 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: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
|
||||
|
||||
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) {
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user