mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-16 18:58:00 +00:00
Merge remote-tracking branch 'core-developers/master'
This commit is contained in:
280
.gitignore
vendored
280
.gitignore
vendored
@@ -1,229 +1,77 @@
|
|||||||
/*.idea
|
# Ignore IDEA config files
|
||||||
|
|
||||||
|
*.idea
|
||||||
*.iml
|
*.iml
|
||||||
/*.tmp
|
*.tmp
|
||||||
/.metadata
|
.metadata
|
||||||
/.recommenders
|
.recommenders
|
||||||
forge-ai/target
|
|
||||||
forge-core/target
|
|
||||||
forge-game/target
|
# Ignore Eclipse config files
|
||||||
|
|
||||||
|
.settings
|
||||||
|
.classpath
|
||||||
|
.project
|
||||||
|
|
||||||
|
# Ignore VS Code config files
|
||||||
|
|
||||||
|
.vscode/settings.json
|
||||||
|
.vscode/launch.json
|
||||||
|
|
||||||
|
|
||||||
|
# Ignore NetBeans config files
|
||||||
|
|
||||||
|
nbactions.xml
|
||||||
|
|
||||||
|
|
||||||
|
# Ignore binaries, temp files and test output, everywhere
|
||||||
|
|
||||||
|
target
|
||||||
|
test-output
|
||||||
|
bin
|
||||||
|
gen
|
||||||
|
*.log
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: specify what these ignores are for (releasing?)
|
||||||
|
|
||||||
|
forge.profile.properties
|
||||||
|
/jgv.txt
|
||||||
|
pom.xml.next
|
||||||
|
pom.xml.releaseBackup
|
||||||
|
pom.xml.tag
|
||||||
|
release.properties
|
||||||
|
|
||||||
|
|
||||||
|
# Ignore mobile-related resources
|
||||||
|
|
||||||
|
forge-gui-android/res/*/*
|
||||||
|
!forge-gui-android/res/*/ic_launcher.png
|
||||||
|
!forge-gui-android/res/*/ic_launcher*.png
|
||||||
|
!forge-gui-android/res/layout/main.xml
|
||||||
forge-gui-android/*.keystore
|
forge-gui-android/*.keystore
|
||||||
forge-gui-android/assets/fallback_skin/Thumbs.db
|
forge-gui-android/**/Thumbs.db
|
||||||
forge-gui-android/bin
|
forge-gui-mobile-dev/**/Thumbs.db
|
||||||
forge-gui-android/gen
|
|
||||||
forge-gui-android/res/Thumbs.db
|
|
||||||
forge-gui-android/res/bin
|
|
||||||
forge-gui-android/res/drawable-hdpi/Thumbs.db
|
|
||||||
forge-gui-android/res/drawable-hdpi/bin
|
|
||||||
forge-gui-android/res/drawable-hdpi/gen
|
|
||||||
forge-gui-android/res/drawable-hdpi/target
|
|
||||||
forge-gui-android/res/drawable-ldpi/Thumbs.db
|
|
||||||
forge-gui-android/res/drawable-ldpi/bin
|
|
||||||
forge-gui-android/res/drawable-ldpi/gen
|
|
||||||
forge-gui-android/res/drawable-ldpi/target
|
|
||||||
forge-gui-android/res/drawable-mdpi/Thumbs.db
|
|
||||||
forge-gui-android/res/drawable-mdpi/bin
|
|
||||||
forge-gui-android/res/drawable-mdpi/gen
|
|
||||||
forge-gui-android/res/drawable-mdpi/target
|
|
||||||
forge-gui-android/res/drawable-xhdpi/Thumbs.db
|
|
||||||
forge-gui-android/res/drawable-xhdpi/bin
|
|
||||||
forge-gui-android/res/drawable-xhdpi/gen
|
|
||||||
forge-gui-android/res/drawable-xhdpi/target
|
|
||||||
forge-gui-android/res/drawable-xxhdpi/Thumbs.db
|
|
||||||
forge-gui-android/res/drawable-xxhdpi/bin
|
|
||||||
forge-gui-android/res/drawable-xxhdpi/gen
|
|
||||||
forge-gui-android/res/drawable-xxhdpi/target
|
|
||||||
forge-gui-android/res/gen
|
|
||||||
forge-gui-android/res/layout/Thumbs.db
|
|
||||||
forge-gui-android/res/layout/bin
|
|
||||||
forge-gui-android/res/layout/gen
|
|
||||||
forge-gui-android/res/layout/target
|
|
||||||
forge-gui-android/res/target
|
|
||||||
forge-gui-android/res/values/Thumbs.db
|
|
||||||
forge-gui-android/res/values/bin
|
|
||||||
forge-gui-android/res/values/gen
|
|
||||||
forge-gui-android/res/values/target
|
|
||||||
forge-gui-android/target
|
|
||||||
forge-gui-desktop/target
|
|
||||||
forge-gui-ios/target
|
|
||||||
forge-gui-mobile-dev/bin
|
|
||||||
forge-gui-mobile-dev/fallback_skin/Thumbs.db
|
|
||||||
forge-gui-mobile-dev/res
|
forge-gui-mobile-dev/res
|
||||||
forge-gui-mobile-dev/target
|
|
||||||
forge-gui-mobile-dev/testAssets
|
forge-gui-mobile-dev/testAssets
|
||||||
forge-gui-mobile/bin
|
|
||||||
forge-gui-mobile/target
|
|
||||||
forge-gui/forge.profile.properties
|
|
||||||
forge-gui/res/*.log
|
|
||||||
forge-gui/res/PerSetTrackingResults
|
|
||||||
forge-gui/res/cardsfolder/*.bat
|
forge-gui/res/cardsfolder/*.bat
|
||||||
|
|
||||||
|
forge-gui/res/PerSetTrackingResults
|
||||||
forge-gui/res/decks
|
forge-gui/res/decks
|
||||||
forge-gui/res/layouts
|
forge-gui/res/layouts
|
||||||
forge-gui/res/pics*
|
forge-gui/res/pics*
|
||||||
forge-gui/res/pics_product
|
forge-gui/res/pics_product
|
||||||
|
|
||||||
|
forge-gui/res/skins/**/PerSetTrackingResults
|
||||||
|
forge-gui/res/skins/**/decks
|
||||||
|
forge-gui/res/skins/**/layouts
|
||||||
|
forge-gui/res/skins/**/pics*
|
||||||
|
forge-gui/res/skins/**/pics_product
|
||||||
|
|
||||||
forge-gui/res/quest/world/1996-05[!!-~]Ice[!!-~]Age/duels/.directory
|
forge-gui/res/quest/world/1996-05[!!-~]Ice[!!-~]Age/duels/.directory
|
||||||
forge-gui/res/skins/*.log
|
|
||||||
forge-gui/res/skins/PerSetTrackingResults
|
|
||||||
forge-gui/res/skins/Thumbs.db
|
|
||||||
forge-gui/res/skins/arabian_nights/*.log
|
|
||||||
forge-gui/res/skins/arabian_nights/PerSetTrackingResults
|
|
||||||
forge-gui/res/skins/arabian_nights/Thumbs.db
|
|
||||||
forge-gui/res/skins/arabian_nights/decks
|
|
||||||
forge-gui/res/skins/arabian_nights/layouts
|
|
||||||
forge-gui/res/skins/arabian_nights/pics*
|
|
||||||
forge-gui/res/skins/arabian_nights/pics_product
|
|
||||||
forge-gui/res/skins/comic/*.log
|
|
||||||
forge-gui/res/skins/comic/PerSetTrackingResults
|
|
||||||
forge-gui/res/skins/comic/Thumbs.db
|
|
||||||
forge-gui/res/skins/comic/decks
|
|
||||||
forge-gui/res/skins/comic/layouts
|
|
||||||
forge-gui/res/skins/comic/pics*
|
|
||||||
forge-gui/res/skins/comic/pics_product
|
|
||||||
forge-gui/res/skins/dark_ascension/*.log
|
|
||||||
forge-gui/res/skins/dark_ascension/PerSetTrackingResults
|
|
||||||
forge-gui/res/skins/dark_ascension/Thumbs.db
|
|
||||||
forge-gui/res/skins/dark_ascension/decks
|
|
||||||
forge-gui/res/skins/dark_ascension/layouts
|
|
||||||
forge-gui/res/skins/dark_ascension/pics*
|
|
||||||
forge-gui/res/skins/dark_ascension/pics_product
|
|
||||||
forge-gui/res/skins/decks
|
|
||||||
forge-gui/res/skins/default/*.log
|
|
||||||
forge-gui/res/skins/default/PerSetTrackingResults
|
|
||||||
forge-gui/res/skins/default/Thumbs.db
|
|
||||||
forge-gui/res/skins/default/decks
|
|
||||||
forge-gui/res/skins/default/layouts
|
|
||||||
forge-gui/res/skins/default/pics*
|
|
||||||
forge-gui/res/skins/default/pics_product
|
|
||||||
forge-gui/res/skins/firebloom/*.log
|
|
||||||
forge-gui/res/skins/firebloom/PerSetTrackingResults
|
|
||||||
forge-gui/res/skins/firebloom/Thumbs.db
|
|
||||||
forge-gui/res/skins/firebloom/decks
|
|
||||||
forge-gui/res/skins/firebloom/layouts
|
|
||||||
forge-gui/res/skins/firebloom/pics*
|
|
||||||
forge-gui/res/skins/firebloom/pics_product
|
|
||||||
forge-gui/res/skins/inferno/*.log
|
|
||||||
forge-gui/res/skins/inferno/PerSetTrackingResults
|
|
||||||
forge-gui/res/skins/inferno/Thumbs.db
|
|
||||||
forge-gui/res/skins/inferno/decks
|
|
||||||
forge-gui/res/skins/inferno/layouts
|
|
||||||
forge-gui/res/skins/inferno/pics*
|
|
||||||
forge-gui/res/skins/inferno/pics_product
|
|
||||||
forge-gui/res/skins/innistrad/*.log
|
|
||||||
forge-gui/res/skins/innistrad/PerSetTrackingResults
|
|
||||||
forge-gui/res/skins/innistrad/Thumbs.db
|
|
||||||
forge-gui/res/skins/innistrad/decks
|
|
||||||
forge-gui/res/skins/innistrad/layouts
|
|
||||||
forge-gui/res/skins/innistrad/pics*
|
|
||||||
forge-gui/res/skins/innistrad/pics_product
|
|
||||||
forge-gui/res/skins/journeyman/*.log
|
|
||||||
forge-gui/res/skins/journeyman/PerSetTrackingResults
|
|
||||||
forge-gui/res/skins/journeyman/Thumbs.db
|
|
||||||
forge-gui/res/skins/journeyman/decks
|
|
||||||
forge-gui/res/skins/journeyman/layouts
|
|
||||||
forge-gui/res/skins/journeyman/pics*
|
|
||||||
forge-gui/res/skins/journeyman/pics_product
|
|
||||||
forge-gui/res/skins/kamigawa/*.log
|
|
||||||
forge-gui/res/skins/kamigawa/PerSetTrackingResults
|
|
||||||
forge-gui/res/skins/kamigawa/Thumbs.db
|
|
||||||
forge-gui/res/skins/kamigawa/decks
|
|
||||||
forge-gui/res/skins/kamigawa/layouts
|
|
||||||
forge-gui/res/skins/kamigawa/pics*
|
|
||||||
forge-gui/res/skins/kamigawa/pics_product
|
|
||||||
forge-gui/res/skins/layouts
|
|
||||||
forge-gui/res/skins/marble_blue/*.log
|
|
||||||
forge-gui/res/skins/marble_blue/PerSetTrackingResults
|
|
||||||
forge-gui/res/skins/marble_blue/Thumbs.db
|
|
||||||
forge-gui/res/skins/marble_blue/decks
|
|
||||||
forge-gui/res/skins/marble_blue/layouts
|
|
||||||
forge-gui/res/skins/marble_blue/pics*
|
|
||||||
forge-gui/res/skins/marble_blue/pics_product
|
|
||||||
forge-gui/res/skins/metalcraft/*.log
|
|
||||||
forge-gui/res/skins/metalcraft/PerSetTrackingResults
|
|
||||||
forge-gui/res/skins/metalcraft/Thumbs.db
|
|
||||||
forge-gui/res/skins/metalcraft/decks
|
|
||||||
forge-gui/res/skins/metalcraft/layouts
|
|
||||||
forge-gui/res/skins/metalcraft/pics*
|
|
||||||
forge-gui/res/skins/metalcraft/pics_product
|
|
||||||
forge-gui/res/skins/mythic_rare/*.log
|
|
||||||
forge-gui/res/skins/mythic_rare/PerSetTrackingResults
|
|
||||||
forge-gui/res/skins/mythic_rare/Thumbs.db
|
|
||||||
forge-gui/res/skins/mythic_rare/decks
|
|
||||||
forge-gui/res/skins/mythic_rare/layouts
|
|
||||||
forge-gui/res/skins/mythic_rare/pics*
|
|
||||||
forge-gui/res/skins/mythic_rare/pics_product
|
|
||||||
forge-gui/res/skins/phyrexia/*.log
|
|
||||||
forge-gui/res/skins/phyrexia/PerSetTrackingResults
|
|
||||||
forge-gui/res/skins/phyrexia/Thumbs.db
|
|
||||||
forge-gui/res/skins/phyrexia/decks
|
|
||||||
forge-gui/res/skins/phyrexia/layouts
|
|
||||||
forge-gui/res/skins/phyrexia/pics*
|
|
||||||
forge-gui/res/skins/phyrexia/pics_product
|
|
||||||
forge-gui/res/skins/pics*
|
|
||||||
forge-gui/res/skins/pics_product
|
|
||||||
forge-gui/res/skins/ravnica/*.log
|
|
||||||
forge-gui/res/skins/ravnica/PerSetTrackingResults
|
|
||||||
forge-gui/res/skins/ravnica/Thumbs.db
|
|
||||||
forge-gui/res/skins/ravnica/decks
|
|
||||||
forge-gui/res/skins/ravnica/layouts
|
|
||||||
forge-gui/res/skins/ravnica/pics*
|
|
||||||
forge-gui/res/skins/ravnica/pics_product
|
|
||||||
forge-gui/res/skins/rebel/*.log
|
|
||||||
forge-gui/res/skins/rebel/PerSetTrackingResults
|
|
||||||
forge-gui/res/skins/rebel/Thumbs.db
|
|
||||||
forge-gui/res/skins/rebel/decks
|
|
||||||
forge-gui/res/skins/rebel/layouts
|
|
||||||
forge-gui/res/skins/rebel/pics*
|
|
||||||
forge-gui/res/skins/rebel/pics_product
|
|
||||||
forge-gui/res/skins/sleeping_forest/*.log
|
|
||||||
forge-gui/res/skins/sleeping_forest/PerSetTrackingResults
|
|
||||||
forge-gui/res/skins/sleeping_forest/Thumbs.db
|
|
||||||
forge-gui/res/skins/sleeping_forest/decks
|
|
||||||
forge-gui/res/skins/sleeping_forest/layouts
|
|
||||||
forge-gui/res/skins/sleeping_forest/pics*
|
|
||||||
forge-gui/res/skins/sleeping_forest/pics_product
|
|
||||||
forge-gui/res/skins/smith/*.log
|
|
||||||
forge-gui/res/skins/smith/PerSetTrackingResults
|
|
||||||
forge-gui/res/skins/smith/Thumbs.db
|
|
||||||
forge-gui/res/skins/smith/decks
|
|
||||||
forge-gui/res/skins/smith/layouts
|
|
||||||
forge-gui/res/skins/smith/pics*
|
|
||||||
forge-gui/res/skins/smith/pics_product
|
|
||||||
forge-gui/res/skins/the_dale/*.log
|
|
||||||
forge-gui/res/skins/the_dale/PerSetTrackingResults
|
|
||||||
forge-gui/res/skins/the_dale/Thumbs.db
|
|
||||||
forge-gui/res/skins/the_dale/decks
|
|
||||||
forge-gui/res/skins/the_dale/layouts
|
|
||||||
forge-gui/res/skins/the_dale/pics*
|
|
||||||
forge-gui/res/skins/the_dale/pics_product
|
|
||||||
forge-gui/res/skins/the_simpsons/*.log
|
|
||||||
forge-gui/res/skins/the_simpsons/PerSetTrackingResults
|
|
||||||
forge-gui/res/skins/the_simpsons/Thumbs.db
|
|
||||||
forge-gui/res/skins/the_simpsons/decks
|
|
||||||
forge-gui/res/skins/the_simpsons/layouts
|
|
||||||
forge-gui/res/skins/the_simpsons/pics*
|
|
||||||
forge-gui/res/skins/the_simpsons/pics_product
|
|
||||||
forge-gui/res/skins/zendikar/*.log
|
|
||||||
forge-gui/res/skins/zendikar/PerSetTrackingResults
|
|
||||||
forge-gui/res/skins/zendikar/Thumbs.db
|
|
||||||
forge-gui/res/skins/zendikar/decks
|
|
||||||
forge-gui/res/skins/zendikar/layouts
|
|
||||||
forge-gui/res/skins/zendikar/pics*
|
|
||||||
forge-gui/res/skins/zendikar/pics_product
|
|
||||||
forge-gui/target
|
|
||||||
forge-gui/tools/AllCards.json
|
forge-gui/tools/AllCards.json
|
||||||
forge-gui/tools/EditionTrackingResults
|
forge-gui/tools/EditionTrackingResults
|
||||||
forge-gui/tools/PerSetTrackingResults
|
forge-gui/tools/PerSetTrackingResults
|
||||||
forge-gui/tools/oracleScript.log
|
|
||||||
/forge.profile.properties
|
|
||||||
/jgv.txt
|
|
||||||
/nbactions.xml
|
|
||||||
/pom.xml.next
|
|
||||||
/pom.xml.releaseBackup
|
|
||||||
/pom.xml.tag
|
|
||||||
/release.properties
|
|
||||||
/target
|
|
||||||
/test-output
|
|
||||||
.settings
|
|
||||||
.classpath
|
|
||||||
.project
|
|
||||||
.vscode/settings.json
|
|
||||||
.vscode/launch.json
|
|
||||||
|
|||||||
@@ -2103,9 +2103,9 @@ public class AiController {
|
|||||||
return filterList(list, CardTraitPredicates.hasParam("AiLogic", logic));
|
return filterList(list, CardTraitPredicates.hasParam("AiLogic", logic));
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<AbilitySub> chooseModeForAbility(SpellAbility sa, int min, int num, boolean allowRepeat) {
|
public List<AbilitySub> chooseModeForAbility(SpellAbility sa, List<AbilitySub> possible, int min, int num, boolean allowRepeat) {
|
||||||
if (simPicker != null) {
|
if (simPicker != null) {
|
||||||
return simPicker.chooseModeForAbility(sa, min, num, allowRepeat);
|
return simPicker.chooseModeForAbility(sa, possible, min, num, allowRepeat);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,7 +101,9 @@ public class ComputerUtil {
|
|||||||
sa = GameActionUtil.addExtraKeywordCost(sa);
|
sa = GameActionUtil.addExtraKeywordCost(sa);
|
||||||
|
|
||||||
if (sa.getApi() == ApiType.Charm && !sa.isWrapper()) {
|
if (sa.getApi() == ApiType.Charm && !sa.isWrapper()) {
|
||||||
CharmEffect.makeChoices(sa);
|
if (!CharmEffect.makeChoices(sa)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (chooseTargets != null) {
|
if (chooseTargets != null) {
|
||||||
chooseTargets.run();
|
chooseTargets.run();
|
||||||
@@ -261,7 +263,9 @@ public class ComputerUtil {
|
|||||||
newSA.setHostCard(game.getAction().moveToStack(source, sa));
|
newSA.setHostCard(game.getAction().moveToStack(source, sa));
|
||||||
|
|
||||||
if (newSA.getApi() == ApiType.Charm && !newSA.isWrapper()) {
|
if (newSA.getApi() == ApiType.Charm && !newSA.isWrapper()) {
|
||||||
CharmEffect.makeChoices(newSA);
|
if (!CharmEffect.makeChoices(sa)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -800,8 +800,8 @@ public class PlayerControllerAi extends PlayerController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<AbilitySub> chooseModeForAbility(SpellAbility sa, int min, int num, boolean allowRepeat) {
|
public List<AbilitySub> chooseModeForAbility(SpellAbility sa, List<AbilitySub> possible, int min, int num, boolean allowRepeat) {
|
||||||
List<AbilitySub> result = brains.chooseModeForAbility(sa, min, num, allowRepeat);
|
List<AbilitySub> result = brains.chooseModeForAbility(sa, possible, min, num, allowRepeat);
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -251,7 +251,7 @@ public class AnimateAi extends SpellAbilityAi {
|
|||||||
&& sa.getTargetRestrictions() != null
|
&& sa.getTargetRestrictions() != null
|
||||||
&& sa.getTargetRestrictions().getMinTargets(sa.getHostCard(), sa) == 0;
|
&& sa.getTargetRestrictions().getMinTargets(sa.getHostCard(), sa) == 0;
|
||||||
|
|
||||||
final CardType types = new CardType();
|
final CardType types = new CardType(true);
|
||||||
if (sa.hasParam("Types")) {
|
if (sa.hasParam("Types")) {
|
||||||
types.addAll(Arrays.asList(sa.getParam("Types").split(",")));
|
types.addAll(Arrays.asList(sa.getParam("Types").split(",")));
|
||||||
}
|
}
|
||||||
@@ -340,6 +340,14 @@ public class AnimateAi extends SpellAbilityAi {
|
|||||||
|
|
||||||
// select the worst of the best
|
// select the worst of the best
|
||||||
final Card worst = ComputerUtilCard.getWorstAI(maxList);
|
final Card worst = ComputerUtilCard.getWorstAI(maxList);
|
||||||
|
if (worst.isLand()) {
|
||||||
|
// e.g. Clan Guildmage, make sure we're not using the same land we want to animate to activate the ability
|
||||||
|
this.holdAnimatedTillMain2(ai, worst);
|
||||||
|
if (!ComputerUtilMana.canPayManaCost(sa, ai, 0)) {
|
||||||
|
this.releaseHeldTillMain2(ai, worst);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
this.rememberAnimatedThisTurn(ai, worst);
|
this.rememberAnimatedThisTurn(ai, worst);
|
||||||
sa.getTargets().add(worst);
|
sa.getTargets().add(worst);
|
||||||
return true;
|
return true;
|
||||||
@@ -383,12 +391,12 @@ public class AnimateAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final CardType types = new CardType();
|
final CardType types = new CardType(true);
|
||||||
if (sa.hasParam("Types")) {
|
if (sa.hasParam("Types")) {
|
||||||
types.addAll(Arrays.asList(sa.getParam("Types").split(",")));
|
types.addAll(Arrays.asList(sa.getParam("Types").split(",")));
|
||||||
}
|
}
|
||||||
|
|
||||||
final CardType removeTypes = new CardType();
|
final CardType removeTypes = new CardType(true);
|
||||||
if (sa.hasParam("RemoveTypes")) {
|
if (sa.hasParam("RemoveTypes")) {
|
||||||
removeTypes.addAll(Arrays.asList(sa.getParam("RemoveTypes").split(",")));
|
removeTypes.addAll(Arrays.asList(sa.getParam("RemoveTypes").split(",")));
|
||||||
}
|
}
|
||||||
@@ -564,4 +572,12 @@ public class AnimateAi extends SpellAbilityAi {
|
|||||||
public static boolean isAnimatedThisTurn(Player ai, Card c) {
|
public static boolean isAnimatedThisTurn(Player ai, Card c) {
|
||||||
return AiCardMemory.isRememberedCard(ai, c, AiCardMemory.MemorySet.ANIMATED_THIS_TURN);
|
return AiCardMemory.isRememberedCard(ai, c, AiCardMemory.MemorySet.ANIMATED_THIS_TURN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void holdAnimatedTillMain2(Player ai, Card c) {
|
||||||
|
AiCardMemory.rememberCard(ai, c, AiCardMemory.MemorySet.HELD_MANA_SOURCES_FOR_MAIN2);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void releaseHeldTillMain2(Player ai, Card c) {
|
||||||
|
AiCardMemory.forgetCard(ai, c, AiCardMemory.MemorySet.HELD_MANA_SOURCES_FOR_MAIN2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,9 @@ package forge.ai.ability;
|
|||||||
|
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import forge.ai.*;
|
import forge.ai.*;
|
||||||
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.ability.effects.CharmEffect;
|
import forge.game.ability.effects.CharmEffect;
|
||||||
|
import forge.game.card.Card;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.AbilitySub;
|
import forge.game.spellability.AbilitySub;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
@@ -21,8 +23,11 @@ public class CharmAi extends SpellAbilityAi {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
final int num = Integer.parseInt(sa.hasParam("CharmNum") ? sa.getParam("CharmNum") : "1");
|
final Card source = sa.getHostCard();
|
||||||
final int min = sa.hasParam("MinCharmNum") ? Integer.parseInt(sa.getParam("MinCharmNum")) : num;
|
|
||||||
|
final int num = AbilityUtils.calculateAmount(source, sa.getParamOrDefault("CharmNum", "1"), sa);
|
||||||
|
final int min = sa.hasParam("MinCharmNum") ? AbilityUtils.calculateAmount(source, sa.getParamOrDefault("MinCharmNum", "1"), sa) : num;
|
||||||
|
|
||||||
boolean timingRight = sa.isTrigger(); //is there a reason to play the charm now?
|
boolean timingRight = sa.isTrigger(); //is there a reason to play the charm now?
|
||||||
|
|
||||||
// Reset the chosen list otherwise it will be locked in forever by earlier calls
|
// Reset the chosen list otherwise it will be locked in forever by earlier calls
|
||||||
|
|||||||
@@ -1,26 +1,20 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import com.google.common.base.Predicates;
|
import com.google.common.base.Predicates;
|
||||||
import com.google.common.collect.Lists;
|
import forge.ai.*;
|
||||||
import forge.ai.AiCardMemory;
|
|
||||||
import forge.ai.ComputerUtilAbility;
|
|
||||||
import forge.ai.ComputerUtilCard;
|
|
||||||
import forge.ai.ComputerUtilMana;
|
|
||||||
import forge.ai.SpellAbilityAi;
|
|
||||||
import forge.card.CardType;
|
import forge.card.CardType;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.card.Card;
|
import forge.game.ability.ApiType;
|
||||||
import forge.game.card.CardCollection;
|
import forge.game.card.*;
|
||||||
import forge.game.card.CardCollectionView;
|
|
||||||
import forge.game.card.CardLists;
|
|
||||||
import forge.game.card.CardPredicates;
|
|
||||||
import forge.game.keyword.Keyword;
|
import forge.game.keyword.Keyword;
|
||||||
import forge.game.phase.PhaseType;
|
import forge.game.phase.PhaseType;
|
||||||
import forge.game.player.Player;
|
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 forge.util.Aggregates;
|
import forge.util.Aggregates;
|
||||||
import java.util.List;
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
public class ChooseTypeAi extends SpellAbilityAi {
|
public class ChooseTypeAi extends SpellAbilityAi {
|
||||||
@Override
|
@Override
|
||||||
@@ -31,6 +25,8 @@ public class ChooseTypeAi extends SpellAbilityAi {
|
|||||||
if (ComputerUtilAbility.getAbilitySourceName(sa).equals("Mirror Entity Avatar")) {
|
if (ComputerUtilAbility.getAbilitySourceName(sa).equals("Mirror Entity Avatar")) {
|
||||||
return doMirrorEntityLogic(aiPlayer, sa);
|
return doMirrorEntityLogic(aiPlayer, sa);
|
||||||
}
|
}
|
||||||
|
} else if ("MostProminentOppControls".equals(sa.getParam("AILogic"))) {
|
||||||
|
return !chooseType(sa, aiPlayer.getOpponents().getCardsIn(ZoneType.Battlefield)).isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
return doTriggerAINoCost(aiPlayer, sa, false);
|
return doTriggerAINoCost(aiPlayer, sa, false);
|
||||||
@@ -44,26 +40,10 @@ public class ChooseTypeAi extends SpellAbilityAi {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
CardCollectionView otb = aiPlayer.getCardsIn(ZoneType.Battlefield);
|
String chosenType = chooseType(sa, aiPlayer.getCardsIn(ZoneType.Battlefield));
|
||||||
List<String> valid = Lists.newArrayList(CardType.getAllCreatureTypes());
|
|
||||||
|
|
||||||
String chosenType = ComputerUtilCard.getMostProminentType(otb, valid);
|
|
||||||
if (chosenType.isEmpty()) {
|
if (chosenType.isEmpty()) {
|
||||||
// Account for the situation when only changelings are on the battlefield
|
|
||||||
boolean allChangeling = false;
|
|
||||||
for (Card c : otb) {
|
|
||||||
if (c.isCreature() && c.hasStartOfKeyword(Keyword.CHANGELING.toString())) {
|
|
||||||
chosenType = Aggregates.random(valid); // just choose a random type for changelings
|
|
||||||
allChangeling = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!allChangeling) {
|
|
||||||
// Still empty, probably no creatures on board
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
int maxX = ComputerUtilMana.determineMaxAffordableX(aiPlayer, sa);
|
int maxX = ComputerUtilMana.determineMaxAffordableX(aiPlayer, sa);
|
||||||
int avgPower = 0;
|
int avgPower = 0;
|
||||||
@@ -127,4 +107,40 @@ public class ChooseTypeAi extends SpellAbilityAi {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String chooseType(SpellAbility sa, CardCollectionView cards) {
|
||||||
|
Set<String> valid = new HashSet<>();
|
||||||
|
|
||||||
|
if (sa.getSubAbility() != null && sa.getSubAbility().getApi() == ApiType.PumpAll
|
||||||
|
&& sa.getSubAbility().isCurse() && sa.getSubAbility().hasParam("NumDef")) {
|
||||||
|
final SpellAbility pumpSa = sa.getSubAbility();
|
||||||
|
final int defense = AbilityUtils.calculateAmount(sa.getHostCard(), pumpSa.getParam("NumDef"), pumpSa);
|
||||||
|
for (Card c : cards) {
|
||||||
|
if (c.isCreature() && c.getNetToughness() <= -defense) {
|
||||||
|
valid.addAll(c.getType().getCreatureTypes());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
valid.addAll(CardType.getAllCreatureTypes());
|
||||||
|
}
|
||||||
|
|
||||||
|
String chosenType = ComputerUtilCard.getMostProminentType(cards, valid);
|
||||||
|
if (chosenType.isEmpty()) {
|
||||||
|
// Account for the situation when only changelings are on the battlefield
|
||||||
|
boolean allChangeling = false;
|
||||||
|
for (Card c : cards) {
|
||||||
|
if (c.isCreature() && c.hasStartOfKeyword(Keyword.CHANGELING.toString())) {
|
||||||
|
chosenType = Aggregates.random(valid); // just choose a random type for changelings
|
||||||
|
allChangeling = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!allChangeling) {
|
||||||
|
// Still empty, probably no creatures on board
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return chosenType;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import forge.ai.ability.ExploreAi;
|
|||||||
import forge.ai.simulation.GameStateEvaluator.Score;
|
import forge.ai.simulation.GameStateEvaluator.Score;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.ability.ApiType;
|
import forge.game.ability.ApiType;
|
||||||
import forge.game.ability.effects.CharmEffect;
|
|
||||||
import forge.game.card.*;
|
import forge.game.card.*;
|
||||||
import forge.game.phase.PhaseType;
|
import forge.game.phase.PhaseType;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
@@ -371,14 +370,12 @@ public class SpellAbilityPicker {
|
|||||||
return bestScore;
|
return bestScore;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<AbilitySub> chooseModeForAbility(SpellAbility sa, int min, int num, boolean allowRepeat) {
|
public List<AbilitySub> chooseModeForAbility(SpellAbility sa, List<AbilitySub> choices, int min, int num, boolean allowRepeat) {
|
||||||
if (interceptor != null) {
|
if (interceptor != null) {
|
||||||
List<AbilitySub> choices = CharmEffect.makePossibleOptions(sa);
|
|
||||||
return interceptor.chooseModesForAbility(choices, min, num, allowRepeat);
|
return interceptor.chooseModesForAbility(choices, min, num, allowRepeat);
|
||||||
}
|
}
|
||||||
if (plan != null && plan.getSelectedDecision() != null && plan.getSelectedDecision().modes != null) {
|
if (plan != null && plan.getSelectedDecision() != null && plan.getSelectedDecision().modes != null) {
|
||||||
Plan.Decision decision = plan.getSelectedDecision();
|
Plan.Decision decision = plan.getSelectedDecision();
|
||||||
List<AbilitySub> choices = CharmEffect.makePossibleOptions(sa);
|
|
||||||
// TODO: Validate that there's no discrepancies between choices and modes?
|
// TODO: Validate that there's no discrepancies between choices and modes?
|
||||||
List<AbilitySub> plannedModes = SpellAbilityChoicesIterator.getModeCombination(choices, decision.modes);
|
List<AbilitySub> plannedModes = SpellAbilityChoicesIterator.getModeCombination(choices, decision.modes);
|
||||||
if (plan.getSelectedDecision().targets != null) {
|
if (plan.getSelectedDecision().targets != null) {
|
||||||
|
|||||||
@@ -150,6 +150,9 @@ public final class ImageKeys {
|
|||||||
// try with upper case set
|
// try with upper case set
|
||||||
file = findFile(dir, setlessFilename + "_" + setCode.toUpperCase());
|
file = findFile(dir, setlessFilename + "_" + setCode.toUpperCase());
|
||||||
if (file != null) { return file; }
|
if (file != null) { return file; }
|
||||||
|
// try with lower case set
|
||||||
|
file = findFile(dir, setlessFilename + "_" + setCode.toLowerCase());
|
||||||
|
if (file != null) { return file; }
|
||||||
// try without set name
|
// try without set name
|
||||||
file = findFile(dir, setlessFilename);
|
file = findFile(dir, setlessFilename);
|
||||||
if (file != null) { return file; }
|
if (file != null) { return file; }
|
||||||
|
|||||||
@@ -123,6 +123,7 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
|
|||||||
private String additionalUnlockSet = "";
|
private String additionalUnlockSet = "";
|
||||||
private boolean smallSetOverride = false;
|
private boolean smallSetOverride = false;
|
||||||
private String boosterMustContain = "";
|
private String boosterMustContain = "";
|
||||||
|
private String boosterReplaceSlotFromPrintSheet = "";
|
||||||
private boolean doublePickToStartRound = false;
|
private boolean doublePickToStartRound = false;
|
||||||
private final CardInSet[] cards;
|
private final CardInSet[] cards;
|
||||||
private final Map<String, Integer> tokenNormalized;
|
private final Map<String, Integer> tokenNormalized;
|
||||||
@@ -193,6 +194,7 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
|
|||||||
public boolean getSmallSetOverride() { return smallSetOverride; }
|
public boolean getSmallSetOverride() { return smallSetOverride; }
|
||||||
public boolean getDoublePickToStartRound() { return doublePickToStartRound; }
|
public boolean getDoublePickToStartRound() { return doublePickToStartRound; }
|
||||||
public String getBoosterMustContain() { return boosterMustContain; }
|
public String getBoosterMustContain() { return boosterMustContain; }
|
||||||
|
public String getBoosterReplaceSlotFromPrintSheet() { return boosterReplaceSlotFromPrintSheet; }
|
||||||
public CardInSet[] getCards() { return cards; }
|
public CardInSet[] getCards() { return cards; }
|
||||||
public boolean isModern() { return getDate().after(parseDate("2003-07-27")); } //8ED and above are modern except some promo cards and others
|
public boolean isModern() { return getDate().after(parseDate("2003-07-27")); } //8ED and above are modern except some promo cards and others
|
||||||
|
|
||||||
@@ -382,6 +384,7 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
|
|||||||
res.doublePickToStartRound = section.getBoolean("DoublePick", false); // for "small" sets with over 200 cards (e.g. Eldritch Moon)
|
res.doublePickToStartRound = section.getBoolean("DoublePick", false); // for "small" sets with over 200 cards (e.g. Eldritch Moon)
|
||||||
|
|
||||||
res.boosterMustContain = section.get("BoosterMustContain", ""); // e.g. Dominaria guaranteed legendary creature
|
res.boosterMustContain = section.get("BoosterMustContain", ""); // e.g. Dominaria guaranteed legendary creature
|
||||||
|
res.boosterReplaceSlotFromPrintSheet = section.get("BoosterReplaceSlotFromPrintSheet", ""); // e.g. Zendikar Rising guaranteed double-faced card
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -487,7 +487,7 @@ public final class CardRules implements ICardCharacteristics {
|
|||||||
if ("T".equals(key)) {
|
if ("T".equals(key)) {
|
||||||
this.faces[this.curFace].addTrigger(value);
|
this.faces[this.curFace].addTrigger(value);
|
||||||
} else if ("Types".equals(key)) {
|
} else if ("Types".equals(key)) {
|
||||||
this.faces[this.curFace].setType(CardType.parse(value));
|
this.faces[this.curFace].setType(CardType.parse(value, false));
|
||||||
} else if ("Text".equals(key) && !"no text".equals(value) && StringUtils.isNotBlank(value)) {
|
} else if ("Text".equals(key) && !"no text".equals(value) && StringUtils.isNotBlank(value)) {
|
||||||
this.faces[this.curFace].setNonAbilityText(value);
|
this.faces[this.curFace].setNonAbilityText(value);
|
||||||
}
|
}
|
||||||
@@ -557,7 +557,7 @@ public final class CardRules implements ICardCharacteristics {
|
|||||||
CardAiHints cah = new CardAiHints(true, true, true, null, null, null);
|
CardAiHints cah = new CardAiHints(true, true, true, null, null, null);
|
||||||
CardFace[] faces = { new CardFace(name), null};
|
CardFace[] faces = { new CardFace(name), null};
|
||||||
faces[0].setColor(ColorSet.fromMask(0));
|
faces[0].setColor(ColorSet.fromMask(0));
|
||||||
faces[0].setType(CardType.parse(""));
|
faces[0].setType(CardType.parse("", false));
|
||||||
faces[0].setOracleText("This card is not supported by Forge. Whenever you start a game with this card, it will be bugged.");
|
faces[0].setOracleText("This card is not supported by Forge. Whenever you start a game with this card, it will be bugged.");
|
||||||
faces[0].setNonAbilityText("This card is not supported by Forge.\nWhenever you start a game with this card, it will be bugged.");
|
faces[0].setNonAbilityText("This card is not supported by Forge.\nWhenever you start a game with this card, it will be bugged.");
|
||||||
faces[0].assignMissingFields();
|
faces[0].assignMissingFields();
|
||||||
|
|||||||
@@ -50,7 +50,9 @@ import forge.util.Settable;
|
|||||||
public final class CardType implements Comparable<CardType>, CardTypeView {
|
public final class CardType implements Comparable<CardType>, CardTypeView {
|
||||||
private static final long serialVersionUID = 4629853583167022151L;
|
private static final long serialVersionUID = 4629853583167022151L;
|
||||||
|
|
||||||
public static final CardTypeView EMPTY = new CardType();
|
public static final CardTypeView EMPTY = new CardType(false);
|
||||||
|
|
||||||
|
public static final String AllCreatureTypes = "AllCreatureTypes";
|
||||||
|
|
||||||
public enum CoreType {
|
public enum CoreType {
|
||||||
Artifact(true),
|
Artifact(true),
|
||||||
@@ -109,11 +111,14 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
|
|||||||
private final Set<CoreType> coreTypes = EnumSet.noneOf(CoreType.class);
|
private final Set<CoreType> coreTypes = EnumSet.noneOf(CoreType.class);
|
||||||
private final Set<Supertype> supertypes = EnumSet.noneOf(Supertype.class);
|
private final Set<Supertype> supertypes = EnumSet.noneOf(Supertype.class);
|
||||||
private final Set<String> subtypes = Sets.newLinkedHashSet();
|
private final Set<String> subtypes = Sets.newLinkedHashSet();
|
||||||
|
private boolean incomplete = false;
|
||||||
private transient String calculatedType = null;
|
private transient String calculatedType = null;
|
||||||
|
|
||||||
public CardType() {
|
public CardType(boolean incomplete) {
|
||||||
|
this.incomplete = incomplete;
|
||||||
}
|
}
|
||||||
public CardType(final Iterable<String> from0) {
|
public CardType(final Iterable<String> from0, boolean incomplete) {
|
||||||
|
this.incomplete = incomplete;
|
||||||
addAll(from0);
|
addAll(from0);
|
||||||
}
|
}
|
||||||
public CardType(final CardType from0) {
|
public CardType(final CardType from0) {
|
||||||
@@ -152,6 +157,7 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
|
|||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
sanisfySubtypes();
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
public boolean addAll(final CardType type) {
|
public boolean addAll(final CardType type) {
|
||||||
@@ -159,6 +165,7 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
|
|||||||
if (coreTypes.addAll(type.coreTypes)) { changed = true; }
|
if (coreTypes.addAll(type.coreTypes)) { changed = true; }
|
||||||
if (supertypes.addAll(type.supertypes)) { changed = true; }
|
if (supertypes.addAll(type.supertypes)) { changed = true; }
|
||||||
if (subtypes.addAll(type.subtypes)) { changed = true; }
|
if (subtypes.addAll(type.subtypes)) { changed = true; }
|
||||||
|
sanisfySubtypes();
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
public boolean addAll(final CardTypeView type) {
|
public boolean addAll(final CardTypeView type) {
|
||||||
@@ -166,6 +173,7 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
|
|||||||
if (Iterables.addAll(coreTypes, type.getCoreTypes())) { changed = true; }
|
if (Iterables.addAll(coreTypes, type.getCoreTypes())) { changed = true; }
|
||||||
if (Iterables.addAll(supertypes, type.getSupertypes())) { changed = true; }
|
if (Iterables.addAll(supertypes, type.getSupertypes())) { changed = true; }
|
||||||
if (Iterables.addAll(subtypes, type.getSubtypes())) { changed = true; }
|
if (Iterables.addAll(subtypes, type.getSubtypes())) { changed = true; }
|
||||||
|
sanisfySubtypes();
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,6 +183,7 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
|
|||||||
if (supertypes.removeAll(type.supertypes)) { changed = true; }
|
if (supertypes.removeAll(type.supertypes)) { changed = true; }
|
||||||
if (subtypes.removeAll(type.subtypes)) { changed = true; }
|
if (subtypes.removeAll(type.subtypes)) { changed = true; }
|
||||||
if (changed) {
|
if (changed) {
|
||||||
|
sanisfySubtypes();
|
||||||
calculatedType = null;
|
calculatedType = null;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -211,6 +220,7 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (changed) {
|
if (changed) {
|
||||||
|
sanisfySubtypes();
|
||||||
calculatedType = null;
|
calculatedType = null;
|
||||||
}
|
}
|
||||||
return changed;
|
return changed;
|
||||||
@@ -223,7 +233,7 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
|
|||||||
}
|
}
|
||||||
boolean changed = Iterables.removeIf(subtypes, Predicates.IS_CREATURE_TYPE);
|
boolean changed = Iterables.removeIf(subtypes, Predicates.IS_CREATURE_TYPE);
|
||||||
// need to remove AllCreatureTypes too when setting Creature Type
|
// need to remove AllCreatureTypes too when setting Creature Type
|
||||||
if (subtypes.remove("AllCreatureTypes")) {
|
if (subtypes.remove(AllCreatureTypes)) {
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
subtypes.addAll(ctypes);
|
subtypes.addAll(ctypes);
|
||||||
@@ -252,7 +262,7 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
|
|||||||
final Set<String> creatureTypes = Sets.newHashSet();
|
final Set<String> creatureTypes = Sets.newHashSet();
|
||||||
if (isCreature() || isTribal()) {
|
if (isCreature() || isTribal()) {
|
||||||
for (final String t : subtypes) {
|
for (final String t : subtypes) {
|
||||||
if (isACreatureType(t) || t.equals("AllCreatureTypes")) {
|
if (isACreatureType(t) || t.equals(AllCreatureTypes)) {
|
||||||
creatureTypes.add(t);
|
creatureTypes.add(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -302,7 +312,7 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
|
|||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public boolean hasSubtype(final String subtype) {
|
public boolean hasSubtype(final String subtype) {
|
||||||
if (isACreatureType(subtype) && subtypes.contains("AllCreatureTypes")) {
|
if (isACreatureType(subtype) && subtypes.contains(AllCreatureTypes)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return subtypes.contains(subtype);
|
return subtypes.contains(subtype);
|
||||||
@@ -315,7 +325,7 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
|
|||||||
creatureType = toMixedCase(creatureType);
|
creatureType = toMixedCase(creatureType);
|
||||||
if (!isACreatureType(creatureType)) { return false; }
|
if (!isACreatureType(creatureType)) { return false; }
|
||||||
|
|
||||||
return subtypes.contains(creatureType) || subtypes.contains("AllCreatureTypes");
|
return subtypes.contains(creatureType) || subtypes.contains(AllCreatureTypes);
|
||||||
}
|
}
|
||||||
private static String toMixedCase(final String s) {
|
private static String toMixedCase(final String s) {
|
||||||
if (s.isEmpty()) {
|
if (s.isEmpty()) {
|
||||||
@@ -485,7 +495,7 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
|
|||||||
if (ct.isRemoveCreatureTypes()) {
|
if (ct.isRemoveCreatureTypes()) {
|
||||||
Iterables.removeIf(newType.subtypes, Predicates.IS_CREATURE_TYPE);
|
Iterables.removeIf(newType.subtypes, Predicates.IS_CREATURE_TYPE);
|
||||||
// need to remove AllCreatureTypes too when removing creature Types
|
// need to remove AllCreatureTypes too when removing creature Types
|
||||||
newType.subtypes.remove("AllCreatureTypes");
|
newType.subtypes.remove(AllCreatureTypes);
|
||||||
}
|
}
|
||||||
if (ct.isRemoveArtifactTypes()) {
|
if (ct.isRemoveArtifactTypes()) {
|
||||||
Iterables.removeIf(newType.subtypes, Predicates.IS_ARTIFACT_TYPE);
|
Iterables.removeIf(newType.subtypes, Predicates.IS_ARTIFACT_TYPE);
|
||||||
@@ -503,29 +513,37 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
|
|||||||
}
|
}
|
||||||
// sanisfy subtypes
|
// sanisfy subtypes
|
||||||
if (newType != null && !newType.subtypes.isEmpty()) {
|
if (newType != null && !newType.subtypes.isEmpty()) {
|
||||||
if (!newType.isCreature() && !newType.isTribal()) {
|
newType.sanisfySubtypes();
|
||||||
Iterables.removeIf(newType.subtypes, Predicates.IS_CREATURE_TYPE);
|
|
||||||
newType.subtypes.remove("AllCreatureTypes");
|
|
||||||
}
|
|
||||||
if (!newType.isLand()) {
|
|
||||||
Iterables.removeIf(newType.subtypes, Predicates.IS_LAND_TYPE);
|
|
||||||
}
|
|
||||||
if (!newType.isArtifact()) {
|
|
||||||
Iterables.removeIf(newType.subtypes, Predicates.IS_ARTIFACT_TYPE);
|
|
||||||
}
|
|
||||||
if (!newType.isEnchantment()) {
|
|
||||||
Iterables.removeIf(newType.subtypes, Predicates.IS_ENCHANTMENT_TYPE);
|
|
||||||
}
|
|
||||||
if (!newType.isInstant() && !newType.isSorcery()) {
|
|
||||||
Iterables.removeIf(newType.subtypes, Predicates.IS_SPELL_TYPE);
|
|
||||||
}
|
|
||||||
if (!newType.isPlaneswalker() && !newType.isEmblem()) {
|
|
||||||
Iterables.removeIf(newType.subtypes, Predicates.IS_WALKER_TYPE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return newType == null ? this : newType;
|
return newType == null ? this : newType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void sanisfySubtypes() {
|
||||||
|
// incomplete types are used for changing effects
|
||||||
|
if (this.incomplete) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!isCreature() && !isTribal()) {
|
||||||
|
Iterables.removeIf(subtypes, Predicates.IS_CREATURE_TYPE);
|
||||||
|
subtypes.remove(AllCreatureTypes);
|
||||||
|
}
|
||||||
|
if (!isLand()) {
|
||||||
|
Iterables.removeIf(subtypes, Predicates.IS_LAND_TYPE);
|
||||||
|
}
|
||||||
|
if (!isArtifact()) {
|
||||||
|
Iterables.removeIf(subtypes, Predicates.IS_ARTIFACT_TYPE);
|
||||||
|
}
|
||||||
|
if (!isEnchantment()) {
|
||||||
|
Iterables.removeIf(subtypes, Predicates.IS_ENCHANTMENT_TYPE);
|
||||||
|
}
|
||||||
|
if (!isInstant() && !isSorcery()) {
|
||||||
|
Iterables.removeIf(subtypes, Predicates.IS_SPELL_TYPE);
|
||||||
|
}
|
||||||
|
if (!isPlaneswalker() && !isEmblem()) {
|
||||||
|
Iterables.removeIf(subtypes, Predicates.IS_WALKER_TYPE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Iterator<String> iterator() {
|
public Iterator<String> iterator() {
|
||||||
final Iterator<CoreType> coreTypeIterator = coreTypes.iterator();
|
final Iterator<CoreType> coreTypeIterator = coreTypes.iterator();
|
||||||
@@ -560,7 +578,64 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
|
|||||||
return toString().compareTo(o.toString());
|
return toString().compareTo(o.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean sharesSubtypeWith(final CardType ctOther) {
|
public boolean sharesCreaturetypeWith(final CardTypeView ctOther) {
|
||||||
|
if (ctOther == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this.subtypes.contains(AllCreatureTypes) && ctOther.hasSubtype(AllCreatureTypes)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for (final String type : getCreatureTypes()) {
|
||||||
|
if (ctOther.hasCreatureType(type)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean sharesLandTypeWith(final CardTypeView ctOther) {
|
||||||
|
if (ctOther == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (final String type : getLandTypes()) {
|
||||||
|
if (ctOther.hasSubtype(type)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean sharesPermanentTypeWith(final CardTypeView ctOther) {
|
||||||
|
if (ctOther == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (final CoreType type : getCoreTypes()) {
|
||||||
|
if (type.isPermanent && ctOther.hasType(type)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean sharesCardTypeWith(final CardTypeView ctOther) {
|
||||||
|
if (ctOther == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (final CoreType type : getCoreTypes()) {
|
||||||
|
if (ctOther.hasType(type)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean sharesSubtypeWith(final CardTypeView ctOther) {
|
||||||
|
if (ctOther == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
for (final String t : ctOther.getSubtypes()) {
|
for (final String t : ctOther.getSubtypes()) {
|
||||||
if (hasSubtype(t)) {
|
if (hasSubtype(t)) {
|
||||||
return true;
|
return true;
|
||||||
@@ -569,11 +644,11 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CardType parse(final String typeText) {
|
public static CardType parse(final String typeText, boolean incomplete) {
|
||||||
// Most types and subtypes, except "Serra's Realm" and
|
// Most types and subtypes, except "Serra's Realm" and
|
||||||
// "Bolas's Meditation Realm" consist of only one word
|
// "Bolas's Meditation Realm" consist of only one word
|
||||||
final char space = ' ';
|
final char space = ' ';
|
||||||
final CardType result = new CardType();
|
final CardType result = new CardType(incomplete);
|
||||||
|
|
||||||
int iTypeStart = 0;
|
int iTypeStart = 0;
|
||||||
int iSpace = typeText.indexOf(space);
|
int iSpace = typeText.indexOf(space);
|
||||||
@@ -593,7 +668,7 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static CardType combine(final CardType a, final CardType b) {
|
public static CardType combine(final CardType a, final CardType b) {
|
||||||
final CardType result = new CardType();
|
final CardType result = new CardType(false);
|
||||||
result.supertypes.addAll(a.supertypes);
|
result.supertypes.addAll(a.supertypes);
|
||||||
result.supertypes.addAll(b.supertypes);
|
result.supertypes.addAll(b.supertypes);
|
||||||
result.coreTypes.addAll(a.coreTypes);
|
result.coreTypes.addAll(a.coreTypes);
|
||||||
|
|||||||
@@ -19,6 +19,12 @@ public interface CardTypeView extends Iterable<String>, Serializable {
|
|||||||
boolean hasSupertype(Supertype supertype);
|
boolean hasSupertype(Supertype supertype);
|
||||||
boolean hasSubtype(String subtype);
|
boolean hasSubtype(String subtype);
|
||||||
boolean hasCreatureType(String creatureType);
|
boolean hasCreatureType(String creatureType);
|
||||||
|
|
||||||
|
public boolean sharesCreaturetypeWith(final CardTypeView ctOther);
|
||||||
|
public boolean sharesLandTypeWith(final CardTypeView ctOther);
|
||||||
|
public boolean sharesPermanentTypeWith(final CardTypeView ctOther);
|
||||||
|
public boolean sharesCardTypeWith(final CardTypeView ctOther);
|
||||||
|
|
||||||
boolean isPermanent();
|
boolean isPermanent();
|
||||||
boolean isCreature();
|
boolean isCreature();
|
||||||
boolean isPlaneswalker();
|
boolean isPlaneswalker();
|
||||||
|
|||||||
@@ -339,6 +339,11 @@ public class BoosterGenerator {
|
|||||||
if (!boosterMustContain.isEmpty()) {
|
if (!boosterMustContain.isEmpty()) {
|
||||||
ensureGuaranteedCardInBooster(result, template, boosterMustContain);
|
ensureGuaranteedCardInBooster(result, template, boosterMustContain);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String boosterReplaceSlotFromPrintSheet = edition.getBoosterReplaceSlotFromPrintSheet();
|
||||||
|
if(!boosterReplaceSlotFromPrintSheet.isEmpty()) {
|
||||||
|
replaceCardFromExtraSheet(result, boosterReplaceSlotFromPrintSheet);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@@ -390,24 +395,66 @@ public class BoosterGenerator {
|
|||||||
|
|
||||||
if (!possibleCards.isEmpty()) {
|
if (!possibleCards.isEmpty()) {
|
||||||
PaperCard toAdd = Aggregates.random(possibleCards);
|
PaperCard toAdd = Aggregates.random(possibleCards);
|
||||||
PaperCard toRepl = null;
|
BoosterGenerator.replaceCard(result, toAdd);
|
||||||
CardRarity tgtRarity = toAdd.getRarity();
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// remove the first card of the same rarity, replace it with toAdd. Keep the foil state.
|
/**
|
||||||
for (PaperCard repl : result) {
|
* Replaces an already present card in the booster with a card from the supplied print sheet.
|
||||||
if (repl.getRarity() == tgtRarity) {
|
* Nothing is replaced if there is no matching rarity found.
|
||||||
toRepl = repl;
|
* @param booster in which a card gets replaced
|
||||||
|
* @param printSheetKey
|
||||||
|
*/
|
||||||
|
public static void replaceCardFromExtraSheet(List<PaperCard> booster, String printSheetKey) {
|
||||||
|
PrintSheet replacementSheet = StaticData.instance().getPrintSheets().get(printSheetKey);
|
||||||
|
PaperCard toAdd = replacementSheet.random(1, false).get(0);
|
||||||
|
BoosterGenerator.replaceCard(booster, toAdd);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces an already present card with the supplied card of the same (or similar in case or rare/mythic)
|
||||||
|
* rarity in the supplied booster. Nothing is replaced if there is no matching rarity found.
|
||||||
|
* @param booster in which a card gets replaced
|
||||||
|
* @param toAdd new card which replaces a card in the booster
|
||||||
|
*/
|
||||||
|
public static void replaceCard(List<PaperCard> booster, PaperCard toAdd) {
|
||||||
|
Predicate<PaperCard> rarityPredicate = null;
|
||||||
|
switch(toAdd.getRarity()){
|
||||||
|
case BasicLand:
|
||||||
|
rarityPredicate = Presets.IS_BASIC_LAND;
|
||||||
|
break;
|
||||||
|
case Common:
|
||||||
|
rarityPredicate = Presets.IS_COMMON;
|
||||||
|
break;
|
||||||
|
case Uncommon:
|
||||||
|
rarityPredicate = Presets.IS_UNCOMMON;
|
||||||
|
break;
|
||||||
|
case Rare:
|
||||||
|
case MythicRare:
|
||||||
|
rarityPredicate = Presets.IS_RARE_OR_MYTHIC;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
rarityPredicate = Presets.IS_SPECIAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
PaperCard toReplace = null;
|
||||||
|
// Find first card in booster that matches the rarity
|
||||||
|
for (PaperCard card : booster) {
|
||||||
|
if(rarityPredicate.apply(card)) {
|
||||||
|
toReplace = card;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (toRepl != null) {
|
|
||||||
if (toRepl.isFoil()) {
|
// Replace card if match is found
|
||||||
|
if (toReplace != null) {
|
||||||
|
// Keep the foil state
|
||||||
|
if (toReplace.isFoil()) {
|
||||||
toAdd = StaticData.instance().getCommonCards().getFoiled(toAdd);
|
toAdd = StaticData.instance().getCommonCards().getFoiled(toAdd);
|
||||||
}
|
}
|
||||||
result.remove(toRepl);
|
booster.remove(toReplace);
|
||||||
result.add(toAdd);
|
booster.add(toAdd);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,9 +20,12 @@ package forge.game;
|
|||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
import com.google.common.base.Predicates;
|
import com.google.common.base.Predicates;
|
||||||
import com.google.common.collect.ArrayListMultimap;
|
import com.google.common.collect.ArrayListMultimap;
|
||||||
|
import com.google.common.collect.HashBasedTable;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
import com.google.common.collect.Multimap;
|
import com.google.common.collect.Multimap;
|
||||||
|
import com.google.common.collect.Table;
|
||||||
import com.google.common.eventbus.EventBus;
|
import com.google.common.eventbus.EventBus;
|
||||||
import forge.card.CardRarity;
|
import forge.card.CardRarity;
|
||||||
import forge.card.CardStateName;
|
import forge.card.CardStateName;
|
||||||
@@ -53,6 +56,8 @@ import forge.util.Visitor;
|
|||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the state of a <i>single game</i>, a new instance is created for each game.
|
* Represents the state of a <i>single game</i>, a new instance is created for each game.
|
||||||
*/
|
*/
|
||||||
@@ -86,6 +91,8 @@ public class Game {
|
|||||||
private Map<Player, PlayerCollection> attackedThisTurn = Maps.newHashMap();
|
private Map<Player, PlayerCollection> attackedThisTurn = Maps.newHashMap();
|
||||||
private Map<Player, PlayerCollection> attackedLastTurn = Maps.newHashMap();
|
private Map<Player, PlayerCollection> attackedLastTurn = Maps.newHashMap();
|
||||||
|
|
||||||
|
private Table<CounterType, Player, List<Pair<Card, Integer>>> countersAddedThisTurn = HashBasedTable.create();
|
||||||
|
|
||||||
private Player monarch = null;
|
private Player monarch = null;
|
||||||
private Player monarchBeginTurn = null;
|
private Player monarchBeginTurn = null;
|
||||||
|
|
||||||
@@ -939,4 +946,46 @@ public class Game {
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void onCleanupPhase() {
|
||||||
|
clearCounterAddedThisTurn();
|
||||||
|
for (Player player : getPlayers()) {
|
||||||
|
player.onCleanupPhase();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addCounterAddedThisTurn(Player putter, CounterType cType, Card card, Integer value) {
|
||||||
|
if (putter == null || card == null || value <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
List<Pair<Card, Integer>> result = countersAddedThisTurn.get(cType, putter);
|
||||||
|
if (result == null) {
|
||||||
|
result = Lists.newArrayList();
|
||||||
|
}
|
||||||
|
result.add(Pair.of(CardUtil.getLKICopy(card), value));
|
||||||
|
if (!countersAddedThisTurn.contains(cType, putter)) {
|
||||||
|
countersAddedThisTurn.put(cType, putter, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCounterAddedThisTurn(CounterType cType, String validPlayer, String validCard, Card source, Player sourceController, SpellAbility spellAbility) {
|
||||||
|
int result = 0;
|
||||||
|
if (!countersAddedThisTurn.containsRow(cType)) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
for (Map.Entry<Player, List<Pair<Card, Integer>>> e : countersAddedThisTurn.row(cType).entrySet()) {
|
||||||
|
if (e.getKey().isValid(validPlayer.split(","), sourceController, source, spellAbility)) {
|
||||||
|
for (Pair<Card, Integer> p : e.getValue()) {
|
||||||
|
if (p.getKey().isValid(validCard.split(","), sourceController, source, spellAbility)) {
|
||||||
|
result += p.getValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearCounterAddedThisTurn() {
|
||||||
|
countersAddedThisTurn.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -286,7 +286,7 @@ public final class AbilityFactory {
|
|||||||
|
|
||||||
spellAbility.setDescription(sb.toString());
|
spellAbility.setDescription(sb.toString());
|
||||||
} else if (api == ApiType.Charm) {
|
} else if (api == ApiType.Charm) {
|
||||||
spellAbility.setDescription(CharmEffect.makeSpellDescription(spellAbility));
|
spellAbility.setDescription(CharmEffect.makeFormatedDescription(spellAbility));
|
||||||
} else {
|
} else {
|
||||||
spellAbility.setDescription("");
|
spellAbility.setDescription("");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -374,7 +374,7 @@ public class AbilityUtils {
|
|||||||
if (StringUtils.isBlank(amount)) { return 0; }
|
if (StringUtils.isBlank(amount)) { return 0; }
|
||||||
if (card == null) { return 0; }
|
if (card == null) { return 0; }
|
||||||
final Player player = card.getController();
|
final Player player = card.getController();
|
||||||
final Game game = player.getGame();
|
final Game game = player == null ? card.getGame() : player.getGame();
|
||||||
|
|
||||||
// Strip and save sign for calculations
|
// Strip and save sign for calculations
|
||||||
final boolean startsWithPlus = amount.charAt(0) == '+';
|
final boolean startsWithPlus = amount.charAt(0) == '+';
|
||||||
@@ -1593,6 +1593,8 @@ public class AbilityUtils {
|
|||||||
final String[] sq;
|
final String[] sq;
|
||||||
sq = l[0].split("\\.");
|
sq = l[0].split("\\.");
|
||||||
|
|
||||||
|
final Game game = c.getGame();
|
||||||
|
|
||||||
if (ctb != null) {
|
if (ctb != null) {
|
||||||
// Count$Compare <int comparator value>.<True>.<False>
|
// Count$Compare <int comparator value>.<True>.<False>
|
||||||
if (sq[0].startsWith("Compare")) {
|
if (sq[0].startsWith("Compare")) {
|
||||||
@@ -1776,6 +1778,13 @@ public class AbilityUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (l[0].startsWith("CountersAddedThisTurn")) {
|
||||||
|
final String[] parts = l[0].split(" ");
|
||||||
|
CounterType cType = CounterType.getType(parts[1]);
|
||||||
|
|
||||||
|
return CardFactoryUtil.doXMath(game.getCounterAddedThisTurn(cType, parts[2], parts[3], c, sa.getActivatingPlayer(), sa), expr, c);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return CardFactoryUtil.xCount(c, s2);
|
return CardFactoryUtil.xCount(c, s2);
|
||||||
|
|||||||
@@ -78,10 +78,13 @@ public abstract class SpellAbilityEffect {
|
|||||||
// Own description
|
// Own description
|
||||||
String stackDesc = params.get("StackDescription");
|
String stackDesc = params.get("StackDescription");
|
||||||
if (stackDesc != null) {
|
if (stackDesc != null) {
|
||||||
if ("SpellDescription".equalsIgnoreCase(stackDesc)) { // by typing "none" they want to suppress output
|
// by typing "SpellDescription" they want to bypass the Effect's string builder
|
||||||
|
if ("SpellDescription".equalsIgnoreCase(stackDesc)) {
|
||||||
if (params.get("SpellDescription") != null) {
|
if (params.get("SpellDescription") != null) {
|
||||||
sb.append(TextUtil.fastReplace(params.get("SpellDescription"),
|
String currentName = (sa.getHostCard().getName());
|
||||||
"CARDNAME", sa.getHostCard().getName()));
|
String desc1 = TextUtil.fastReplace(params.get("SpellDescription"), "CARDNAME", currentName);
|
||||||
|
String desc = TextUtil.fastReplace(desc1, "NICKNAME", currentName.split(",")[0]);
|
||||||
|
sb.append(desc);
|
||||||
}
|
}
|
||||||
if (sa.getTargets() != null && !sa.getTargets().getTargets().isEmpty()) {
|
if (sa.getTargets() != null && !sa.getTargets().getTargets().isEmpty()) {
|
||||||
sb.append(" (Targeting: ").append(sa.getTargets().getTargets()).append(")");
|
sb.append(" (Targeting: ").append(sa.getTargets().getTargets()).append(")");
|
||||||
@@ -147,7 +150,11 @@ public abstract class SpellAbilityEffect {
|
|||||||
if ("}".equals(t)) { isPlainText = true; continue; }
|
if ("}".equals(t)) { isPlainText = true; continue; }
|
||||||
|
|
||||||
if (isPlainText) {
|
if (isPlainText) {
|
||||||
|
if(t.startsWith("NICKNAME")) {
|
||||||
|
sb.append(TextUtil.fastReplace(t,"NICKNAME", sa.getHostCard().getName().split(",")[0]));
|
||||||
|
} else {
|
||||||
sb.append(TextUtil.fastReplace(t, "CARDNAME", sa.getHostCard().getName()));
|
sb.append(TextUtil.fastReplace(t, "CARDNAME", sa.getHostCard().getName()));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
final List<? extends GameObject> objs;
|
final List<? extends GameObject> objs;
|
||||||
if (t.startsWith("p:")) {
|
if (t.startsWith("p:")) {
|
||||||
|
|||||||
@@ -51,12 +51,12 @@ public class AnimateAllEffect extends AnimateEffectBase {
|
|||||||
|
|
||||||
final boolean permanent = sa.hasParam("Permanent");
|
final boolean permanent = sa.hasParam("Permanent");
|
||||||
|
|
||||||
final CardType types = new CardType();
|
final CardType types = new CardType(true);
|
||||||
if (sa.hasParam("Types")) {
|
if (sa.hasParam("Types")) {
|
||||||
types.addAll(Arrays.asList(sa.getParam("Types").split(",")));
|
types.addAll(Arrays.asList(sa.getParam("Types").split(",")));
|
||||||
}
|
}
|
||||||
|
|
||||||
final CardType removeTypes = new CardType();
|
final CardType removeTypes = new CardType(true);
|
||||||
if (sa.hasParam("RemoveTypes")) {
|
if (sa.hasParam("RemoveTypes")) {
|
||||||
removeTypes.addAll(Arrays.asList(sa.getParam("RemoveTypes").split(",")));
|
removeTypes.addAll(Arrays.asList(sa.getParam("RemoveTypes").split(",")));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,12 +64,12 @@ public class AnimateEffect extends AnimateEffectBase {
|
|||||||
|
|
||||||
final boolean permanent = sa.hasParam("Permanent");
|
final boolean permanent = sa.hasParam("Permanent");
|
||||||
|
|
||||||
final CardType types = new CardType();
|
final CardType types = new CardType(true);
|
||||||
if (sa.hasParam("Types")) {
|
if (sa.hasParam("Types")) {
|
||||||
types.addAll(Arrays.asList(sa.getParam("Types").split(",")));
|
types.addAll(Arrays.asList(sa.getParam("Types").split(",")));
|
||||||
}
|
}
|
||||||
|
|
||||||
final CardType removeTypes = new CardType();
|
final CardType removeTypes = new CardType(true);
|
||||||
if (sa.hasParam("RemoveTypes")) {
|
if (sa.hasParam("RemoveTypes")) {
|
||||||
removeTypes.addAll(Arrays.asList(sa.getParam("RemoveTypes").split(",")));
|
removeTypes.addAll(Arrays.asList(sa.getParam("RemoveTypes").split(",")));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,9 @@ import forge.util.collect.FCollection;
|
|||||||
import forge.util.collect.FCollectionView;
|
import forge.util.collect.FCollectionView;
|
||||||
import forge.util.Localizer;
|
import forge.util.Localizer;
|
||||||
import forge.util.CardTranslation;
|
import forge.util.CardTranslation;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@@ -201,6 +203,26 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
sb.append(" Then shuffle that library.");
|
sb.append(" Then shuffle that library.");
|
||||||
|
} else if (origin.equals("Sideboard")) {
|
||||||
|
sb.append(chooserNames);
|
||||||
|
//currently Reveal is always True in ChangeZone
|
||||||
|
if (sa.hasParam("Reveal")) {
|
||||||
|
sb.append(" may reveal ").append(num).append(" ").append(type).append(" from outside the game and put ");
|
||||||
|
if (num == 1) {
|
||||||
|
sb.append("it ");
|
||||||
|
} else {
|
||||||
|
sb.append("them ");
|
||||||
|
}
|
||||||
|
sb.append("into their ").append(destination.toLowerCase()).append(".");
|
||||||
|
} else {
|
||||||
|
if (sa.hasParam("Mandatory")) {
|
||||||
|
sb.append(" puts ");
|
||||||
|
} else {
|
||||||
|
sb.append(" may put ");
|
||||||
|
}
|
||||||
|
sb.append(num).append(" ").append(type).append(" from outside the game into their ");
|
||||||
|
sb.append(destination.toLowerCase()).append(".");
|
||||||
|
}
|
||||||
} else if (origin.equals("Hand")) {
|
} else if (origin.equals("Hand")) {
|
||||||
sb.append(chooserNames);
|
sb.append(chooserNames);
|
||||||
if (!chooserNames.equals(fetcherNames)) {
|
if (!chooserNames.equals(fetcherNames)) {
|
||||||
@@ -282,7 +304,6 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final StringBuilder sbTargets = new StringBuilder();
|
final StringBuilder sbTargets = new StringBuilder();
|
||||||
|
|
||||||
Iterable<Card> tgts;
|
Iterable<Card> tgts;
|
||||||
if (sa.usesTargeting()) {
|
if (sa.usesTargeting()) {
|
||||||
tgts = sa.getTargets().getTargetCards();
|
tgts = sa.getTargets().getTargetCards();
|
||||||
@@ -290,10 +311,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
// otherwise add self to list and go from there
|
// otherwise add self to list and go from there
|
||||||
tgts = sa.knownDetermineDefined(sa.getParam("Defined"));
|
tgts = sa.knownDetermineDefined(sa.getParam("Defined"));
|
||||||
}
|
}
|
||||||
|
sbTargets.append(" ").append(StringUtils.join(tgts, ", "));
|
||||||
for (final Card c : tgts) {
|
|
||||||
sbTargets.append(" ").append(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
final String targetname = sbTargets.toString();
|
final String targetname = sbTargets.toString();
|
||||||
|
|
||||||
@@ -302,12 +320,16 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
final String fromGraveyard = " from the graveyard";
|
final String fromGraveyard = " from the graveyard";
|
||||||
|
|
||||||
if (destination.equals(ZoneType.Battlefield)) {
|
if (destination.equals(ZoneType.Battlefield)) {
|
||||||
sb.append("Put").append(targetname);
|
|
||||||
if (ZoneType.Graveyard.equals(origin)) {
|
if (ZoneType.Graveyard.equals(origin)) {
|
||||||
sb.append(fromGraveyard);
|
sb.append("Return").append(targetname);
|
||||||
|
} else {
|
||||||
|
sb.append("Put").append(targetname);
|
||||||
}
|
}
|
||||||
|
if (ZoneType.Graveyard.equals(origin)) {
|
||||||
|
sb.append(fromGraveyard).append(" to the battlefield");
|
||||||
|
} else {
|
||||||
sb.append(" onto the battlefield");
|
sb.append(" onto the battlefield");
|
||||||
|
}
|
||||||
if (sa.hasParam("Tapped")) {
|
if (sa.hasParam("Tapped")) {
|
||||||
sb.append(" tapped");
|
sb.append(" tapped");
|
||||||
}
|
}
|
||||||
@@ -1159,9 +1181,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (sa.hasParam("FaceDownAddType")) {
|
if (sa.hasParam("FaceDownAddType")) {
|
||||||
for (String type : sa.getParam("FaceDownAddType").split(",")) {
|
c.addType(Arrays.asList(sa.getParam("FaceDownAddType").split(" & ")));
|
||||||
c.addType(type);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sa.hasParam("FaceDownPower") || sa.hasParam("FaceDownToughness")
|
if (sa.hasParam("FaceDownPower") || sa.hasParam("FaceDownToughness")
|
||||||
|
|||||||
@@ -20,12 +20,14 @@ public class CharmEffect extends SpellAbilityEffect {
|
|||||||
|
|
||||||
public static List<AbilitySub> makePossibleOptions(final SpellAbility sa) {
|
public static List<AbilitySub> makePossibleOptions(final SpellAbility sa) {
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
Iterable<Object> restriction = null;
|
List<String> restriction = null;
|
||||||
|
|
||||||
if (sa.hasParam("ChoiceRestriction")) {
|
if (sa.hasParam("ChoiceRestriction")) {
|
||||||
String rest = sa.getParam("ChoiceRestriction");
|
String rest = sa.getParam("ChoiceRestriction");
|
||||||
if (rest.equals("NotRemembered")) {
|
if (rest.equals("ThisGame")) {
|
||||||
restriction = source.getRemembered();
|
restriction = source.getChosenModesGame(sa);
|
||||||
|
} else if (rest.equals("ThisTurn")) {
|
||||||
|
restriction = source.getChosenModesTurn(sa);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,16 +35,11 @@ public class CharmEffect extends SpellAbilityEffect {
|
|||||||
List<AbilitySub> choices = Lists.newArrayList(sa.getAdditionalAbilityList("Choices"));
|
List<AbilitySub> choices = Lists.newArrayList(sa.getAdditionalAbilityList("Choices"));
|
||||||
if (restriction != null) {
|
if (restriction != null) {
|
||||||
List<AbilitySub> toRemove = Lists.newArrayList();
|
List<AbilitySub> toRemove = Lists.newArrayList();
|
||||||
for (Object o : restriction) {
|
|
||||||
if (o instanceof AbilitySub) {
|
|
||||||
String abText = ((AbilitySub)o).getDescription();
|
|
||||||
for (AbilitySub ch : choices) {
|
for (AbilitySub ch : choices) {
|
||||||
if (ch.getDescription().equals(abText)) {
|
if (restriction.contains(ch.getDescription())) {
|
||||||
toRemove.add(ch);
|
toRemove.add(ch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
choices.removeAll(toRemove);
|
choices.removeAll(toRemove);
|
||||||
}
|
}
|
||||||
// set CharmOrder
|
// set CharmOrder
|
||||||
@@ -54,55 +51,24 @@ public class CharmEffect extends SpellAbilityEffect {
|
|||||||
return choices;
|
return choices;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String makeSpellDescription(SpellAbility sa) {
|
|
||||||
int num = Integer.parseInt(sa.getParamOrDefault("CharmNum", "1"));
|
|
||||||
int min = Integer.parseInt(sa.getParamOrDefault("MinCharmNum", String.valueOf(num)));
|
|
||||||
boolean repeat = sa.hasParam("CanRepeatModes");
|
|
||||||
boolean random = sa.hasParam("Random");
|
|
||||||
boolean oppChooses = "Opponent".equals(sa.getParam("Chooser"));
|
|
||||||
|
|
||||||
List<AbilitySub> list = CharmEffect.makePossibleOptions(sa);
|
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
sb.append(sa.getCostDescription());
|
|
||||||
sb.append(oppChooses ? "An opponent chooses " : "Choose ");
|
|
||||||
|
|
||||||
if (num == min) {
|
|
||||||
sb.append(Lang.getNumeral(num));
|
|
||||||
} else if (min == 0) {
|
|
||||||
sb.append("up to ").append(Lang.getNumeral(num));
|
|
||||||
} else {
|
|
||||||
sb.append(Lang.getNumeral(min)).append(" or ").append(list.size() == 2 ? "both" : "more");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (random) {
|
|
||||||
sb.append("at random.");
|
|
||||||
}
|
|
||||||
if (repeat) {
|
|
||||||
sb.append(". You may choose the same mode more than once.");
|
|
||||||
}
|
|
||||||
sb.append(" - ");
|
|
||||||
int i = 0;
|
|
||||||
for (AbilitySub sub : list) {
|
|
||||||
if (i > 0) {
|
|
||||||
sb.append("; ");
|
|
||||||
}
|
|
||||||
sb.append(sub.getParam("SpellDescription"));
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String makeFormatedDescription(SpellAbility sa) {
|
public static String makeFormatedDescription(SpellAbility sa) {
|
||||||
int num = Integer.parseInt(sa.getParamOrDefault("CharmNum", "1"));
|
Card source = sa.getHostCard();
|
||||||
int min = Integer.parseInt(sa.getParamOrDefault("MinCharmNum", String.valueOf(num)));
|
|
||||||
|
List<AbilitySub> list = CharmEffect.makePossibleOptions(sa);
|
||||||
|
final int num;
|
||||||
|
// hotfix for Vindictive Lich when using getCardForUi
|
||||||
|
if (source.getController() == null && sa.getParamOrDefault("CharmNum", "1").contains("MaxUniqueOpponents")) {
|
||||||
|
// using getCardForUi game is not set, so can't guess max charm
|
||||||
|
num = Integer.MAX_VALUE;
|
||||||
|
} else {
|
||||||
|
num = Math.min(AbilityUtils.calculateAmount(source, sa.getParamOrDefault("CharmNum", "1"), sa), list.size());
|
||||||
|
}
|
||||||
|
final int min = sa.hasParam("MinCharmNum") ? AbilityUtils.calculateAmount(source, sa.getParamOrDefault("MinCharmNum", "1"), sa) : num;
|
||||||
|
|
||||||
boolean repeat = sa.hasParam("CanRepeatModes");
|
boolean repeat = sa.hasParam("CanRepeatModes");
|
||||||
boolean random = sa.hasParam("Random");
|
boolean random = sa.hasParam("Random");
|
||||||
boolean oppChooses = "Opponent".equals(sa.getParam("Chooser"));
|
boolean oppChooses = "Opponent".equals(sa.getParam("Chooser"));
|
||||||
|
|
||||||
List<AbilitySub> list = CharmEffect.makePossibleOptions(sa);
|
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append(sa.getCostDescription());
|
sb.append(sa.getCostDescription());
|
||||||
sb.append(oppChooses ? "An opponent chooses " : "Choose ");
|
sb.append(oppChooses ? "An opponent chooses " : "Choose ");
|
||||||
@@ -117,8 +83,10 @@ public class CharmEffect extends SpellAbilityEffect {
|
|||||||
|
|
||||||
if (sa.hasParam("ChoiceRestriction")) {
|
if (sa.hasParam("ChoiceRestriction")) {
|
||||||
String rest = sa.getParam("ChoiceRestriction");
|
String rest = sa.getParam("ChoiceRestriction");
|
||||||
if (rest.equals("NotRemembered")) {
|
if (rest.equals("ThisGame")) {
|
||||||
sb.append(" that hasn't been chosen");
|
sb.append(" that hasn't been chosen");
|
||||||
|
} else if (rest.equals("ThisTurn")) {
|
||||||
|
sb.append(" that hasn't been chosen this turn");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,24 +131,30 @@ public class CharmEffect extends SpellAbilityEffect {
|
|||||||
//this resets all previous choices
|
//this resets all previous choices
|
||||||
sa.setSubAbility(null);
|
sa.setSubAbility(null);
|
||||||
|
|
||||||
|
List<AbilitySub> choices = makePossibleOptions(sa);
|
||||||
|
|
||||||
// Entwine does use all Choices
|
// Entwine does use all Choices
|
||||||
if (sa.isEntwine()) {
|
if (sa.isEntwine()) {
|
||||||
chainAbilities(sa, makePossibleOptions(sa));
|
chainAbilities(sa, choices);
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
final int num = sa.hasParam("CharmNumOnResolve") ?
|
|
||||||
AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("CharmNumOnResolve"), sa)
|
|
||||||
: Integer.parseInt(sa.getParamOrDefault("CharmNum", "1"));
|
|
||||||
final int min = sa.hasParam("MinCharmNum") ? Integer.parseInt(sa.getParam("MinCharmNum")) : num;
|
|
||||||
|
|
||||||
if (sa.hasParam("Random")) {
|
|
||||||
chainAbilities(sa, Aggregates.random(makePossibleOptions(sa), num));
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Card source = sa.getHostCard();
|
Card source = sa.getHostCard();
|
||||||
Player activator = sa.getActivatingPlayer();
|
Player activator = sa.getActivatingPlayer();
|
||||||
|
|
||||||
|
final int num = Math.min(AbilityUtils.calculateAmount(source, sa.getParamOrDefault("CharmNum", "1"), sa), choices.size());
|
||||||
|
final int min = sa.hasParam("MinCharmNum") ? AbilityUtils.calculateAmount(source, sa.getParamOrDefault("MinCharmNum", "1"), sa) : num;
|
||||||
|
|
||||||
|
// if the amount of choices is smaller than min then they can't be chosen
|
||||||
|
if (min > choices.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sa.hasParam("Random")) {
|
||||||
|
chainAbilities(sa, Aggregates.random(choices, num));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
Player chooser = sa.getActivatingPlayer();
|
Player chooser = sa.getActivatingPlayer();
|
||||||
|
|
||||||
if (sa.hasParam("Chooser")) {
|
if (sa.hasParam("Chooser")) {
|
||||||
@@ -193,11 +167,18 @@ public class CharmEffect extends SpellAbilityEffect {
|
|||||||
source.setChosenPlayer(chooser);
|
source.setChosenPlayer(chooser);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<AbilitySub> chosen = chooser.getController().chooseModeForAbility(sa, min, num, sa.hasParam("CanRepeatModes"));
|
List<AbilitySub> chosen = chooser.getController().chooseModeForAbility(sa, choices, min, num, sa.hasParam("CanRepeatModes"));
|
||||||
chainAbilities(sa, chosen);
|
chainAbilities(sa, chosen);
|
||||||
|
|
||||||
|
// trigger without chosen modes are removed from stack
|
||||||
|
if (sa.isTrigger()) {
|
||||||
return chosen != null && !chosen.isEmpty();
|
return chosen != null && !chosen.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// for spells and activated abilities it is possible to chose zero if minCharmNum allows it
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private static void chainAbilities(SpellAbility sa, List<AbilitySub> chosen) {
|
private static void chainAbilities(SpellAbility sa, List<AbilitySub> chosen) {
|
||||||
|
|
||||||
if (chosen == null) {
|
if (chosen == null) {
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ public class StoreSVarEffect extends SpellAbilityEffect {
|
|||||||
int value = 0;
|
int value = 0;
|
||||||
|
|
||||||
if (type.equals("Count")) {
|
if (type.equals("Count")) {
|
||||||
value = CardFactoryUtil.xCount(source, expr);
|
value = AbilityUtils.xCount(source, expr, sa);
|
||||||
}
|
}
|
||||||
else if (type.equals("Number")) {
|
else if (type.equals("Number")) {
|
||||||
value = Integer.valueOf(expr);
|
value = Integer.valueOf(expr);
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ import forge.ImageKeys;
|
|||||||
import forge.StaticData;
|
import forge.StaticData;
|
||||||
import forge.card.*;
|
import forge.card.*;
|
||||||
import forge.card.CardDb.SetPreference;
|
import forge.card.CardDb.SetPreference;
|
||||||
import forge.card.CardType.CoreType;
|
|
||||||
import forge.card.mana.ManaCost;
|
import forge.card.mana.ManaCost;
|
||||||
import forge.card.mana.ManaCostParser;
|
import forge.card.mana.ManaCostParser;
|
||||||
import forge.game.*;
|
import forge.game.*;
|
||||||
@@ -34,7 +33,6 @@ import forge.game.ability.AbilityFactory;
|
|||||||
import forge.game.ability.AbilityKey;
|
import forge.game.ability.AbilityKey;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.ability.ApiType;
|
import forge.game.ability.ApiType;
|
||||||
import forge.game.ability.effects.CharmEffect;
|
|
||||||
import forge.game.combat.Combat;
|
import forge.game.combat.Combat;
|
||||||
import forge.game.cost.Cost;
|
import forge.game.cost.Cost;
|
||||||
import forge.game.cost.CostSacrifice;
|
import forge.game.cost.CostSacrifice;
|
||||||
@@ -280,6 +278,11 @@ public class Card extends GameEntity implements Comparable<Card> {
|
|||||||
private final Table<SpellAbility, StaticAbility, Integer> numberTurnActivationsStatic = HashBasedTable.create();
|
private final Table<SpellAbility, StaticAbility, Integer> numberTurnActivationsStatic = HashBasedTable.create();
|
||||||
private final Table<SpellAbility, StaticAbility, Integer> numberGameActivationsStatic = HashBasedTable.create();
|
private final Table<SpellAbility, StaticAbility, Integer> numberGameActivationsStatic = HashBasedTable.create();
|
||||||
|
|
||||||
|
private final Map<SpellAbility, List<String>> chosenModesTurn = Maps.newHashMap();
|
||||||
|
private final Map<SpellAbility, List<String>> chosenModesGame = Maps.newHashMap();
|
||||||
|
|
||||||
|
private final Table<SpellAbility, StaticAbility, List<String>> chosenModesTurnStatic = HashBasedTable.create();
|
||||||
|
private final Table<SpellAbility, StaticAbility, List<String>> chosenModesGameStatic = HashBasedTable.create();
|
||||||
|
|
||||||
// Enumeration for CMC request types
|
// Enumeration for CMC request types
|
||||||
public enum SplitCMCMode {
|
public enum SplitCMCMode {
|
||||||
@@ -664,6 +667,7 @@ public class Card extends GameEntity implements Comparable<Card> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
facedown = false;
|
facedown = false;
|
||||||
|
updateStateForView(); //fixes cards with backside viewable
|
||||||
// need to run faceup commands, currently
|
// need to run faceup commands, currently
|
||||||
// it does cleanup the modified facedown state
|
// it does cleanup the modified facedown state
|
||||||
if (result) {
|
if (result) {
|
||||||
@@ -1239,7 +1243,7 @@ public class Card extends GameEntity implements Comparable<Card> {
|
|||||||
final int loyaltyBefore = getCurrentLoyalty();
|
final int loyaltyBefore = getCurrentLoyalty();
|
||||||
|
|
||||||
setCounters(counterType, newValue);
|
setCounters(counterType, newValue);
|
||||||
getController().addCounterToPermThisTurn(counterType, addAmount);
|
getGame().addCounterAddedThisTurn(source, counterType, this, addAmount);
|
||||||
view.updateCounters(this);
|
view.updateCounters(this);
|
||||||
|
|
||||||
//fire card stats changed event if p/t bonuses or loyalty changed from added counters
|
//fire card stats changed event if p/t bonuses or loyalty changed from added counters
|
||||||
@@ -1267,7 +1271,8 @@ public class Card extends GameEntity implements Comparable<Card> {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setCounters(counterType, newValue);
|
setCounters(counterType, newValue);
|
||||||
getController().addCounterToPermThisTurn(counterType, addAmount);
|
|
||||||
|
getGame().addCounterAddedThisTurn(source, counterType, this, addAmount);
|
||||||
view.updateCounters(this);
|
view.updateCounters(this);
|
||||||
}
|
}
|
||||||
if (newValue <= 0) {
|
if (newValue <= 0) {
|
||||||
@@ -1714,8 +1719,7 @@ public class Card extends GameEntity implements Comparable<Card> {
|
|||||||
final String costString2 = keyword.split(":")[2];
|
final String costString2 = keyword.split(":")[2];
|
||||||
final Cost cost1 = new Cost(costString1, false);
|
final Cost cost1 = new Cost(costString1, false);
|
||||||
final Cost cost2 = new Cost(costString2, false);
|
final Cost cost2 = new Cost(costString2, false);
|
||||||
sbLong.append("As an additional cost to cast ")
|
sbLong.append("As an additional cost to cast this spell, ")
|
||||||
.append(getName()).append(", ")
|
|
||||||
.append(cost1.toSimpleString())
|
.append(cost1.toSimpleString())
|
||||||
.append(" or pay ")
|
.append(" or pay ")
|
||||||
.append(cost2.toSimpleString())
|
.append(cost2.toSimpleString())
|
||||||
@@ -2070,8 +2074,9 @@ public class Card extends GameEntity implements Comparable<Card> {
|
|||||||
}
|
}
|
||||||
final Card host = stAb.getHostCard();
|
final Card host = stAb.getHostCard();
|
||||||
if (isValid(stAb.getParam("ValidAttacker").split(","), host.getController(), host, null)) {
|
if (isValid(stAb.getParam("ValidAttacker").split(","), host.getController(), host, null)) {
|
||||||
String desc = stAb.toString();
|
String currentName = (host.getName());
|
||||||
desc = TextUtil.fastReplace(desc, "CARDNAME", host.getName());
|
String desc1 = TextUtil.fastReplace(stAb.toString(), "CARDNAME", currentName);
|
||||||
|
String desc = TextUtil.fastReplace(desc1,"NICKNAME", currentName.split(",")[0]);
|
||||||
if (host.getEffectSource() != null) {
|
if (host.getEffectSource() != null) {
|
||||||
desc = TextUtil.fastReplace(desc, "EFFECTSOURCE", host.getEffectSource().getName());
|
desc = TextUtil.fastReplace(desc, "EFFECTSOURCE", host.getEffectSource().getName());
|
||||||
}
|
}
|
||||||
@@ -2203,8 +2208,7 @@ public class Card extends GameEntity implements Comparable<Card> {
|
|||||||
final String[] k = keyword.split(":");
|
final String[] k = keyword.split(":");
|
||||||
final Cost cost1 = new Cost(k[1], false);
|
final Cost cost1 = new Cost(k[1], false);
|
||||||
final Cost cost2 = new Cost(k[2], false);
|
final Cost cost2 = new Cost(k[2], false);
|
||||||
sbBefore.append("As an additional cost to cast ")
|
sbBefore.append("As an additional cost to cast this spell, ")
|
||||||
.append(state.getName()).append(", ")
|
|
||||||
.append(cost1.toSimpleString())
|
.append(cost1.toSimpleString())
|
||||||
.append(" or pay ")
|
.append(" or pay ")
|
||||||
.append(cost2.toSimpleString())
|
.append(cost2.toSimpleString())
|
||||||
@@ -2325,14 +2329,7 @@ public class Card extends GameEntity implements Comparable<Card> {
|
|||||||
|
|
||||||
private String formatSpellAbility(final SpellAbility sa) {
|
private String formatSpellAbility(final SpellAbility sa) {
|
||||||
final StringBuilder sb = new StringBuilder();
|
final StringBuilder sb = new StringBuilder();
|
||||||
final String elementText = sa.toString();
|
sb.append(sa.toString()).append("\r\n");
|
||||||
|
|
||||||
//Determine if a card has multiple choices, then format it in an easier to read list.
|
|
||||||
if (ApiType.Charm.equals(sa.getApi())) {
|
|
||||||
sb.append(CharmEffect.makeFormatedDescription(sa));
|
|
||||||
} else {
|
|
||||||
sb.append(elementText).append("\r\n");
|
|
||||||
}
|
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3100,6 +3097,10 @@ public class Card extends GameEntity implements Comparable<Card> {
|
|||||||
currentState.addType(type0);
|
currentState.addType(type0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final void addType(final Iterable<String> type0) {
|
||||||
|
currentState.addType(type0);
|
||||||
|
}
|
||||||
|
|
||||||
public final void removeType(final CardType.Supertype st) {
|
public final void removeType(final CardType.Supertype st) {
|
||||||
currentState.removeType(st);
|
currentState.removeType(st);
|
||||||
}
|
}
|
||||||
@@ -3197,11 +3198,11 @@ public class Card extends GameEntity implements Comparable<Card> {
|
|||||||
CardType addType = null;
|
CardType addType = null;
|
||||||
CardType removeType = null;
|
CardType removeType = null;
|
||||||
if (types != null) {
|
if (types != null) {
|
||||||
addType = new CardType(types);
|
addType = new CardType(types, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (removeTypes != null) {
|
if (removeTypes != null) {
|
||||||
removeType = new CardType(removeTypes);
|
removeType = new CardType(removeTypes, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
addChangedCardTypes(addType, removeType, removeSuperTypes, removeCardTypes, removeSubTypes,
|
addChangedCardTypes(addType, removeType, removeSuperTypes, removeCardTypes, removeSubTypes,
|
||||||
@@ -3962,7 +3963,7 @@ public class Card extends GameEntity implements Comparable<Card> {
|
|||||||
public final void addChangedTextTypeWord(final String originalWord, final String newWord, final Long timestamp) {
|
public final void addChangedTextTypeWord(final String originalWord, final String newWord, final Long timestamp) {
|
||||||
changedTextTypes.add(timestamp, originalWord, newWord);
|
changedTextTypes.add(timestamp, originalWord, newWord);
|
||||||
if (getType().hasSubtype(originalWord)) {
|
if (getType().hasSubtype(originalWord)) {
|
||||||
addChangedCardTypes(CardType.parse(newWord), CardType.parse(originalWord),
|
addChangedCardTypes(CardType.parse(newWord, true), CardType.parse(originalWord, true),
|
||||||
false, false, false, false, false, false, false, timestamp);
|
false, false, false, false, false, false, false, timestamp);
|
||||||
}
|
}
|
||||||
updateKeywordsChangedText(timestamp);
|
updateKeywordsChangedText(timestamp);
|
||||||
@@ -4603,84 +4604,34 @@ public class Card extends GameEntity implements Comparable<Card> {
|
|||||||
if (c1 == null) {
|
if (c1 == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
return getType().sharesCreaturetypeWith(c1.getType());
|
||||||
for (final String type : getType().getCreatureTypes()) {
|
|
||||||
if (type.equals("AllCreatureTypes") && c1.hasACreatureType()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (c1.getType().hasCreatureType(type)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public final boolean sharesLandTypeWith(final Card c1) {
|
public final boolean sharesLandTypeWith(final Card c1) {
|
||||||
if (c1 == null) {
|
if (c1 == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
return getType().sharesLandTypeWith(c1.getType());
|
||||||
for (final String type : getType().getLandTypes()) {
|
|
||||||
if (c1.getType().hasSubtype(type)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public final boolean sharesPermanentTypeWith(final Card c1) {
|
public final boolean sharesPermanentTypeWith(final Card c1) {
|
||||||
if (c1 == null) {
|
if (c1 == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
return getType().sharesPermanentTypeWith(c1.getType());
|
||||||
for (final CoreType type : getType().getCoreTypes()) {
|
|
||||||
if (type.isPermanent && c1.getType().hasType(type)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public final boolean sharesCardTypeWith(final Card c1) {
|
public final boolean sharesCardTypeWith(final Card c1) {
|
||||||
for (final CoreType type : getType().getCoreTypes()) {
|
if (c1 == null) {
|
||||||
if (c1.getType().hasType(type)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
return getType().sharesCardTypeWith(c1.getType());
|
||||||
public final boolean sharesTypeWith(final Card c1) {
|
|
||||||
for (final String type : getType()) {
|
|
||||||
if (c1.getType().hasStringType(type)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public final boolean sharesControllerWith(final Card c1) {
|
public final boolean sharesControllerWith(final Card c1) {
|
||||||
return c1 != null && getController().equals(c1.getController());
|
return c1 != null && getController().equals(c1.getController());
|
||||||
}
|
}
|
||||||
|
|
||||||
public final boolean hasACreatureType() {
|
|
||||||
for (final String type : getType().getSubtypes()) {
|
|
||||||
if (forge.card.CardType.isACreatureType(type) || type.equals("AllCreatureTypes")) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final boolean hasALandType() {
|
|
||||||
for (final String type : getType().getSubtypes()) {
|
|
||||||
if (forge.card.CardType.isALandType(type) || forge.card.CardType.isABasicLandType(type)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final boolean hasABasicLandType() {
|
public final boolean hasABasicLandType() {
|
||||||
for (final String type : getType().getSubtypes()) {
|
for (final String type : getType().getSubtypes()) {
|
||||||
if (forge.card.CardType.isABasicLandType(type)) {
|
if (forge.card.CardType.isABasicLandType(type)) {
|
||||||
@@ -5316,8 +5267,8 @@ public class Card extends GameEntity implements Comparable<Card> {
|
|||||||
|
|
||||||
public final void animateBestow(final boolean updateView) {
|
public final void animateBestow(final boolean updateView) {
|
||||||
bestowTimestamp = getGame().getNextTimestamp();
|
bestowTimestamp = getGame().getNextTimestamp();
|
||||||
addChangedCardTypes(new CardType(Collections.singletonList("Aura")),
|
addChangedCardTypes(new CardType(Collections.singletonList("Aura"), true),
|
||||||
new CardType(Collections.singletonList("Creature")),
|
new CardType(Collections.singletonList("Creature"), true),
|
||||||
false, false, false, false, false, false, true, bestowTimestamp, updateView);
|
false, false, false, false, false, false, true, bestowTimestamp, updateView);
|
||||||
addChangedCardKeywords(Collections.singletonList("Enchant creature"), Lists.newArrayList(),
|
addChangedCardKeywords(Collections.singletonList("Enchant creature"), Lists.newArrayList(),
|
||||||
false, false, bestowTimestamp, updateView);
|
false, false, bestowTimestamp, updateView);
|
||||||
@@ -5915,6 +5866,7 @@ public class Card extends GameEntity implements Comparable<Card> {
|
|||||||
clearBlockedThisTurn();
|
clearBlockedThisTurn();
|
||||||
resetMayPlayTurn();
|
resetMayPlayTurn();
|
||||||
resetExtertedThisTurn();
|
resetExtertedThisTurn();
|
||||||
|
resetChosenModeTurn();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasETBTrigger(final boolean drawbackOnly) {
|
public boolean hasETBTrigger(final boolean drawbackOnly) {
|
||||||
@@ -6045,6 +5997,15 @@ public class Card extends GameEntity implements Comparable<Card> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isPermanent() && !isLand() && source.getActivatingPlayer().hasKeyword("You can't sacrifice nonland permanents to cast spells or activate abilities.")) {
|
||||||
|
Cost srcCost = source.getPayCosts();
|
||||||
|
if (srcCost != null) {
|
||||||
|
if (srcCost.hasSpecificCostType(CostSacrifice.class)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return getController().canSacrificeBy(source);
|
return getController().canSacrificeBy(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6582,6 +6543,94 @@ public class Card extends GameEntity implements Comparable<Card> {
|
|||||||
numberTurnActivationsStatic.clear();
|
numberTurnActivationsStatic.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<String> getChosenModesTurn(SpellAbility ability) {
|
||||||
|
SpellAbility original = null;
|
||||||
|
SpellAbility root = ability.getRootAbility();
|
||||||
|
|
||||||
|
// because trigger spell abilities are copied, try to get original one
|
||||||
|
if (root.isTrigger()) {
|
||||||
|
original = root.getTrigger().getOverridingAbility();
|
||||||
|
} else {
|
||||||
|
original = ability.getOriginalAbility();
|
||||||
|
if (original == null) {
|
||||||
|
original = ability;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ability.getGrantorStatic() != null) {
|
||||||
|
return chosenModesTurnStatic.get(original, ability.getGrantorStatic());
|
||||||
|
}
|
||||||
|
return chosenModesTurn.get(original);
|
||||||
|
}
|
||||||
|
public List<String> getChosenModesGame(SpellAbility ability) {
|
||||||
|
SpellAbility original = null;
|
||||||
|
SpellAbility root = ability.getRootAbility();
|
||||||
|
|
||||||
|
// because trigger spell abilities are copied, try to get original one
|
||||||
|
if (root.isTrigger()) {
|
||||||
|
original = root.getTrigger().getOverridingAbility();
|
||||||
|
} else {
|
||||||
|
original = ability.getOriginalAbility();
|
||||||
|
if (original == null) {
|
||||||
|
original = ability;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ability.getGrantorStatic() != null) {
|
||||||
|
return chosenModesGameStatic.get(original, ability.getGrantorStatic());
|
||||||
|
}
|
||||||
|
return chosenModesGame.get(original);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addChosenModes(SpellAbility ability, String mode) {
|
||||||
|
SpellAbility original = null;
|
||||||
|
SpellAbility root = ability.getRootAbility();
|
||||||
|
|
||||||
|
// because trigger spell abilities are copied, try to get original one
|
||||||
|
if (root.isTrigger()) {
|
||||||
|
original = root.getTrigger().getOverridingAbility();
|
||||||
|
} else {
|
||||||
|
original = ability.getOriginalAbility();
|
||||||
|
if (original == null) {
|
||||||
|
original = ability;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ability.getGrantorStatic() != null) {
|
||||||
|
List<String> result = chosenModesTurnStatic.get(original, ability.getGrantorStatic());
|
||||||
|
if (result == null) {
|
||||||
|
result = Lists.newArrayList();
|
||||||
|
chosenModesTurnStatic.put(original, ability.getGrantorStatic(), result);
|
||||||
|
}
|
||||||
|
result.add(mode);
|
||||||
|
result = chosenModesGameStatic.get(original, ability.getGrantorStatic());
|
||||||
|
if (result == null) {
|
||||||
|
result = Lists.newArrayList();
|
||||||
|
chosenModesGameStatic.put(original, ability.getGrantorStatic(), result);
|
||||||
|
}
|
||||||
|
result.add(mode);
|
||||||
|
} else {
|
||||||
|
List<String> result = chosenModesTurn.get(original);
|
||||||
|
if (result == null) {
|
||||||
|
result = Lists.newArrayList();
|
||||||
|
chosenModesTurn.put(original, result);
|
||||||
|
}
|
||||||
|
result.add(mode);
|
||||||
|
|
||||||
|
result = chosenModesGame.get(original);
|
||||||
|
if (result == null) {
|
||||||
|
result = Lists.newArrayList();
|
||||||
|
chosenModesGame.put(original, result);
|
||||||
|
}
|
||||||
|
result.add(mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resetChosenModeTurn() {
|
||||||
|
chosenModesTurn.clear();
|
||||||
|
chosenModesTurnStatic.clear();
|
||||||
|
}
|
||||||
|
|
||||||
public int getPlaneswalkerAbilityActivated() {
|
public int getPlaneswalkerAbilityActivated() {
|
||||||
return planeswalkerAbilityActivated;
|
return planeswalkerAbilityActivated;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -587,7 +587,7 @@ public class CardFactory {
|
|||||||
String shortColors = "";
|
String shortColors = "";
|
||||||
|
|
||||||
if (sa.hasParam("AddTypes")) {
|
if (sa.hasParam("AddTypes")) {
|
||||||
types.addAll(Arrays.asList(sa.getParam("AddTypes").split(",")));
|
types.addAll(Arrays.asList(sa.getParam("AddTypes").split(" & ")));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sa.hasParam("AddKeywords")) {
|
if (sa.hasParam("AddKeywords")) {
|
||||||
@@ -654,9 +654,7 @@ public class CardFactory {
|
|||||||
state.removeType(CardType.Supertype.Legendary);
|
state.removeType(CardType.Supertype.Legendary);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (final String type : types) {
|
state.addType(types);
|
||||||
state.addType(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (creatureTypes != null) {
|
if (creatureTypes != null) {
|
||||||
state.setCreatureTypes(creatureTypes);
|
state.setCreatureTypes(creatureTypes);
|
||||||
@@ -731,7 +729,8 @@ public class CardFactory {
|
|||||||
String name = TextUtil.fastReplace(
|
String name = TextUtil.fastReplace(
|
||||||
TextUtil.fastReplace(host.getName(), ",", ""),
|
TextUtil.fastReplace(host.getName(), ",", ""),
|
||||||
" ", "_").toLowerCase();
|
" ", "_").toLowerCase();
|
||||||
state.setImageKey(ImageKeys.getTokenKey("embalm_" + name));
|
String set = host.getSetCode().toLowerCase();
|
||||||
|
state.setImageKey(ImageKeys.getTokenKey("embalm_" + name + "_" + set));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sa.hasParam("Eternalize") && out.isEternalized()) {
|
if (sa.hasParam("Eternalize") && out.isEternalized()) {
|
||||||
@@ -744,7 +743,8 @@ public class CardFactory {
|
|||||||
String name = TextUtil.fastReplace(
|
String name = TextUtil.fastReplace(
|
||||||
TextUtil.fastReplace(host.getName(), ",", ""),
|
TextUtil.fastReplace(host.getName(), ",", ""),
|
||||||
" ", "_").toLowerCase();
|
" ", "_").toLowerCase();
|
||||||
state.setImageKey(ImageKeys.getTokenKey("eternalize_" + name));
|
String set = host.getSetCode().toLowerCase();
|
||||||
|
state.setImageKey(ImageKeys.getTokenKey("eternalize_" + name + "_" + set));
|
||||||
}
|
}
|
||||||
|
|
||||||
// set the host card for copied replacement effects
|
// set the host card for copied replacement effects
|
||||||
@@ -793,7 +793,7 @@ public class CardFactory {
|
|||||||
if (sa.hasParam("SetCreatureTypes")) {
|
if (sa.hasParam("SetCreatureTypes")) {
|
||||||
// currently only Changeling and similar should be affected by that
|
// currently only Changeling and similar should be affected by that
|
||||||
// other cards using AddType$ ChosenType should not
|
// other cards using AddType$ ChosenType should not
|
||||||
if (sta.hasParam("AddType") && "AllCreatureTypes".equals(sta.getParam("AddType"))) {
|
if (sta.hasParam("AddType") && CardType.AllCreatureTypes.equals(sta.getParam("AddType"))) {
|
||||||
state.removeStaticAbility(sta);
|
state.removeStaticAbility(sta);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -841,14 +841,6 @@ public class CardFactoryUtil {
|
|||||||
return doXMath(maxNum, m, c);
|
return doXMath(maxNum, m, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Count$CountersAddedToPermYouCtrl <CounterType>
|
|
||||||
if (l[0].startsWith("CountersAddedToPermYouCtrl")) {
|
|
||||||
final String[] components = l[0].split(" ", 2);
|
|
||||||
final CounterType counterType = CounterType.getType(components[1]);
|
|
||||||
int n = cc.getCounterToPermThisTurn(counterType);
|
|
||||||
return doXMath(n, m, c);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (l[0].startsWith("CommanderCastFromCommandZone")) {
|
if (l[0].startsWith("CommanderCastFromCommandZone")) {
|
||||||
// only used by Opal Palace, and it does add the trigger to the card
|
// only used by Opal Palace, and it does add the trigger to the card
|
||||||
return doXMath(cc.getCommanderCast(c), m, c);
|
return doXMath(cc.getCommanderCast(c), m, c);
|
||||||
@@ -1124,7 +1116,7 @@ public class CardFactoryUtil {
|
|||||||
for (Card card : cards) {
|
for (Card card : cards) {
|
||||||
Iterables.addAll(creatTypes, card.getType().getCreatureTypes());
|
Iterables.addAll(creatTypes, card.getType().getCreatureTypes());
|
||||||
}
|
}
|
||||||
int n = creatTypes.contains("AllCreatureTypes") ? CardType.getAllCreatureTypes().size() : creatTypes.size();
|
int n = creatTypes.contains(CardType.AllCreatureTypes) ? CardType.getAllCreatureTypes().size() : creatTypes.size();
|
||||||
return doXMath(n, m, c);
|
return doXMath(n, m, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1206,7 +1198,7 @@ public class CardFactoryUtil {
|
|||||||
// Figure out how to count each class separately.
|
// Figure out how to count each class separately.
|
||||||
for (Card card : adventurers) {
|
for (Card card : adventurers) {
|
||||||
Set<String> creatureTypes = card.getType().getCreatureTypes();
|
Set<String> creatureTypes = card.getType().getCreatureTypes();
|
||||||
boolean anyType = creatureTypes.contains("AllCreatureTypes");
|
boolean anyType = creatureTypes.contains(CardType.AllCreatureTypes);
|
||||||
creatureTypes.retainAll(partyTypes);
|
creatureTypes.retainAll(partyTypes);
|
||||||
|
|
||||||
if (anyType || creatureTypes.size() == 4) {
|
if (anyType || creatureTypes.size() == 4) {
|
||||||
@@ -2005,7 +1997,7 @@ public class CardFactoryUtil {
|
|||||||
// Remove Duplicated types
|
// Remove Duplicated types
|
||||||
final Set<String> creatureTypes = c.getType().getCreatureTypes();
|
final Set<String> creatureTypes = c.getType().getCreatureTypes();
|
||||||
for (String creatureType : creatureTypes) {
|
for (String creatureType : creatureTypes) {
|
||||||
if (creatureType.equals("AllCreatureTypes")) {
|
if (creatureType.equals(CardType.AllCreatureTypes)) {
|
||||||
allCreatureType++;
|
allCreatureType++;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -2913,7 +2905,7 @@ public class CardFactoryUtil {
|
|||||||
|
|
||||||
final String repeatStr = "DB$ RepeatEach | RepeatPlayers$ OpponentsOtherThanDefendingPlayer | ChangeZoneTable$ True";
|
final String repeatStr = "DB$ RepeatEach | RepeatPlayers$ OpponentsOtherThanDefendingPlayer | ChangeZoneTable$ True";
|
||||||
|
|
||||||
final String copyStr = "DB$ CopyPermanent | Defined$ Self | Tapped$ True | Optional$ True | TokenAttacking$ Remembered"
|
final String copyStr = "DB$ CopyPermanent | Defined$ Self | TokenTapped$ True | Optional$ True | TokenAttacking$ Remembered"
|
||||||
+ " | ChoosePlayerOrPlaneswalker$ True | ImprintTokens$ True";
|
+ " | ChoosePlayerOrPlaneswalker$ True | ImprintTokens$ True";
|
||||||
|
|
||||||
final String delTrigStr = "DB$ DelayedTrigger | Mode$ Phase | Phase$ EndCombat | RememberObjects$ Imprinted"
|
final String delTrigStr = "DB$ DelayedTrigger | Mode$ Phase | Phase$ EndCombat | RememberObjects$ Imprinted"
|
||||||
@@ -4693,8 +4685,10 @@ public class CardFactoryUtil {
|
|||||||
}
|
}
|
||||||
altCostSA.setRestrictions(restriction);
|
altCostSA.setRestrictions(restriction);
|
||||||
|
|
||||||
final String costDescription = params.containsKey("Description") ? params.get("Description")
|
String costDescription = TextUtil.fastReplace(params.get("Description"),"CARDNAME", card.getName());
|
||||||
: TextUtil.concatWithSpace("You may", abCost.toStringAlt(),"rather than pay", TextUtil.addSuffix(card.getName(),"'s mana cost."));
|
if (costDescription.isEmpty()) {
|
||||||
|
costDescription = TextUtil.concatWithSpace("You may", abCost.toStringAlt(), "rather than pay", TextUtil.addSuffix(card.getName(), "'s mana cost."));
|
||||||
|
}
|
||||||
|
|
||||||
altCostSA.setDescription(costDescription);
|
altCostSA.setDescription(costDescription);
|
||||||
if (params.containsKey("References")) {
|
if (params.containsKey("References")) {
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ import io.sentry.event.BreadcrumbBuilder;
|
|||||||
|
|
||||||
public class CardState extends GameObject {
|
public class CardState extends GameObject {
|
||||||
private String name = "";
|
private String name = "";
|
||||||
private CardType type = new CardType();
|
private CardType type = new CardType(false);
|
||||||
private ManaCost manaCost = ManaCost.NO_COST;
|
private ManaCost manaCost = ManaCost.NO_COST;
|
||||||
private byte color = MagicColor.COLORLESS;
|
private byte color = MagicColor.COLORLESS;
|
||||||
private int basePower = 0;
|
private int basePower = 0;
|
||||||
@@ -116,6 +116,11 @@ public class CardState extends GameObject {
|
|||||||
view.updateType(this);
|
view.updateType(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public final void addType(Iterable<String> type0) {
|
||||||
|
if (type.addAll(type0)) {
|
||||||
|
view.updateType(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
public final void setType(final CardType type0) {
|
public final void setType(final CardType type0) {
|
||||||
if (type0 == type) {
|
if (type0 == type) {
|
||||||
// Logic below would incorrectly clear the type if it's the same object.
|
// Logic below would incorrectly clear the type if it's the same object.
|
||||||
|
|||||||
@@ -327,7 +327,7 @@ public final class CardUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static CardState getFaceDownCharacteristic(Card c) {
|
public static CardState getFaceDownCharacteristic(Card c) {
|
||||||
final CardType type = new CardType();
|
final CardType type = new CardType(false);
|
||||||
type.add("Creature");
|
type.add("Creature");
|
||||||
|
|
||||||
final CardState ret = new CardState(c, CardStateName.FaceDown);
|
final CardState ret = new CardState(c, CardStateName.FaceDown);
|
||||||
@@ -337,7 +337,8 @@ public final class CardUtil {
|
|||||||
ret.setName("");
|
ret.setName("");
|
||||||
ret.setType(type);
|
ret.setType(type);
|
||||||
|
|
||||||
ret.setImageKey(ImageKeys.getTokenKey(ImageKeys.MORPH_IMAGE));
|
//show hidden if exiled facedown
|
||||||
|
ret.setImageKey(ImageKeys.getTokenKey(c.isInZone(ZoneType.Exile) ? ImageKeys.HIDDEN_CARD : ImageKeys.MORPH_IMAGE));
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -134,6 +134,18 @@ public class CardView extends GameEntityView {
|
|||||||
return get(TrackableProperty.SplitCard);
|
return get(TrackableProperty.SplitCard);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isDoubleFacedCard() {
|
||||||
|
return get(TrackableProperty.DoubleFaced);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAdventureCard() {
|
||||||
|
return get(TrackableProperty.Adventure);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isModalCard() {
|
||||||
|
return get(TrackableProperty.Modal);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
public boolean isTransformed() {
|
public boolean isTransformed() {
|
||||||
return getCurrentState().getState() == CardStateName.Transformed;
|
return getCurrentState().getState() == CardStateName.Transformed;
|
||||||
@@ -678,6 +690,12 @@ public class CardView extends GameEntityView {
|
|||||||
public CardStateView getAlternateState() {
|
public CardStateView getAlternateState() {
|
||||||
return get(TrackableProperty.AlternateState);
|
return get(TrackableProperty.AlternateState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean hasBackSide() {
|
||||||
|
return get(TrackableProperty.HasBackSide);
|
||||||
|
}
|
||||||
|
public String getBackSideName() { return get(TrackableProperty.BackSideName); }
|
||||||
|
|
||||||
CardStateView createAlternateState(final CardStateName state0) {
|
CardStateView createAlternateState(final CardStateName state0) {
|
||||||
return new CardStateView(getId(), state0, tracker);
|
return new CardStateView(getId(), state0, tracker);
|
||||||
}
|
}
|
||||||
@@ -685,6 +703,10 @@ public class CardView extends GameEntityView {
|
|||||||
public CardStateView getState(final boolean alternate0) {
|
public CardStateView getState(final boolean alternate0) {
|
||||||
return alternate0 ? getAlternateState() : getCurrentState();
|
return alternate0 ? getAlternateState() : getCurrentState();
|
||||||
}
|
}
|
||||||
|
void updateBackSide(String stateName, boolean hasBackSide) {
|
||||||
|
set(TrackableProperty.HasBackSide, hasBackSide);
|
||||||
|
set(TrackableProperty.BackSideName, stateName);
|
||||||
|
}
|
||||||
void updateState(Card c) {
|
void updateState(Card c) {
|
||||||
updateName(c);
|
updateName(c);
|
||||||
updateDamage(c);
|
updateDamage(c);
|
||||||
@@ -694,6 +716,13 @@ public class CardView extends GameEntityView {
|
|||||||
set(TrackableProperty.SplitCard, isSplitCard);
|
set(TrackableProperty.SplitCard, isSplitCard);
|
||||||
set(TrackableProperty.FlipCard, c.isFlipCard());
|
set(TrackableProperty.FlipCard, c.isFlipCard());
|
||||||
set(TrackableProperty.Facedown, c.isFaceDown());
|
set(TrackableProperty.Facedown, c.isFaceDown());
|
||||||
|
set(TrackableProperty.Adventure, c.isAdventureCard());
|
||||||
|
set(TrackableProperty.DoubleFaced, c.isDoubleFaced());
|
||||||
|
set(TrackableProperty.Modal, c.isModal());
|
||||||
|
|
||||||
|
//backside
|
||||||
|
if (c.getAlternateState()!=null)
|
||||||
|
updateBackSide(c.getAlternateState().getName(), c.hasBackSide());
|
||||||
|
|
||||||
final Card cloner = c.getCloner();
|
final Card cloner = c.getCloner();
|
||||||
|
|
||||||
@@ -733,6 +762,9 @@ public class CardView extends GameEntityView {
|
|||||||
alternateState = c.getState(CardStateName.Original);
|
alternateState = c.getState(CardStateName.Original);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (c.hasBackSide() && isFaceDown()) //fixes facedown cards with backside...
|
||||||
|
alternateState = c.getState(CardStateName.Original);
|
||||||
|
|
||||||
if (alternateState == null) {
|
if (alternateState == null) {
|
||||||
set(TrackableProperty.AlternateState, null);
|
set(TrackableProperty.AlternateState, null);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -706,7 +706,7 @@ public class Cost implements Serializable {
|
|||||||
boolean first = true;
|
boolean first = true;
|
||||||
|
|
||||||
if (bFlag) {
|
if (bFlag) {
|
||||||
cost.append("As an additional cost to cast CARDNAME, ");
|
cost.append("As an additional cost to cast this spell, ");
|
||||||
} else {
|
} else {
|
||||||
// usually no additional mana cost for spells
|
// usually no additional mana cost for spells
|
||||||
// only three Alliances cards have additional mana costs, but they
|
// only three Alliances cards have additional mana costs, but they
|
||||||
@@ -724,7 +724,11 @@ public class Cost implements Serializable {
|
|||||||
if (!first) {
|
if (!first) {
|
||||||
cost.append(" and ");
|
cost.append(" and ");
|
||||||
}
|
}
|
||||||
|
if (bFlag) {
|
||||||
|
cost.append(StringUtils.uncapitalize(part.toString()));
|
||||||
|
} else {
|
||||||
cost.append(part.toString());
|
cost.append(part.toString());
|
||||||
|
}
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ public enum Keyword {
|
|||||||
DOUBLE_STRIKE("Double Strike", SimpleKeyword.class, true, "This creature deals both first-strike and regular combat damage."),
|
DOUBLE_STRIKE("Double Strike", SimpleKeyword.class, true, "This creature deals both first-strike and regular combat damage."),
|
||||||
DREDGE("Dredge", KeywordWithAmount.class, false, "If you would draw a card, instead you may put exactly {%d:card} from the top of your library into your graveyard. If you do, return this card from your graveyard to your hand. Otherwise, draw a card."),
|
DREDGE("Dredge", KeywordWithAmount.class, false, "If you would draw a card, instead you may put exactly {%d:card} from the top of your library into your graveyard. If you do, return this card from your graveyard to your hand. Otherwise, draw a card."),
|
||||||
ECHO("Echo", KeywordWithCost.class, false, "At the beginning of your upkeep, if this permanent came under your control since the beginning of your last upkeep, sacrifice it unless you pay %s."),
|
ECHO("Echo", KeywordWithCost.class, false, "At the beginning of your upkeep, if this permanent came under your control since the beginning of your last upkeep, sacrifice it unless you pay %s."),
|
||||||
EMBALM("Embalm", KeywordWithCost.class, false, "Create a token that's a copy of this card, except it's white, it has no mana cost, and it's a Zombie in addition to its other types. Embalm only as a sorcery."),
|
EMBALM("Embalm", KeywordWithCost.class, false, "%s, Exile this card from your graveyard: Create a token that's a copy of this card, except it's white, it has no mana cost, and it's a Zombie in addition to its other types. Embalm only as a sorcery."),
|
||||||
EMERGE("Emerge", KeywordWithCost.class, false, "You may cast this spell by sacrificing a creature and paying the emerge cost reduced by that creature's converted mana cost."),
|
EMERGE("Emerge", KeywordWithCost.class, false, "You may cast this spell by sacrificing a creature and paying the emerge cost reduced by that creature's converted mana cost."),
|
||||||
ENCHANT("Enchant", KeywordWithType.class, false, "Target a %s as you cast this. This card enters the battlefield attached to that %s."),
|
ENCHANT("Enchant", KeywordWithType.class, false, "Target a %s as you cast this. This card enters the battlefield attached to that %s."),
|
||||||
ENTWINE("Entwine", KeywordWithCost.class, true, "You may choose all modes of this spell instead of just one. If you do, you pay an additional %s."),
|
ENTWINE("Entwine", KeywordWithCost.class, true, "You may choose all modes of this spell instead of just one. If you do, you pay an additional %s."),
|
||||||
@@ -54,7 +54,7 @@ public enum Keyword {
|
|||||||
EQUIP("Equip", Equip.class, false, "%s: Attach to target %s you control. Equip only as a sorcery."),
|
EQUIP("Equip", Equip.class, false, "%s: Attach to target %s you control. Equip only as a sorcery."),
|
||||||
ESCAPE("Escape", KeywordWithCost.class, false, "You may cast this card from your graveyard for its escape cost."),
|
ESCAPE("Escape", KeywordWithCost.class, false, "You may cast this card from your graveyard for its escape cost."),
|
||||||
ESCALATE("Escalate", KeywordWithCost.class, true, "Pay this cost for each mode chosen beyond the first."),
|
ESCALATE("Escalate", KeywordWithCost.class, true, "Pay this cost for each mode chosen beyond the first."),
|
||||||
ETERNALIZE("Eternalize", KeywordWithCost.class, false, "Create a token that's a copy of this card, except it's black, it's 4/4, it has no mana cost, and it's a Zombie in addition to its other types. Eternalize only as a sorcery."),
|
ETERNALIZE("Eternalize", KeywordWithCost.class, false, "%s, Exile this card from your graveyard: Create a token that's a copy of this card, except it's black, it's 4/4, it has no mana cost, and it's a Zombie in addition to its other types. Eternalize only as a sorcery."),
|
||||||
EVOKE("Evoke", KeywordWithCost.class, false, "You may cast this spell for its evoke cost. If you do, it's sacrificed when it enters the battlefield."),
|
EVOKE("Evoke", KeywordWithCost.class, false, "You may cast this spell for its evoke cost. If you do, it's sacrificed when it enters the battlefield."),
|
||||||
EVOLVE("Evolve", SimpleKeyword.class, false, "Whenever a creature enters the battlefield under your control, if that creature has greater power or toughness than this creature, put a +1/+1 counter on this creature."),
|
EVOLVE("Evolve", SimpleKeyword.class, false, "Whenever a creature enters the battlefield under your control, if that creature has greater power or toughness than this creature, put a +1/+1 counter on this creature."),
|
||||||
EXALTED("Exalted", SimpleKeyword.class, false, "Whenever a creature you control attacks alone, that creature gets +1/+1 until end of turn."),
|
EXALTED("Exalted", SimpleKeyword.class, false, "Whenever a creature you control attacks alone, that creature gets +1/+1 until end of turn."),
|
||||||
@@ -109,7 +109,7 @@ public enum Keyword {
|
|||||||
PERSIST("Persist", SimpleKeyword.class, false, "When this creature dies, if it had no -1/-1 counters on it, return it to the battlefield under its owner's control with a -1/-1 counter on it."),
|
PERSIST("Persist", SimpleKeyword.class, false, "When this creature dies, if it had no -1/-1 counters on it, return it to the battlefield under its owner's control with a -1/-1 counter on it."),
|
||||||
PHASING("Phasing", SimpleKeyword.class, true, "This phases in or out before you untap during each of your untap steps. While it's phased out, it's treated as though it doesn't exist."),
|
PHASING("Phasing", SimpleKeyword.class, true, "This phases in or out before you untap during each of your untap steps. While it's phased out, it's treated as though it doesn't exist."),
|
||||||
POISONOUS("Poisonous", KeywordWithAmount.class, false, "Whenever this creature deals combat damage to a player, that player gets {%d:poison counter}."),
|
POISONOUS("Poisonous", KeywordWithAmount.class, false, "Whenever this creature deals combat damage to a player, that player gets {%d:poison counter}."),
|
||||||
PRESENCE("Presence", KeywordWithType.class, false, "As an additional cost to cast CARDNAME, you may reveal a %s card from your hand."),
|
PRESENCE("Presence", KeywordWithType.class, false, "As an additional cost to cast this spell, you may reveal a %s card from your hand."),
|
||||||
PROTECTION("Protection", Protection.class, false, "This creature can't be blocked, targeted, dealt damage, or equipped/enchanted by %s."),
|
PROTECTION("Protection", Protection.class, false, "This creature can't be blocked, targeted, dealt damage, or equipped/enchanted by %s."),
|
||||||
PROVOKE("Provoke", SimpleKeyword.class, false, "Whenever this creature attacks, you may have target creature defending player controls untap and block it if able."),
|
PROVOKE("Provoke", SimpleKeyword.class, false, "Whenever this creature attacks, you may have target creature defending player controls untap and block it if able."),
|
||||||
PROWESS("Prowess", SimpleKeyword.class, false, "Whenever you cast a noncreature spell, this creature gets +1/+1 until end of turn."),
|
PROWESS("Prowess", SimpleKeyword.class, false, "Whenever you cast a noncreature spell, this creature gets +1/+1 until end of turn."),
|
||||||
|
|||||||
@@ -501,9 +501,7 @@ public class PhaseHandler implements java.io.Serializable {
|
|||||||
bPreventCombatDamageThisTurn = false;
|
bPreventCombatDamageThisTurn = false;
|
||||||
if (!bRepeatCleanup) {
|
if (!bRepeatCleanup) {
|
||||||
// only call onCleanupPhase when Cleanup is not repeated
|
// only call onCleanupPhase when Cleanup is not repeated
|
||||||
for (Player player : game.getPlayers()) {
|
game.onCleanupPhase();
|
||||||
player.onCleanupPhase();
|
|
||||||
}
|
|
||||||
setPlayerTurn(handleNextTurn());
|
setPlayerTurn(handleNextTurn());
|
||||||
// "Trigger" for begin turn to get around a phase skipping
|
// "Trigger" for begin turn to get around a phase skipping
|
||||||
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
|
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
|
||||||
|
|||||||
@@ -114,8 +114,6 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
|
|
||||||
private CardCollection sacrificedThisTurn = new CardCollection();
|
private CardCollection sacrificedThisTurn = new CardCollection();
|
||||||
|
|
||||||
private Map<CounterType, Integer> countersAddedtoPermThisTurn = Maps.newHashMap();
|
|
||||||
|
|
||||||
/** A list of tokens not in play, but on their way.
|
/** A list of tokens not in play, but on their way.
|
||||||
* This list is kept in order to not break ETB-replacement
|
* This list is kept in order to not break ETB-replacement
|
||||||
* on tokens. */
|
* on tokens. */
|
||||||
@@ -2119,7 +2117,7 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final boolean hasProwl(final String type) {
|
public final boolean hasProwl(final String type) {
|
||||||
if (prowl.contains("AllCreatureTypes")) {
|
if (prowl.contains(CardType.AllCreatureTypes)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return prowl.contains(type);
|
return prowl.contains(type);
|
||||||
@@ -2289,20 +2287,6 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
sacrificedThisTurn.clear();
|
sacrificedThisTurn.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void addCounterToPermThisTurn(final CounterType type, final int x) {
|
|
||||||
countersAddedtoPermThisTurn.put(type, getCounterToPermThisTurn(type) + x);
|
|
||||||
}
|
|
||||||
|
|
||||||
public final Integer getCounterToPermThisTurn(final CounterType type) {
|
|
||||||
if (countersAddedtoPermThisTurn.containsKey(type))
|
|
||||||
return countersAddedtoPermThisTurn.get(type);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final void resetCounterToPermThisTurn() {
|
|
||||||
countersAddedtoPermThisTurn.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public final int getSpellsCastThisTurn() {
|
public final int getSpellsCastThisTurn() {
|
||||||
return spellsCastThisTurn;
|
return spellsCastThisTurn;
|
||||||
}
|
}
|
||||||
@@ -2521,7 +2505,6 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
resetSurveilThisTurn();
|
resetSurveilThisTurn();
|
||||||
resetCycledThisTurn();
|
resetCycledThisTurn();
|
||||||
resetSacrificedThisTurn();
|
resetSacrificedThisTurn();
|
||||||
resetCounterToPermThisTurn();
|
|
||||||
clearAssignedDamage();
|
clearAssignedDamage();
|
||||||
resetAttackersDeclaredThisTurn();
|
resetAttackersDeclaredThisTurn();
|
||||||
resetAttackedOpponentsThisTurn();
|
resetAttackedOpponentsThisTurn();
|
||||||
|
|||||||
@@ -213,7 +213,7 @@ public abstract class PlayerController {
|
|||||||
public abstract boolean chooseFlipResult(SpellAbility sa, Player flipper, boolean[] results, boolean call);
|
public abstract boolean chooseFlipResult(SpellAbility sa, Player flipper, boolean[] results, boolean call);
|
||||||
public abstract Card chooseProtectionShield(GameEntity entityBeingDamaged, List<String> options, Map<String, Card> choiceMap);
|
public abstract Card chooseProtectionShield(GameEntity entityBeingDamaged, List<String> options, Map<String, Card> choiceMap);
|
||||||
|
|
||||||
public abstract List<AbilitySub> chooseModeForAbility(SpellAbility sa, int min, int num, boolean allowRepeat);
|
public abstract List<AbilitySub> chooseModeForAbility(SpellAbility sa, List<AbilitySub> possible, int min, int num, boolean allowRepeat);
|
||||||
|
|
||||||
public abstract byte chooseColor(String message, SpellAbility sa, ColorSet colors);
|
public abstract byte chooseColor(String message, SpellAbility sa, ColorSet colors);
|
||||||
public abstract byte chooseColorAllowColorless(String message, Card c, ColorSet colors);
|
public abstract byte chooseColorAllowColorless(String message, Card c, ColorSet colors);
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
package forge.game.replacement;
|
package forge.game.replacement;
|
||||||
|
|
||||||
|
import forge.game.GameEntity;
|
||||||
import forge.game.ability.AbilityKey;
|
import forge.game.ability.AbilityKey;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
@@ -47,6 +48,7 @@ public class ReplaceDamage extends ReplacementEffect {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean canReplace(Map<AbilityKey, Object> runParams) {
|
public boolean canReplace(Map<AbilityKey, Object> runParams) {
|
||||||
|
|
||||||
if (!(runParams.containsKey(AbilityKey.Prevention) == (hasParam("PreventionEffect") || hasParam("Prevent")))) {
|
if (!(runParams.containsKey(AbilityKey.Prevention) == (hasParam("PreventionEffect") || hasParam("Prevent")))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -117,10 +119,15 @@ public class ReplaceDamage extends ReplacementEffect {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for DamageRedirection, the Thing where the damage is redirected to must be a creature or planeswalker or a player
|
|
||||||
if (hasParam("DamageTarget")) {
|
if (hasParam("DamageTarget")) {
|
||||||
|
//Lava Burst and Whippoorwill check
|
||||||
|
SpellAbility cause = (SpellAbility) runParams.get(AbilityKey.Cause);
|
||||||
|
GameEntity affected = (GameEntity) runParams.get(AbilityKey.Affected);
|
||||||
|
if (((cause != null) && (cause.hasParam("NoRedirection")) || (affected.hasKeyword("Damage that would be dealt to CARDNAME can't be redirected.")))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// check for DamageRedirection, the Thing where the damage is redirected to must be a creature or planeswalker or a player
|
||||||
String def = getParam("DamageTarget");
|
String def = getParam("DamageTarget");
|
||||||
|
|
||||||
for (Player p : AbilityUtils.getDefinedPlayers(hostCard, def, null)) {
|
for (Player p : AbilityUtils.getDefinedPlayers(hostCard, def, null)) {
|
||||||
if (!p.getGame().getPlayers().contains(p)) {
|
if (!p.getGame().getPlayers().contains(p)) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -216,6 +216,12 @@ public abstract class ReplacementEffect extends TriggerReplacementBase {
|
|||||||
if (desc.contains("CARDNAME")) {
|
if (desc.contains("CARDNAME")) {
|
||||||
desc = TextUtil.fastReplace(desc, "CARDNAME", getHostCard().toString());
|
desc = TextUtil.fastReplace(desc, "CARDNAME", getHostCard().toString());
|
||||||
}
|
}
|
||||||
|
if (desc.contains("EFFECTSOURCE")) {
|
||||||
|
desc = TextUtil.fastReplace(desc, "EFFECTSOURCE", getHostCard().getEffectSource().toString());
|
||||||
|
}
|
||||||
|
if (desc.contains("NICKNAME")) {
|
||||||
|
desc = TextUtil.fastReplace(desc, "NICKNAME", getHostCard().toString().split(",")[0]);
|
||||||
|
}
|
||||||
return desc;
|
return desc;
|
||||||
} else {
|
} else {
|
||||||
return "";
|
return "";
|
||||||
|
|||||||
@@ -699,6 +699,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
|||||||
String desc = node.getDescription();
|
String desc = node.getDescription();
|
||||||
if (node.getHostCard() != null) {
|
if (node.getHostCard() != null) {
|
||||||
desc = TextUtil.fastReplace(desc, "CARDNAME", node.getHostCard().getName());
|
desc = TextUtil.fastReplace(desc, "CARDNAME", node.getHostCard().getName());
|
||||||
|
desc = TextUtil.fastReplace(desc,"NICKNAME",node.getHostCard().getName().split(",")[0]);
|
||||||
if (node.getOriginalHost() != null) {
|
if (node.getOriginalHost() != null) {
|
||||||
desc = TextUtil.fastReplace(desc, "ORIGINALHOST", node.getOriginalHost().getName());
|
desc = TextUtil.fastReplace(desc, "ORIGINALHOST", node.getOriginalHost().getName());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -216,6 +216,7 @@ public class StaticAbility extends CardTraitBase implements IIdentifiable, Clone
|
|||||||
if (hasParam("Description") && !this.isSuppressed()) {
|
if (hasParam("Description") && !this.isSuppressed()) {
|
||||||
String desc = getParam("Description");
|
String desc = getParam("Description");
|
||||||
desc = TextUtil.fastReplace(desc, "CARDNAME", this.hostCard.getName());
|
desc = TextUtil.fastReplace(desc, "CARDNAME", this.hostCard.getName());
|
||||||
|
desc = TextUtil.fastReplace(desc, "NICKNAME", this.hostCard.getName().split(",")[0]);
|
||||||
|
|
||||||
return desc;
|
return desc;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -111,11 +111,9 @@ public abstract class Trigger extends TriggerReplacementBase {
|
|||||||
if (hasParam("TriggerDescription") && !this.isSuppressed()) {
|
if (hasParam("TriggerDescription") && !this.isSuppressed()) {
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
String desc = getParam("TriggerDescription");
|
String currentName = (getHostCard().getName());
|
||||||
if(active)
|
String desc1 = TextUtil.fastReplace(getParam("TriggerDescription"),"CARDNAME", currentName);
|
||||||
desc = TextUtil.fastReplace(desc, "CARDNAME", getHostCard().toString());
|
String desc = TextUtil.fastReplace(desc1,"NICKNAME", currentName.split(",")[0]);
|
||||||
else
|
|
||||||
desc = TextUtil.fastReplace(desc, "CARDNAME", getHostCard().getName());
|
|
||||||
if (getHostCard().getEffectSource() != null) {
|
if (getHostCard().getEffectSource() != null) {
|
||||||
if(active)
|
if(active)
|
||||||
desc = TextUtil.fastReplace(desc, "EFFECTSOURCE", getHostCard().getEffectSource().toString());
|
desc = TextUtil.fastReplace(desc, "EFFECTSOURCE", getHostCard().getEffectSource().toString());
|
||||||
|
|||||||
@@ -575,6 +575,8 @@ public class TriggerHandler {
|
|||||||
|
|
||||||
sa.setStackDescription(sa.toString());
|
sa.setStackDescription(sa.toString());
|
||||||
if (sa.getApi() == ApiType.Charm && !sa.isWrapper()) {
|
if (sa.getApi() == ApiType.Charm && !sa.isWrapper()) {
|
||||||
|
// need to be set for demonic pact to look for chosen modes
|
||||||
|
sa.setTrigger(regtrig);
|
||||||
if (!CharmEffect.makeChoices(sa)) {
|
if (!CharmEffect.makeChoices(sa)) {
|
||||||
// 603.3c If no mode is chosen, the ability is removed from the stack.
|
// 603.3c If no mode is chosen, the ability is removed from the stack.
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -246,9 +246,9 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sp.getApi() == ApiType.Charm && sp.hasParam("RememberChoice")) {
|
if (sp.getApi() == ApiType.Charm && sp.hasParam("ChoiceRestriction")) {
|
||||||
// Remember the Choice here for later handling
|
// Remember the Choice here for later handling
|
||||||
source.addRemembered(sp.getSubAbility());
|
source.addChosenModes(sp, sp.getSubAbility().getDescription());
|
||||||
}
|
}
|
||||||
|
|
||||||
//cancel auto-pass for all opponents of activating player
|
//cancel auto-pass for all opponents of activating player
|
||||||
|
|||||||
@@ -25,6 +25,9 @@ public enum TrackableProperty {
|
|||||||
|
|
||||||
Flipped(TrackableTypes.BooleanType),
|
Flipped(TrackableTypes.BooleanType),
|
||||||
Facedown(TrackableTypes.BooleanType),
|
Facedown(TrackableTypes.BooleanType),
|
||||||
|
Modal(TrackableTypes.BooleanType),
|
||||||
|
Adventure(TrackableTypes.BooleanType),
|
||||||
|
DoubleFaced(TrackableTypes.BooleanType),
|
||||||
|
|
||||||
//TODO?
|
//TODO?
|
||||||
Cloner(TrackableTypes.StringType),
|
Cloner(TrackableTypes.StringType),
|
||||||
@@ -164,6 +167,10 @@ public enum TrackableProperty {
|
|||||||
CanPlay(TrackableTypes.BooleanType),
|
CanPlay(TrackableTypes.BooleanType),
|
||||||
PromptIfOnlyPossibleAbility(TrackableTypes.BooleanType),
|
PromptIfOnlyPossibleAbility(TrackableTypes.BooleanType),
|
||||||
|
|
||||||
|
//HasBackSide
|
||||||
|
BackSideName(TrackableTypes.StringType),
|
||||||
|
HasBackSide(TrackableTypes.BooleanType),
|
||||||
|
|
||||||
//StackItem
|
//StackItem
|
||||||
Key(TrackableTypes.StringType),
|
Key(TrackableTypes.StringType),
|
||||||
SourceTrigger(TrackableTypes.IntegerType),
|
SourceTrigger(TrackableTypes.IntegerType),
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package forge.app;
|
package forge.app;
|
||||||
|
|
||||||
|
import android.app.ActivityManager;
|
||||||
import android.app.AlarmManager;
|
import android.app.AlarmManager;
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
@@ -56,9 +57,15 @@ public class Main extends AndroidApplication {
|
|||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
//get total device RAM in mb
|
||||||
|
ActivityManager actManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
|
||||||
|
ActivityManager.MemoryInfo memInfo = new ActivityManager.MemoryInfo();
|
||||||
|
actManager.getMemoryInfo(memInfo);
|
||||||
|
int totalMemory = Math.round(memInfo.totalMem / 1024f / 1024f);
|
||||||
|
|
||||||
boolean permissiongranted = checkPermission();
|
boolean permissiongranted = checkPermission();
|
||||||
Gadapter = new AndroidAdapter(this.getContext());
|
Gadapter = new AndroidAdapter(this.getContext());
|
||||||
initForge(Gadapter, permissiongranted);
|
initForge(Gadapter, permissiongranted, totalMemory);
|
||||||
|
|
||||||
//permission
|
//permission
|
||||||
if(!permissiongranted){
|
if(!permissiongranted){
|
||||||
@@ -185,7 +192,7 @@ public class Main extends AndroidApplication {
|
|||||||
builder.show();
|
builder.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initForge(AndroidAdapter adapter, boolean permissiongranted){
|
private void initForge(AndroidAdapter adapter, boolean permissiongranted, int totalRAM){
|
||||||
boolean isPortrait;
|
boolean isPortrait;
|
||||||
if (permissiongranted){
|
if (permissiongranted){
|
||||||
//establish assets directory
|
//establish assets directory
|
||||||
@@ -225,12 +232,12 @@ public class Main extends AndroidApplication {
|
|||||||
Main.this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
|
Main.this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
initialize(Forge.getApp(new AndroidClipboard(), adapter, assetsDir, propertyConfig, isPortrait));
|
initialize(Forge.getApp(new AndroidClipboard(), adapter, assetsDir, propertyConfig, isPortrait, totalRAM));
|
||||||
} else {
|
} else {
|
||||||
isPortrait = true;
|
isPortrait = true;
|
||||||
//set current orientation
|
//set current orientation
|
||||||
Main.this.setRequestedOrientation(Main.this.getResources().getConfiguration().orientation);
|
Main.this.setRequestedOrientation(Main.this.getResources().getConfiguration().orientation);
|
||||||
initialize(Forge.getApp(new AndroidClipboard(), adapter, "", false, isPortrait));
|
initialize(Forge.getApp(new AndroidClipboard(), adapter, "", false, isPortrait, totalRAM));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import com.google.common.primitives.Ints;
|
|||||||
import forge.gui.framework.ICDoc;
|
import forge.gui.framework.ICDoc;
|
||||||
import forge.model.FModel;
|
import forge.model.FModel;
|
||||||
import forge.quest.data.QuestPreferences;
|
import forge.quest.data.QuestPreferences;
|
||||||
|
import forge.quest.data.QuestPreferences.QPref;
|
||||||
import forge.screens.home.quest.VSubmenuQuestPrefs.PrefInput;
|
import forge.screens.home.quest.VSubmenuQuestPrefs.PrefInput;
|
||||||
import forge.util.Localizer;
|
import forge.util.Localizer;
|
||||||
|
|
||||||
@@ -51,10 +52,23 @@ public enum CSubmenuQuestPrefs implements ICDoc {
|
|||||||
if (i0.getText().equals(i0.getPreviousText())) { return; }
|
if (i0.getText().equals(i0.getPreviousText())) { return; }
|
||||||
final QuestPreferences prefs = FModel.getQuestPreferences();
|
final QuestPreferences prefs = FModel.getQuestPreferences();
|
||||||
|
|
||||||
final Integer val = Ints.tryParse(i0.getText());
|
String validationError = null;
|
||||||
resetErrors();
|
|
||||||
final Localizer localizer = Localizer.getInstance();
|
final Localizer localizer = Localizer.getInstance();
|
||||||
final String validationError = val == null ? localizer.getMessage("lblEnteraNumber") : prefs.validatePreference(i0.getQPref(), val.intValue());
|
resetErrors();
|
||||||
|
|
||||||
|
if(QPref.UNLOCK_DISTANCE_MULTIPLIER.equals(i0.getQPref())
|
||||||
|
|| QPref.WILD_OPPONENTS_MULTIPLIER.equals(i0.getQPref())) {
|
||||||
|
Double val = null;
|
||||||
|
try {
|
||||||
|
val = new Double(i0.getText());
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
validationError = val == null ? localizer.getMessage("lblEnteraDecimal") : null;
|
||||||
|
} else {
|
||||||
|
final Integer val = Ints.tryParse(i0.getText());
|
||||||
|
validationError = val == null ? localizer.getMessage("lblEnteraNumber") : prefs.validatePreference(i0.getQPref(), val.intValue());
|
||||||
|
}
|
||||||
|
|
||||||
if (validationError != null) {
|
if (validationError != null) {
|
||||||
showError(i0, validationError);
|
showError(i0, validationError);
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -292,6 +292,10 @@ public enum VSubmenuQuestPrefs implements IVSubmenu<CSubmenuQuestPrefs> {
|
|||||||
pnlDifficulty.add(new PrefInput(QPref.PENALTY_LOSS, QuestPreferencesErrType.DIFFICULTY), fieldConstraints + ", wrap");
|
pnlDifficulty.add(new PrefInput(QPref.PENALTY_LOSS, QuestPreferencesErrType.DIFFICULTY), fieldConstraints + ", wrap");
|
||||||
pnlDifficulty.add(new FLabel.Builder().text(localizer.getMessage("lblMoreDuelChoices")).fontAlign(SwingConstants.RIGHT).build(), labelConstraints);
|
pnlDifficulty.add(new FLabel.Builder().text(localizer.getMessage("lblMoreDuelChoices")).fontAlign(SwingConstants.RIGHT).build(), labelConstraints);
|
||||||
pnlDifficulty.add(new PrefInput(QPref.MORE_DUEL_CHOICES, QuestPreferencesErrType.DIFFICULTY), fieldConstraints + ", wrap");
|
pnlDifficulty.add(new PrefInput(QPref.MORE_DUEL_CHOICES, QuestPreferencesErrType.DIFFICULTY), fieldConstraints + ", wrap");
|
||||||
|
pnlDifficulty.add(new FLabel.Builder().text(localizer.getMessage("lblWildOpponentMultiplier")).fontAlign(SwingConstants.RIGHT).build(), labelConstraints);
|
||||||
|
pnlDifficulty.add(new PrefInput(QPref.WILD_OPPONENTS_MULTIPLIER, QuestPreferencesErrType.DIFFICULTY), fieldConstraints + ", wrap");
|
||||||
|
pnlDifficulty.add(new FLabel.Builder().text(localizer.getMessage("lblWildOpponentNumber")).fontAlign(SwingConstants.RIGHT).build(), labelConstraints);
|
||||||
|
pnlDifficulty.add(new PrefInput(QPref.WILD_OPPONENTS_NUMBER, QuestPreferencesErrType.DIFFICULTY), fieldConstraints + ", wrap");
|
||||||
}
|
}
|
||||||
private void populateBooster() {
|
private void populateBooster() {
|
||||||
pnlBooster.setOpaque(false);
|
pnlBooster.setOpaque(false);
|
||||||
|
|||||||
@@ -202,8 +202,13 @@ public class SimulateMatch {
|
|||||||
System.out.println(l);
|
System.out.println(l);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If both players life totals to 0 in a single turn, the game should end in a draw
|
||||||
|
if(g1.getOutcome().isDraw()){
|
||||||
|
System.out.println(String.format("Game %d ended in a Draw! Took %d ms.", 1+iGame, sw.getTime()));
|
||||||
|
} else {
|
||||||
System.out.println(String.format("\nGame %d ended in %d ms. %s has won!\n", 1+iGame, sw.getTime(), g1.getOutcome().getWinningLobbyPlayer().getName()));
|
System.out.println(String.format("\nGame %d ended in %d ms. %s has won!\n", 1+iGame, sw.getTime(), g1.getOutcome().getWinningLobbyPlayer().getName()));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void simulateTournament(Map<String, List<String>> params, GameRules rules, boolean outputGamelog) {
|
private static void simulateTournament(Map<String, List<String>> params, GameRules rules, boolean outputGamelog) {
|
||||||
String tournament = params.get("t").get(0);
|
String tournament = params.get("t").get(0);
|
||||||
|
|||||||
@@ -446,7 +446,7 @@ public class PlayerControllerForTests extends PlayerController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<AbilitySub> chooseModeForAbility(SpellAbility sa, int min, int num, boolean allowRepeat) {
|
public List<AbilitySub> chooseModeForAbility(SpellAbility sa, List<AbilitySub> possible, int min, int num, boolean allowRepeat) {
|
||||||
throw new IllegalStateException("Erring on the side of caution here...");
|
throw new IllegalStateException("Erring on the side of caution here...");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,8 +32,8 @@ public class Main extends IOSApplication.Delegate {
|
|||||||
config.useAccelerometer = false;
|
config.useAccelerometer = false;
|
||||||
config.useCompass = false;
|
config.useCompass = false;
|
||||||
ForgePreferences prefs = FModel.getPreferences();
|
ForgePreferences prefs = FModel.getPreferences();
|
||||||
boolean propertyConfig = prefs != null && prefs.getPrefBoolean(ForgePreferences.FPref.UI_NETPLAY_COMPAT);
|
boolean propertyConfig = prefs != null && prefs.getPrefBoolean(ForgePreferences.FPref.UI_NETPLAY_COMPAT);//todo get totalRAM
|
||||||
final ApplicationListener app = Forge.getApp(new IOSClipboard(), new IOSAdapter(), assetsDir, propertyConfig, false);
|
final ApplicationListener app = Forge.getApp(new IOSClipboard(), new IOSAdapter(), assetsDir, propertyConfig, false, 0);
|
||||||
final IOSApplication iosApp = new IOSApplication(app, config);
|
final IOSApplication iosApp = new IOSApplication(app, config);
|
||||||
return iosApp;
|
return iosApp;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -96,8 +96,8 @@ public class Main {
|
|||||||
|
|
||||||
ForgePreferences prefs = FModel.getPreferences();
|
ForgePreferences prefs = FModel.getPreferences();
|
||||||
boolean propertyConfig = prefs != null && prefs.getPrefBoolean(ForgePreferences.FPref.UI_NETPLAY_COMPAT);
|
boolean propertyConfig = prefs != null && prefs.getPrefBoolean(ForgePreferences.FPref.UI_NETPLAY_COMPAT);
|
||||||
new LwjglApplication(Forge.getApp(new LwjglClipboard(), new DesktopAdapter(switchOrientationFile),
|
new LwjglApplication(Forge.getApp(new LwjglClipboard(), new DesktopAdapter(switchOrientationFile),//todo get totalRAM
|
||||||
desktopMode ? desktopModeAssetsDir : assetsDir, propertyConfig, false), config);
|
desktopMode ? desktopModeAssetsDir : assetsDir, propertyConfig, false, 0), config);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class DesktopAdapter implements IDeviceAdapter {
|
private static class DesktopAdapter implements IDeviceAdapter {
|
||||||
|
|||||||
@@ -27,8 +27,7 @@ public abstract class CachedCardImage implements ImageFetcher.Callback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void fetch() {
|
public void fetch() {
|
||||||
Texture image = ImageCache.getImage(key, false);
|
if (!ImageCache.imageKeyFileExists(key)) {
|
||||||
if (image == null) {
|
|
||||||
fetcher.fetchImage(key, this);
|
fetcher.fetchImage(key, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ import forge.util.Localizer;
|
|||||||
import forge.util.Utils;
|
import forge.util.Utils;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileFilter;
|
|
||||||
import java.util.ArrayDeque;
|
import java.util.ArrayDeque;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Deque;
|
import java.util.Deque;
|
||||||
@@ -71,14 +70,21 @@ public class Forge implements ApplicationListener {
|
|||||||
public static boolean hdbuttons = false;
|
public static boolean hdbuttons = false;
|
||||||
public static boolean hdstart = false;
|
public static boolean hdstart = false;
|
||||||
public static boolean isPortraitMode = false;
|
public static boolean isPortraitMode = false;
|
||||||
|
public static boolean gameInProgress = false;
|
||||||
|
public static int cacheSize = 400;
|
||||||
|
public static int totalDeviceRAM = 0;
|
||||||
|
|
||||||
public static ApplicationListener getApp(Clipboard clipboard0, IDeviceAdapter deviceAdapter0, String assetDir0, boolean value, boolean androidOrientation) {
|
public static ApplicationListener getApp(Clipboard clipboard0, IDeviceAdapter deviceAdapter0, String assetDir0, boolean value, boolean androidOrientation, int totalRAM) {
|
||||||
if (GuiBase.getInterface() == null) {
|
if (GuiBase.getInterface() == null) {
|
||||||
clipboard = clipboard0;
|
clipboard = clipboard0;
|
||||||
deviceAdapter = deviceAdapter0;
|
deviceAdapter = deviceAdapter0;
|
||||||
GuiBase.setInterface(new GuiMobile(assetDir0));
|
GuiBase.setInterface(new GuiMobile(assetDir0));
|
||||||
GuiBase.enablePropertyConfig(value);
|
GuiBase.enablePropertyConfig(value);
|
||||||
isPortraitMode = androidOrientation;
|
isPortraitMode = androidOrientation;
|
||||||
|
totalDeviceRAM = totalRAM;
|
||||||
|
//increase cacheSize for devices with RAM more than 5GB, default is 400. Some phones have more than 10GB RAM (Mi 10, OnePlus 8, S20, etc..)
|
||||||
|
if (totalDeviceRAM>5000) //devices with more than 10GB RAM will have 1000 Cache size, 700 Cache size for morethan 5GB RAM
|
||||||
|
cacheSize = totalDeviceRAM>10000 ? 1000: 700;
|
||||||
}
|
}
|
||||||
return app;
|
return app;
|
||||||
}
|
}
|
||||||
@@ -142,8 +148,18 @@ public class Forge implements ApplicationListener {
|
|||||||
splashScreen.getProgressBar().setDescription(localizer.getMessage("lblFinishingStartup"));
|
splashScreen.getProgressBar().setDescription(localizer.getMessage("lblFinishingStartup"));
|
||||||
|
|
||||||
//add reminder to preload
|
//add reminder to preload
|
||||||
if (enablePreloadExtendedArt)
|
if (enablePreloadExtendedArt) {
|
||||||
|
if(totalDeviceRAM>0)
|
||||||
|
splashScreen.getProgressBar().setDescription(localizer.getMessage("lblPreloadExtendedArt")+"\nDetected RAM: " +totalDeviceRAM+"MB. Cache size: "+cacheSize);
|
||||||
|
else
|
||||||
splashScreen.getProgressBar().setDescription(localizer.getMessage("lblPreloadExtendedArt"));
|
splashScreen.getProgressBar().setDescription(localizer.getMessage("lblPreloadExtendedArt"));
|
||||||
|
} else {
|
||||||
|
if(totalDeviceRAM>0)
|
||||||
|
splashScreen.getProgressBar().setDescription(localizer.getMessage("lblFinishingStartup")+"\nDetected RAM: " +totalDeviceRAM+"MB. Cache size: "+cacheSize);
|
||||||
|
else
|
||||||
|
splashScreen.getProgressBar().setDescription(localizer.getMessage("lblFinishingStartup"));
|
||||||
|
}
|
||||||
|
|
||||||
Gdx.app.postRunnable(new Runnable() {
|
Gdx.app.postRunnable(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
@@ -160,25 +176,17 @@ public class Forge implements ApplicationListener {
|
|||||||
private void preloadExtendedArt() {
|
private void preloadExtendedArt() {
|
||||||
if (!enablePreloadExtendedArt)
|
if (!enablePreloadExtendedArt)
|
||||||
return;
|
return;
|
||||||
List<String> keys = new ArrayList<>();
|
List<String> borderlessCardlistkeys = FileUtil.readFile(ForgeConstants.BORDERLESS_CARD_LIST_FILE);
|
||||||
File[] directories = new File(ForgeConstants.CACHE_CARD_PICS_DIR).listFiles(new FileFilter() {
|
if(borderlessCardlistkeys.isEmpty())
|
||||||
@Override
|
return;
|
||||||
public boolean accept(File file) {
|
List<String> filteredkeys = new ArrayList<>();
|
||||||
if (!file.getName().startsWith("MPS_"))
|
for (String cardname : borderlessCardlistkeys){
|
||||||
return false;
|
File image = new File(ForgeConstants.CACHE_CARD_PICS_DIR+ForgeConstants.PATH_SEPARATOR+cardname+".jpg");
|
||||||
return file.isDirectory();
|
if (image.exists())
|
||||||
|
filteredkeys.add(cardname);
|
||||||
}
|
}
|
||||||
});
|
if (!filteredkeys.isEmpty())
|
||||||
for (File folder : directories) {
|
ImageCache.preloadCache(filteredkeys);
|
||||||
File[] files = new File(folder.toString()).listFiles();
|
|
||||||
for (File file : files) {
|
|
||||||
if (file.isFile()) {
|
|
||||||
keys.add(folder.getName() + "/" +file.getName().replace(".jpg","").replace(".png",""));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!keys.isEmpty())
|
|
||||||
ImageCache.preloadCache((Iterable<String>)keys);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void afterDbLoaded() {
|
private void afterDbLoaded() {
|
||||||
@@ -402,10 +410,16 @@ public class Forge implements ApplicationListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void setCurrentScreen(FScreen screen0) {
|
private static void setCurrentScreen(FScreen screen0) {
|
||||||
|
String toNewScreen = screen0 != null ? screen0.toString() : "";
|
||||||
|
String previousScreen = currentScreen != null ? currentScreen.toString() : "";
|
||||||
|
|
||||||
|
gameInProgress = toNewScreen.toLowerCase().contains("match") || previousScreen.toLowerCase().contains("match");
|
||||||
|
boolean dispose = toNewScreen.toLowerCase().contains("homescreen");
|
||||||
try {
|
try {
|
||||||
endKeyInput(); //end key input before switching screens
|
endKeyInput(); //end key input before switching screens
|
||||||
ForgeAnimation.endAll(); //end all active animations before switching screens
|
ForgeAnimation.endAll(); //end all active animations before switching screens
|
||||||
try {
|
try {
|
||||||
|
if(dispose)
|
||||||
ImageCache.disposeTexture();
|
ImageCache.disposeTexture();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|||||||
@@ -566,17 +566,25 @@ public class Graphics {
|
|||||||
}
|
}
|
||||||
public float getfloatAlphaComposite() { return alphaComposite; }
|
public float getfloatAlphaComposite() { return alphaComposite; }
|
||||||
|
|
||||||
|
public void drawBorderImage(FImage image, Color borderColor, Color tintColor, float x, float y, float w, float h, boolean tint) {
|
||||||
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;
|
float oldalpha = alphaComposite;
|
||||||
setAlphaComposite(0.8f);
|
if(tint){
|
||||||
drawRoundRect(2f, Color.WHITE, x, y, w, h, (h-w)/12);
|
drawRoundRect(2f, borderLining(borderColor.toString()), x, y, w, h, (h-w)/12);
|
||||||
setAlphaComposite(1f);
|
fillRoundRect(tintColor, x, y, w, h, (h-w)/12);
|
||||||
fillRoundRect(color, x, y, w, h, (h-w)/12);
|
} else {
|
||||||
|
image.draw(this, x, y, w, h);
|
||||||
|
fillRoundRect(borderColor, x, y, w, h, (h-w)/10);//show corners edges
|
||||||
|
}
|
||||||
setAlphaComposite(oldalpha);
|
setAlphaComposite(oldalpha);
|
||||||
}
|
}
|
||||||
|
public void drawborderImage(Color borderColor, float x, float y, float w, float h) {
|
||||||
|
float oldalpha = alphaComposite;
|
||||||
|
fillRoundRect(borderColor, x, y, w, h, (h-w)/12);
|
||||||
|
setAlphaComposite(oldalpha);
|
||||||
|
}
|
||||||
|
public void drawImage(FImage image, Color borderColor, float x, float y, float w, float h) {
|
||||||
|
image.draw(this, x, y, w, h);
|
||||||
|
fillRoundRect(borderColor, x+1, y+1, w-1.5f, h-1.5f, (h-w)/10);//used by zoom let some edges show...
|
||||||
}
|
}
|
||||||
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);
|
||||||
@@ -733,4 +741,13 @@ public class Graphics {
|
|||||||
public float adjustY(float y, float height) {
|
public float adjustY(float y, float height) {
|
||||||
return regionHeight - y - bounds.y - height; //flip y-axis
|
return regionHeight - y - bounds.y - height; //flip y-axis
|
||||||
}
|
}
|
||||||
|
public Color borderLining(String c){
|
||||||
|
if (c == null || c == "")
|
||||||
|
return Color.valueOf("#fffffd");
|
||||||
|
int c_r = Integer.parseInt(c.substring(0,2),16);
|
||||||
|
int c_g = Integer.parseInt(c.substring(2,4),16);
|
||||||
|
int c_b = Integer.parseInt(c.substring(4,6),16);
|
||||||
|
int brightness = ((c_r * 299) + (c_g * 587) + (c_b * 114)) / 1000;
|
||||||
|
return brightness > 155 ? Color.valueOf("#171717") : Color.valueOf("#fffffd");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,10 +27,10 @@ import forge.screens.SplashScreen;
|
|||||||
import forge.toolbox.FProgressBar;
|
import forge.toolbox.FProgressBar;
|
||||||
|
|
||||||
public class FSkin {
|
public class FSkin {
|
||||||
private static final Map<FSkinProp, FSkinImage> images = new HashMap<>();
|
private static final Map<FSkinProp, FSkinImage> images = new HashMap<>(512);
|
||||||
private static final Map<Integer, TextureRegion> avatars = new HashMap<>();
|
private static final Map<Integer, TextureRegion> avatars = new HashMap<>(150);
|
||||||
private static final Map<Integer, TextureRegion> sleeves = new HashMap<>();
|
private static final Map<Integer, TextureRegion> sleeves = new HashMap<>(64);
|
||||||
private static final Map<Integer, TextureRegion> borders = new HashMap<>();
|
private static final Map<Integer, TextureRegion> borders = new HashMap<>(2);
|
||||||
|
|
||||||
private static Array<String> allSkins;
|
private static Array<String> allSkins;
|
||||||
private static FileHandle preferredDir;
|
private static FileHandle preferredDir;
|
||||||
@@ -275,6 +275,20 @@ public class FSkin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
pxPreferredAvatars.dispose();
|
pxPreferredAvatars.dispose();
|
||||||
|
} else if (!FSkin.preferredName.isEmpty()){
|
||||||
|
//workaround bug crash fix if missing sprite avatar on preferred theme for quest tournament...
|
||||||
|
//i really don't know why it needs to populate the avatars twice.... needs investigation
|
||||||
|
final int pw = pxDefaultAvatars.getWidth();
|
||||||
|
final int ph = pxDefaultAvatars.getHeight();
|
||||||
|
|
||||||
|
for (int j = 0; j < ph; j += 100) {
|
||||||
|
for (int i = 0; i < pw; i += 100) {
|
||||||
|
if (i == 0 && j == 0) { continue; }
|
||||||
|
pxTest = new Color(pxDefaultAvatars.getPixel(i + 50, j + 50));
|
||||||
|
if (pxTest.a == 0) { continue; }
|
||||||
|
FSkin.avatars.put(counter++, new TextureRegion(txDefaultAvatars, i, j, 100, 100));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final int aw = pxDefaultAvatars.getWidth();
|
final int aw = pxDefaultAvatars.getWidth();
|
||||||
|
|||||||
@@ -27,23 +27,27 @@ import com.google.common.cache.LoadingCache;
|
|||||||
import com.google.common.cache.RemovalCause;
|
import com.google.common.cache.RemovalCause;
|
||||||
import com.google.common.cache.RemovalListener;
|
import com.google.common.cache.RemovalListener;
|
||||||
import com.google.common.cache.RemovalNotification;
|
import com.google.common.cache.RemovalNotification;
|
||||||
|
import forge.Forge;
|
||||||
import forge.ImageKeys;
|
import forge.ImageKeys;
|
||||||
import forge.card.CardEdition;
|
import forge.card.CardEdition;
|
||||||
import forge.card.CardRenderer;
|
import forge.card.CardRenderer;
|
||||||
|
import forge.deck.Deck;
|
||||||
import forge.game.card.CardView;
|
import forge.game.card.CardView;
|
||||||
import forge.game.player.IHasIcon;
|
import forge.game.player.IHasIcon;
|
||||||
import forge.item.IPaperCard;
|
|
||||||
import forge.item.InventoryItem;
|
import forge.item.InventoryItem;
|
||||||
|
import forge.item.PaperCard;
|
||||||
import forge.model.FModel;
|
import forge.model.FModel;
|
||||||
import forge.properties.ForgeConstants;
|
import forge.properties.ForgeConstants;
|
||||||
import forge.util.ImageUtil;
|
import forge.util.ImageUtil;
|
||||||
|
import forge.util.TextUtil;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -65,7 +69,7 @@ public class ImageCache {
|
|||||||
|
|
||||||
private static final Set<String> missingIconKeys = new HashSet<>();
|
private static final Set<String> missingIconKeys = new HashSet<>();
|
||||||
private static final LoadingCache<String, Texture> cache = CacheBuilder.newBuilder()
|
private static final LoadingCache<String, Texture> cache = CacheBuilder.newBuilder()
|
||||||
.maximumSize(400)
|
.maximumSize(Forge.cacheSize)
|
||||||
.expireAfterAccess(15, TimeUnit.MINUTES)
|
.expireAfterAccess(15, TimeUnit.MINUTES)
|
||||||
.removalListener(new RemovalListener<String, Texture>() {
|
.removalListener(new RemovalListener<String, Texture>() {
|
||||||
@Override
|
@Override
|
||||||
@@ -73,13 +77,14 @@ public class ImageCache {
|
|||||||
if(removalNotification.wasEvicted()||removalNotification.getCause() == RemovalCause.EXPIRED) {
|
if(removalNotification.wasEvicted()||removalNotification.getCause() == RemovalCause.EXPIRED) {
|
||||||
removalNotification.getValue().dispose();
|
removalNotification.getValue().dispose();
|
||||||
}
|
}
|
||||||
|
CardRenderer.clearcardArtCache();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.build(new ImageLoader());
|
.build(new ImageLoader());
|
||||||
public static final Texture defaultImage;
|
public static final Texture defaultImage;
|
||||||
public static FImage BlackBorder = FSkinImage.IMG_BORDER_BLACK;
|
public static FImage BlackBorder = FSkinImage.IMG_BORDER_BLACK;
|
||||||
public static FImage WhiteBorder = FSkinImage.IMG_BORDER_WHITE;
|
public static FImage WhiteBorder = FSkinImage.IMG_BORDER_WHITE;
|
||||||
private static final Map<Texture, Boolean> Borders = new HashMap<>();
|
private static final Map<String, Pair<String, Boolean>> imageBorder = new HashMap<>(1024);
|
||||||
|
|
||||||
private static boolean imageLoaded, delayLoadRequested;
|
private static boolean imageLoaded, delayLoadRequested;
|
||||||
public static void allowSingleLoad() {
|
public static void allowSingleLoad() {
|
||||||
@@ -102,12 +107,12 @@ public class ImageCache {
|
|||||||
cache.invalidateAll();
|
cache.invalidateAll();
|
||||||
cache.cleanUp();
|
cache.cleanUp();
|
||||||
missingIconKeys.clear();
|
missingIconKeys.clear();
|
||||||
Borders.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void disposeTexture(){
|
public static void disposeTexture(){
|
||||||
for (Texture t: cache.asMap().values()) {
|
for (Texture t: cache.asMap().values()) {
|
||||||
if (!t.toString().contains("pics/icons")) //fixes quest avatars black texture. todo: filter textures that are safe to dispose...
|
if (!t.toString().contains("pics/icons")) //fixes quest avatars black texture. todo: filter textures that are safe to dispose...
|
||||||
|
if(!t.toString().contains("@")) //generated texture don't need to be disposed manually
|
||||||
t.dispose();
|
t.dispose();
|
||||||
}
|
}
|
||||||
CardRenderer.clearcardArtCache();
|
CardRenderer.clearcardArtCache();
|
||||||
@@ -132,6 +137,39 @@ public class ImageCache {
|
|||||||
return new FTextureImage(icon);
|
return new FTextureImage(icon);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* checks the card image exists from the disk.
|
||||||
|
*/
|
||||||
|
public static boolean imageKeyFileExists(String imageKey) {
|
||||||
|
if (StringUtils.isEmpty(imageKey))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (imageKey.length() < 2)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
final String prefix = imageKey.substring(0, 2);
|
||||||
|
|
||||||
|
if (prefix.equals(ImageKeys.CARD_PREFIX)) {
|
||||||
|
PaperCard paperCard = ImageUtil.getPaperCardFromImageKey(imageKey);
|
||||||
|
if (paperCard == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
final boolean backFace = imageKey.endsWith(ImageKeys.BACKFACE_POSTFIX);
|
||||||
|
final String cardfilename = ImageUtil.getImageKey(paperCard, backFace, true);
|
||||||
|
if (!new File(ForgeConstants.CACHE_CARD_PICS_DIR + "/" + cardfilename + ".jpg").exists())
|
||||||
|
if (!new File(ForgeConstants.CACHE_CARD_PICS_DIR + "/" + cardfilename + ".png").exists())
|
||||||
|
if (!new File(ForgeConstants.CACHE_CARD_PICS_DIR + "/" + TextUtil.fastReplace(cardfilename,".full", ".fullborder") + ".jpg").exists())
|
||||||
|
return false;
|
||||||
|
} else if (prefix.equals(ImageKeys.TOKEN_PREFIX)) {
|
||||||
|
final String tokenfilename = imageKey.substring(2) + ".jpg";
|
||||||
|
|
||||||
|
if (!new File(ForgeConstants.CACHE_TOKEN_PICS_DIR, tokenfilename).exists())
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This requests the original unscaled image from the cache for the given key.
|
* This requests the original unscaled image from the cache for the given key.
|
||||||
* If the image does not exist then it can return a default image if desired.
|
* If the image does not exist then it can return a default image if desired.
|
||||||
@@ -185,19 +223,27 @@ public class ImageCache {
|
|||||||
if (useDefaultIfNotFound) {
|
if (useDefaultIfNotFound) {
|
||||||
image = defaultImage;
|
image = defaultImage;
|
||||||
cache.put(imageKey, defaultImage);
|
cache.put(imageKey, defaultImage);
|
||||||
if (Borders.get(image) == null)
|
if (imageBorder.get(image.toString()) == null)
|
||||||
Borders.put(image, false); //black border
|
imageBorder.put(image.toString(), Pair.of(Color.valueOf("#171717").toString(), false)); //black border
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
public static void preloadCache(Iterable<String> keys) {
|
public static void preloadCache(Iterable<String> keys) {
|
||||||
try {
|
for (String imageKey : keys){
|
||||||
cache.getAll(keys);
|
if(getImage(imageKey, false) == null)
|
||||||
} catch (ExecutionException e) {
|
System.err.println("could not load card image:"+imageKey);
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public static void preloadCache(Deck deck) {
|
||||||
|
if(deck == null||!Forge.enablePreloadExtendedArt)
|
||||||
|
return;
|
||||||
|
for (PaperCard p : deck.getAllCardsInASinglePool().toFlatList()) {
|
||||||
|
if (getImage(p.getImageKey(false),false) == null)
|
||||||
|
System.err.println("could not load card image:"+p.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
public static TextureRegion croppedBorderImage(Texture image) {
|
public static TextureRegion croppedBorderImage(Texture image) {
|
||||||
if (!image.toString().contains(".fullborder."))
|
if (!image.toString().contains(".fullborder."))
|
||||||
return new TextureRegion(image);
|
return new TextureRegion(image);
|
||||||
@@ -208,24 +254,10 @@ public class ImageCache {
|
|||||||
int ry = Math.round((image.getHeight() - rh)/2f)-2;
|
int ry = Math.round((image.getHeight() - rh)/2f)-2;
|
||||||
return new TextureRegion(image, rx, ry, rw, rh);
|
return new TextureRegion(image, rx, ry, rw, rh);
|
||||||
}
|
}
|
||||||
public static Color borderColor(IPaperCard c) {
|
public static Color borderColor(Texture t) {
|
||||||
if (c == null)
|
if (t == null)
|
||||||
return Color.valueOf("#171717");
|
|
||||||
|
|
||||||
CardEdition ed = FModel.getMagicDb().getEditions().get(c.getEdition());
|
|
||||||
if (ed != null && ed.isWhiteBorder())
|
|
||||||
return Color.valueOf("#fffffd");
|
|
||||||
return Color.valueOf("#171717");
|
|
||||||
}
|
|
||||||
public static Color borderColor(CardView c) {
|
|
||||||
if (c == null)
|
|
||||||
return Color.valueOf("#171717");
|
|
||||||
|
|
||||||
CardView.CardStateView state = c.getCurrentState();
|
|
||||||
CardEdition ed = FModel.getMagicDb().getEditions().get(state.getSetCode());
|
|
||||||
if (ed != null && ed.isWhiteBorder() && state.getFoilIndex() == 0)
|
|
||||||
return Color.valueOf("#fffffd");
|
|
||||||
return Color.valueOf("#171717");
|
return Color.valueOf("#171717");
|
||||||
|
return Color.valueOf(imageBorder.get(t.toString()).getLeft());
|
||||||
}
|
}
|
||||||
public static int getFSkinBorders(CardView c) {
|
public static int getFSkinBorders(CardView c) {
|
||||||
if (c == null)
|
if (c == null)
|
||||||
@@ -240,21 +272,21 @@ public class ImageCache {
|
|||||||
public static boolean isBorderlessCardArt(Texture t) {
|
public static boolean isBorderlessCardArt(Texture t) {
|
||||||
return ImageLoader.isBorderless(t);
|
return ImageLoader.isBorderless(t);
|
||||||
}
|
}
|
||||||
public static void updateBorders(Texture t, boolean val){
|
public static void updateBorders(String textureString, Pair<String, Boolean> colorPair){
|
||||||
Borders.put(t, val);
|
imageBorder.put(textureString, colorPair);
|
||||||
}
|
}
|
||||||
public static FImage getBorder(Texture t) {
|
public static FImage getBorder(String textureString) {
|
||||||
if (Borders.get(t) == null)
|
if (imageBorder.get(textureString) == null)
|
||||||
return BlackBorder;
|
return BlackBorder;
|
||||||
return Borders.get(t) ? WhiteBorder : BlackBorder;
|
return imageBorder.get(textureString).getRight() ? WhiteBorder : BlackBorder;
|
||||||
}
|
}
|
||||||
public static FImage getBorderImage(Texture t, boolean canshow) {
|
public static FImage getBorderImage(String textureString, boolean canshow) {
|
||||||
if (!canshow)
|
if (!canshow)
|
||||||
return BlackBorder;
|
return BlackBorder;
|
||||||
return getBorder(t);
|
return getBorder(textureString);
|
||||||
}
|
}
|
||||||
public static FImage getBorderImage(Texture t) {
|
public static FImage getBorderImage(String textureString) {
|
||||||
return getBorder(t);
|
return getBorder(textureString);
|
||||||
}
|
}
|
||||||
public static Color getTint(CardView c) {
|
public static Color getTint(CardView c) {
|
||||||
if (c == null)
|
if (c == null)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package forge.assets;
|
package forge.assets;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.ArrayList;
|
import java.util.List;
|
||||||
|
|
||||||
import com.badlogic.gdx.files.FileHandle;
|
import com.badlogic.gdx.files.FileHandle;
|
||||||
import com.badlogic.gdx.graphics.Color;
|
import com.badlogic.gdx.graphics.Color;
|
||||||
@@ -14,142 +14,15 @@ import forge.FThreads;
|
|||||||
|
|
||||||
import forge.Forge;
|
import forge.Forge;
|
||||||
import forge.ImageKeys;
|
import forge.ImageKeys;
|
||||||
|
import forge.properties.ForgeConstants;
|
||||||
|
import forge.util.FileUtil;
|
||||||
import forge.util.TextUtil;
|
import forge.util.TextUtil;
|
||||||
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
import static forge.assets.ImageCache.croppedBorderImage;
|
import static forge.assets.ImageCache.croppedBorderImage;
|
||||||
|
|
||||||
final class ImageLoader extends CacheLoader<String, Texture> {
|
final class ImageLoader extends CacheLoader<String, Texture> {
|
||||||
private static ArrayList<String> borderlessCardlistKey = new ArrayList<String>() {
|
private static List<String> borderlessCardlistKey = FileUtil.readFile(ForgeConstants.BORDERLESS_CARD_LIST_FILE);
|
||||||
{//TODO: load the values from text list instead of hardcoded
|
|
||||||
add("2XM/Academy Ruins2.fullborder");
|
|
||||||
add("2XM/Atraxa, Praetors' Voice2.fullborder");
|
|
||||||
add("2XM/Avacyn, Angel of Hope2.fullborder");
|
|
||||||
add("2XM/Batterskull2.fullborder");
|
|
||||||
add("2XM/Blightsteel Colossus2.fullborder");
|
|
||||||
add("2XM/Blood Moon2.fullborder");
|
|
||||||
add("2XM/Brainstorm2.fullborder");
|
|
||||||
add("2XM/Chrome Mox2.fullborder");
|
|
||||||
add("2XM/Council's Judgment2.fullborder");
|
|
||||||
add("2XM/Crop Rotation2.fullborder");
|
|
||||||
add("2XM/Cyclonic Rift2.fullborder");
|
|
||||||
add("2XM/Dark Confidant2.fullborder");
|
|
||||||
add("2XM/Doubling Season2.fullborder");
|
|
||||||
add("2XM/Expedition Map2.fullborder");
|
|
||||||
add("2XM/Exploration2.fullborder");
|
|
||||||
add("2XM/Fatal Push2.fullborder");
|
|
||||||
add("2XM/Force of Will2.fullborder");
|
|
||||||
add("2XM/Goblin Guide2.fullborder");
|
|
||||||
add("2XM/Jace, the Mind Sculptor2.fullborder");
|
|
||||||
add("2XM/Kaalia of the Vast2.fullborder");
|
|
||||||
add("2XM/Karn Liberated2.fullborder");
|
|
||||||
add("2XM/Lightning Greaves2.fullborder");
|
|
||||||
add("2XM/Mana Crypt2.fullborder");
|
|
||||||
add("2XM/Meddling Mage2.fullborder");
|
|
||||||
add("2XM/Mox Opal2.fullborder");
|
|
||||||
add("2XM/Noble Hierarch2.fullborder");
|
|
||||||
add("2XM/Phyrexian Metamorph2.fullborder");
|
|
||||||
add("2XM/Sneak Attack2.fullborder");
|
|
||||||
add("2XM/Stoneforge Mystic2.fullborder");
|
|
||||||
add("2XM/Sword of Body and Mind2.fullborder");
|
|
||||||
add("2XM/Sword of Feast and Famine2.fullborder");
|
|
||||||
add("2XM/Sword of Fire and Ice2.fullborder");
|
|
||||||
add("2XM/Sword of Light and Shadow2.fullborder");
|
|
||||||
add("2XM/Sword of War and Peace2.fullborder");
|
|
||||||
add("2XM/Thoughtseize2.fullborder");
|
|
||||||
add("2XM/Toxic Deluge2.fullborder");
|
|
||||||
add("2XM/Urza's Mine2.fullborder");
|
|
||||||
add("2XM/Urza's Power Plant2.fullborder");
|
|
||||||
add("2XM/Urza's Tower2.fullborder");
|
|
||||||
add("2XM/Wurmcoil Engine2.fullborder");
|
|
||||||
add("ELD/Garruk, Cursed Huntsman2.fullborder");
|
|
||||||
add("ELD/Oko, Thief of Crowns2.fullborder");
|
|
||||||
add("ELD/The Royal Scions2.fullborder");
|
|
||||||
add("IKO/Brokkos, Apex of Forever2.fullborder");
|
|
||||||
add("IKO/Brokkos, Apex of Forever3.fullborder");
|
|
||||||
add("IKO/Crystalline Giant3.fullborder");
|
|
||||||
add("IKO/Cubwarden2.fullborder");
|
|
||||||
add("IKO/Dirge Bat2.fullborder");
|
|
||||||
add("IKO/Dirge Bat3.fullborder");
|
|
||||||
add("IKO/Everquill Phoenix2.fullborder");
|
|
||||||
add("IKO/Everquill Phoenix3.fullborder");
|
|
||||||
add("IKO/Gemrazer2.fullborder");
|
|
||||||
add("IKO/Gemrazer3.fullborder");
|
|
||||||
add("IKO/Gyruda, Doom of Depths3.fullborder");
|
|
||||||
add("IKO/Huntmaster Liger2.fullborder");
|
|
||||||
add("IKO/Huntmaster Liger3.fullborder");
|
|
||||||
add("IKO/Illuna, Apex of Wishes2.fullborder");
|
|
||||||
add("IKO/Illuna, Apex of Wishes3.fullborder");
|
|
||||||
add("IKO/Indatha Triome2.fullborder");
|
|
||||||
add("IKO/Ketria Triome2.fullborder");
|
|
||||||
add("IKO/Lukka, Coppercoat Outcast2.fullborder");
|
|
||||||
add("IKO/Luminous Broodmoth3.fullborder");
|
|
||||||
add("IKO/Mysterious Egg2.fullborder");
|
|
||||||
add("IKO/Narset of the Ancient Way2.fullborder");
|
|
||||||
add("IKO/Nethroi, Apex of Death2.fullborder");
|
|
||||||
add("IKO/Nethroi, Apex of Death3.fullborder");
|
|
||||||
add("IKO/Pollywog Symbiote2.fullborder");
|
|
||||||
add("IKO/Raugrin Triome2.fullborder");
|
|
||||||
add("IKO/Savai Triome2.fullborder");
|
|
||||||
add("IKO/Sea-Dasher Octopus2.fullborder");
|
|
||||||
add("IKO/Snapdax, Apex of the Hunt2.fullborder");
|
|
||||||
add("IKO/Snapdax, Apex of the Hunt3.fullborder");
|
|
||||||
add("IKO/Sprite Dragon3.fullborder");
|
|
||||||
add("IKO/Titanoth Rex2.fullborder");
|
|
||||||
add("IKO/Vadrok, Apex of Thunder2.fullborder");
|
|
||||||
add("IKO/Vadrok, Apex of Thunder3.fullborder");
|
|
||||||
add("IKO/Vivien, Monsters' Advocate2.fullborder");
|
|
||||||
add("IKO/Void Beckoner2.fullborder");
|
|
||||||
add("IKO/Yidaro, Wandering Monster3.fullborder");
|
|
||||||
add("IKO/Zagoth Triome2.fullborder");
|
|
||||||
add("IKO/Zilortha, Strength Incarnate.fullborder");
|
|
||||||
add("M21/Basri Ket2.fullborder");
|
|
||||||
add("M21/Chandra, Heart of Fire2.fullborder");
|
|
||||||
add("M21/Containment Priest2.fullborder");
|
|
||||||
add("M21/Cultivate2.fullborder");
|
|
||||||
add("M21/Garruk, Unleashed2.fullborder");
|
|
||||||
add("M21/Grim Tutor2.fullborder");
|
|
||||||
add("M21/Liliana, Waker of the Dead2.fullborder");
|
|
||||||
add("M21/Massacre Wurm2.fullborder");
|
|
||||||
add("M21/Scavenging Ooze2.fullborder");
|
|
||||||
add("M21/Solemn Simulacrum2.fullborder");
|
|
||||||
add("M21/Teferi, Master of Time2.fullborder");
|
|
||||||
add("M21/Ugin, the Spirit Dragon2.fullborder");
|
|
||||||
add("M21/Ugin, the Spirit Dragon3.fullborder");
|
|
||||||
add("PLGS/Hangarback Walker.fullborder");
|
|
||||||
add("SLD/Acidic Slime.fullborder");
|
|
||||||
add("SLD/Captain Sisay.fullborder");
|
|
||||||
add("SLD/Meren of Clan Nel Toth.fullborder");
|
|
||||||
add("SLD/Narset, Enlightened Master.fullborder");
|
|
||||||
add("SLD/Necrotic Ooze.fullborder");
|
|
||||||
add("SLD/Oona, Queen of the Fae.fullborder");
|
|
||||||
add("SLD/Saskia the Unyielding.fullborder");
|
|
||||||
add("SLD/The Mimeoplasm.fullborder");
|
|
||||||
add("SLD/Voidslime.fullborder");
|
|
||||||
add("THB/Ashiok, Nightmare Muse2.fullborder");
|
|
||||||
add("THB/Calix, Destiny's Hand2.fullborder");
|
|
||||||
add("THB/Elspeth, Sun's Nemesis2.fullborder");
|
|
||||||
add("UST/Forest.fullborder");
|
|
||||||
add("UST/Island.fullborder");
|
|
||||||
add("UST/Mountain.fullborder");
|
|
||||||
add("UST/Plains.fullborder");
|
|
||||||
add("UST/Swamp.fullborder");
|
|
||||||
add("ZNR/Boulderloft Pathway2.fullborder");
|
|
||||||
add("ZNR/Branchloft Pathway2.fullborder");
|
|
||||||
add("ZNR/Brightclimb Pathway2.fullborder");
|
|
||||||
add("ZNR/Clearwater Pathway2.fullborder");
|
|
||||||
add("ZNR/Cragcrown Pathway2.fullborder");
|
|
||||||
add("ZNR/Grimclimb Pathway2.fullborder");
|
|
||||||
add("ZNR/Jace, Mirror Mage2.fullborder");
|
|
||||||
add("ZNR/Lavaglide Pathway2.fullborder");
|
|
||||||
add("ZNR/Murkwater Pathway2.fullborder");
|
|
||||||
add("ZNR/Nahiri, Heir of the Ancients2.fullborder");
|
|
||||||
add("ZNR/Needleverge Pathway2.fullborder");
|
|
||||||
add("ZNR/Nissa of Shadowed Boughs2.fullborder");
|
|
||||||
add("ZNR/Pillarverge Pathway2.fullborder");
|
|
||||||
add("ZNR/Riverglide Pathway2.fullborder");
|
|
||||||
add("ZNR/Timbercrown Pathway2.fullborder");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Texture n;
|
Texture n;
|
||||||
@Override
|
@Override
|
||||||
@@ -162,7 +35,7 @@ final class ImageLoader extends CacheLoader<String, Texture> {
|
|||||||
try {
|
try {
|
||||||
Texture t = new Texture(fh, textureFilter);
|
Texture t = new Texture(fh, textureFilter);
|
||||||
//update
|
//update
|
||||||
ImageCache.updateBorders(t, extendedArt ? false: isCloserToWhite(getpixelColor(t)));
|
ImageCache.updateBorders(t.toString(), extendedArt ? Pair.of(Color.valueOf("#171717").toString(), false): isCloserToWhite(getpixelColor(t)));
|
||||||
if (textureFilter)
|
if (textureFilter)
|
||||||
t.setFilter(Texture.TextureFilter.MipMapLinearLinear, Texture.TextureFilter.Linear);
|
t.setFilter(Texture.TextureFilter.MipMapLinearLinear, Texture.TextureFilter.Linear);
|
||||||
if (extendedArt)
|
if (extendedArt)
|
||||||
@@ -240,6 +113,8 @@ final class ImageLoader extends CacheLoader<String, Texture> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isBorderless(String imagekey) {
|
public boolean isBorderless(String imagekey) {
|
||||||
|
if(borderlessCardlistKey.isEmpty())
|
||||||
|
return false;
|
||||||
if (imagekey.length() > 7) {
|
if (imagekey.length() > 7) {
|
||||||
if ((!imagekey.substring(0, 7).contains("MPS_KLD"))&&(imagekey.substring(0, 4).contains("MPS_"))) //MPS_ sets except MPD_KLD
|
if ((!imagekey.substring(0, 7).contains("MPS_KLD"))&&(imagekey.substring(0, 4).contains("MPS_"))) //MPS_ sets except MPD_KLD
|
||||||
return true;
|
return true;
|
||||||
@@ -248,6 +123,8 @@ final class ImageLoader extends CacheLoader<String, Texture> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isBorderless(Texture t) {
|
public static boolean isBorderless(Texture t) {
|
||||||
|
if(borderlessCardlistKey.isEmpty())
|
||||||
|
return false;
|
||||||
//generated texture/pixmap?
|
//generated texture/pixmap?
|
||||||
if (t.toString().contains("com.badlogic.gdx.graphics.Texture@"))
|
if (t.toString().contains("com.badlogic.gdx.graphics.Texture@"))
|
||||||
return true;
|
return true;
|
||||||
@@ -269,13 +146,13 @@ final class ImageLoader extends CacheLoader<String, Texture> {
|
|||||||
pixmap.dispose();
|
pixmap.dispose();
|
||||||
return color.toString();
|
return color.toString();
|
||||||
}
|
}
|
||||||
public static boolean isCloserToWhite(String c){
|
public static Pair<String, Boolean> isCloserToWhite(String c){
|
||||||
if (c == null || c == "")
|
if (c == null || c == "")
|
||||||
return false;
|
return Pair.of(Color.valueOf("#171717").toString(), false);
|
||||||
int c_r = Integer.parseInt(c.substring(0,2),16);
|
int c_r = Integer.parseInt(c.substring(0,2),16);
|
||||||
int c_g = Integer.parseInt(c.substring(2,4),16);
|
int c_g = Integer.parseInt(c.substring(2,4),16);
|
||||||
int c_b = Integer.parseInt(c.substring(4,6),16);
|
int c_b = Integer.parseInt(c.substring(4,6),16);
|
||||||
int brightness = ((c_r * 299) + (c_g * 587) + (c_b * 114)) / 1000;
|
int brightness = ((c_r * 299) + (c_g * 587) + (c_b * 114)) / 1000;
|
||||||
return brightness > 155;
|
return Pair.of(c,brightness > 155);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import forge.util.TextBounds;
|
|||||||
|
|
||||||
//Encodes text for drawing with symbols and reminder text
|
//Encodes text for drawing with symbols and reminder text
|
||||||
public class TextRenderer {
|
public class TextRenderer {
|
||||||
private static final Map<String, FSkinImage> symbolLookup = new HashMap<>();
|
private static final Map<String, FSkinImage> symbolLookup = new HashMap<>(64);
|
||||||
static {
|
static {
|
||||||
symbolLookup.put("C", FSkinImage.MANA_COLORLESS);
|
symbolLookup.put("C", FSkinImage.MANA_COLORLESS);
|
||||||
symbolLookup.put("W", FSkinImage.MANA_W);
|
symbolLookup.put("W", FSkinImage.MANA_W);
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ import java.util.StringTokenizer;
|
|||||||
|
|
||||||
public class CardFaceSymbols {
|
public class CardFaceSymbols {
|
||||||
public static final float FONT_SIZE_FACTOR = 0.85f;
|
public static final float FONT_SIZE_FACTOR = 0.85f;
|
||||||
private static final Map<String, FSkinImage> MANA_IMAGES = new HashMap<>();
|
private static final Map<String, FSkinImage> MANA_IMAGES = new HashMap<>(128);
|
||||||
|
|
||||||
public static void loadImages() {
|
public static void loadImages() {
|
||||||
for (int i = 0; i <= 20; i++) {
|
for (int i = 0; i <= 20; i++) {
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ public class CardImage implements FImage {
|
|||||||
g.drawImage(image, x, y, w, h);
|
g.drawImage(image, x, y, w, h);
|
||||||
else {
|
else {
|
||||||
float radius = (h - w)/8;
|
float radius = (h - w)/8;
|
||||||
g.drawfillBorder(3, ImageCache.borderColor(card), x, y, w, h, radius);
|
g.drawborderImage(ImageCache.borderColor(image), x, y, w, h);
|
||||||
g.drawImage(ImageCache.croppedBorderImage(image), x+radius/2.2f, y+radius/2, w*0.96f, h*0.96f);
|
g.drawImage(ImageCache.croppedBorderImage(image), x+radius/2.2f, y+radius/2, w*0.96f, h*0.96f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,8 +73,11 @@ public class CardImageRenderer {
|
|||||||
prevImageHeight = h;
|
prevImageHeight = h;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void drawFaceDownCard(Graphics g, float x, float y, float w, float h) {
|
public static void drawFaceDownCard(CardView card, Graphics g, float x, float y, float w, float h) {
|
||||||
// TODO: improve the way a face-down card back is represented so it doesn't look as ugly
|
//try to draw the card sleeves first
|
||||||
|
if (FSkin.getSleeves().get(card.getOwner()) != null)
|
||||||
|
g.drawImage(FSkin.getSleeves().get(card.getOwner()), x, y, w, h);
|
||||||
|
else
|
||||||
drawArt(g, x, y, w, h);
|
drawArt(g, x, y, w, h);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,7 +95,7 @@ public class CardImageRenderer {
|
|||||||
final boolean canShow = MatchController.instance.mayView(card);
|
final boolean canShow = MatchController.instance.mayView(card);
|
||||||
|
|
||||||
if (!canShow) {
|
if (!canShow) {
|
||||||
drawFaceDownCard(g, x, y, w, h);
|
drawFaceDownCard(card, g, x, y, w, h);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -333,7 +336,17 @@ 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 canshow = MatchController.instance.mayView(card);
|
boolean canshow = MatchController.instance.mayView(card);
|
||||||
final Texture image = ImageCache.getImage(card.getState(altState).getImageKey(), true);
|
Texture image = null;
|
||||||
|
try {
|
||||||
|
image = ImageCache.getImage(card.getState(altState).getImageKey(), true);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
//System.err.println(card.toString()+" : " +ex.getMessage());
|
||||||
|
//TODO: don't know why this is needed, needs further investigation...
|
||||||
|
if (!card.hasAlternateState()) {
|
||||||
|
altState = false;
|
||||||
|
image = ImageCache.getImage(card.getState(altState).getImageKey(), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
FImage sleeves = MatchController.getPlayerSleeve(card.getOwner());
|
FImage sleeves = MatchController.getPlayerSleeve(card.getOwner());
|
||||||
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);
|
||||||
@@ -390,7 +403,7 @@ public class CardImageRenderer {
|
|||||||
if (ImageCache.isBorderlessCardArt(image))
|
if (ImageCache.isBorderlessCardArt(image))
|
||||||
g.drawImage(image, x, y, w, h);
|
g.drawImage(image, x, y, w, h);
|
||||||
else {
|
else {
|
||||||
g.drawImage(ImageCache.getBorderImage(image, canshow), x, y, w, h);
|
g.drawImage(ImageCache.getBorderImage(image.toString()), ImageCache.borderColor(image), x, y, w, h);
|
||||||
g.drawImage(ImageCache.croppedBorderImage(image), x + radius / 2.4f-minusxy, y + radius / 2-minusxy, w * croppedArea, h * croppedArea);
|
g.drawImage(ImageCache.croppedBorderImage(image), x + radius / 2.4f-minusxy, y + radius / 2-minusxy, w * croppedArea, h * croppedArea);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ import forge.util.TextBounds;
|
|||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.EnumSet;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -192,7 +193,7 @@ public class CardRenderer {
|
|||||||
return Math.round(MANA_SYMBOL_SIZE + FSkinFont.get(12).getLineHeight() + 3 * FList.PADDING + 1);
|
return Math.round(MANA_SYMBOL_SIZE + FSkinFont.get(12).getLineHeight() + 3 * FList.PADDING + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Map<String, FImageComplex> cardArtCache = new HashMap<>();
|
private static final Map<String, FImageComplex> cardArtCache = new HashMap<>(1024);
|
||||||
public static final float CARD_ART_RATIO = 1.302f;
|
public static final float CARD_ART_RATIO = 1.302f;
|
||||||
public static final float CARD_ART_HEIGHT_PERCENTAGE = 0.43f;
|
public static final float CARD_ART_HEIGHT_PERCENTAGE = 0.43f;
|
||||||
|
|
||||||
@@ -463,7 +464,7 @@ public class CardRenderer {
|
|||||||
if (ImageCache.isBorderlessCardArt(image))
|
if (ImageCache.isBorderlessCardArt(image))
|
||||||
g.drawImage(image, x, y, w, h);
|
g.drawImage(image, x, y, w, h);
|
||||||
else {
|
else {
|
||||||
g.drawImage(ImageCache.getBorderImage(image), x, y, w, h);
|
g.drawImage(ImageCache.getBorderImage(image.toString()), x, y, w, h);
|
||||||
g.drawImage(ImageCache.croppedBorderImage(image), x + radius / 2.4f-minusxy, y + radius / 2-minusxy, w * croppedArea, h * croppedArea);
|
g.drawImage(ImageCache.croppedBorderImage(image), x + radius / 2.4f-minusxy, y + radius / 2-minusxy, w * croppedArea, h * croppedArea);
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
@@ -484,8 +485,12 @@ public class CardRenderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
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) {
|
||||||
|
drawCard(g, card, x, y, w, h, pos, rotate, false);
|
||||||
|
}
|
||||||
|
public static void drawCard(Graphics g, CardView card, float x, float y, float w, float h, CardStackPosition pos, boolean rotate, boolean showAltState) {
|
||||||
boolean canshow = MatchController.instance.mayView(card);
|
boolean canshow = MatchController.instance.mayView(card);
|
||||||
Texture image = new RendererCachedCardImage(card, false).getImage(card.getCurrentState().getImageKey());
|
boolean showsleeves = card.isFaceDown() && card.isInZone(EnumSet.of(ZoneType.Exile)); //fix facedown card image ie gonti lord of luxury
|
||||||
|
Texture image = new RendererCachedCardImage(card, false).getImage( showAltState ? card.getAlternateState().getImageKey() : card.getCurrentState().getImageKey());
|
||||||
FImage sleeves = MatchController.getPlayerSleeve(card.getOwner());
|
FImage sleeves = MatchController.getPlayerSleeve(card.getOwner());
|
||||||
float radius = (h - w)/8;
|
float radius = (h - w)/8;
|
||||||
float croppedArea = isModernFrame(card) ? CROP_MULTIPLIER : 0.97f;
|
float croppedArea = isModernFrame(card) ? CROP_MULTIPLIER : 0.97f;
|
||||||
@@ -497,6 +502,8 @@ public class CardRenderer {
|
|||||||
if (image != null) {
|
if (image != null) {
|
||||||
if (image == ImageCache.defaultImage) {
|
if (image == ImageCache.defaultImage) {
|
||||||
CardImageRenderer.drawCardImage(g, card, false, x, y, w, h, pos);
|
CardImageRenderer.drawCardImage(g, card, false, x, y, w, h, pos);
|
||||||
|
} else if (showsleeves) {
|
||||||
|
g.drawImage(sleeves, x, y, w, h);
|
||||||
} 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){
|
||||||
@@ -515,7 +522,7 @@ public class CardRenderer {
|
|||||||
g.drawImage(image, x, y, w, h);
|
g.drawImage(image, x, y, w, h);
|
||||||
else {
|
else {
|
||||||
boolean t = (card.getCurrentState().getOriginalColors() != card.getCurrentState().getColors()) || card.getCurrentState().hasChangeColors();
|
boolean t = (card.getCurrentState().getOriginalColors() != card.getCurrentState().getColors()) || card.getCurrentState().hasChangeColors();
|
||||||
g.drawBorderImage(ImageCache.getBorderImage(image, canshow), ImageCache.getTint(card), x, y, w, h, t); //tint check for changed colors
|
g.drawBorderImage(ImageCache.getBorderImage(image.toString(), canshow), ImageCache.borderColor(image), ImageCache.getTint(card), x, y, w, h, t); //tint check for changed colors
|
||||||
g.drawImage(ImageCache.croppedBorderImage(image), x + radius / 2.4f-minusxy, y + radius / 2-minusxy, w * croppedArea, h * croppedArea);
|
g.drawImage(ImageCache.croppedBorderImage(image), x + radius / 2.4f-minusxy, y + radius / 2-minusxy, w * croppedArea, h * croppedArea);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -536,15 +543,15 @@ 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) {
|
||||||
drawCardWithOverlays(g, card, x, y, w, h, pos, false);
|
drawCardWithOverlays(g, card, x, y, w, h, pos, false, false);
|
||||||
}
|
}
|
||||||
public static void drawCardWithOverlays(Graphics g, CardView card, float x, float y, float w, float h, CardStackPosition pos, boolean stackview) {
|
public static void drawCardWithOverlays(Graphics g, CardView card, float x, float y, float w, float h, CardStackPosition pos, boolean stackview, boolean showAltState) {
|
||||||
boolean canShow = MatchController.instance.mayView(card);
|
boolean canShow = MatchController.instance.mayView(card);
|
||||||
float oldAlpha = g.getfloatAlphaComposite();
|
float oldAlpha = g.getfloatAlphaComposite();
|
||||||
boolean unselectable = !MatchController.instance.isSelectable(card) && MatchController.instance.isSelecting();
|
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, showAltState);
|
||||||
|
|
||||||
float padding = w * PADDING_MULTIPLIER; //adjust for card border
|
float padding = w * PADDING_MULTIPLIER; //adjust for card border
|
||||||
x += padding;
|
x += padding;
|
||||||
@@ -553,7 +560,7 @@ public class CardRenderer {
|
|||||||
h -= 2 * padding;
|
h -= 2 * padding;
|
||||||
|
|
||||||
// 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 = showAltState ? card.getAlternateState() : card.getCurrentState();
|
||||||
final boolean isFaceDown = card.isFaceDown();
|
final boolean isFaceDown = card.isFaceDown();
|
||||||
final DetailColors borderColor = isFaceDown ? CardDetailUtil.DetailColors.FACE_DOWN : CardDetailUtil.getBorderColor(details, canShow); // canShow doesn't work here for face down Morphs
|
final DetailColors borderColor = isFaceDown ? CardDetailUtil.DetailColors.FACE_DOWN : CardDetailUtil.getBorderColor(details, canShow); // canShow doesn't work here for face down Morphs
|
||||||
Color color = FSkinColor.fromRGB(borderColor.r, borderColor.g, borderColor.b);
|
Color color = FSkinColor.fromRGB(borderColor.r, borderColor.g, borderColor.b);
|
||||||
@@ -930,7 +937,7 @@ public class CardRenderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
drawManaCost(g, card.getCurrentState().getManaCost(), x - padding, y, w + 2 * padding, h, manaSymbolSize);
|
drawManaCost(g, showAltState ? card.getAlternateState().getManaCost() : card.getCurrentState().getManaCost(), x - padding, y, w + 2 * padding, h, manaSymbolSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,10 +46,15 @@ public class CardZoom extends FOverlay {
|
|||||||
private static String currentActivateAction;
|
private static String currentActivateAction;
|
||||||
private static Rectangle flipIconBounds;
|
private static Rectangle flipIconBounds;
|
||||||
private static boolean showAltState;
|
private static boolean showAltState;
|
||||||
|
private static boolean showBackSide = false;
|
||||||
|
|
||||||
public static void show(Object item) {
|
public static void show(Object item) {
|
||||||
|
show(item, false);
|
||||||
|
}
|
||||||
|
public static void show(Object item, boolean showbackside) {
|
||||||
List<Object> items0 = new ArrayList<>();
|
List<Object> items0 = new ArrayList<>();
|
||||||
items0.add(item);
|
items0.add(item);
|
||||||
|
showBackSide = showbackside; //reverse the displayed zoomed card for the choice list
|
||||||
show(items0, 0, null);
|
show(items0, 0, null);
|
||||||
}
|
}
|
||||||
public static void show(FCollectionView<?> items0, int currentIndex0, ActivateHandler activateHandler0) {
|
public static void show(FCollectionView<?> items0, int currentIndex0, ActivateHandler activateHandler0) {
|
||||||
@@ -158,10 +163,15 @@ public class CardZoom extends FOverlay {
|
|||||||
@Override
|
@Override
|
||||||
public boolean tap(float x, float y, int count) {
|
public boolean tap(float x, float y, int count) {
|
||||||
if (flipIconBounds != null && flipIconBounds.contains(x, y)) {
|
if (flipIconBounds != null && flipIconBounds.contains(x, y)) {
|
||||||
|
if (!showBackSide)
|
||||||
showAltState = !showAltState;
|
showAltState = !showAltState;
|
||||||
|
else
|
||||||
|
showBackSide = !showBackSide;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
hide();
|
hide();
|
||||||
|
showBackSide = false;
|
||||||
|
showAltState = false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,14 +179,20 @@ public class CardZoom extends FOverlay {
|
|||||||
public boolean fling(float velocityX, float velocityY) {
|
public boolean fling(float velocityX, float velocityY) {
|
||||||
if (Math.abs(velocityX) > Math.abs(velocityY)) {
|
if (Math.abs(velocityX) > Math.abs(velocityY)) {
|
||||||
incrementCard(velocityX > 0 ? -1 : 1);
|
incrementCard(velocityX > 0 ? -1 : 1);
|
||||||
|
showBackSide = false;
|
||||||
|
showAltState = false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (velocityY > 0) {
|
if (velocityY > 0) {
|
||||||
zoomMode = !zoomMode;
|
zoomMode = !zoomMode;
|
||||||
|
showBackSide = false;
|
||||||
|
showAltState = false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (currentActivateAction != null && activateHandler != null) {
|
if (currentActivateAction != null && activateHandler != null) {
|
||||||
hide();
|
hide();
|
||||||
|
showBackSide = false;
|
||||||
|
showAltState = false;
|
||||||
activateHandler.activate(currentIndex);
|
activateHandler.activate(currentIndex);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -282,10 +298,9 @@ public class CardZoom extends FOverlay {
|
|||||||
float x = (w - cardWidth) / 2;
|
float x = (w - cardWidth) / 2;
|
||||||
y = (h - cardHeight) / 2;
|
y = (h - cardHeight) / 2;
|
||||||
if (zoomMode) {
|
if (zoomMode) {
|
||||||
CardImageRenderer.drawZoom(g, currentCard, gameView, showAltState, x, y, cardWidth, cardHeight, getWidth(), getHeight(), true);
|
CardImageRenderer.drawZoom(g, currentCard, gameView, showBackSide? showBackSide : showAltState, x, y, cardWidth, cardHeight, getWidth(), getHeight(), true);
|
||||||
}
|
} else {
|
||||||
else {
|
CardImageRenderer.drawDetails(g, currentCard, gameView, showBackSide? showBackSide : showAltState, x, y, cardWidth, cardHeight);
|
||||||
CardImageRenderer.drawDetails(g, currentCard, gameView, showAltState, x, y, cardWidth, cardHeight);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flipIconBounds != null) {
|
if (flipIconBounds != null) {
|
||||||
@@ -307,6 +322,8 @@ public class CardZoom extends FOverlay {
|
|||||||
}
|
}
|
||||||
g.fillRect(FDialog.MSG_BACK_COLOR, 0, h - messageHeight, w, messageHeight);
|
g.fillRect(FDialog.MSG_BACK_COLOR, 0, h - messageHeight, w, messageHeight);
|
||||||
g.drawText(zoomMode ? Localizer.getInstance().getMessage("lblSwipeDownDetailView") : Localizer.getInstance().getMessage("lblSwipeDownPictureView"), FDialog.MSG_FONT, FDialog.MSG_FORE_COLOR, 0, h - messageHeight, w, messageHeight, false, Align.center, true);
|
g.drawText(zoomMode ? Localizer.getInstance().getMessage("lblSwipeDownDetailView") : Localizer.getInstance().getMessage("lblSwipeDownPictureView"), FDialog.MSG_FONT, FDialog.MSG_FORE_COLOR, 0, h - messageHeight, w, messageHeight, false, Align.center, true);
|
||||||
|
|
||||||
|
interrupt(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -318,4 +335,15 @@ public class CardZoom extends FOverlay {
|
|||||||
void setSelectedIndex(int index);
|
void setSelectedIndex(int index);
|
||||||
void activate(int index);
|
void activate(int index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void interrupt(boolean resume) {
|
||||||
|
if (MatchController.instance.hasLocalPlayers())
|
||||||
|
return;
|
||||||
|
if(resume && MatchController.instance.isGamePaused()) {
|
||||||
|
MatchController.instance.resumeMatch();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(!MatchController.instance.isGamePaused())
|
||||||
|
MatchController.instance.pauseMatch();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package forge.deck;
|
|||||||
import forge.FThreads;
|
import forge.FThreads;
|
||||||
import forge.Forge;
|
import forge.Forge;
|
||||||
import forge.GuiBase;
|
import forge.GuiBase;
|
||||||
|
import forge.assets.ImageCache;
|
||||||
import forge.deck.FDeckEditor.EditorType;
|
import forge.deck.FDeckEditor.EditorType;
|
||||||
import forge.deck.io.DeckPreferences;
|
import forge.deck.io.DeckPreferences;
|
||||||
import forge.error.BugReporter;
|
import forge.error.BugReporter;
|
||||||
@@ -472,6 +473,8 @@ public class FDeckChooser extends FScreen {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
needRefreshOnActivate = true;
|
needRefreshOnActivate = true;
|
||||||
|
/*preload deck to cache*/
|
||||||
|
ImageCache.preloadCache(deck.getDeck());
|
||||||
Forge.openScreen(new FDeckEditor(editorType, deck, true));
|
Forge.openScreen(new FDeckEditor(editorType, deck, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1147,7 +1150,7 @@ public class FDeckChooser extends FScreen {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
chooser.show(null, true);
|
chooser.show(null, false); /*setting selectMax to true will select all available option*/
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import forge.assets.FImage;
|
|||||||
import forge.assets.FSkin;
|
import forge.assets.FSkin;
|
||||||
import forge.assets.FSkinImage;
|
import forge.assets.FSkinImage;
|
||||||
import forge.assets.FTextureRegionImage;
|
import forge.assets.FTextureRegionImage;
|
||||||
|
import forge.assets.ImageCache;
|
||||||
import forge.item.PaperCard;
|
import forge.item.PaperCard;
|
||||||
import forge.itemmanager.CardManager;
|
import forge.itemmanager.CardManager;
|
||||||
import forge.itemmanager.ItemManagerConfig;
|
import forge.itemmanager.ItemManagerConfig;
|
||||||
@@ -112,6 +113,9 @@ public class FDeckViewer extends FScreen {
|
|||||||
public static void show(final Deck deck0) {
|
public static void show(final Deck deck0) {
|
||||||
if (deck0 == null) { return; }
|
if (deck0 == null) { return; }
|
||||||
|
|
||||||
|
/*preload deck to cache*/
|
||||||
|
ImageCache.preloadCache(deck0);
|
||||||
|
|
||||||
deckViewer = new FDeckViewer(deck0);
|
deckViewer = new FDeckViewer(deck0);
|
||||||
deckViewer.setRotate180(MatchController.getView() != null && MatchController.getView().isTopHumanPlayerActive());
|
deckViewer.setRotate180(MatchController.getView() != null && MatchController.getView().isTopHumanPlayerActive());
|
||||||
Forge.openScreen(deckViewer);
|
Forge.openScreen(deckViewer);
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import forge.GuiBase;
|
import forge.GuiBase;
|
||||||
|
import forge.assets.ImageCache;
|
||||||
import forge.deck.CardPool;
|
import forge.deck.CardPool;
|
||||||
import forge.deck.Deck;
|
import forge.deck.Deck;
|
||||||
import forge.deck.DeckSection;
|
import forge.deck.DeckSection;
|
||||||
@@ -625,6 +626,10 @@ public abstract class LobbyScreen extends LaunchScreen implements ILobbyView {
|
|||||||
if (isNewPanel) {
|
if (isNewPanel) {
|
||||||
panel.setVisible(true);
|
panel.setVisible(true);
|
||||||
}
|
}
|
||||||
|
if (Forge.gameInProgress) {
|
||||||
|
/*preload deck to cache*/
|
||||||
|
ImageCache.preloadCache(decks[i]);
|
||||||
|
}
|
||||||
Gdx.graphics.requestRendering();
|
Gdx.graphics.requestRendering();
|
||||||
}
|
}
|
||||||
else if (hasPanel) {
|
else if (hasPanel) {
|
||||||
|
|||||||
@@ -445,7 +445,6 @@ public class MatchController extends AbstractGuiGame {
|
|||||||
@Override
|
@Override
|
||||||
public void afterGameEnd() {
|
public void afterGameEnd() {
|
||||||
Forge.back();
|
Forge.back();
|
||||||
ImageCache.disposeTexture();
|
|
||||||
//view = null;
|
//view = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -355,6 +355,32 @@ public class MatchScreen extends FScreen {
|
|||||||
final GameView game = MatchController.instance.getGameView();
|
final GameView game = MatchController.instance.getGameView();
|
||||||
if (game == null) { return; }
|
if (game == null) { return; }
|
||||||
|
|
||||||
|
if(gameMenu!=null) {
|
||||||
|
if(gameMenu.getChildCount()>3){
|
||||||
|
if(viewWinLose == null) {
|
||||||
|
gameMenu.getChildAt(0).setEnabled(true);
|
||||||
|
gameMenu.getChildAt(1).setEnabled(true);
|
||||||
|
gameMenu.getChildAt(2).setEnabled(true);
|
||||||
|
gameMenu.getChildAt(3).setEnabled(true);
|
||||||
|
gameMenu.getChildAt(4).setEnabled(false);
|
||||||
|
} else {
|
||||||
|
gameMenu.getChildAt(0).setEnabled(false);
|
||||||
|
gameMenu.getChildAt(1).setEnabled(false);
|
||||||
|
gameMenu.getChildAt(2).setEnabled(false);
|
||||||
|
gameMenu.getChildAt(3).setEnabled(false);
|
||||||
|
gameMenu.getChildAt(4).setEnabled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(devMenu!=null) {
|
||||||
|
if(devMenu.isVisible()){
|
||||||
|
if(viewWinLose == null)
|
||||||
|
devMenu.setEnabled(true);
|
||||||
|
else
|
||||||
|
devMenu.setEnabled(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//draw arrows for paired cards
|
//draw arrows for paired cards
|
||||||
Set<CardView> pairedCards = new HashSet<>();
|
Set<CardView> pairedCards = new HashSet<>();
|
||||||
for (VPlayerPanel playerPanel : playerPanels.values()) {
|
for (VPlayerPanel playerPanel : playerPanels.values()) {
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ public class VGameMenu extends FDropDownMenu {
|
|||||||
SettingsScreen.show(false);
|
SettingsScreen.show(false);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
addItem(new FMenuItem(localizer.getMessage("lblShowWinLoseOverlay"), null, new FEventHandler() {
|
addItem(new FMenuItem(localizer.getMessage("lblShowWinLoseOverlay"), FSkinImage.ENDTURN, new FEventHandler() {
|
||||||
@Override
|
@Override
|
||||||
public void handleEvent(FEvent e) {
|
public void handleEvent(FEvent e) {
|
||||||
MatchController.instance.showWinlose();
|
MatchController.instance.showWinlose();
|
||||||
|
|||||||
@@ -382,7 +382,7 @@ public class VStack extends FDropDown {
|
|||||||
|
|
||||||
x += PADDING;
|
x += PADDING;
|
||||||
y += PADDING;
|
y += PADDING;
|
||||||
CardRenderer.drawCardWithOverlays(g, stackInstance.getSourceCard(), x, y, CARD_WIDTH, CARD_HEIGHT, CardStackPosition.Top, true);
|
CardRenderer.drawCardWithOverlays(g, stackInstance.getSourceCard(), x, y, CARD_WIDTH, CARD_HEIGHT, CardStackPosition.Top, true, false);
|
||||||
|
|
||||||
x += CARD_WIDTH + PADDING;
|
x += CARD_WIDTH + PADDING;
|
||||||
w -= x + PADDING - BORDER_THICKNESS;
|
w -= x + PADDING - BORDER_THICKNESS;
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package forge.screens.match.winlose;
|
package forge.screens.match.winlose;
|
||||||
|
|
||||||
import forge.Forge;
|
import forge.Forge;
|
||||||
import forge.assets.ImageCache;
|
|
||||||
import forge.game.GameView;
|
import forge.game.GameView;
|
||||||
import forge.game.player.PlayerView;
|
import forge.game.player.PlayerView;
|
||||||
import forge.screens.match.MatchController;
|
import forge.screens.match.MatchController;
|
||||||
@@ -85,7 +84,6 @@ public class ControlWinLose {
|
|||||||
view.hide();
|
view.hide();
|
||||||
if(humancount == 0) {
|
if(humancount == 0) {
|
||||||
Forge.back();
|
Forge.back();
|
||||||
ImageCache.disposeTexture();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import forge.Graphics;
|
|||||||
import forge.assets.FImage;
|
import forge.assets.FImage;
|
||||||
import forge.assets.FSkinColor;
|
import forge.assets.FSkinColor;
|
||||||
import forge.assets.FSkinFont;
|
import forge.assets.FSkinFont;
|
||||||
|
import forge.assets.ImageCache;
|
||||||
import forge.card.CardFaceSymbols;
|
import forge.card.CardFaceSymbols;
|
||||||
import forge.card.CardRenderer;
|
import forge.card.CardRenderer;
|
||||||
import forge.card.ColorSet;
|
import forge.card.ColorSet;
|
||||||
@@ -76,6 +77,8 @@ public class ConquestCommandersScreen extends FScreen {
|
|||||||
public void handleEvent(FEvent e) {
|
public void handleEvent(FEvent e) {
|
||||||
final ConquestCommander commander = lstCommanders.getSelectedItem();
|
final ConquestCommander commander = lstCommanders.getSelectedItem();
|
||||||
if (commander != null) {
|
if (commander != null) {
|
||||||
|
/*preload deck to cache*/
|
||||||
|
ImageCache.preloadCache(commander.getDeck());
|
||||||
preventRefreshOnActivate = true; //refresh not needed since deck changes won't affect commander display
|
preventRefreshOnActivate = true; //refresh not needed since deck changes won't affect commander display
|
||||||
Forge.openScreen(new ConquestDeckEditor(commander));
|
Forge.openScreen(new ConquestDeckEditor(commander));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import com.badlogic.gdx.utils.Align;
|
|||||||
import forge.FThreads;
|
import forge.FThreads;
|
||||||
import forge.Forge;
|
import forge.Forge;
|
||||||
import forge.assets.FSkinFont;
|
import forge.assets.FSkinFont;
|
||||||
|
import forge.assets.ImageCache;
|
||||||
import forge.deck.DeckProxy;
|
import forge.deck.DeckProxy;
|
||||||
import forge.deck.DeckgenUtil;
|
import forge.deck.DeckgenUtil;
|
||||||
import forge.deck.FDeckChooser;
|
import forge.deck.FDeckChooser;
|
||||||
@@ -152,6 +153,9 @@ public class QuestDecksScreen extends FScreen {
|
|||||||
final DeckProxy deck = lstDecks.getSelectedItem();
|
final DeckProxy deck = lstDecks.getSelectedItem();
|
||||||
if (deck == null) { return; }
|
if (deck == null) { return; }
|
||||||
|
|
||||||
|
/*preload deck to cache*/
|
||||||
|
ImageCache.preloadCache(deck.getDeck());
|
||||||
|
|
||||||
needRefreshOnActivate = true;
|
needRefreshOnActivate = true;
|
||||||
Forge.openScreen(new QuestDeckEditor(deck));
|
Forge.openScreen(new QuestDeckEditor(deck));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -106,6 +106,10 @@ public class QuestPrefsScreen extends FScreen {
|
|||||||
scroller.add(new PrefsOption(localizer.getMessage("lblColorBias"), QPref.STARTING_POOL_COLOR_BIAS, PrefsGroup.DIFFICULTY_ALL));
|
scroller.add(new PrefsOption(localizer.getMessage("lblColorBias"), QPref.STARTING_POOL_COLOR_BIAS, PrefsGroup.DIFFICULTY_ALL));
|
||||||
scroller.add(new PrefsOption(localizer.getMessage("lblPenaltyforLoss"), QPref.PENALTY_LOSS, PrefsGroup.DIFFICULTY_ALL));
|
scroller.add(new PrefsOption(localizer.getMessage("lblPenaltyforLoss"), QPref.PENALTY_LOSS, PrefsGroup.DIFFICULTY_ALL));
|
||||||
|
|
||||||
|
//wild opponents addon
|
||||||
|
scroller.add(new PrefsOption(localizer.getMessage("lblWildOpponentMultiplier"), QPref.WILD_OPPONENTS_MULTIPLIER, PrefsGroup.DIFFICULTY_ALL));
|
||||||
|
scroller.add(new PrefsOption(localizer.getMessage("lblWildOpponentNumber"), QPref.WILD_OPPONENTS_NUMBER, PrefsGroup.DIFFICULTY_ALL));
|
||||||
|
|
||||||
//Difficulty Adjustments (Easy)
|
//Difficulty Adjustments (Easy)
|
||||||
scroller.add(new PrefsHeader(localizer.getMessage("lblDifficultyAdjustmentsEasy"), FSkinImage.QUEST_NOTES, PrefsGroup.DIFFICULTY_EASY));
|
scroller.add(new PrefsHeader(localizer.getMessage("lblDifficultyAdjustmentsEasy"), FSkinImage.QUEST_NOTES, PrefsGroup.DIFFICULTY_EASY));
|
||||||
scroller.add(new PrefsOption(localizer.getMessage("lblWinsForBooster"), QPref.WINS_BOOSTER_EASY, PrefsGroup.DIFFICULTY_EASY));
|
scroller.add(new PrefsOption(localizer.getMessage("lblWinsForBooster"), QPref.WINS_BOOSTER_EASY, PrefsGroup.DIFFICULTY_EASY));
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import forge.GuiBase;
|
|||||||
import forge.assets.FSkinColor;
|
import forge.assets.FSkinColor;
|
||||||
import forge.assets.FSkinFont;
|
import forge.assets.FSkinFont;
|
||||||
import forge.assets.FSkinImage;
|
import forge.assets.FSkinImage;
|
||||||
|
import forge.assets.ImageCache;
|
||||||
import forge.deck.CardPool;
|
import forge.deck.CardPool;
|
||||||
import forge.deck.Deck;
|
import forge.deck.Deck;
|
||||||
import forge.deck.DeckGroup;
|
import forge.deck.DeckGroup;
|
||||||
@@ -25,6 +26,7 @@ import forge.quest.QuestEventDraft;
|
|||||||
import forge.quest.QuestTournamentController;
|
import forge.quest.QuestTournamentController;
|
||||||
import forge.quest.QuestDraftUtils.Mode;
|
import forge.quest.QuestDraftUtils.Mode;
|
||||||
import forge.quest.data.QuestEventDraftContainer;
|
import forge.quest.data.QuestEventDraftContainer;
|
||||||
|
import forge.screens.LoadingOverlay;
|
||||||
import forge.screens.limited.DraftingProcessScreen;
|
import forge.screens.limited.DraftingProcessScreen;
|
||||||
import forge.toolbox.FButton;
|
import forge.toolbox.FButton;
|
||||||
import forge.toolbox.FContainer;
|
import forge.toolbox.FContainer;
|
||||||
@@ -227,8 +229,18 @@ public class QuestTournamentsScreen extends QuestLaunchScreen implements IQuestT
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void startDraft(BoosterDraft draft) {
|
public void startDraft(BoosterDraft draft) {
|
||||||
|
FThreads.invokeInEdtLater(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
LoadingOverlay.show("Loading Quest Tournament", new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
Forge.openScreen(new DraftingProcessScreen(draft, EditorType.QuestDraft, controller));
|
Forge.openScreen(new DraftingProcessScreen(draft, EditorType.QuestDraft, controller));
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private Deck getDeck() {
|
private Deck getDeck() {
|
||||||
DeckGroup deckGroup = FModel.getQuest().getDraftDecks().get(QuestEventDraft.DECK_NAME);
|
DeckGroup deckGroup = FModel.getQuest().getDraftDecks().get(QuestEventDraft.DECK_NAME);
|
||||||
@@ -241,6 +253,8 @@ public class QuestTournamentsScreen extends QuestLaunchScreen implements IQuestT
|
|||||||
public void editDeck(boolean isExistingDeck) {
|
public void editDeck(boolean isExistingDeck) {
|
||||||
Deck deck = getDeck();
|
Deck deck = getDeck();
|
||||||
if (deck != null) {
|
if (deck != null) {
|
||||||
|
/*preload deck to cache*/
|
||||||
|
ImageCache.preloadCache(deck);
|
||||||
if (isExistingDeck) {
|
if (isExistingDeck) {
|
||||||
Forge.openScreen(new QuestDraftDeckEditor(deck.getName()));
|
Forge.openScreen(new QuestDraftDeckEditor(deck.getName()));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,6 +60,9 @@ public class FButton extends FDisplayObject implements IButton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void resetImg() {
|
private void resetImg() {
|
||||||
|
imgL = FSkinImage.BTN_UP_LEFT;
|
||||||
|
imgM = FSkinImage.BTN_UP_CENTER;
|
||||||
|
imgR = FSkinImage.BTN_UP_RIGHT;
|
||||||
if (hdbuttonskin())
|
if (hdbuttonskin())
|
||||||
{
|
{
|
||||||
imgL = FSkinImage.HDBTN_UP_LEFT;
|
imgL = FSkinImage.HDBTN_UP_LEFT;
|
||||||
@@ -95,6 +98,9 @@ public class FButton extends FDisplayObject implements IButton {
|
|||||||
resetImg();
|
resetImg();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
imgL = FSkinImage.BTN_DISABLED_LEFT;
|
||||||
|
imgM = FSkinImage.BTN_DISABLED_CENTER;
|
||||||
|
imgR = FSkinImage.BTN_DISABLED_RIGHT;
|
||||||
if (hdbuttonskin())
|
if (hdbuttonskin())
|
||||||
{
|
{
|
||||||
imgL = FSkinImage.HDBTN_DISABLED_LEFT;
|
imgL = FSkinImage.HDBTN_DISABLED_LEFT;
|
||||||
@@ -121,6 +127,9 @@ public class FButton extends FDisplayObject implements IButton {
|
|||||||
toggled = b0;
|
toggled = b0;
|
||||||
|
|
||||||
if (toggled) {
|
if (toggled) {
|
||||||
|
imgL = FSkinImage.BTN_TOGGLE_LEFT;
|
||||||
|
imgM = FSkinImage.BTN_TOGGLE_CENTER;
|
||||||
|
imgR = FSkinImage.BTN_TOGGLE_RIGHT;
|
||||||
if (hdbuttonskin())
|
if (hdbuttonskin())
|
||||||
{
|
{
|
||||||
imgL = FSkinImage.HDBTN_TOGGLE_LEFT;
|
imgL = FSkinImage.HDBTN_TOGGLE_LEFT;
|
||||||
@@ -136,6 +145,9 @@ public class FButton extends FDisplayObject implements IButton {
|
|||||||
resetImg();
|
resetImg();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
imgL = FSkinImage.BTN_DISABLED_LEFT;
|
||||||
|
imgM = FSkinImage.BTN_DISABLED_CENTER;
|
||||||
|
imgR = FSkinImage.BTN_DISABLED_RIGHT;
|
||||||
if (hdbuttonskin())
|
if (hdbuttonskin())
|
||||||
{
|
{
|
||||||
imgL = FSkinImage.HDBTN_DISABLED_LEFT;
|
imgL = FSkinImage.HDBTN_DISABLED_LEFT;
|
||||||
@@ -170,6 +182,9 @@ public class FButton extends FDisplayObject implements IButton {
|
|||||||
@Override
|
@Override
|
||||||
public final boolean press(float x, float y) {
|
public final boolean press(float x, float y) {
|
||||||
if (isToggled()) { return true; }
|
if (isToggled()) { return true; }
|
||||||
|
imgL = FSkinImage.BTN_DOWN_LEFT;
|
||||||
|
imgM = FSkinImage.BTN_DOWN_CENTER;
|
||||||
|
imgR = FSkinImage.BTN_DOWN_RIGHT;
|
||||||
|
|
||||||
if (hdbuttonskin())
|
if (hdbuttonskin())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -367,6 +367,24 @@ public class FChoiceList<T> extends FList<T> implements ActivateHandler {
|
|||||||
g.drawText(getChoiceText(value), font, foreColor, x, y, w, h, false, Align.center, true);
|
g.drawText(getChoiceText(value), font, foreColor, x, y, w, h, false, Align.center, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//simple check for cardview needed on some special renderer for cards
|
||||||
|
private boolean showAlternate(CardView cardView, String value){
|
||||||
|
boolean showAlt = false;
|
||||||
|
if(cardView.hasAlternateState()){
|
||||||
|
if(cardView.hasBackSide())
|
||||||
|
showAlt = value.contains(cardView.getBackSideName());
|
||||||
|
else if (cardView.isAdventureCard())
|
||||||
|
showAlt = value.equals(cardView.getAlternateState().getAbilityText());
|
||||||
|
else if (cardView.isSplitCard()) {
|
||||||
|
//special case if aftermath cards can be cast from graveyard like yawgmoths will, you will have choices
|
||||||
|
if (cardView.getAlternateState().getOracleText().contains("Aftermath"))
|
||||||
|
showAlt = cardView.getAlternateState().getOracleText().contains(value);
|
||||||
|
else
|
||||||
|
showAlt = value.equals(cardView.getAlternateState().getAbilityText());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return showAlt;
|
||||||
|
}
|
||||||
//special renderer for cards
|
//special renderer for cards
|
||||||
protected class PaperCardItemRenderer extends ItemRenderer {
|
protected class PaperCardItemRenderer extends ItemRenderer {
|
||||||
@Override
|
@Override
|
||||||
@@ -464,7 +482,8 @@ public class FChoiceList<T> extends FList<T> implements ActivateHandler {
|
|||||||
@Override
|
@Override
|
||||||
public boolean tap(Integer index, T value, float x, float y, int count) {
|
public boolean tap(Integer index, T value, float x, float y, int count) {
|
||||||
if (x <= VStack.CARD_WIDTH + 2 * FList.PADDING) {
|
if (x <= VStack.CARD_WIDTH + 2 * FList.PADDING) {
|
||||||
CardZoom.show(((IHasCardView)value).getCardView());
|
CardView cv = ((IHasCardView)value).getCardView();
|
||||||
|
CardZoom.show(cv, showAlternate(cv, value.toString()));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -472,13 +491,16 @@ public class FChoiceList<T> extends FList<T> implements ActivateHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean longPress(Integer index, T value, float x, float y) {
|
public boolean longPress(Integer index, T value, float x, float y) {
|
||||||
CardZoom.show(((IHasCardView)value).getCardView());
|
CardView cv = ((IHasCardView)value).getCardView();
|
||||||
|
CardZoom.show(cv, showAlternate(cv, value.toString()));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void drawValue(Graphics g, T value, FSkinFont font, FSkinColor foreColor, boolean pressed, float x, float y, float w, float h) {
|
public void drawValue(Graphics g, T value, FSkinFont font, FSkinColor foreColor, boolean pressed, float x, float y, float w, float h) {
|
||||||
CardRenderer.drawCardWithOverlays(g, ((IHasCardView)value).getCardView(), x, y, VStack.CARD_WIDTH, VStack.CARD_HEIGHT, CardStackPosition.Top);
|
CardView cv = ((IHasCardView)value).getCardView();
|
||||||
|
boolean showAlternate = showAlternate(cv, value.toString());
|
||||||
|
CardRenderer.drawCardWithOverlays(g, cv, x, y, VStack.CARD_WIDTH, VStack.CARD_HEIGHT, CardStackPosition.Top, false, showAlternate);
|
||||||
|
|
||||||
float dx = VStack.CARD_WIDTH + FList.PADDING;
|
float dx = VStack.CARD_WIDTH + FList.PADDING;
|
||||||
x += dx;
|
x += dx;
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
Agetian
|
Agetian
|
||||||
apantel
|
apantel
|
||||||
Austinio7116
|
Austinio7116
|
||||||
|
CCTV-1
|
||||||
Churrufli
|
Churrufli
|
||||||
DrDev
|
DrDev
|
||||||
Elwin
|
Elwin
|
||||||
excessum
|
excessum
|
||||||
Flair
|
Flair
|
||||||
Gos
|
Gos
|
||||||
|
guytrash
|
||||||
Hanmac
|
Hanmac
|
||||||
Indigo Dragon
|
Indigo Dragon
|
||||||
Jamin Collins
|
Jamin Collins
|
||||||
@@ -16,7 +18,9 @@ KrazyTheFox
|
|||||||
leriomaggio
|
leriomaggio
|
||||||
Luke
|
Luke
|
||||||
Marek14
|
Marek14
|
||||||
|
Marvel
|
||||||
mcrawford620
|
mcrawford620
|
||||||
|
medusa
|
||||||
Meerkov
|
Meerkov
|
||||||
Myrd
|
Myrd
|
||||||
nefigah
|
nefigah
|
||||||
@@ -28,8 +32,11 @@ Seravy
|
|||||||
Sirspud
|
Sirspud
|
||||||
Sloth
|
Sloth
|
||||||
slyfox7777777
|
slyfox7777777
|
||||||
|
Snoops
|
||||||
Sol
|
Sol
|
||||||
|
squee1968
|
||||||
Swordshine
|
Swordshine
|
||||||
|
Svaldan
|
||||||
tjtillman
|
tjtillman
|
||||||
tojammot
|
tojammot
|
||||||
torridus
|
torridus
|
||||||
|
|||||||
@@ -6130,3 +6130,41 @@ Bloodchief's Thirst|ZNR|2
|
|||||||
Roil Eruption|ZNR|2
|
Roil Eruption|ZNR|2
|
||||||
Roiling Regrowth|ZNR|2
|
Roiling Regrowth|ZNR|2
|
||||||
Kargan Warleader|ZNR|2
|
Kargan Warleader|ZNR|2
|
||||||
|
|
||||||
|
[ZNRModalDoubleFaceCards]
|
||||||
|
1 Agadeem's Awakening|ZNR
|
||||||
|
1 Emeria's Call|ZNR
|
||||||
|
1 Sea Gate Restoration|ZNR
|
||||||
|
1 Shatterskull Smashing|ZNR
|
||||||
|
1 Turntimber Symbiosis|ZNR
|
||||||
|
2 Branchloft Pathway|ZNR
|
||||||
|
2 Brightclimb Pathway|ZNR
|
||||||
|
2 Clearwater Pathway|ZNR
|
||||||
|
2 Cragcrown Pathway|ZNR
|
||||||
|
2 Glasspool Mimic|ZNR
|
||||||
|
2 Hagra Mauling|ZNR
|
||||||
|
2 Kazandu Mammoth|ZNR
|
||||||
|
2 Needleverge Pathway|ZNR
|
||||||
|
2 Ondu Inversion|ZNR
|
||||||
|
2 Riverglide Pathway|ZNR
|
||||||
|
2 Valakut Awakening|ZNR
|
||||||
|
6 Akoum Warrior|ZNR
|
||||||
|
6 Bala Ged Recovery|ZNR
|
||||||
|
6 Beyeen Veil|ZNR
|
||||||
|
6 Blackbloom Rogue|ZNR
|
||||||
|
6 Jwari Disruption|ZNR
|
||||||
|
6 Kabira Takedown|ZNR
|
||||||
|
6 Kazuul's Fury|ZNR
|
||||||
|
6 Khalni Ambush|ZNR
|
||||||
|
6 Makindi Stampede|ZNR
|
||||||
|
6 Malakir Rebirth|ZNR
|
||||||
|
6 Pelakka Predation|ZNR
|
||||||
|
6 Sejiri Shelter|ZNR
|
||||||
|
6 Silundi Vision|ZNR
|
||||||
|
6 Skyclave Cleric|ZNR
|
||||||
|
6 Song-Mad Treachery|ZNR
|
||||||
|
6 Spikefield Hazard|ZNR
|
||||||
|
6 Tangled Florahedron|ZNR
|
||||||
|
6 Umara Wizard|ZNR
|
||||||
|
6 Vastwood Fortification|ZNR
|
||||||
|
6 Zof Consumption|ZNR
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
Name:Admiral's Order
|
Name:Admiral's Order
|
||||||
ManaCost:1 U U
|
ManaCost:1 U U
|
||||||
Types:Instant
|
Types:Instant
|
||||||
|
SVar:AltCost:Cost$ U | CheckSVar$ X | References$ X | Description$ Raid — If you attacked this turn, you may pay {U} rather than pay this spell's mana cost.
|
||||||
A:SP$ Counter | Cost$ 1 U U | TargetType$ Spell | TgtPrompt$ Select target spell | ValidTgts$ Card | SpellDescription$ Counter target spell.
|
A:SP$ Counter | Cost$ 1 U U | TargetType$ Spell | TgtPrompt$ Select target spell | ValidTgts$ Card | SpellDescription$ Counter target spell.
|
||||||
A:SP$ Counter | Cost$ U | CheckSVar$ X | References$ X | SVarCompare$ GE1 | TargetType$ Spell | TgtPrompt$ Select target spell | ValidTgts$ Card | SpellDescription$ Raid — If you attacked this turn, you may pay {U} rather than pay this spell's mana cost. Counter target spell.
|
|
||||||
SVar:X:Count$AttackersDeclared
|
SVar:X:Count$AttackersDeclared
|
||||||
Oracle:Raid — If you attacked this turn, you may pay {U} rather than pay this spell's mana cost.\nCounter target spell.
|
Oracle:Raid — If you attacked this turn, you may pay {U} rather than pay this spell's mana cost.\nCounter target spell.
|
||||||
|
|||||||
@@ -2,9 +2,8 @@ Name:Aegis of Honor
|
|||||||
ManaCost:W
|
ManaCost:W
|
||||||
Types:Enchantment
|
Types:Enchantment
|
||||||
A:AB$ Effect | Cost$ 1 | ReplacementEffects$ SelflessDamage | SVars$ SelflessDmg,ExileEffect | References$ SelflessDamage,SelflessDmg,ExileEffect | AILogic$ RedirectSpellDamageFromPlayer | Stackable$ False | SpellDescription$ The next time an instant or sorcery spell would deal damage to you this turn, that spell deals that damage to its controller instead.
|
A:AB$ Effect | Cost$ 1 | ReplacementEffects$ SelflessDamage | SVars$ SelflessDmg,ExileEffect | References$ SelflessDamage,SelflessDmg,ExileEffect | AILogic$ RedirectSpellDamageFromPlayer | Stackable$ False | SpellDescription$ The next time an instant or sorcery spell would deal damage to you this turn, that spell deals that damage to its controller instead.
|
||||||
SVar:SelflessDamage:Event$ DamageDone | ValidTarget$ You | ValidSource$ Instant,Sorcery | ReplaceWith$ SelflessDmg | Description$ The next time a source of your choice would deal damage this turn, that damage is dealt to that source's controller instead.
|
SVar:SelflessDamage:Event$ DamageDone | ValidTarget$ You | ValidSource$ Instant,Sorcery | ReplaceWith$ SelflessDmg | DamageTarget$ ReplacedSourceController | Description$ The next time a source of your choice would deal damage this turn, that damage is dealt to that source's controller instead.
|
||||||
SVar:SelflessDmg:DB$ ReplaceEffect | VarName$ Affected | VarValue$ ReplacedSourceController | VarType$ Player | SubAbility$ ExileEffect
|
SVar:SelflessDmg:DB$ ReplaceEffect | VarName$ Affected | VarValue$ ReplacedSourceController | VarType$ Player | SubAbility$ ExileEffect
|
||||||
SVar:ExileEffect:DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile
|
SVar:ExileEffect:DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile
|
||||||
SVar:NonStackingEffect:True
|
SVar:NonStackingEffect:True
|
||||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/aegis_of_honor.jpg
|
|
||||||
Oracle:{1}: The next time an instant or sorcery spell would deal damage to you this turn, that spell deals that damage to its controller instead.
|
Oracle:{1}: The next time an instant or sorcery spell would deal damage to you this turn, that spell deals that damage to its controller instead.
|
||||||
|
|||||||
@@ -4,6 +4,5 @@ Types:Creature Elf Warrior
|
|||||||
PT:1+*/1+*
|
PT:1+*/1+*
|
||||||
S:Mode$ Continuous | EffectZone$ All | CharacteristicDefining$ True | SetPower$ X | SetToughness$ X | Description$ CARDNAME's power and toughness are each equal to 1 plus the number of lands you control.
|
S:Mode$ Continuous | EffectZone$ All | CharacteristicDefining$ True | SetPower$ X | SetToughness$ X | Description$ CARDNAME's power and toughness are each equal to 1 plus the number of lands you control.
|
||||||
SVar:X:Count$Valid Land.YouCtrl/Plus.1
|
SVar:X:Count$Valid Land.YouCtrl/Plus.1
|
||||||
SVar:AltCost:Cost$ ExileFromHand<2/Card.Green> | Description$ You may exile two green cards from your hand rather than pay CARDNAME's mana cost.
|
SVar:AltCost:Cost$ ExileFromHand<2/Card.Green> | Description$ You may exile two green cards from your hand rather than pay this spell's mana cost.
|
||||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/allosaurus_rider.jpg
|
|
||||||
Oracle:You may exile two green cards from your hand rather than pay this spell's mana cost.\nAllosaurus Rider's power and toughness are each equal to 1 plus the number of lands you control.
|
Oracle:You may exile two green cards from your hand rather than pay this spell's mana cost.\nAllosaurus Rider's power and toughness are each equal to 1 plus the number of lands you control.
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
Name:Angelic Favor
|
Name:Angelic Favor
|
||||||
ManaCost:3 W
|
ManaCost:3 W
|
||||||
Types:Instant
|
Types:Instant
|
||||||
A:SP$ Token | Cost$ 3 W | TokenScript$ w_4_4_angel_flying | LegacyImage$ w 4 4 angel flying nms | AtEOT$ Exile | ActivationPhases$ BeginCombat->EndCombat | SpellDescription$ Cast CARDNAME only during combat. Create a 4/4 white Angel creature token with flying. Exile it at the beginning of the next end step.
|
SVar:AltCost:Cost$ tapXType<1/Creature> | IsPresent$ Plains.YouCtrl | Description$ If you control a Plains, you may tap an untapped creature you control rather than pay this spell's mana cost.
|
||||||
SVar:AltCost:Cost$ tapXType<1/Creature> | IsPresent$ Plains.YouCtrl | Description$ If you control a Plains, you may tap an untapped creature you control rather than pay CARDNAME's mana cost.
|
A:SP$ Token | Cost$ 3 W | TokenScript$ w_4_4_angel_flying | AtEOT$ Exile | ActivationPhases$ BeginCombat->EndCombat | StackDescription$ {p:You} creates a 4/4 white Angel creature token with flying. Exile it at the beginning of the next end step. | SpellDescription$ Cast this spell only during combat. Create a 4/4 white Angel creature token with flying. Exile it at the beginning of the next end step.
|
||||||
DeckHas:Ability$Token
|
DeckHas:Ability$Token
|
||||||
AI:RemoveDeck:All
|
AI:RemoveDeck:All
|
||||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/angelic_favor.jpg
|
|
||||||
Oracle:If you control a Plains, you may tap an untapped creature you control rather than pay this spell's mana cost.\nCast this spell only during combat.\nCreate a 4/4 white Angel creature token with flying. Exile it at the beginning of the next end step.
|
Oracle:If you control a Plains, you may tap an untapped creature you control rather than pay this spell's mana cost.\nCast this spell only during combat.\nCreate a 4/4 white Angel creature token with flying. Exile it at the beginning of the next end step.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
Name:Archive Trap
|
Name:Archive Trap
|
||||||
ManaCost:3 U U
|
ManaCost:3 U U
|
||||||
Types:Instant Trap
|
Types:Instant Trap
|
||||||
|
SVar:AltCost:Cost$ 0 | CheckSVar$ TrapTrigger | References$ TrapTrigger | Description$ If an opponent searched their library this turn, you may pay {0} rather than pay this spell's mana cost.
|
||||||
A:SP$ Mill | Cost$ 3 U U | NumCards$ 13 | ValidTgts$ Opponent | TgtPrompt$ Choose an opponent | SpellDescription$ Target opponent mills thirteen cards.
|
A:SP$ Mill | Cost$ 3 U U | NumCards$ 13 | ValidTgts$ Opponent | TgtPrompt$ Choose an opponent | SpellDescription$ Target opponent mills thirteen cards.
|
||||||
A:SP$ Mill | Cost$ 0 | CheckSVar$ TrapTrigger | NumCards$ 13 | ValidTgts$ Opponent | TgtPrompt$ Choose an opponent | CostDesc$ If an opponent searched their library this turn, you may pay {0} rather than pay CARDNAME's mana cost. | References$ TrapTrigger | SpellDescription$
|
|
||||||
SVar:TrapTrigger:Count$SearchedLibrary.Opponent
|
SVar:TrapTrigger:Count$SearchedLibrary.Opponent
|
||||||
Oracle:If an opponent searched their library this turn, you may pay {0} rather than pay this spell's mana cost.\nTarget opponent mills thirteen cards.
|
Oracle:If an opponent searched their library this turn, you may pay {0} rather than pay this spell's mana cost.\nTarget opponent mills thirteen cards.
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
Name:Arrow Volley Trap
|
Name:Arrow Volley Trap
|
||||||
ManaCost:3 W W
|
ManaCost:3 W W
|
||||||
Types:Instant Trap
|
Types:Instant Trap
|
||||||
|
SVar:AltCost:Cost$ 1 W | IsPresent$ Creature.attacking | PresentCompare$ GE4 | Description$ If four or more creatures are attacking, you may pay {1}{W} rather than pay this spell's mana cost.
|
||||||
A:SP$ DealDamage | Cost$ 3 W W | ValidTgts$ Creature.attacking | TgtPrompt$ Select target attacking creature to distribute damage to | NumDmg$ 5 | TargetMin$ 1 | TargetMax$ 5 | DividedAsYouChoose$ 5 | SpellDescription$ CARDNAME deals 5 damage divided as you choose among any number of target attacking creatures.
|
A:SP$ DealDamage | Cost$ 3 W W | ValidTgts$ Creature.attacking | TgtPrompt$ Select target attacking creature to distribute damage to | NumDmg$ 5 | TargetMin$ 1 | TargetMax$ 5 | DividedAsYouChoose$ 5 | SpellDescription$ CARDNAME deals 5 damage divided as you choose among any number of target attacking creatures.
|
||||||
A:SP$ DealDamage | Cost$ 1 W | ValidTgts$ Creature.attacking | TgtPrompt$ Select target attacking creature to distribute damage to | NumDmg$ 5 | TargetMin$ 1 | TargetMax$ 5 | DividedAsYouChoose$ 5 | IsPresent$ Creature.attacking | PresentCompare$ GE4 | CostDesc$ If four or more creatures are attacking, you may pay {1}{W} rather than pay CARDNAME's mana cost. | SpellDescription$
|
|
||||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/arrow_volley_trap.jpg
|
|
||||||
Oracle:If four or more creatures are attacking, you may pay {1}{W} rather than pay this spell's mana cost.\nArrow Volley Trap deals 5 damage divided as you choose among any number of target attacking creatures.
|
Oracle:If four or more creatures are attacking, you may pay {1}{W} rather than pay this spell's mana cost.\nArrow Volley Trap deals 5 damage divided as you choose among any number of target attacking creatures.
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
Name:Baloth Cage Trap
|
Name:Baloth Cage Trap
|
||||||
ManaCost:3 G G
|
ManaCost:3 G G
|
||||||
Types:Instant Trap
|
Types:Instant Trap
|
||||||
A:SP$ Token | Cost$ 3 G G | TokenAmount$ 1 | TokenScript$ g_4_4_beast | TokenOwner$ You | LegacyImage$ g 4 4 beast zen | SpellDescription$ Create a 4/4 green Beast creature token.
|
SVar:AltCost:Cost$ 1 G | CheckSVar$ ArtifactsEntered | References$ ArtifactsEntered | Description$ If an opponent had an artifact enter the battlefield under their control this turn, you may pay {1}{G} rather than pay this spell's mana cost.
|
||||||
A:SP$ Token | Cost$ 1 G | CheckSVar$ ArtifactsEntered | TokenAmount$ 1 | TokenScript$ g_4_4_beast | TokenOwner$ You | LegacyImage$ g 4 4 beast zen | SpellDescription$ If an opponent had an artifact enter the battlefield under their control this turn, you may pay {1}{G} rather than pay Baloth Cage Trap's mana cost.
|
A:SP$ Token | Cost$ 3 G G | TokenAmount$ 1 | TokenScript$ g_4_4_beast | TokenOwner$ You | StackDescription$ {p:You} creates a 4/4 green Beast creature token. | SpellDescription$ Create a 4/4 green Beast creature token.
|
||||||
SVar:ArtifactsEntered:Count$ThisTurnEntered_Battlefield_Artifact.OppCtrl
|
SVar:ArtifactsEntered:Count$ThisTurnEntered_Battlefield_Artifact.OppCtrl
|
||||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/baloth_cage_trap.jpg
|
Oracle:If an opponent had an artifact enter the battlefield under their control this turn, you may pay {1}{G} rather than pay this spell's mana cost.\nCreate a 4/4 green Beast creature token.
|
||||||
Oracle:If an opponent had an artifact enter the battlefield under their control this turn, you may pay {1}{G} rather than pay Baloth Cage Trap's mana cost.\nCreate a 4/4 green Beast creature token.
|
|
||||||
|
|||||||
10
forge-gui/res/cardsfolder/b/baloth_packhunter.txt
Normal file
10
forge-gui/res/cardsfolder/b/baloth_packhunter.txt
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
Name:Baloth Packhunter
|
||||||
|
ManaCost:3 G
|
||||||
|
Types:Creature Beast
|
||||||
|
PT:3/3
|
||||||
|
K:Trample
|
||||||
|
T:Mode$ ChangesZone | ValidCard$ Card.Self | Destination$ Battlefield | Execute$ TrigPutCounters | TriggerDescription$ When CARDNAME enters the battlefield, put two +1/+1 counters on each other creature you control named Baloth Packhunter.
|
||||||
|
SVar:TrigPutCounters:DB$ PutCounterAll | ValidCards$ Creature.Other+namedBaloth Packhunter | CounterType$ P1P1 | CounterNum$ 2
|
||||||
|
DeckHints:Name$CARDNAME
|
||||||
|
DeckHas:Ability$Counters
|
||||||
|
Oracle:Trample\nWhen Baloth Packhunter enters the battlefield, put two +1/+1 counters on each other creature you control named Baloth Packhunter.
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
Name:Blazing Shoal
|
Name:Blazing Shoal
|
||||||
ManaCost:X R R
|
ManaCost:X R R
|
||||||
Types:Instant Arcane
|
Types:Instant Arcane
|
||||||
A:SP$ Pump | Cost$ X R R | ValidTgts$ Creature | TgtPrompt$ Select target creature | NumAtt$ +X | References$ X | SpellDescription$ Target creature gets +X/+0 until end of turn.
|
SVar:AltCost:Cost$ ExileFromHand<1/Card.Red+Other/red card> | Description$ You may exile a red card with converted mana cost X from your hand rather than pay this spell's mana cost.
|
||||||
A:SP$ Pump | Cost$ ExileFromHand<1/Card.Red> | CostDesc$ You may exile a red card from your hand rather than pay Blazing Shoal's mana cost. | ValidTgts$ Creature | NumAtt$ +Y | References$ Y | SpellDescription$ Target creature gets +X/+0 until end of turn, where X is the exiled card's converted mana cost.
|
A:SP$ Pump | Cost$ X R R | ValidTgts$ Creature | TgtPrompt$ Select target creature | NumAtt$ +Z | References$ X,Y,Z | SpellDescription$ Target creature gets +X/+0 until end of turn.
|
||||||
SVar:X:Count$xPaid
|
SVar:X:Count$xPaid
|
||||||
SVar:Y:Exiled$CardManaCost
|
SVar:Y:Exiled$CardManaCost
|
||||||
|
SVar:Z:SVar$Y/Plus.X
|
||||||
AI:RemoveDeck:All
|
AI:RemoveDeck:All
|
||||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/blazing_shoal.jpg
|
|
||||||
Oracle:You may exile a red card with converted mana cost X from your hand rather than pay this spell's mana cost.\nTarget creature gets +X/+0 until end of turn.
|
Oracle:You may exile a red card with converted mana cost X from your hand rather than pay this spell's mana cost.\nTarget creature gets +X/+0 until end of turn.
|
||||||
|
|||||||
@@ -2,8 +2,7 @@ Name:Blood of the Martyr
|
|||||||
ManaCost:W W W
|
ManaCost:W W W
|
||||||
Types:Instant
|
Types:Instant
|
||||||
A:SP$ Effect | Cost$ W W W | Name$ Blood of the Martyr Effect | ReplacementEffects$ MartyrDamage | SVars$ DmgYou | SpellDescription$ Until end of turn, if damage would be dealt to any creature, you may have that damage dealt to you instead.
|
A:SP$ Effect | Cost$ W W W | Name$ Blood of the Martyr Effect | ReplacementEffects$ MartyrDamage | SVars$ DmgYou | SpellDescription$ Until end of turn, if damage would be dealt to any creature, you may have that damage dealt to you instead.
|
||||||
SVar:Damage:Event$ DamageDone | ValidTarget$ Creature | Optional$ True | OptionalDecider$ You | ReplaceWith$ DmgYou | Description$ Until end of turn, if damage would be dealt to any creature, you may have that damage dealt to you instead.
|
SVar:Damage:Event$ DamageDone | ValidTarget$ Creature | Optional$ True | OptionalDecider$ You | DamageTarget$ You | ReplaceWith$ DmgYou | Description$ Until end of turn, if damage would be dealt to any creature, you may have that damage dealt to you instead.
|
||||||
SVar:DmgYou:DB$ ReplaceEffect | VarName$ Affected | VarValue$ You | VarType$ Player
|
SVar:DmgYou:DB$ ReplaceEffect | VarName$ Affected | VarValue$ You | VarType$ Player
|
||||||
AI:RemoveDeck:All
|
AI:RemoveDeck:All
|
||||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/blood_of_the_martyr.jpg
|
|
||||||
Oracle:Until end of turn, if damage would be dealt to any creature, you may have that damage dealt to you instead.
|
Oracle:Until end of turn, if damage would be dealt to any creature, you may have that damage dealt to you instead.
|
||||||
|
|||||||
@@ -2,8 +2,7 @@ Name:Bounty of the Hunt
|
|||||||
ManaCost:3 G G
|
ManaCost:3 G G
|
||||||
Types:Instant
|
Types:Instant
|
||||||
A:SP$ PutCounter | Cost$ 3 G G | ValidTgts$ Creature | TgtPrompt$ Select target creature to distribute counters to | CounterType$ P1P1 | CounterNum$ 3 | TargetMin$ 1 | TargetMax$ 3 | DividedAsYouChoose$ 3 | RemovePhase$ Cleanup | SpellDescription$ Distribute three +1/+1 counters among one, two, or three target creatures. For each +1/+1 counter you put on a creature this way, remove a +1/+1 counter from that creature at the beginning of the next cleanup step.
|
A:SP$ PutCounter | Cost$ 3 G G | ValidTgts$ Creature | TgtPrompt$ Select target creature to distribute counters to | CounterType$ P1P1 | CounterNum$ 3 | TargetMin$ 1 | TargetMax$ 3 | DividedAsYouChoose$ 3 | RemovePhase$ Cleanup | SpellDescription$ Distribute three +1/+1 counters among one, two, or three target creatures. For each +1/+1 counter you put on a creature this way, remove a +1/+1 counter from that creature at the beginning of the next cleanup step.
|
||||||
SVar:AltCost:Cost$ExileFromHand<1/Card.Green> | Description$ You may exile a green card from your hand rather than pay CARDNAME's mana cost.
|
SVar:AltCost:Cost$ExileFromHand<1/Card.Green+Other> | Description$ You may exile a green card from your hand rather than pay this spell's mana cost.
|
||||||
DeckHas:Ability$Counters
|
DeckHas:Ability$Counters
|
||||||
AI:RemoveDeck:All
|
AI:RemoveDeck:All
|
||||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/bounty_of_the_hunt.jpg
|
|
||||||
Oracle:You may exile a green card from your hand rather than pay this spell's mana cost.\nDistribute three +1/+1 counters among one, two, or three target creatures. For each +1/+1 counter you put on a creature this way, remove a +1/+1 counter from that creature at the beginning of the next cleanup step.
|
Oracle:You may exile a green card from your hand rather than pay this spell's mana cost.\nDistribute three +1/+1 counters among one, two, or three target creatures. For each +1/+1 counter you put on a creature this way, remove a +1/+1 counter from that creature at the beginning of the next cleanup step.
|
||||||
|
|||||||
@@ -3,9 +3,8 @@ ManaCost:7 B B
|
|||||||
Types:Creature Bringer
|
Types:Creature Bringer
|
||||||
PT:5/5
|
PT:5/5
|
||||||
K:Trample
|
K:Trample
|
||||||
SVar:AltCost:Cost$ W U B R G | Description$ You may pay {W}{U}{B}{R}{G} rather than pay CARDNAME's mana cost.
|
SVar:AltCost:Cost$ W U B R G | Description$ You may pay {W}{U}{B}{R}{G} rather than pay this spell's mana cost.
|
||||||
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Battlefield | OptionalDecider$ You | Execute$ TrigChange | TriggerDescription$ At the beginning of your upkeep, you may pay 2 life. If you do, search your library for a card, then shuffle your library and put that card on top of it.
|
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Battlefield | OptionalDecider$ You | Execute$ TrigChange | TriggerDescription$ At the beginning of your upkeep, you may pay 2 life. If you do, search your library for a card, then shuffle your library and put that card on top of it.
|
||||||
SVar:TrigChange:AB$ChangeZone | Cost$ PayLife<2> | Origin$ Library | Destination$ Library | LibraryPosition$ 0 | ChangeType$ Card | ChangeNum$ 1
|
SVar:TrigChange:AB$ ChangeZone | Cost$ PayLife<2> | Origin$ Library | Destination$ Library | LibraryPosition$ 0 | ChangeType$ Card | ChangeNum$ 1
|
||||||
AI:RemoveDeck:All
|
AI:RemoveDeck:All
|
||||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/bringer_of_the_black_dawn.jpg
|
|
||||||
Oracle:You may pay {W}{U}{B}{R}{G} rather than pay this spell's mana cost.\nTrample\nAt the beginning of your upkeep, you may pay 2 life. If you do, search your library for a card, then shuffle your library and put that card on top of it.
|
Oracle:You may pay {W}{U}{B}{R}{G} rather than pay this spell's mana cost.\nTrample\nAt the beginning of your upkeep, you may pay 2 life. If you do, search your library for a card, then shuffle your library and put that card on top of it.
|
||||||
|
|||||||
@@ -3,8 +3,7 @@ ManaCost:7 U U
|
|||||||
Types:Creature Bringer
|
Types:Creature Bringer
|
||||||
PT:5/5
|
PT:5/5
|
||||||
K:Trample
|
K:Trample
|
||||||
|
SVar:AltCost:Cost$ W U B R G | Description$ You may pay {W}{U}{B}{R}{G} rather than pay this spell's mana cost.
|
||||||
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Battlefield | OptionalDecider$ You | Execute$ TrigDraw | TriggerDescription$ At the beginning of your upkeep, you may draw two cards.
|
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Battlefield | OptionalDecider$ You | Execute$ TrigDraw | TriggerDescription$ At the beginning of your upkeep, you may draw two cards.
|
||||||
SVar:TrigDraw:DB$Draw | Defined$ You | NumCards$ 2
|
SVar:TrigDraw:DB$ Draw | Defined$ You | NumCards$ 2
|
||||||
SVar:AltCost:Cost$ W U B R G | Description$ You may pay {W}{U}{B}{R}{G} rather than pay CARDNAME's mana cost.
|
|
||||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/bringer_of_the_blue_dawn.jpg
|
|
||||||
Oracle:You may pay {W}{U}{B}{R}{G} rather than pay this spell's mana cost.\nTrample\nAt the beginning of your upkeep, you may draw two cards.
|
Oracle:You may pay {W}{U}{B}{R}{G} rather than pay this spell's mana cost.\nTrample\nAt the beginning of your upkeep, you may draw two cards.
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ ManaCost:7 G G
|
|||||||
Types:Creature Bringer
|
Types:Creature Bringer
|
||||||
PT:5/5
|
PT:5/5
|
||||||
K:Trample
|
K:Trample
|
||||||
SVar:AltCost:Cost$ W U B R G | Description$ You may pay {W}{U}{B}{R}{G} rather than pay CARDNAME's mana cost.
|
SVar:AltCost:Cost$ W U B R G | Description$ You may pay {W}{U}{B}{R}{G} rather than pay this spell's mana cost.
|
||||||
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Battlefield | OptionalDecider$ You | Execute$ TrigToken | TriggerDescription$ At the beginning of your upkeep, you may create a 3/3 green Beast creature token.
|
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Battlefield | OptionalDecider$ You | Execute$ TrigToken | TriggerDescription$ At the beginning of your upkeep, you may create a 3/3 green Beast creature token.
|
||||||
SVar:TrigToken:DB$Token | TokenAmount$ 1 | TokenScript$ g_3_3_beast | TokenOwner$ You | LegacyImage$ g 3 3 beast 5dn
|
SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ g_3_3_beast | TokenOwner$ You
|
||||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/bringer_of_the_green_dawn.jpg
|
DeckHas:Ability$Token
|
||||||
Oracle:You may pay {W}{U}{B}{R}{G} rather than pay this spell's mana cost.\nTrample\nAt the beginning of your upkeep, you may create a 3/3 green Beast creature token.
|
Oracle:You may pay {W}{U}{B}{R}{G} rather than pay this spell's mana cost.\nTrample\nAt the beginning of your upkeep, you may create a 3/3 green Beast creature token.
|
||||||
|
|||||||
@@ -3,8 +3,7 @@ ManaCost:7 R R
|
|||||||
Types:Creature Bringer
|
Types:Creature Bringer
|
||||||
PT:5/5
|
PT:5/5
|
||||||
K:Trample
|
K:Trample
|
||||||
SVar:AltCost:Cost$ W U B R G | Description$ You may pay {W}{U}{B}{R}{G} rather than pay CARDNAME's mana cost.
|
SVar:AltCost:Cost$ W U B R G | Description$ You may pay {W}{U}{B}{R}{G} rather than pay this spell's mana cost.
|
||||||
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Battlefield | OptionalDecider$ You | Execute$ TrigChange | TriggerDescription$ At the beginning of your upkeep, you may untap target creature and gain control of it until end of turn. That creature gains haste until end of turn.
|
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Battlefield | OptionalDecider$ You | Execute$ TrigChange | TriggerDescription$ At the beginning of your upkeep, you may untap target creature and gain control of it until end of turn. That creature gains haste until end of turn.
|
||||||
SVar:TrigChange:DB$GainControl | ValidTgts$ Creature | TgtPrompt$ Select target creature | LoseControl$ EOT | Untap$ True | AddKWs$ Haste
|
SVar:TrigChange:DB$ GainControl | ValidTgts$ Creature | TgtPrompt$ Select target creature | LoseControl$ EOT | Untap$ True | AddKWs$ Haste
|
||||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/bringer_of_the_red_dawn.jpg
|
|
||||||
Oracle:You may pay {W}{U}{B}{R}{G} rather than pay this spell's mana cost.\nTrample\nAt the beginning of your upkeep, you may untap target creature and gain control of it until end of turn. That creature gains haste until end of turn.
|
Oracle:You may pay {W}{U}{B}{R}{G} rather than pay this spell's mana cost.\nTrample\nAt the beginning of your upkeep, you may untap target creature and gain control of it until end of turn. That creature gains haste until end of turn.
|
||||||
|
|||||||
@@ -3,8 +3,9 @@ ManaCost:7 W W
|
|||||||
Types:Creature Bringer
|
Types:Creature Bringer
|
||||||
PT:5/5
|
PT:5/5
|
||||||
K:Trample
|
K:Trample
|
||||||
SVar:AltCost:Cost$ W U B R G | Description$ You may pay {W}{U}{B}{R}{G} rather than pay CARDNAME's mana cost.
|
SVar:AltCost:Cost$ W U B R G | Description$ You may pay {W}{U}{B}{R}{G} rather than pay this spell's mana cost.
|
||||||
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Battlefield | OptionalDecider$ You | Execute$ TrigChange | TriggerDescription$ At the beginning of your upkeep, you may return target artifact card from your graveyard to the battlefield.
|
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Battlefield | OptionalDecider$ You | Execute$ TrigChange | TriggerDescription$ At the beginning of your upkeep, you may return target artifact card from your graveyard to the battlefield.
|
||||||
SVar:TrigChange:AB$ChangeZone | Origin$ Graveyard | Destination$ Battlefield | ValidTgts$ Artifact.YouCtrl | Cost$ 0
|
SVar:TrigChange:AB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | ValidTgts$ Artifact.YouCtrl | Cost$ 0
|
||||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/bringer_of_the_white_dawn.jpg
|
DeckHas:Ability$Graveyard
|
||||||
|
DeckNeeds:Type$Artifact
|
||||||
Oracle:You may pay {W}{U}{B}{R}{G} rather than pay this spell's mana cost.\nTrample\nAt the beginning of your upkeep, you may return target artifact card from your graveyard to the battlefield.
|
Oracle:You may pay {W}{U}{B}{R}{G} rather than pay this spell's mana cost.\nTrample\nAt the beginning of your upkeep, you may return target artifact card from your graveyard to the battlefield.
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
Name:Burn at the Stake
|
Name:Burn at the Stake
|
||||||
ManaCost:2 R R R
|
ManaCost:2 R R R
|
||||||
Types:Sorcery
|
Types:Sorcery
|
||||||
A:SP$ DealDamage | Cost$ 2 R R R tapXType<X/Creature> | CostDesc$ As an additional cost to cast CARDNAME, tap any number of untapped creatures you control. | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | NumDmg$ BurnAtTheStakeDmg | References$ X,BurnAtTheStakeDmg | SpellDescription$ CARDNAME deals damage to any target equal to three times the number of creatures tapped this way.
|
A:SP$ DealDamage | Cost$ 2 R R R tapXType<X/Creature> | CostDesc$ As an additional cost to cast this spell, tap any number of untapped creatures you control. | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | NumDmg$ BurnAtTheStakeDmg | References$ X,BurnAtTheStakeDmg | SpellDescription$ CARDNAME deals damage to any target equal to three times the number of creatures tapped this way.
|
||||||
SVar:X:XChoice
|
SVar:X:XChoice
|
||||||
SVar:BurnAtTheStakeDmg:Number$3/Times.ChosenX
|
SVar:BurnAtTheStakeDmg:Number$3/Times.ChosenX
|
||||||
AI:RemoveDeck:All
|
AI:RemoveDeck:All
|
||||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/burn_at_the_stake.jpg
|
|
||||||
Oracle:As an additional cost to cast this spell, tap any number of untapped creatures you control.\nBurn at the Stake deals damage to any target equal to three times the number of creatures tapped this way.
|
Oracle:As an additional cost to cast this spell, tap any number of untapped creatures you control.\nBurn at the Stake deals damage to any target equal to three times the number of creatures tapped this way.
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
Name:Burning Wish
|
Name:Burning Wish
|
||||||
ManaCost:1 R
|
ManaCost:1 R
|
||||||
Types:Sorcery
|
Types:Sorcery
|
||||||
A:SP$ ChangeZone | Cost$ 1 R | Origin$ Sideboard | Destination$ Hand | ChangeType$ Sorcery.YouOwn | ChangeNum$ 1 | SubAbility$ DBChange | SpellDescription$ You may choose a sorcery card you own from outside the game, reveal that card, and put it into your hand. Exile CARDNAME.
|
A:SP$ ChangeZone | Cost$ 1 R | Reveal$ True | Origin$ Sideboard | Destination$ Hand | ChangeType$ Sorcery.YouOwn | ChangeTypeDesc$ sorcery card they own | ChangeNum$ 1 | SubAbility$ DBChange | Hidden$ True | SpellDescription$ You may reveal a sorcery card you own from outside the game and put it into your hand. Exile CARDNAME.
|
||||||
SVar:DBChange:DB$ ChangeZone | Origin$ Stack | Destination$ Exile
|
SVar:DBChange:DB$ ChangeZone | Origin$ Stack | Destination$ Exile
|
||||||
AI:RemoveDeck:Random
|
AI:RemoveDeck:Random
|
||||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/burning_wish.jpg
|
Oracle:You may reveal a sorcery card you own from outside the game and put it into your hand. Exile Burning Wish.
|
||||||
Oracle:You may choose a sorcery card you own from outside the game, reveal that card, and put it into your hand. Exile Burning Wish.
|
|
||||||
|
|||||||
@@ -5,14 +5,11 @@ R:Event$ Moved | ValidCard$ Card.Self | Destination$ Battlefield | ReplaceWith$
|
|||||||
SVar:DBChooseOpp:DB$ ChoosePlayer | Defined$ You | Choices$ Player.Opponent | ChoiceTitle$ Choose an opponent to give control to: | AILogic$ Curse | SubAbility$ MoveToPlay
|
SVar:DBChooseOpp:DB$ ChoosePlayer | Defined$ You | Choices$ Player.Opponent | ChoiceTitle$ Choose an opponent to give control to: | AILogic$ Curse | SubAbility$ MoveToPlay
|
||||||
SVar:MoveToPlay:DB$ ChangeZone | Hidden$ True | Origin$ All | Destination$ Battlefield | Defined$ ReplacedCard | GainControl$ True | NewController$ ChosenPlayer | SubAbility$ ClearRemembered
|
SVar:MoveToPlay:DB$ ChangeZone | Hidden$ True | Origin$ All | Destination$ Battlefield | Defined$ ReplacedCard | GainControl$ True | NewController$ ChosenPlayer | SubAbility$ ClearRemembered
|
||||||
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | Execute$ TrigCharm | TriggerZones$ Battlefield | TriggerDescription$ At the beginning of your upkeep, ABILITY
|
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | Execute$ TrigCharm | TriggerZones$ Battlefield | TriggerDescription$ At the beginning of your upkeep, ABILITY
|
||||||
SVar:TrigCharm:DB$ Charm | Choices$ LifePact,DiscardPact,ZombiesPact | ChoiceRestriction$ NotRemembered | RememberChoice$ True | CharmNum$ 1
|
SVar:TrigCharm:DB$ Charm | Choices$ LifePact,DiscardPact,ZombiesPact | ChoiceRestriction$ ThisGame | CharmNum$ 1
|
||||||
SVar:LifePact:DB$ SetLife | Defined$ You | LifeAmount$ 4 | ChoiceName$ LifePact | SpellDescription$ Your life total becomes 4.
|
SVar:LifePact:DB$ SetLife | Defined$ You | LifeAmount$ 4 | SpellDescription$ Your life total becomes 4.
|
||||||
SVar:DiscardPact:DB$ Discard | Defined$ You | Mode$ Hand | ChoiceName$ DiscardPact | SpellDescription$ Discard your hand.
|
SVar:DiscardPact:DB$ Discard | Defined$ You | Mode$ Hand | SpellDescription$ Discard your hand.
|
||||||
SVar:ZombiesPact:DB$ RepeatEach | RepeatPlayers$ Player.Opponent | RepeatSubAbility$ MakeZombies | ChoiceName$ ZombiesPact | ChangeZoneTable$ True | SpellDescription$ Each opponent creates five 2/2 black Zombie creature tokens.
|
SVar:ZombiesPact:DB$ RepeatEach | RepeatPlayers$ Player.Opponent | RepeatSubAbility$ MakeZombies | ChangeZoneTable$ True | SpellDescription$ Each opponent creates five 2/2 black Zombie creature tokens.
|
||||||
SVar:MakeZombies:DB$ Token | LegacyImage$ b 2 2 zombie rna | TokenAmount$ 5 | TokenScript$ b_2_2_zombie | TokenOwner$ Remembered | SpellDescription$ Each opponent creates five 2/2 black Zombie creature tokens.
|
SVar:MakeZombies:DB$ Token | LegacyImage$ b 2 2 zombie rna | TokenAmount$ 5 | TokenScript$ b_2_2_zombie | TokenOwner$ Remembered | SpellDescription$ Each opponent creates five 2/2 black Zombie creature tokens.
|
||||||
# Clear RememberChoice just in case it's not getting cleared by Zone changes
|
|
||||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ ClearRemembered | Static$ True
|
|
||||||
SVar:ClearRemembered:DB$ Cleanup | ClearRemembered$ True | ClearChosenPlayer$ True
|
|
||||||
AI:RemoveDeck:Random
|
AI:RemoveDeck:Random
|
||||||
DeckHas:Ability$Token
|
DeckHas:Ability$Token
|
||||||
Oracle:Captive Audience enters the battlefield under the control of an opponent of your choice.\nAt the beginning of your upkeep, choose one that hasn't been chosen —\n• Your life total becomes 4.\n• Discard your hand.\n• Each opponent creates five 2/2 black Zombie creature tokens.
|
Oracle:Captive Audience enters the battlefield under the control of an opponent of your choice.\nAt the beginning of your upkeep, choose one that hasn't been chosen —\n• Your life total becomes 4.\n• Discard your hand.\n• Each opponent creates five 2/2 black Zombie creature tokens.
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
Name:Cathartic Reunion
|
Name:Cathartic Reunion
|
||||||
ManaCost:1 R
|
ManaCost:1 R
|
||||||
Types:Sorcery
|
Types:Sorcery
|
||||||
A:SP$ Draw | Cost$ 1 R Discard<2/Card> | CostDesc$ As an additional cost to cast CARDNAME, discard two cards. | NumCards$ 3 | Defined$ You | SpellDescription$ Draw three cards.
|
A:SP$ Draw | Cost$ 1 R Discard<2/Card/cards> | CostDesc$ As an additional cost to cast this spell, discard two cards. | NumCards$ 3 | Defined$ You | SpellDescription$ Draw three cards.
|
||||||
DeckHas:Ability$Discard
|
DeckHas:Ability$Discard
|
||||||
DeckHints:Keyword$Madness & Ability$Delirium
|
DeckHints:Keyword$Madness & Ability$Delirium
|
||||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/cathartic_reunion.jpg
|
|
||||||
Oracle:As an additional cost to cast this spell, discard two cards.\nDraw three cards.
|
Oracle:As an additional cost to cast this spell, discard two cards.\nDraw three cards.
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
Name:Cave-In
|
Name:Cave-In
|
||||||
ManaCost:3 R R
|
ManaCost:3 R R
|
||||||
Types:Sorcery
|
Types:Sorcery
|
||||||
|
SVar:AltCost:Cost$ ExileFromHand<1/Card.Red+Other> | Description$ You may exile a red card from your hand rather than pay this spell's mana cost.
|
||||||
A:SP$ DamageAll | Cost$ 3 R R | NumDmg$ 2 | ValidCards$ Creature | ValidPlayers$ Player | ValidDescription$ each creature and each player. | SpellDescription$ CARDNAME deals 2 damage to each creature and each player.
|
A:SP$ DamageAll | Cost$ 3 R R | NumDmg$ 2 | ValidCards$ Creature | ValidPlayers$ Player | ValidDescription$ each creature and each player. | SpellDescription$ CARDNAME deals 2 damage to each creature and each player.
|
||||||
SVar:AltCost:Cost$ ExileFromHand<1/Card.Red> | Description$ You may exile a red card from your hand rather than pay Cave-In's mana cost.
|
|
||||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/cave_in.jpg
|
|
||||||
Oracle:You may exile a red card from your hand rather than pay this spell's mana cost.\nCave-In deals 2 damage to each creature and each player.
|
Oracle:You may exile a red card from your hand rather than pay this spell's mana cost.\nCave-In deals 2 damage to each creature and each player.
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
Name:Coax from the Blind Eternities
|
Name:Coax from the Blind Eternities
|
||||||
ManaCost:2 U
|
ManaCost:2 U
|
||||||
Types:Sorcery
|
Types:Sorcery
|
||||||
A:SP$ ChangeZone | Cost$ 2 U | Origin$ Sideboard,Exile | Destination$ Hand | ChangeType$ Card.Eldrazi+YouOwn | ChangeNum$ 1 | SpellDescription$ You may choose an Eldrazi card you own from outside the game or in exile, reveal that card, and put it into your hand.
|
A:SP$ ChangeZone | Cost$ 2 U | Origin$ Sideboard,Exile | Destination$ Hand | ChangeType$ Card.Eldrazi+YouOwn | ChangeNum$ 1 | Hidden$ True | Reveal$ True | StackDescription$ {p:You} may reveal an Eldrazi card they own from outside the game or in exile and put it into their hand. | SpellDescription$ You may reveal an Eldrazi card you own from outside the game or in exile and put it into your hand.
|
||||||
AI:RemoveDeck:Random
|
AI:RemoveDeck:Random
|
||||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/coax_from_the_blind_eternities.jpg
|
Oracle:You may reveal an Eldrazi card you own from outside the game or in exile and put it into your hand.
|
||||||
Oracle:You may choose an Eldrazi card you own from outside the game or in exile, reveal that card, and put it into your hand.
|
|
||||||
|
|||||||
@@ -2,11 +2,10 @@ Name:Cobra Trap
|
|||||||
ManaCost:4 G G
|
ManaCost:4 G G
|
||||||
Types:Instant Trap
|
Types:Instant Trap
|
||||||
T:Mode$ Destroyed | ValidCauser$ Player.Opponent | ValidCard$ Permanent.nonCreature+YouCtrl | Execute$ TrackValidDestroy | Static$ True
|
T:Mode$ Destroyed | ValidCauser$ Player.Opponent | ValidCard$ Permanent.nonCreature+YouCtrl | Execute$ TrackValidDestroy | Static$ True
|
||||||
SVar:TrackValidDestroy:DB$ StoreSVar | SVar$ SetTrap | Type$ CountSVar | Expression$ SetTrap/Plus.1 | References$ SetTrap
|
SVar:TrackValidDestroy:DB$ Pump | RememberObjects$ TriggeredCard
|
||||||
T:Mode$ Phase | Phase$ Cleanup | Execute$ TrigReset | Static$ True
|
T:Mode$ TurnBegin | Execute$ TrigReset | Static$ True
|
||||||
SVar:TrigReset:DB$ StoreSVar | SVar$ SetTrap | Type$ Number | Expression$ 0 | References$ SetSVar
|
SVar:TrigReset:DB$ Cleanup | ClearRemembered$ True
|
||||||
SVar:SetTrap:Number$0
|
SVar:SetTrap:Remembered$Amount
|
||||||
SVar:AltCost:Cost$ G | CheckSVar$ SetTrap | SVarCompare$ GE1 | References$ SetTrap | Description$ If a noncreature permanent under your control was destroyed this turn by a spell or ability an opponent controlled, you may pay {G} rather than pay CARDNAME's mana cost.
|
SVar:AltCost:Cost$ G | CheckSVar$ SetTrap | References$ SetTrap | Description$ If a noncreature permanent under your control was destroyed this turn by a spell or ability an opponent controlled, you may pay {G} rather than pay this spell's mana cost.
|
||||||
A:SP$ Token | Cost$ 4 G G | TokenAmount$ 4 | TokenScript$ g_1_1_snake | TokenOwner$ You | LegacyImage$ g 1 1 snake zen | SpellDescription$ Create four 1/1 green Snake creature tokens.
|
A:SP$ Token | Cost$ 4 G G | TokenAmount$ 4 | TokenScript$ g_1_1_snake | TokenOwner$ You | StackDescription$ {p:You} creates four 1/1 green Snake creature tokens. | SpellDescription$ Create four 1/1 green Snake creature tokens.
|
||||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/cobra_trap.jpg
|
|
||||||
Oracle:If a noncreature permanent under your control was destroyed this turn by a spell or ability an opponent controlled, you may pay {G} rather than pay this spell's mana cost.\nCreate four 1/1 green Snake creature tokens.
|
Oracle:If a noncreature permanent under your control was destroyed this turn by a spell or ability an opponent controlled, you may pay {G} rather than pay this spell's mana cost.\nCreate four 1/1 green Snake creature tokens.
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user