mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-12 00:38:44 +00:00
Compare commits
169 Commits
paperCardI
...
Hanmac-pat
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a21656521f | ||
|
|
bdec4095c0 | ||
|
|
8475456c0b | ||
|
|
651caccd71 | ||
|
|
8e9fb8570e | ||
|
|
5a183a6042 | ||
|
|
67e6a7aa1a | ||
|
|
b76c67f309 | ||
|
|
99c6b6d815 | ||
|
|
cadc39699d | ||
|
|
8c06aab7b3 | ||
|
|
25c59cd5dd | ||
|
|
9825239e43 | ||
|
|
fb69f245da | ||
|
|
7ef8dddc2a | ||
|
|
2abcae84b1 | ||
|
|
b4beb6c182 | ||
|
|
786d14663b | ||
|
|
f8269e69c4 | ||
|
|
29274d0acf | ||
|
|
d6b925f171 | ||
|
|
f1f18d1823 | ||
|
|
03ff2147db | ||
|
|
c66a72806d | ||
|
|
e89f2cbac0 | ||
|
|
d831530c50 | ||
|
|
9fe18c2af8 | ||
|
|
fe4ff7ac0f | ||
|
|
a8ca3d8188 | ||
|
|
3b9867c537 | ||
|
|
01d22c26f4 | ||
|
|
e8f7fe5a95 | ||
|
|
bd0b8fbc65 | ||
|
|
05e20b4e92 | ||
|
|
716d58ad4b | ||
|
|
ca12ad529a | ||
|
|
6eea82706d | ||
|
|
d69d005ce0 | ||
|
|
b98b322dfe | ||
|
|
e73e72d150 | ||
|
|
8cabc244f7 | ||
|
|
4f04e5cc13 | ||
|
|
bcf9d2585d | ||
|
|
e03b07f940 | ||
|
|
a45c6aa37e | ||
|
|
dd672945f2 | ||
|
|
e634be4273 | ||
|
|
9acf5bee41 | ||
|
|
1e3297ab64 | ||
|
|
58df290c8e | ||
|
|
e2075886b1 | ||
|
|
ad53abc75a | ||
|
|
a599c318dd | ||
|
|
14e2a0c5e2 | ||
|
|
2ae3efc12e | ||
|
|
fb18134c12 | ||
|
|
2215a101a3 | ||
|
|
4df2fe7ac5 | ||
|
|
c24369d1ec | ||
|
|
41eb86029a | ||
|
|
357026ce66 | ||
|
|
9b558cb069 | ||
|
|
303020ca75 | ||
|
|
b900abcc71 | ||
|
|
c4ba6df6e9 | ||
|
|
cc4a507799 | ||
|
|
d8de8ec696 | ||
|
|
e6563814e8 | ||
|
|
016d51669f | ||
|
|
f19828b2ec | ||
|
|
36fc87c2a1 | ||
|
|
4322bddabf | ||
|
|
c128a9d4ba | ||
|
|
2feeffc95c | ||
|
|
53de238a7e | ||
|
|
c207e74369 | ||
|
|
04ca02a77a | ||
|
|
6ff89c71b6 | ||
|
|
d7c6a8e53e | ||
|
|
150741a443 | ||
|
|
460f322c44 | ||
|
|
ef5d35fe38 | ||
|
|
16044556b5 | ||
|
|
81d5079468 | ||
|
|
edc4b22cbc | ||
|
|
c01dfd4740 | ||
|
|
53a4a46d1f | ||
|
|
d61e7e7102 | ||
|
|
17f4df9293 | ||
|
|
24071582bb | ||
|
|
5754466a0d | ||
|
|
a18eda92b7 | ||
|
|
11d0a8f2fe | ||
|
|
2b8b386882 | ||
|
|
45a5027451 | ||
|
|
2cb72f55f3 | ||
|
|
fa27fbab46 | ||
|
|
87821fe287 | ||
|
|
cee4bcd867 | ||
|
|
b9cc4c18a8 | ||
|
|
4577e61940 | ||
|
|
9ac6147a98 | ||
|
|
74d4ea8a78 | ||
|
|
e2f4c7f872 | ||
|
|
07db8e5a94 | ||
|
|
ed7fb03b9c | ||
|
|
f0ebc3e6a0 | ||
|
|
f2570b4cef | ||
|
|
97e1939021 | ||
|
|
bebb894f3e | ||
|
|
288c533c91 | ||
|
|
6891f9a5d5 | ||
|
|
37a62eed6b | ||
|
|
3ee6b0e58d | ||
|
|
7e6f772345 | ||
|
|
ef28173b65 | ||
|
|
a26614c6b6 | ||
|
|
ee00293e48 | ||
|
|
ce977387e6 | ||
|
|
8cc45dbbd8 | ||
|
|
851a4c104c | ||
| 45ddf3fded | |||
|
|
cb538f4026 | ||
|
|
6bd6db3c16 | ||
|
|
6af3940ffb | ||
|
|
66c864c8d7 | ||
|
|
15354dd8e8 | ||
|
|
215070fe3f | ||
|
|
ed73883e80 | ||
|
|
df85ebc0aa | ||
|
|
885a6af943 | ||
|
|
94820d4782 | ||
|
|
7f044e5ac3 | ||
|
|
1284f23620 | ||
|
|
bdcc8acdc4 | ||
|
|
73a9dfcf43 | ||
|
|
7a277ce283 | ||
|
|
f8b3f9dd30 | ||
|
|
840f3ea96c | ||
|
|
51929434be | ||
|
|
3ec480afa4 | ||
|
|
3d19e9e444 | ||
|
|
6d987791f7 | ||
|
|
7b7da00c22 | ||
|
|
34791bd892 | ||
|
|
e6bcd1be72 | ||
|
|
4cac354983 | ||
|
|
042f7f77a9 | ||
|
|
5fbb1dd0cd | ||
|
|
bb14dfc00e | ||
|
|
f83cc4ccfa | ||
|
|
d0d6835a5d | ||
|
|
104bc8fc55 | ||
|
|
057dd867a8 | ||
|
|
9eed8f5095 | ||
|
|
1f62be9773 | ||
|
|
7a46f92059 | ||
|
|
cfa79b9676 | ||
|
|
c0a63fa15b | ||
|
|
c61537ae16 | ||
|
|
3871095b92 | ||
|
|
dee846da49 | ||
|
|
2477553d13 | ||
|
|
c9df7d7f8e | ||
|
|
53cb093f9e | ||
|
|
28e86970dc | ||
|
|
f847fc1669 | ||
|
|
f3df55177a | ||
|
|
be6f345127 |
40
.github/workflows/maven-publish.yml
vendored
40
.github/workflows/maven-publish.yml
vendored
@@ -81,7 +81,7 @@ jobs:
|
||||
export _JAVA_OPTIONS="-Xmx2g"
|
||||
d=$(date +%m.%d)
|
||||
# build only desktop and only try to move desktop files
|
||||
mvn -U -B clean -P windows-linux install -e -T 1C release:clean release:prepare release:perform -DskipTests
|
||||
mvn -U -B clean -P windows-linux install -DskipTests -Dskip.flatten=true -e -T 1C release:clean release:prepare release:perform
|
||||
mkdir izpack
|
||||
# move bz2 and jar from work dir to izpack dir
|
||||
mv /home/runner/work/forge/forge/forge-installer/*/*.{bz2,jar} izpack/
|
||||
@@ -128,6 +128,44 @@ jobs:
|
||||
removeArtifacts: true
|
||||
makeLatest: true
|
||||
|
||||
- name: 🔧 Install XML tools
|
||||
run: sudo apt-get install -y libxml2-utils
|
||||
|
||||
- name: 🔼 Bump versionCode in root POM
|
||||
id: bump_version
|
||||
run: |
|
||||
cd /home/runner/work/forge/forge/
|
||||
|
||||
current_version=$(xmllint --xpath "//*[local-name()='versionCode']/text()" pom.xml)
|
||||
echo "Current versionCode: $current_version"
|
||||
|
||||
IFS='.' read -r major minor patch <<< "${current_version}"
|
||||
new_patch=$(printf "%02d" $((10#$patch + 1)))
|
||||
new_version="${major}.${minor}.${new_patch}"
|
||||
|
||||
sed -i -E "s|<versionCode>.*</versionCode>|<versionCode>${new_version}</versionCode>|" pom.xml
|
||||
|
||||
echo "version_code=${new_version}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: ♻️ Restore {revision} in child POMs
|
||||
run: |
|
||||
find . -name pom.xml ! -path "./pom.xml" | while read -r pom; do
|
||||
sed -i -E 's|<version>2\.0+\.[0-9]+(-SNAPSHOT)?</version>|<version>${revision}</version>|' "$pom"
|
||||
done
|
||||
|
||||
- name: 💾 Commit restored {revision}
|
||||
run: |
|
||||
# Add only pom.xml files
|
||||
find . -name pom.xml -exec git add {} \;
|
||||
|
||||
# Commit if there are changes
|
||||
if git diff --cached --quiet; then
|
||||
echo "No pom.xml changes to commit."
|
||||
else
|
||||
git commit -m "Restore POM files for preparation of next release" || echo "No changes to commit"
|
||||
git push
|
||||
fi
|
||||
|
||||
- name: Send failure notification to Discord
|
||||
if: failure() # This step runs only if the job fails
|
||||
run: |
|
||||
|
||||
@@ -6,7 +6,7 @@ Dev instructions here: [Getting Started](https://github.com/Card-Forge/forge/wik
|
||||
|
||||
## Requirements / Tools
|
||||
|
||||
- you favourite Java IDE (IntelliJ, Eclipse, VSCodium, Emacs, Vi...)
|
||||
- your favourite Java IDE (IntelliJ, Eclipse, VSCodium, Emacs, Vi...)
|
||||
- Java JDK 17 or later
|
||||
- Git
|
||||
- Git client (optional)
|
||||
@@ -22,42 +22,41 @@ Dev instructions here: [Getting Started](https://github.com/Card-Forge/forge/wik
|
||||
|
||||
- Clone your forked project to your local machine
|
||||
|
||||
- Go to the project location on your machine. Run Maven to download all dependencies and build a snapshot. Example for Windows & Linux: `mvn -U -B clean -P windows-linux install`
|
||||
- Go to the project location on your machine. Run Maven to download all dependencies and build a snapshot. Example for Windows & Linux: `mvn -U -B clean -P windows-linux install`
|
||||
|
||||
## IntelliJ
|
||||
|
||||
IntelliJ is the recommended IDE for Forge development. Quick start guide for [setting up the Forge project within IntelliJ](https://github.com/Card-Forge/forge/wiki/IntelliJ-setup).
|
||||
|
||||
|
||||
## Eclipse
|
||||
|
||||
Eclipse includes Maven integration so a separate install is not necessary. For other IDEs, your mileage may vary.
|
||||
Eclipse includes Maven integration so a separate install is not necessary. For other IDEs, your mileage may vary.
|
||||
At this time, Eclipse is not the recommended IDE for Forge development.
|
||||
|
||||
### Project Setup
|
||||
|
||||
- Follow the instructions for cloning from GitHub. You'll need to setup an account and your SSH key.
|
||||
- Follow the instructions for cloning from GitHub. You'll need to setup an account and your SSH key.
|
||||
|
||||
If you are on a Windows machine you can use Putty with TortoiseGit for SSH keys. Run puttygen.exe to generate the key -- save the private key and export
|
||||
the OpenSSH public key. If you just leave the dialog open, you can copy and paste the key from it to your GitHub profile under
|
||||
"SSH keys". Run pageant.exe and add the private key generated earlier. TortoiseGit will use this for accessing GitHub.
|
||||
If you are on a Windows machine you can use Putty with TortoiseGit for SSH keys. Run puttygen.exe to generate the key -- save the private key and export
|
||||
the OpenSSH public key. If you just leave the dialog open, you can copy and paste the key from it to your GitHub profile under
|
||||
"SSH keys". Run pageant.exe and add the private key generated earlier. TortoiseGit will use this for accessing GitHub.
|
||||
|
||||
- Fork the Forge git repo to your GitHub account.
|
||||
|
||||
- Clone your forked repo to your local machine.
|
||||
|
||||
- Make sure the Java SDK is installed -- not just the JRE. Java 17 or newer required. If you execute `java -version` at the shell or command prompt, it should report version 17 or later.
|
||||
- Make sure the Java SDK is installed -- not just the JRE. Java 17 or newer required. If you execute `java -version` at the shell or command prompt, it should report version 17 or later.
|
||||
|
||||
- Install Eclipse 2021-12 or later for Java. Launch it.
|
||||
- Install Eclipse 2021-12 or later for Java. Launch it.
|
||||
|
||||
- Create a workspace. Go to the workbench. Right-click inside of Package Explorer > Import... > Maven > Existing Maven Projects > Navigate to root path of the local forge repo and
|
||||
- Create a workspace. Go to the workbench. Right-click inside of Package Explorer > Import... > Maven > Existing Maven Projects > Navigate to root path of the local forge repo and
|
||||
ensure everything is checked > Finish.
|
||||
|
||||
- Let Eclipse run through building the project. You may be prompted for resolving any missing Maven plugins -- accept the ones offered. You may see errors appear in the "Problems" tab. These should
|
||||
be automatically resolved as plug-ins are installed and Eclipse continues the build process. If this is the first time for some plug-in installs, Eclipse may prompt you to restart. Do so. Be patient
|
||||
- Let Eclipse run through building the project. You may be prompted for resolving any missing Maven plugins -- accept the ones offered. You may see errors appear in the "Problems" tab. These should
|
||||
be automatically resolved as plug-ins are installed and Eclipse continues the build process. If this is the first time for some plug-in installs, Eclipse may prompt you to restart. Do so. Be patient
|
||||
for this first time through.
|
||||
|
||||
- Once everything builds, all errors should disappear. You can now advance to Project launch.
|
||||
- Once everything builds, all errors should disappear. You can now advance to Project launch.
|
||||
|
||||
### Project Launch
|
||||
|
||||
@@ -67,15 +66,15 @@ This is the standard configuration used for releasing to Windows / Linux / MacOS
|
||||
|
||||
- Right-click on forge-gui-desktop > Run As... > Java Application > "Main - forge.view" > Ok
|
||||
|
||||
- The familiar Forge splash screen, etc. should appear. Enjoy!
|
||||
- The familiar Forge splash screen, etc. should appear. Enjoy!
|
||||
|
||||
#### Mobile (Desktop dev)
|
||||
|
||||
This is the configuration used for doing mobile development using the Windows / Linux / MacOS front-end. Knowledge of libgdx is helpful here.
|
||||
This is the configuration used for doing mobile development using the Windows / Linux / MacOS front-end. Knowledge of libgdx is helpful here.
|
||||
|
||||
- Right-click on forge-gui-mobile-dev > Run As... > Java Application > "Main - forge.app" > Ok.
|
||||
|
||||
- A view similar to a mobile phone should appear. Enjoy!
|
||||
- A view similar to a mobile phone should appear. Enjoy!
|
||||
|
||||
### Eclipse / Android SDK Integration
|
||||
|
||||
@@ -99,7 +98,7 @@ TBD
|
||||
|
||||
#### Android Platform
|
||||
|
||||
In Intellij, if the SDK Manager is not already running, go to Tools > Android > Android SDK Manager. Install the following options / versions:
|
||||
In Intellij, if the SDK Manager is not already running, go to Tools > Android > Android SDK Manager. Install the following options / versions:
|
||||
|
||||
- Android SDK Build-tools 35.0.0
|
||||
- Android 15 (API 35) SDK Platform
|
||||
@@ -124,10 +123,11 @@ TBD
|
||||
|
||||
SNAPSHOT builds can be built via the Maven integration in Eclipse.
|
||||
|
||||
1) Create a Maven build for the forge top-level project. Right-click on the forge project. Run as.. > Maven build...
|
||||
1. Create a Maven build for the forge top-level project. Right-click on the forge project. Run as.. > Maven build...
|
||||
|
||||
- On the Main tab, set Goals: clean install, set Profiles: windows-linux
|
||||
|
||||
2) Run forge Maven build. If everything built, you should see "BUILD SUCCESS" in the Console View.
|
||||
2. Run forge Maven build. If everything built, you should see "BUILD SUCCESS" in the Console View.
|
||||
|
||||
The resulting snapshot will be found at: forge-gui-desktop/target/forge-gui-desktop-[version]-SNAPSHOT
|
||||
|
||||
@@ -141,7 +141,7 @@ Card scripting resources are found in the forge-gui/res/ path.
|
||||
|
||||
### Project Hierarchy
|
||||
|
||||
Forge is divided into 4 primary projects with additional projects that target specific platform releases. The primary projects are:
|
||||
Forge is divided into 4 primary projects with additional projects that target specific platform releases. The primary projects are:
|
||||
|
||||
- forge-ai
|
||||
- forge-core
|
||||
@@ -158,32 +158,38 @@ The platform-specific projects are:
|
||||
|
||||
#### forge-ai
|
||||
|
||||
The forge-ai project contains the computer opponent logic for gameplay. It includes decision-making algorithms for specific abilities, cards and turn phases.
|
||||
|
||||
#### forge-core
|
||||
|
||||
The forge-core project contains the core game engine, card mechanics, rules engine, and fundamental game logic. It includes the implementation of Magic: The Gathering rules, card interactions, and the game state management system.
|
||||
|
||||
#### forge-game
|
||||
|
||||
The forge-game project handles the game session management, player interactions, and game flow control. It includes implementations for multiplayer support, game modes, matchmaking, and game state persistence. This module bridges the core game engine with the user interface and networking components.
|
||||
|
||||
#### forge-gui
|
||||
|
||||
The forge-gui project includes the scripting resource definitions in the res/ path.
|
||||
The forge-gui project contains the user interface components and rendering logic for the game. It includes the main game window, card displays, player interactions, and the scripting resource definitions in the res/ path.
|
||||
|
||||
#### forge-gui-android
|
||||
|
||||
Libgdx-based backend targeting Android. Requires Android SDK and relies on forge-gui-mobile for GUI logic.
|
||||
Libgdx-based backend targeting Android. Requires Android SDK and relies on forge-gui-mobile for GUI logic.
|
||||
|
||||
#### forge-gui-desktop
|
||||
|
||||
Java Swing based GUI targeting desktop machines.
|
||||
|
||||
Screen layout and game logic revolving around the GUI is found here. For example, the overlay arrows (when enabled) that indicate attackers and blockers, or the targets of the stack are defined and drawn by this.
|
||||
Screen layout and game logic revolving around the GUI is found here. For example, the overlay arrows (when enabled) that indicate attackers and blockers, or the targets of the stack are defined and drawn by this.
|
||||
|
||||
#### forge-gui-ios
|
||||
|
||||
Libgdx-based backend targeting iOS. Relies on forge-gui-mobile for GUI logic.
|
||||
Libgdx-based backend targeting iOS. Relies on forge-gui-mobile for GUI logic.
|
||||
|
||||
#### forge-gui-mobile
|
||||
|
||||
Mobile GUI game logic utilizing [libgdx](https://libgdx.badlogicgames.com/) library. Screen layout and game logic revolving around the GUI for the mobile platforms is found here.
|
||||
Mobile GUI game logic utilizing [libgdx](https://libgdx.badlogicgames.com/) library. Screen layout and game logic revolving around the GUI for the mobile platforms is found here.
|
||||
|
||||
#### forge-gui-mobile-dev
|
||||
|
||||
Libgdx backend for desktop development for mobile backends. Utilizes LWJGL. Relies on forge-gui-mobile for GUI logic.
|
||||
Libgdx backend for desktop development for mobile backends. Utilizes LWJGL. Relies on forge-gui-mobile for GUI logic.
|
||||
|
||||
@@ -32,6 +32,7 @@ Join the **Forge community** on [Discord](https://discord.gg/HcPJNyD66a)!
|
||||
4. **Java Requirement:** Ensure you have **Java 17 or later** installed.
|
||||
|
||||
### 📱 Android Installation
|
||||
- _(Note: **Android 11** is the minimum requirements with at least **6GB RAM** to run smoothly. You need to enable **"Install unknown apps"** for Forge to initialize and update itself)_
|
||||
- Download the **APK** from the [Snapshot Build](https://github.com/Card-Forge/forge/releases/tag/daily-snapshots). On the first launch, Forge will automatically download all necessary assets.
|
||||
|
||||
---
|
||||
|
||||
@@ -359,18 +359,28 @@ public class SpecialCardAi {
|
||||
private static final int demonSacThreshold = Integer.MAX_VALUE; // if we're in dire conditions, sac everything from worst to best hoping to find an answer
|
||||
|
||||
public static boolean considerSacrificingCreature(final Player ai, final SpellAbility sa) {
|
||||
Card c = sa.getHostCard();
|
||||
|
||||
// Only check for sacrifice if it's the owner's turn, and it can attack.
|
||||
// TODO: Maybe check if sacrificing a creature allows AI to kill the opponent with the rest on their turn?
|
||||
if (!CombatUtil.canAttack(c) ||
|
||||
!ai.getGame().getPhaseHandler().isPlayerTurn(sa.getActivatingPlayer())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CardCollection flyingCreatures = CardLists.filter(ai.getCardsIn(ZoneType.Battlefield),
|
||||
CardPredicates.UNTAPPED.and(
|
||||
CardPredicates.hasKeyword(Keyword.FLYING).or(CardPredicates.hasKeyword(Keyword.REACH))));
|
||||
CardPredicates.UNTAPPED.and(
|
||||
CardPredicates.hasKeyword(Keyword.FLYING).or(CardPredicates.hasKeyword(Keyword.REACH))));
|
||||
boolean hasUsefulBlocker = false;
|
||||
|
||||
for (Card c : flyingCreatures) {
|
||||
if (!ComputerUtilCard.isUselessCreature(ai, c)) {
|
||||
for (Card fc : flyingCreatures) {
|
||||
if (!ComputerUtilCard.isUselessCreature(ai, fc)) {
|
||||
hasUsefulBlocker = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ai.getLife() <= sa.getHostCard().getNetPower() && !hasUsefulBlocker;
|
||||
return ai.getLife() <= c.getNetPower() && !hasUsefulBlocker;
|
||||
}
|
||||
|
||||
public static int getSacThreshold() {
|
||||
|
||||
@@ -33,6 +33,11 @@ public final class ImageKeys {
|
||||
public static final String RADIATION_IMAGE = "radiation";
|
||||
|
||||
public static final String BACKFACE_POSTFIX = "$alt";
|
||||
public static final String SPECFACE_W = "$wspec";
|
||||
public static final String SPECFACE_U = "$uspec";
|
||||
public static final String SPECFACE_B = "$bspec";
|
||||
public static final String SPECFACE_R = "$rspec";
|
||||
public static final String SPECFACE_G = "$gspec";
|
||||
|
||||
private static String CACHE_CARD_PICS_DIR, CACHE_TOKEN_PICS_DIR, CACHE_ICON_PICS_DIR, CACHE_BOOSTER_PICS_DIR,
|
||||
CACHE_FATPACK_PICS_DIR, CACHE_BOOSTERBOX_PICS_DIR, CACHE_PRECON_PICS_DIR, CACHE_TOURNAMENTPACK_PICS_DIR;
|
||||
@@ -92,28 +97,13 @@ public final class ImageKeys {
|
||||
return cachedCards.get(key);
|
||||
}
|
||||
public static File getImageFile(String key) {
|
||||
return getImageFile(key, false);
|
||||
}
|
||||
public static File getImageFile(String key, boolean artCrop) {
|
||||
if (StringUtils.isEmpty(key))
|
||||
return null;
|
||||
|
||||
final String dir;
|
||||
final String filename;
|
||||
String[] tempdata = null;
|
||||
if (key.startsWith(ImageKeys.CARD_PREFIX)) {
|
||||
tempdata = key.substring(ImageKeys.CARD_PREFIX.length()).split("\\|");
|
||||
String tokenname = tempdata[0];
|
||||
if (tempdata.length > 1) {
|
||||
tokenname += "_" + tempdata[1];
|
||||
}
|
||||
if (tempdata.length > 2) {
|
||||
tokenname += "_" + tempdata[2];
|
||||
}
|
||||
filename = tokenname ;
|
||||
|
||||
dir = CACHE_CARD_PICS_DIR;
|
||||
} else if (key.startsWith(ImageKeys.TOKEN_PREFIX)) {
|
||||
if (key.startsWith(ImageKeys.TOKEN_PREFIX)) {
|
||||
tempdata = key.substring(ImageKeys.TOKEN_PREFIX.length()).split("\\|");
|
||||
String tokenname = tempdata[0];
|
||||
if (tempdata.length > 1) {
|
||||
@@ -164,31 +154,7 @@ public final class ImageKeys {
|
||||
cachedCards.put(filename, file);
|
||||
return file;
|
||||
}
|
||||
if (tempdata != null && dir.equals(CACHE_CARD_PICS_DIR)) {
|
||||
String setlessFilename = tempdata[0] + (artCrop ? ".artcrop" : ".fullborder");
|
||||
String setCode = tempdata.length > 1 ? tempdata[1] : "";
|
||||
String collectorNumber = tempdata.length > 2 ? tempdata[2] : "";
|
||||
if (!setCode.isEmpty()) {
|
||||
if (!collectorNumber.isEmpty()) {
|
||||
file = findFile(dir, setCode + "/" + collectorNumber + "_" + setlessFilename);
|
||||
if (file != null) {
|
||||
cachedCards.put(filename, file);
|
||||
return file;
|
||||
}
|
||||
}
|
||||
file = findFile(dir, setCode + "/" + setlessFilename);
|
||||
if (file != null) {
|
||||
cachedCards.put(filename, file);
|
||||
return file;
|
||||
}
|
||||
}
|
||||
file = findFile(dir, setlessFilename);
|
||||
if (file != null) {
|
||||
cachedCards.put(filename, file);
|
||||
return file;
|
||||
}
|
||||
}
|
||||
if (tempdata != null && dir.equals(CACHE_TOKEN_PICS_DIR)) {
|
||||
if (dir.equals(CACHE_TOKEN_PICS_DIR)) {
|
||||
String setlessFilename = tempdata[0];
|
||||
String setCode = tempdata.length > 1 ? tempdata[1] : "";
|
||||
String collectorNumber = tempdata.length > 2 ? tempdata[2] : "";
|
||||
|
||||
@@ -433,17 +433,6 @@ public final class CardEdition implements Comparable<CardEdition> {
|
||||
|
||||
public Multimap<String, EditionEntry> getTokens() { return tokenMap; }
|
||||
|
||||
public EditionEntry getTokenFromCollectorNumber(String collectorNumber) {
|
||||
if(collectorNumber == null || collectorNumber.isEmpty())
|
||||
return null;
|
||||
for(EditionEntry c : this.tokenMap.values()) {
|
||||
//Could build a map for this one too if it's used for more than one-offs.
|
||||
if (c.collectorNumber.equalsIgnoreCase(collectorNumber))
|
||||
return c;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getTokenSet(String token) {
|
||||
if (tokenMap.containsKey(token)) {
|
||||
return this.getCode();
|
||||
|
||||
@@ -20,8 +20,6 @@ package forge.card;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import forge.StaticData;
|
||||
import forge.card.mana.IParserManaCost;
|
||||
import forge.card.mana.ManaCost;
|
||||
import forge.card.mana.ManaCostShard;
|
||||
@@ -151,10 +149,6 @@ public final class CardRules implements ICardCharacteristics {
|
||||
return splitType;
|
||||
}
|
||||
|
||||
public boolean hasBackSide() {
|
||||
return CardSplitType.DUAL_FACED_CARDS.contains(splitType) || splitType == CardSplitType.Flip;
|
||||
}
|
||||
|
||||
public ICardFace getMainPart() {
|
||||
return mainPart;
|
||||
}
|
||||
@@ -171,32 +165,20 @@ public final class CardRules implements ICardCharacteristics {
|
||||
return Iterables.concat(Arrays.asList(mainPart, otherPart), specializedParts.values());
|
||||
}
|
||||
|
||||
public String getImageName(CardStateName state) {
|
||||
if (splitType == CardSplitType.Split) {
|
||||
return mainPart.getName() + otherPart.getName();
|
||||
} else if (state.equals(splitType.getChangedStateName())) {
|
||||
if (otherPart != null) {
|
||||
return otherPart.getName();
|
||||
} else if (this.hasBackSide()) {
|
||||
if (!getMeldWith().isEmpty()) {
|
||||
final CardDb db = StaticData.instance().getCommonCards();
|
||||
return db.getRules(getMeldWith()).getOtherPart().getName();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
switch (state) {
|
||||
case SpecializeW:
|
||||
case SpecializeU:
|
||||
case SpecializeB:
|
||||
case SpecializeR:
|
||||
case SpecializeG:
|
||||
ICardFace face = specializedParts.get(state);
|
||||
return face != null ? face.getName() : null;
|
||||
default:
|
||||
return getName();
|
||||
}
|
||||
public ICardFace getWSpecialize() {
|
||||
return specializedParts.get(CardStateName.SpecializeW);
|
||||
}
|
||||
public ICardFace getUSpecialize() {
|
||||
return specializedParts.get(CardStateName.SpecializeU);
|
||||
}
|
||||
public ICardFace getBSpecialize() {
|
||||
return specializedParts.get(CardStateName.SpecializeB);
|
||||
}
|
||||
public ICardFace getRSpecialize() {
|
||||
return specializedParts.get(CardStateName.SpecializeR);
|
||||
}
|
||||
public ICardFace getGSpecialize() {
|
||||
return specializedParts.get(CardStateName.SpecializeG);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
@@ -303,7 +285,10 @@ public final class CardRules implements ICardCharacteristics {
|
||||
return true;
|
||||
}
|
||||
CardType type = mainPart.getType();
|
||||
if (type.isLegendary() && canBeCreature()) {
|
||||
if (!type.isLegendary()) {
|
||||
return false;
|
||||
}
|
||||
if (canBeCreature() || type.isVehicle() || type.isSpacecraft()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -516,7 +516,7 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
|
||||
@Override
|
||||
public boolean isAttachment() { return isAura() || isEquipment() || isFortification(); }
|
||||
@Override
|
||||
public boolean isAura() { return hasSubtype("Aura"); }
|
||||
public boolean isAura() { return hasSubtype("Aura"); }
|
||||
@Override
|
||||
public boolean isEquipment() { return hasSubtype("Equipment"); }
|
||||
@Override
|
||||
@@ -529,6 +529,9 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
|
||||
return hasSubtype("Contraption");
|
||||
}
|
||||
|
||||
public boolean isVehicle() { return hasSubtype("Vehicle"); }
|
||||
public boolean isSpacecraft() { return hasSubtype("Spacecraft"); }
|
||||
|
||||
@Override
|
||||
public boolean isSaga() {
|
||||
return hasSubtype("Saga");
|
||||
|
||||
@@ -24,6 +24,7 @@ import forge.util.CardTranslation;
|
||||
import forge.util.ImageUtil;
|
||||
import forge.util.Localizer;
|
||||
import forge.util.TextUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
@@ -346,13 +347,34 @@ public class PaperCard implements Comparable<IPaperCard>, InventoryItemFromSet,
|
||||
if (pc == null) {
|
||||
pc = StaticData.instance().getVariantCards().getCard(name, edition, artIndex);
|
||||
if (pc == null) {
|
||||
throw new IOException(TextUtil.concatWithSpace("Card", name, "not found"));
|
||||
System.out.println("PaperCard: " + name + " not found with set and index " + edition + ", " + artIndex);
|
||||
pc = readObjectAlternate(name, edition);
|
||||
if (pc == null) {
|
||||
throw new IOException(TextUtil.concatWithSpace("Card", name, "not found with set and index", edition, Integer.toString(artIndex)));
|
||||
}
|
||||
System.out.println("Alternate object found: " + pc.getName() + ", " + pc.getEdition() + ", " + pc.getArtIndex());
|
||||
}
|
||||
}
|
||||
rules = pc.getRules();
|
||||
rarity = pc.getRarity();
|
||||
}
|
||||
|
||||
private IPaperCard readObjectAlternate(String name, String edition) throws ClassNotFoundException, IOException {
|
||||
IPaperCard pc = StaticData.instance().getCommonCards().getCard(name, edition);
|
||||
if (pc == null) {
|
||||
pc = StaticData.instance().getVariantCards().getCard(name, edition);
|
||||
}
|
||||
|
||||
if (pc == null) {
|
||||
pc = StaticData.instance().getCommonCards().getCard(name);
|
||||
if (pc == null) {
|
||||
pc = StaticData.instance().getVariantCards().getCard(name);
|
||||
}
|
||||
}
|
||||
|
||||
return pc;
|
||||
}
|
||||
|
||||
@Serial
|
||||
private Object readResolve() throws ObjectStreamException {
|
||||
//If we deserialize an old PaperCard with no flags, reinitialize as a fresh copy to set default flags.
|
||||
@@ -363,14 +385,20 @@ public class PaperCard implements Comparable<IPaperCard>, InventoryItemFromSet,
|
||||
|
||||
@Override
|
||||
public String getImageKey(boolean altState) {
|
||||
return altState ? this.getCardAltImageKey() : this.getCardImageKey();
|
||||
String normalizedName = StringUtils.stripAccents(name);
|
||||
String imageKey = ImageKeys.CARD_PREFIX + normalizedName + CardDb.NameSetSeparator
|
||||
+ edition + CardDb.NameSetSeparator + artIndex;
|
||||
if (altState) {
|
||||
imageKey += ImageKeys.BACKFACE_POSTFIX;
|
||||
}
|
||||
return imageKey;
|
||||
}
|
||||
|
||||
private String cardImageKey = null;
|
||||
@Override
|
||||
public String getCardImageKey() {
|
||||
if (this.cardImageKey == null)
|
||||
this.cardImageKey = ImageUtil.getImageKey(this, CardStateName.Original);
|
||||
this.cardImageKey = ImageUtil.getImageKey(this, "", true);
|
||||
return cardImageKey;
|
||||
}
|
||||
|
||||
@@ -379,9 +407,9 @@ public class PaperCard implements Comparable<IPaperCard>, InventoryItemFromSet,
|
||||
public String getCardAltImageKey() {
|
||||
if (this.cardAltImageKey == null){
|
||||
if (this.hasBackFace())
|
||||
this.cardAltImageKey = ImageUtil.getImageKey(this, this.getRules().getSplitType().getChangedStateName());
|
||||
this.cardAltImageKey = ImageUtil.getImageKey(this, "back", true);
|
||||
else // altImageKey will be the same as cardImageKey
|
||||
this.cardAltImageKey = getCardImageKey();
|
||||
this.cardAltImageKey = ImageUtil.getImageKey(this, "", true);
|
||||
}
|
||||
return cardAltImageKey;
|
||||
}
|
||||
@@ -391,9 +419,9 @@ public class PaperCard implements Comparable<IPaperCard>, InventoryItemFromSet,
|
||||
public String getCardWSpecImageKey() {
|
||||
if (this.cardWSpecImageKey == null) {
|
||||
if (this.rules.getSplitType() == CardSplitType.Specialize)
|
||||
this.cardWSpecImageKey = ImageUtil.getImageKey(this, CardStateName.SpecializeW);
|
||||
this.cardWSpecImageKey = ImageUtil.getImageKey(this, "white", true);
|
||||
else // just use cardImageKey
|
||||
this.cardWSpecImageKey = getCardImageKey();
|
||||
this.cardWSpecImageKey = ImageUtil.getImageKey(this, "", true);
|
||||
}
|
||||
return cardWSpecImageKey;
|
||||
}
|
||||
@@ -403,9 +431,9 @@ public class PaperCard implements Comparable<IPaperCard>, InventoryItemFromSet,
|
||||
public String getCardUSpecImageKey() {
|
||||
if (this.cardUSpecImageKey == null) {
|
||||
if (this.rules.getSplitType() == CardSplitType.Specialize)
|
||||
this.cardUSpecImageKey = ImageUtil.getImageKey(this, CardStateName.SpecializeU);
|
||||
this.cardUSpecImageKey = ImageUtil.getImageKey(this, "blue", true);
|
||||
else // just use cardImageKey
|
||||
this.cardUSpecImageKey = getCardImageKey();
|
||||
this.cardUSpecImageKey = ImageUtil.getImageKey(this, "", true);
|
||||
}
|
||||
return cardUSpecImageKey;
|
||||
}
|
||||
@@ -415,9 +443,9 @@ public class PaperCard implements Comparable<IPaperCard>, InventoryItemFromSet,
|
||||
public String getCardBSpecImageKey() {
|
||||
if (this.cardBSpecImageKey == null) {
|
||||
if (this.rules.getSplitType() == CardSplitType.Specialize)
|
||||
this.cardBSpecImageKey = ImageUtil.getImageKey(this, CardStateName.SpecializeB);
|
||||
this.cardBSpecImageKey = ImageUtil.getImageKey(this, "black", true);
|
||||
else // just use cardImageKey
|
||||
this.cardBSpecImageKey = getCardImageKey();
|
||||
this.cardBSpecImageKey = ImageUtil.getImageKey(this, "", true);
|
||||
}
|
||||
return cardBSpecImageKey;
|
||||
}
|
||||
@@ -427,9 +455,9 @@ public class PaperCard implements Comparable<IPaperCard>, InventoryItemFromSet,
|
||||
public String getCardRSpecImageKey() {
|
||||
if (this.cardRSpecImageKey == null) {
|
||||
if (this.rules.getSplitType() == CardSplitType.Specialize)
|
||||
this.cardRSpecImageKey = ImageUtil.getImageKey(this, CardStateName.SpecializeR);
|
||||
this.cardRSpecImageKey = ImageUtil.getImageKey(this, "red", true);
|
||||
else // just use cardImageKey
|
||||
this.cardRSpecImageKey = getCardImageKey();
|
||||
this.cardRSpecImageKey = ImageUtil.getImageKey(this, "", true);
|
||||
}
|
||||
return cardRSpecImageKey;
|
||||
}
|
||||
@@ -439,16 +467,18 @@ public class PaperCard implements Comparable<IPaperCard>, InventoryItemFromSet,
|
||||
public String getCardGSpecImageKey() {
|
||||
if (this.cardGSpecImageKey == null) {
|
||||
if (this.rules.getSplitType() == CardSplitType.Specialize)
|
||||
this.cardGSpecImageKey = ImageUtil.getImageKey(this, CardStateName.SpecializeG);
|
||||
this.cardGSpecImageKey = ImageUtil.getImageKey(this, "green", true);
|
||||
else // just use cardImageKey
|
||||
this.cardGSpecImageKey = getCardImageKey();
|
||||
this.cardGSpecImageKey = ImageUtil.getImageKey(this, "", true);
|
||||
}
|
||||
return cardGSpecImageKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasBackFace(){
|
||||
return this.rules.hasBackSide();
|
||||
CardSplitType cst = this.rules.getSplitType();
|
||||
return cst == CardSplitType.Transform || cst == CardSplitType.Flip || cst == CardSplitType.Meld
|
||||
|| cst == CardSplitType.Modal;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -401,46 +401,68 @@ public class BoosterGenerator {
|
||||
System.out.println(numCards + " of type " + slotType);
|
||||
|
||||
// For cards that end in '+', attempt to convert this card to foil.
|
||||
boolean convertCardFoil = slotType.endsWith("+");
|
||||
if (convertCardFoil) {
|
||||
boolean convertAllToFoil = slotType.endsWith("+");
|
||||
if (convertAllToFoil) {
|
||||
slotType = slotType.substring(0, slotType.length() - 1);
|
||||
}
|
||||
|
||||
// Unpack Base
|
||||
BoosterSlot boosterSlot = boosterSlots.get(slotType);
|
||||
String determineSheet = boosterSlot.replaceSlot();
|
||||
Map<String, Integer> slotReplacementCount = bulkSlotReplacement(boosterSlot, numCards);
|
||||
|
||||
if (determineSheet.endsWith("+")) {
|
||||
determineSheet = determineSheet.substring(0, determineSheet.length() - 1);
|
||||
convertCardFoil = true;
|
||||
}
|
||||
List<PaperCard> paperCards = Lists.newArrayList();
|
||||
for(Map.Entry<String, Integer> entry : slotReplacementCount.entrySet()) {
|
||||
String determineSheet = entry.getKey();
|
||||
int numCardsToGenerate = entry.getValue();
|
||||
|
||||
String setCode = template.getEdition();
|
||||
|
||||
// Ok, so we have a sheet now. Most should be standard sheets, but some named edition sheets
|
||||
List<PaperCard> paperCards;
|
||||
PrintSheet ps;
|
||||
try {
|
||||
// Apply the edition to the sheet name by default. We'll try again if thats not a real sheet
|
||||
ps = getPrintSheet(determineSheet + " " + setCode);
|
||||
} catch(Exception e) {
|
||||
ps = getPrintSheet(determineSheet);
|
||||
}
|
||||
if (convertCardFoil) {
|
||||
paperCards = Lists.newArrayList();
|
||||
for(PaperCard pc : ps.random(numCards, true)) {
|
||||
paperCards.add(pc.getFoiled());
|
||||
if (determineSheet == null || determineSheet.isEmpty() || numCardsToGenerate == 0) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
paperCards = ps.random(numCards, true);
|
||||
}
|
||||
|
||||
result.addAll(paperCards);
|
||||
// If the sheet ends with a '+', convert all cards in replacement section to foil
|
||||
boolean convertThisToFoil = false;
|
||||
if (determineSheet.endsWith("+")) {
|
||||
determineSheet = determineSheet.substring(0, determineSheet.length() - 1);
|
||||
convertThisToFoil = true;
|
||||
}
|
||||
|
||||
String setCode = template.getEdition();
|
||||
PrintSheet ps;
|
||||
try {
|
||||
// Apply the edition to the sheet name by default. We'll try again if thats not a real sheet
|
||||
ps = getPrintSheet(determineSheet + " " + setCode);
|
||||
} catch (Exception e) {
|
||||
ps = getPrintSheet(determineSheet);
|
||||
}
|
||||
if (convertAllToFoil || convertThisToFoil) {
|
||||
for (PaperCard pc : ps.random(numCardsToGenerate, true)) {
|
||||
paperCards.add(pc.getFoiled());
|
||||
}
|
||||
} else {
|
||||
paperCards.addAll(ps.random(numCardsToGenerate, true));
|
||||
}
|
||||
|
||||
result.addAll(paperCards);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Map<String, Integer> bulkSlotReplacement(BoosterSlot boosterSlot, int numCards) {
|
||||
Map<String, Integer> slotReplacementCount = new HashMap<>();
|
||||
|
||||
for(int i = 0; i < numCards; i++) {
|
||||
String determineSheet = boosterSlot.replaceSlot();
|
||||
if (slotReplacementCount.containsKey(determineSheet)) {
|
||||
slotReplacementCount.put(determineSheet, slotReplacementCount.get(determineSheet) + 1);
|
||||
} else {
|
||||
slotReplacementCount.put(determineSheet, 1);
|
||||
}
|
||||
}
|
||||
|
||||
return slotReplacementCount;
|
||||
}
|
||||
|
||||
private static void ensureGuaranteedCardInBooster(List<PaperCard> result, SealedTemplate template, String boosterMustContain) {
|
||||
// First, see if there's already a card of the given type
|
||||
String[] types = TextUtil.split(boosterMustContain, ' ');
|
||||
|
||||
@@ -5,11 +5,12 @@ import forge.StaticData;
|
||||
import forge.card.CardDb;
|
||||
import forge.card.CardRules;
|
||||
import forge.card.CardSplitType;
|
||||
import forge.card.CardStateName;
|
||||
import forge.item.IPaperCard;
|
||||
import forge.item.PaperCard;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
|
||||
public class ImageUtil {
|
||||
public static float getNearestHQSize(float baseSize, float actualSize) {
|
||||
//get nearest power of actualSize to baseSize so that the image renders good
|
||||
@@ -25,17 +26,20 @@ public class ImageUtil {
|
||||
key = imageKey.substring(ImageKeys.CARD_PREFIX.length());
|
||||
else
|
||||
return null;
|
||||
|
||||
if (key.endsWith(ImageKeys.BACKFACE_POSTFIX)) {
|
||||
key = key.substring(0, key.length() - ImageKeys.BACKFACE_POSTFIX.length());
|
||||
}
|
||||
|
||||
if (key.isEmpty())
|
||||
return null;
|
||||
|
||||
String[] tempdata = key.split("\\|");
|
||||
PaperCard cp = StaticData.instance().fetchCard(tempdata[0], tempdata[1], tempdata[2]);
|
||||
|
||||
CardDb db = StaticData.instance().getCommonCards();
|
||||
PaperCard cp = null;
|
||||
//db shouldn't be null
|
||||
if (db != null) {
|
||||
cp = db.getCard(key);
|
||||
if (cp == null) {
|
||||
db = StaticData.instance().getVariantCards();
|
||||
if (db != null)
|
||||
cp = db.getCard(key);
|
||||
}
|
||||
}
|
||||
if (cp == null)
|
||||
System.err.println("Can't find PaperCard from key: " + key);
|
||||
// return cp regardless if it's null
|
||||
@@ -52,21 +56,6 @@ public class ImageUtil {
|
||||
return key;
|
||||
}
|
||||
|
||||
public static String getImageRelativePath(String name, String set, String collectorNumber, boolean artChop) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append(set).append("/");
|
||||
if (!collectorNumber.isEmpty() && !collectorNumber.equals(IPaperCard.NO_COLLECTOR_NUMBER)) {
|
||||
sb.append(collectorNumber).append("_");
|
||||
}
|
||||
sb.append(StringUtils.stripAccents(name));
|
||||
|
||||
sb.append(artChop ? ".artcrop" : ".fullborder");
|
||||
sb.append(".jpg");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
public static String getImageRelativePath(PaperCard cp, String face, boolean includeSet, boolean isDownloadUrl) {
|
||||
final String nameToUse = cp == null ? null : getNameToUse(cp, face);
|
||||
if (nameToUse == null) {
|
||||
@@ -113,7 +102,7 @@ public class ImageUtil {
|
||||
|
||||
if (includeSet) {
|
||||
String editionAliased = isDownloadUrl ? StaticData.instance().getEditions().getCode2ByCode(edition) : ImageKeys.getSetFolder(edition);
|
||||
if (editionAliased == "") //FIXME: Custom Cards Workaround
|
||||
if (editionAliased.isEmpty()) //FIXME: Custom Cards Workaround
|
||||
editionAliased = edition;
|
||||
return TextUtil.concatNoSpace(editionAliased, "/", fname);
|
||||
} else {
|
||||
@@ -136,15 +125,25 @@ public class ImageUtil {
|
||||
else
|
||||
return null;
|
||||
} else if (face.equals("white")) {
|
||||
return card.getImageName(CardStateName.SpecializeW);
|
||||
if (card.getWSpecialize() != null) {
|
||||
return card.getWSpecialize().getName();
|
||||
}
|
||||
} else if (face.equals("blue")) {
|
||||
return card.getImageName(CardStateName.SpecializeU);
|
||||
if (card.getUSpecialize() != null) {
|
||||
return card.getUSpecialize().getName();
|
||||
}
|
||||
} else if (face.equals("black")) {
|
||||
return card.getImageName(CardStateName.SpecializeB);
|
||||
if (card.getBSpecialize() != null) {
|
||||
return card.getBSpecialize().getName();
|
||||
}
|
||||
} else if (face.equals("red")) {
|
||||
return card.getImageName(CardStateName.SpecializeR);
|
||||
if (card.getRSpecialize() != null) {
|
||||
return card.getRSpecialize().getName();
|
||||
}
|
||||
} else if (face.equals("green")) {
|
||||
return card.getImageName(CardStateName.SpecializeG);
|
||||
if (card.getGSpecialize() != null) {
|
||||
return card.getGSpecialize().getName();
|
||||
}
|
||||
} else if (CardSplitType.Split == cp.getRules().getSplitType()) {
|
||||
return card.getMainPart().getName() + card.getOtherPart().getName();
|
||||
} else if (!IPaperCard.NO_FUNCTIONAL_VARIANT.equals(cp.getFunctionalVariant())) {
|
||||
@@ -153,86 +152,66 @@ public class ImageUtil {
|
||||
return cp.getName();
|
||||
}
|
||||
|
||||
public static String getNameToUse(PaperCard cp, CardStateName face) {
|
||||
if (!IPaperCard.NO_FUNCTIONAL_VARIANT.equals(cp.getFunctionalVariant())) {
|
||||
return cp.getFunctionalVariant();
|
||||
}
|
||||
final CardRules card = cp.getRules();
|
||||
return card.getImageName(face);
|
||||
}
|
||||
|
||||
public static String getImageKey(PaperCard cp, String face, boolean includeSet) {
|
||||
return getImageRelativePath(cp, face, includeSet, false);
|
||||
}
|
||||
|
||||
public static String getImageKey(PaperCard cp, CardStateName face) {
|
||||
String name = getNameToUse(cp, face);
|
||||
String number = cp.getCollectorNumber();
|
||||
String suffix = "";
|
||||
switch (face) {
|
||||
case SpecializeB:
|
||||
number += "b";
|
||||
break;
|
||||
case SpecializeG:
|
||||
number += "g";
|
||||
break;
|
||||
case SpecializeR:
|
||||
number += "r";
|
||||
break;
|
||||
case SpecializeU:
|
||||
number += "u";
|
||||
break;
|
||||
case SpecializeW:
|
||||
number += "w";
|
||||
break;
|
||||
case Meld:
|
||||
case Modal:
|
||||
case Secondary:
|
||||
case Transformed:
|
||||
suffix = ImageKeys.BACKFACE_POSTFIX;
|
||||
break;
|
||||
case Flipped:
|
||||
break; // add info to rotate the image?
|
||||
default:
|
||||
break;
|
||||
};
|
||||
return ImageKeys.CARD_PREFIX + name + CardDb.NameSetSeparator + cp.getEdition()
|
||||
+ CardDb.NameSetSeparator + number + CardDb.NameSetSeparator + cp.getArtIndex() + suffix;
|
||||
}
|
||||
|
||||
public static String getDownloadUrl(PaperCard cp, String face) {
|
||||
return getImageRelativePath(cp, face, true, true);
|
||||
}
|
||||
|
||||
public static String getScryfallDownloadUrl(String collectorNumber, String setCode, String langCode, String faceParam, boolean useArtCrop){
|
||||
return getScryfallDownloadUrl(collectorNumber, setCode, langCode, faceParam, useArtCrop, false);
|
||||
public static String getScryfallDownloadUrl(PaperCard cp, String face, String setCode, String langCode, boolean useArtCrop){
|
||||
return getScryfallDownloadUrl(cp, face, setCode, langCode, useArtCrop, false);
|
||||
}
|
||||
|
||||
public static String getScryfallDownloadUrl(String collectorNumber, String setCode, String langCode, String faceParam, boolean useArtCrop, boolean hyphenateAlchemy){
|
||||
public static String getScryfallDownloadUrl(PaperCard cp, String face, String setCode, String langCode, boolean useArtCrop, boolean hyphenateAlchemy){
|
||||
String editionCode;
|
||||
if (setCode != null && !setCode.isEmpty())
|
||||
editionCode = setCode;
|
||||
else
|
||||
editionCode = cp.getEdition().toLowerCase();
|
||||
String cardCollectorNumber = cp.getCollectorNumber();
|
||||
// Hack to account for variations in Arabian Nights
|
||||
collectorNumber = collectorNumber.replace("+", "†");
|
||||
cardCollectorNumber = cardCollectorNumber.replace("+", "†");
|
||||
// override old planechase sets from their modified id since scryfall move the planechase cards outside their original setcode
|
||||
if (collectorNumber.startsWith("OHOP")) {
|
||||
setCode = "ohop";
|
||||
collectorNumber = collectorNumber.substring("OHOP".length());
|
||||
} else if (collectorNumber.startsWith("OPCA")) {
|
||||
setCode = "opca";
|
||||
collectorNumber = collectorNumber.substring("OPCA".length());
|
||||
} else if (collectorNumber.startsWith("OPC2")) {
|
||||
setCode = "opc2";
|
||||
collectorNumber = collectorNumber.substring("OPC2".length());
|
||||
if (cardCollectorNumber.startsWith("OHOP")) {
|
||||
editionCode = "ohop";
|
||||
cardCollectorNumber = cardCollectorNumber.substring("OHOP".length());
|
||||
} else if (cardCollectorNumber.startsWith("OPCA")) {
|
||||
editionCode = "opca";
|
||||
cardCollectorNumber = cardCollectorNumber.substring("OPCA".length());
|
||||
} else if (cardCollectorNumber.startsWith("OPC2")) {
|
||||
editionCode = "opc2";
|
||||
cardCollectorNumber = cardCollectorNumber.substring("OPC2".length());
|
||||
} else if (hyphenateAlchemy) {
|
||||
if (!collectorNumber.startsWith("A")) {
|
||||
if (!cardCollectorNumber.startsWith("A")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
collectorNumber = collectorNumber.replace("A", "A-");
|
||||
cardCollectorNumber = cardCollectorNumber.replace("A", "A-");
|
||||
}
|
||||
String versionParam = useArtCrop ? "art_crop" : "normal";
|
||||
if (!faceParam.isEmpty()) {
|
||||
faceParam = (faceParam.equals("back") ? "&face=back" : "&face=front");
|
||||
String faceParam = "";
|
||||
if (cp.getRules().getOtherPart() != null) {
|
||||
faceParam = (face.equals("back") ? "&face=back" : "&face=front");
|
||||
} else if (cp.getRules().getSplitType() == CardSplitType.Meld
|
||||
&& !cardCollectorNumber.endsWith("a")
|
||||
&& !cardCollectorNumber.endsWith("b")) {
|
||||
// Only the bottom half of a meld card shares a collector number.
|
||||
// Hanweir Garrison EMN already has a appended.
|
||||
cardCollectorNumber += face.equals("back") ? "b" : "a";
|
||||
}
|
||||
return String.format("%s/%s/%s?format=image&version=%s%s", setCode, collectorNumber,
|
||||
|
||||
String cardCollectorNumberEncoded;
|
||||
try {
|
||||
cardCollectorNumberEncoded = URLEncoder.encode(cardCollectorNumber, "UTF-8");
|
||||
} catch (Exception e) {
|
||||
// Unlikely, for the possibility that "UTF-8" is not supported.
|
||||
System.err.println("UTF-8 encoding not supported on this device.");
|
||||
cardCollectorNumberEncoded = cardCollectorNumber;
|
||||
}
|
||||
|
||||
return String.format("%s/%s/%s?format=image&version=%s%s", editionCode, cardCollectorNumberEncoded,
|
||||
langCode, versionParam, faceParam);
|
||||
}
|
||||
|
||||
@@ -258,4 +237,4 @@ public class ImageUtil {
|
||||
}
|
||||
return out.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,7 +193,9 @@ public class ForgeScript {
|
||||
return sa.isCrew();
|
||||
} else if (property.equals("Saddle")) {
|
||||
return sa.isKeyword(Keyword.SADDLE);
|
||||
} else if (property.equals("Cycling")) {
|
||||
} else if (property.equals("Station")) {
|
||||
return sa.isKeyword(Keyword.STATION);
|
||||
}else if (property.equals("Cycling")) {
|
||||
return sa.isCycling();
|
||||
} else if (property.equals("Dash")) {
|
||||
return sa.isDash();
|
||||
|
||||
@@ -519,6 +519,7 @@ public class GameAction {
|
||||
}
|
||||
card.setZone(zoneTo);
|
||||
}
|
||||
copied.clearMergedCards();
|
||||
} else {
|
||||
storeChangesZoneAll(copied, zoneFrom, zoneTo, params);
|
||||
// "enter the battlefield as a copy" - apply code here
|
||||
@@ -548,20 +549,20 @@ public class GameAction {
|
||||
|
||||
copied.updateStateForView();
|
||||
|
||||
// we don't want always trigger before counters are placed
|
||||
game.getTriggerHandler().suppressMode(TriggerType.Always);
|
||||
// Need to apply any static effects to produce correct triggers
|
||||
checkStaticAbilities();
|
||||
|
||||
// needed for counters + ascend
|
||||
if (!suppress && toBattlefield) {
|
||||
game.getTriggerHandler().registerActiveTrigger(copied, false);
|
||||
}
|
||||
|
||||
if (!table.isEmpty()) {
|
||||
// we don't want always trigger before counters are placed
|
||||
game.getTriggerHandler().suppressMode(TriggerType.Always);
|
||||
// Need to apply any static effects to produce correct triggers
|
||||
checkStaticAbilities();
|
||||
// do ETB counters after zone add
|
||||
table.replaceCounterEffect(game, null, true, true, params);
|
||||
game.getTriggerHandler().clearSuppression(TriggerType.Always);
|
||||
}
|
||||
// do ETB counters after zone add
|
||||
table.replaceCounterEffect(game, null, true, true, params);
|
||||
|
||||
game.getTriggerHandler().clearSuppression(TriggerType.Always);
|
||||
|
||||
// update static abilities after etb counters have been placed
|
||||
checkStaticAbilities();
|
||||
@@ -645,7 +646,8 @@ public class GameAction {
|
||||
// Ask controller if it wants to be on top or bottom of other meld.
|
||||
unmeldPosition++;
|
||||
}
|
||||
changeZone(null, zoneTo, unmeld, position, cause, params);
|
||||
unmeld = changeZone(null, zoneTo, unmeld, position, cause, params);
|
||||
storeChangesZoneAll(unmeld, zoneFrom, zoneTo, params);
|
||||
}
|
||||
} else if (toBattlefield) {
|
||||
for (Player p : game.getPlayers()) {
|
||||
@@ -2794,6 +2796,7 @@ public class GameAction {
|
||||
if (aura == null) {
|
||||
return false;
|
||||
}
|
||||
aura.setActivatingPlayer(source.getController());
|
||||
|
||||
Set<ZoneType> zones = EnumSet.noneOf(ZoneType.class);
|
||||
boolean canTargetPlayer = false;
|
||||
|
||||
@@ -130,7 +130,6 @@ public enum AbilityKey {
|
||||
SourceSA("SourceSA"),
|
||||
SpellAbility("SpellAbility"),
|
||||
SpellAbilityTargets("SpellAbilityTargets"),
|
||||
StackInstance("StackInstance"),
|
||||
StackSa("StackSa"),
|
||||
SurveilNum("SurveilNum"),
|
||||
Target("Target"),
|
||||
@@ -140,7 +139,7 @@ public enum AbilityKey {
|
||||
Valiant("Valiant"),
|
||||
Won("Won"),
|
||||
|
||||
// below used across different Replacements, don't reuse
|
||||
// below shared across different Replacements, don't reuse
|
||||
InternalTriggerTable("InternalTriggerTable"),
|
||||
SimultaneousETB("SimultaneousETB"); // for CR 614.13c
|
||||
|
||||
|
||||
@@ -663,7 +663,7 @@ public class AbilityUtils {
|
||||
Object o = root.getTriggeringObject(AbilityKey.fromString(calcX[0].substring(9)));
|
||||
val = o instanceof Player ? playerXProperty((Player) o, calcX[1], card, ability) : 0;
|
||||
}
|
||||
else if (calcX[0].equals("TriggeredSpellAbility") || calcX[0].equals("TriggeredStackInstance") || calcX[0].equals("SpellTargeted")) {
|
||||
else if (calcX[0].equals("TriggeredSpellAbility") || calcX[0].equals("SpellTargeted")) {
|
||||
final SpellAbility sat = Iterables.getFirst(getDefinedSpellAbilities(card, calcX[0], sa), null);
|
||||
val = sat == null ? 0 : xCount(sat.getHostCard(), calcX[1], sat);
|
||||
}
|
||||
@@ -1281,8 +1281,6 @@ public class AbilityUtils {
|
||||
final Object o = root.getTriggeringObject(AbilityKey.fromString(triggeringType));
|
||||
if (o instanceof SpellAbility) {
|
||||
s = (SpellAbility) o;
|
||||
} else if (o instanceof SpellAbilityStackInstance) {
|
||||
s = ((SpellAbilityStackInstance) o).getSpellAbility();
|
||||
}
|
||||
} else if (defined.endsWith("Targeted") && sa instanceof SpellAbility) {
|
||||
final List<TargetChoices> targets = defined.startsWith("This") ? Arrays.asList(((SpellAbility)sa).getTargets()) : ((SpellAbility)sa).getAllTargetChoices();
|
||||
@@ -1685,11 +1683,11 @@ public class AbilityUtils {
|
||||
return doXMath(x, expr, c, ctb);
|
||||
} else if (TriggerType.SpellCast.equals(t.getMode())) {
|
||||
// Cast Trigger like Hydroid Krasis
|
||||
SpellAbilityStackInstance castSI = (SpellAbilityStackInstance) root.getTriggeringObject(AbilityKey.StackInstance);
|
||||
if (castSI == null || castSI.getSpellAbility().getXManaCostPaid() == null) {
|
||||
SpellAbility castSA = (SpellAbility) root.getTriggeringObject(AbilityKey.SpellAbility);
|
||||
if (castSA == null || castSA.getXManaCostPaid() == null) {
|
||||
return doXMath(0, expr, c, ctb);
|
||||
}
|
||||
return doXMath(castSI.getSpellAbility().getXManaCostPaid(), expr, c, ctb);
|
||||
return doXMath(castSA.getXManaCostPaid(), expr, c, ctb);
|
||||
} else if (TriggerType.Cycled.equals(t.getMode())) {
|
||||
SpellAbility cycleSA = (SpellAbility) sa.getTriggeringObject(AbilityKey.Cause);
|
||||
if (cycleSA == null || cycleSA.getXManaCostPaid() == null) {
|
||||
@@ -3751,6 +3749,10 @@ public class AbilityUtils {
|
||||
return Aggregates.max(paidList, Card::getNetToughness);
|
||||
}
|
||||
|
||||
if (string.startsWith("TapPowerValue")) {
|
||||
return CardLists.getTotalPower(paidList, ctb);
|
||||
}
|
||||
|
||||
if (string.startsWith("SumToughness")) {
|
||||
return Aggregates.sum(paidList, Card::getNetToughness);
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ public class ChangeTargetsEffect extends SpellAbilityEffect {
|
||||
final Player chooser = sa.hasParam("Chooser") ? getDefinedPlayersOrTargeted(sa, "Chooser").get(0) : activator;
|
||||
|
||||
final MagicStack stack = activator.getGame().getStack();
|
||||
|
||||
|
||||
for (final SpellAbility tgtSA : sas) {
|
||||
SpellAbilityStackInstance si = stack.getInstanceMatchingSpellAbilityID(tgtSA);
|
||||
if (si == null) {
|
||||
@@ -72,8 +72,8 @@ public class ChangeTargetsEffect extends SpellAbilityEffect {
|
||||
// 2. prepare new target choices
|
||||
SpellAbilityStackInstance replaceIn = chosenTarget.getKey();
|
||||
GameObject oldTarget = chosenTarget.getValue();
|
||||
TargetChoices oldTargetBlock = replaceIn.getTargetChoices();
|
||||
TargetChoices newTargetBlock = oldTargetBlock.clone();
|
||||
TargetChoices newTargetBlock = replaceIn.getTargetChoices();
|
||||
TargetChoices oldTargetBlock = newTargetBlock.clone();
|
||||
// gets the divided value from old target
|
||||
Integer div = oldTargetBlock.getDividedValue(oldTarget);
|
||||
// 3. test if updated choices would be correct.
|
||||
@@ -87,7 +87,7 @@ public class ChangeTargetsEffect extends SpellAbilityEffect {
|
||||
if (div != null) {
|
||||
newTargetBlock.addDividedAllocation(newTarget, div);
|
||||
}
|
||||
replaceIn.updateTarget(newTargetBlock, sa.getHostCard());
|
||||
replaceIn.updateTarget(oldTargetBlock, sa.getHostCard());
|
||||
}
|
||||
} else {
|
||||
while (changingTgtSI != null) {
|
||||
@@ -104,25 +104,26 @@ public class ChangeTargetsEffect extends SpellAbilityEffect {
|
||||
if (candidates.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
changingTgtSA.resetTargets();
|
||||
GameEntity choice = Aggregates.random(candidates);
|
||||
TargetChoices oldTarget = changingTgtSA.getTargets();
|
||||
changingTgtSA.resetTargets();
|
||||
changingTgtSA.getTargets().add(choice);
|
||||
if (changingTgtSA.isDividedAsYouChoose()) {
|
||||
changingTgtSA.addDividedAllocation(choice, div);
|
||||
}
|
||||
|
||||
changingTgtSI.updateTarget(changingTgtSA.getTargets(), sa.getHostCard());
|
||||
changingTgtSI.updateTarget(oldTarget, sa.getHostCard());
|
||||
}
|
||||
else if (sa.hasParam("DefinedMagnet")) {
|
||||
GameObject newTarget = Iterables.getFirst(getDefinedCardsOrTargeted(sa, "DefinedMagnet"), null);
|
||||
if (newTarget != null && changingTgtSA.canTarget(newTarget)) {
|
||||
int div = changingTgtSA.getTotalDividedValue();
|
||||
TargetChoices oldTarget = changingTgtSA.getTargets();
|
||||
changingTgtSA.resetTargets();
|
||||
changingTgtSA.getTargets().add(newTarget);
|
||||
changingTgtSI.updateTarget(changingTgtSA.getTargets(), sa.getHostCard());
|
||||
if (changingTgtSA.isDividedAsYouChoose()) {
|
||||
changingTgtSA.addDividedAllocation(newTarget, div);
|
||||
}
|
||||
changingTgtSI.updateTarget(oldTarget, sa.getHostCard());
|
||||
}
|
||||
} else {
|
||||
// Update targets, with a potential new target
|
||||
@@ -132,8 +133,9 @@ public class ChangeTargetsEffect extends SpellAbilityEffect {
|
||||
source = changingTgtSA.getTargetCard();
|
||||
}
|
||||
Predicate<GameObject> filter = sa.hasParam("TargetRestriction") ? GameObjectPredicates.restriction(sa.getParam("TargetRestriction").split(","), activator, source, sa) : null;
|
||||
TargetChoices newTarget = chooser.getController().chooseNewTargetsFor(changingTgtSA, filter, false);
|
||||
changingTgtSI.updateTarget(newTarget, sa.getHostCard());
|
||||
TargetChoices oldTarget = changingTgtSA.getTargets();
|
||||
chooser.getController().chooseNewTargetsFor(changingTgtSA, filter, false);
|
||||
changingTgtSI.updateTarget(oldTarget, sa.getHostCard());
|
||||
}
|
||||
}
|
||||
changingTgtSI = changingTgtSI.getSubInstance();
|
||||
|
||||
@@ -2,6 +2,7 @@ package forge.game.ability.effects;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import forge.GameCommand;
|
||||
import forge.game.Game;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
@@ -36,6 +37,10 @@ public class TextBoxExchangeEffect extends SpellAbilityEffect {
|
||||
|
||||
@Override
|
||||
public void resolve(final SpellAbility sa) {
|
||||
if (!checkValidDuration(sa.getParam("Duration"), sa)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final List<Card> tgtCards = getTargetCards(sa);
|
||||
if (tgtCards.size() < 2) {
|
||||
return;
|
||||
@@ -55,6 +60,37 @@ public class TextBoxExchangeEffect extends SpellAbilityEffect {
|
||||
swapTextBox(c1, data2, ts);
|
||||
swapTextBox(c2, data1, ts);
|
||||
|
||||
if (sa.hasParam("Duration")) {
|
||||
final GameCommand revertTextExchange = new GameCommand() {
|
||||
private static final long serialVersionUID = 5331255714437747836L;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
// Check if the cards are still there
|
||||
Card card1 = game.getCardState(c1, null);
|
||||
Card card2 = game.getCardState(c2, null);
|
||||
|
||||
if (card1 != null && c1.equalsWithGameTimestamp(card1)) {
|
||||
card1.removeChangedCardTraits(ts, 0);
|
||||
card1.removeChangedCardKeywords(ts, 0, false);
|
||||
card1.updateChangedText();
|
||||
card1.updateStateForView();
|
||||
game.fireEvent(new GameEventCardStatsChanged(card1));
|
||||
}
|
||||
|
||||
if (card2 != null && c2.equalsWithGameTimestamp(card2)) {
|
||||
card2.removeChangedCardTraits(ts, 0);
|
||||
card2.removeChangedCardKeywords(ts, 0, false);
|
||||
card2.updateChangedText();
|
||||
card2.updateStateForView();
|
||||
game.fireEvent(new GameEventCardStatsChanged(card2));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
addUntilCommand(sa, revertTextExchange);
|
||||
}
|
||||
|
||||
game.fireEvent(new GameEventCardStatsChanged(c1));
|
||||
game.fireEvent(new GameEventCardStatsChanged(c2));
|
||||
}
|
||||
|
||||
@@ -1407,6 +1407,10 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
|
||||
public final CardCollectionView getMergedCards() {
|
||||
return CardCollection.getView(mergedCards);
|
||||
}
|
||||
public final void setMergedCards(Iterable<Card> mc) {
|
||||
mergedCards = new CardCollection(mc);
|
||||
}
|
||||
|
||||
public final Card getTopMergedCard() {
|
||||
return mergedCards.get(0);
|
||||
}
|
||||
@@ -2739,7 +2743,8 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
|
||||
|| keyword.startsWith("Class") || keyword.startsWith("Blitz")
|
||||
|| keyword.startsWith("Specialize") || keyword.equals("Ravenous")
|
||||
|| keyword.equals("For Mirrodin") || keyword.equals("Job select") || keyword.startsWith("Craft")
|
||||
|| keyword.startsWith("Landwalk") || keyword.startsWith("Visit") || keyword.startsWith("Mobilize")) {
|
||||
|| keyword.startsWith("Landwalk") || keyword.startsWith("Visit") || keyword.startsWith("Mobilize")
|
||||
|| keyword.startsWith("Station")) {
|
||||
// keyword parsing takes care of adding a proper description
|
||||
} else if (keyword.equals("Read ahead")) {
|
||||
sb.append(Localizer.getInstance().getMessage("lblReadAhead")).append(" (").append(Localizer.getInstance().getMessage("lblReadAheadDesc"));
|
||||
@@ -4229,12 +4234,12 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
|
||||
return ImmutableList.of();
|
||||
}
|
||||
Iterable<CardChangedType> byText = changedTypeByText == null ? ImmutableList.of() : ImmutableList.of(this.changedTypeByText);
|
||||
return ImmutableList.copyOf(Iterables.concat(
|
||||
changedCardTypesByText.values(), // Layer 3
|
||||
byText, // Layer 3 by Word Changes,
|
||||
changedCardTypesCharacterDefining.values(), // Layer 4
|
||||
changedCardTypes.values() // Layer 6
|
||||
));
|
||||
return ImmutableList.<CardChangedType>builder()
|
||||
.addAll(changedCardTypesByText.values()) // Layer 3
|
||||
.addAll(byText) // Layer 3 by Word Changes,
|
||||
.addAll(changedCardTypesCharacterDefining.values()) // Layer 4
|
||||
.addAll(changedCardTypes.values()) // Layer 6
|
||||
.build();
|
||||
}
|
||||
|
||||
public boolean clearChangedCardTypes() {
|
||||
@@ -6515,6 +6520,29 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
|
||||
if(uiCard != null)
|
||||
uiCard.currentState.setImageKey(iFN);
|
||||
}
|
||||
public final void setImageKey(final IPaperCard ipc, final CardStateName stateName) {
|
||||
if (ipc == null)
|
||||
return;
|
||||
switch (stateName) {
|
||||
case SpecializeB:
|
||||
setImageKey(ipc.getCardBSpecImageKey());
|
||||
break;
|
||||
case SpecializeR:
|
||||
setImageKey(ipc.getCardRSpecImageKey());
|
||||
break;
|
||||
case SpecializeG:
|
||||
setImageKey(ipc.getCardGSpecImageKey());
|
||||
break;
|
||||
case SpecializeU:
|
||||
setImageKey(ipc.getCardUSpecImageKey());
|
||||
break;
|
||||
case SpecializeW:
|
||||
setImageKey(ipc.getCardWSpecImageKey());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public String getImageKey(CardStateName state) {
|
||||
if (!getRenderForUI()) {
|
||||
@@ -6814,6 +6842,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
|
||||
}
|
||||
public final void setSpecialized(final boolean bool) {
|
||||
specialized = bool;
|
||||
setImageKey(getPaperCard(), getCurrentStateName());
|
||||
}
|
||||
public final boolean canSpecialize() {
|
||||
return getRules() != null && getRules().getSplitType() == CardSplitType.Specialize;
|
||||
|
||||
@@ -66,8 +66,15 @@ public class CardCopyService {
|
||||
out.setCollectible(copyFrom.isCollectible());
|
||||
|
||||
// this's necessary for forge.game.GameAction.unattachCardLeavingBattlefield(Card)
|
||||
out.setAttachedCards(copyFrom.getAttachedCards());
|
||||
out.setEntityAttachedTo(copyFrom.getEntityAttachedTo());
|
||||
if (copyFrom.hasCardAttachments()) {
|
||||
out.setAttachedCards(copyFrom.getAttachedCards());
|
||||
}
|
||||
if (copyFrom.isAttachedToEntity()) {
|
||||
out.setEntityAttachedTo(copyFrom.getEntityAttachedTo());
|
||||
}
|
||||
if (copyFrom.hasMergedCard()) {
|
||||
out.setMergedCards(copyFrom.getMergedCards());
|
||||
}
|
||||
|
||||
out.setLeavesPlayCommands(copyFrom.getLeavesPlayCommands());
|
||||
|
||||
|
||||
@@ -187,7 +187,7 @@ public class CardFactory {
|
||||
c.setRarity(cp.getRarity());
|
||||
|
||||
// Would like to move this away from in-game entities
|
||||
String originalPicture = cp.getCardImageKey();
|
||||
String originalPicture = cp.getImageKey(false);
|
||||
c.setImageKey(originalPicture);
|
||||
|
||||
if(cp.isToken())
|
||||
@@ -198,11 +198,11 @@ public class CardFactory {
|
||||
if (c.hasAlternateState()) {
|
||||
if (c.isFlipCard()) {
|
||||
c.setState(CardStateName.Flipped, false);
|
||||
c.setImageKey(cp.getCardAltImageKey());
|
||||
c.setImageKey(cp.getImageKey(true));
|
||||
}
|
||||
else if (c.isDoubleFaced() && cardRules != null) {
|
||||
c.setState(cardRules.getSplitType().getChangedStateName(), false);
|
||||
c.setImageKey(cp.getCardAltImageKey());
|
||||
c.setImageKey(cp.getImageKey(true));
|
||||
}
|
||||
else if (c.isSplitCard()) {
|
||||
c.setState(CardStateName.LeftSplit, false);
|
||||
@@ -216,23 +216,23 @@ public class CardFactory {
|
||||
c.setImageKey(originalPicture);
|
||||
} else if (c.canSpecialize()) {
|
||||
c.setState(CardStateName.SpecializeW, false);
|
||||
c.setImageKey(cp.getCardWSpecImageKey());
|
||||
c.setImageKey(cp.getImageKey(false) + ImageKeys.SPECFACE_W);
|
||||
c.setSetCode(cp.getEdition());
|
||||
c.setRarity(cp.getRarity());
|
||||
c.setState(CardStateName.SpecializeU, false);
|
||||
c.setImageKey(cp.getCardUSpecImageKey());
|
||||
c.setImageKey(cp.getImageKey(false) + ImageKeys.SPECFACE_U);
|
||||
c.setSetCode(cp.getEdition());
|
||||
c.setRarity(cp.getRarity());
|
||||
c.setState(CardStateName.SpecializeB, false);
|
||||
c.setImageKey(cp.getCardBSpecImageKey());
|
||||
c.setImageKey(cp.getImageKey(false) + ImageKeys.SPECFACE_B);
|
||||
c.setSetCode(cp.getEdition());
|
||||
c.setRarity(cp.getRarity());
|
||||
c.setState(CardStateName.SpecializeR, false);
|
||||
c.setImageKey(cp.getCardRSpecImageKey());
|
||||
c.setImageKey(cp.getImageKey(false) + ImageKeys.SPECFACE_R);
|
||||
c.setSetCode(cp.getEdition());
|
||||
c.setRarity(cp.getRarity());
|
||||
c.setState(CardStateName.SpecializeG, false);
|
||||
c.setImageKey(cp.getCardGSpecImageKey());
|
||||
c.setImageKey(cp.getImageKey(false) + ImageKeys.SPECFACE_G);
|
||||
c.setSetCode(cp.getEdition());
|
||||
c.setRarity(cp.getRarity());
|
||||
}
|
||||
@@ -742,7 +742,7 @@ public class CardFactory {
|
||||
TextUtil.fastReplace(host.getName(), ",", ""),
|
||||
" ", "_").toLowerCase();
|
||||
String set = host.getSetCode().toLowerCase();
|
||||
state.setImageKey(ImageKeys.getTokenKey("offspring_" + name + "_" + set));
|
||||
state.setImageKey(ImageKeys.getTokenKey("offspring_" + name + "|" + set));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -2122,23 +2122,13 @@ public class CardFactoryUtil {
|
||||
} else if (keyword.startsWith("Amplify")) {
|
||||
final String[] ampString = keyword.split(":");
|
||||
final String amplifyMagnitude = ampString[1];
|
||||
final String ampTypes = ampString[2];
|
||||
String[] refinedTypes = ampTypes.split(",");
|
||||
final StringBuilder types = new StringBuilder();
|
||||
for (int i = 0; i < refinedTypes.length; i++) {
|
||||
types.append("Card.").append(refinedTypes[i]).append("+YouCtrl");
|
||||
if (i + 1 != refinedTypes.length) {
|
||||
types.append(",");
|
||||
}
|
||||
}
|
||||
|
||||
// Setup ETB replacement effects
|
||||
final String actualRep = "Event$ Moved | Destination$ Battlefield | ValidCard$ Card.Self |"
|
||||
+ " | ReplacementResult$ Updated | Description$ Amplify " + amplifyMagnitude + " ("
|
||||
+ inst.getReminderText() + ")";
|
||||
|
||||
final String abString = "DB$ Reveal | AnyNumber$ True | RevealValid$ "
|
||||
+ types.toString() + " | RememberRevealed$ True";
|
||||
final String abString = "DB$ Reveal | AnyNumber$ True | RevealValid$ Card.YouOwn+sharesCreatureTypeWith+Other+NotDefinedReplacedSimultaneousETB | RememberRevealed$ True";
|
||||
|
||||
SpellAbility saReveal = AbilityFactory.getAbility(abString, card);
|
||||
|
||||
@@ -2792,7 +2782,7 @@ public class CardFactoryUtil {
|
||||
final String[] k = keyword.split(":");
|
||||
final Cost blitzCost = new Cost(k[1], false);
|
||||
|
||||
final SpellAbility newSA = card.getFirstSpellAbility().copyWithManaCostReplaced(host.getController(), blitzCost);
|
||||
final SpellAbility newSA = card.getFirstSpellAbilityWithFallback().copyWithManaCostReplaced(host.getController(), blitzCost);
|
||||
|
||||
if (k.length > 2) {
|
||||
newSA.getMapParams().put("ValidAfterStack", k[2]);
|
||||
@@ -2905,7 +2895,7 @@ public class CardFactoryUtil {
|
||||
}
|
||||
desc += ")";
|
||||
|
||||
final SpellAbility sa = card.getFirstSpellAbility();
|
||||
final SpellAbility sa = card.getFirstSpellAbilityWithFallback();
|
||||
final SpellAbility newSA = sa.copyWithDefinedCost(new Cost(costStr, false));
|
||||
|
||||
newSA.getRestrictions().setIsPresent(validStr + ".YouCtrl+CanBeSacrificedBy");
|
||||
@@ -3591,6 +3581,15 @@ public class CardFactoryUtil {
|
||||
sa.setSVar("ScavengeX", "Exiled$CardPower");
|
||||
sa.setIntrinsic(intrinsic);
|
||||
inst.addSpellAbility(sa);
|
||||
} else if (keyword.startsWith("Station")) {
|
||||
String effect = "AB$ PutCounter | Cost$ tapXType<1/Creature.Other> | Defined$ Self " +
|
||||
"| CounterType$ CHARGE | CounterNum$ StationX | SorcerySpeed$ True " +
|
||||
"| CostDesc$ | SpellDescription$ Station (" + inst.getReminderText() + ")";
|
||||
|
||||
final SpellAbility sa = AbilityFactory.getAbility(effect, card);
|
||||
sa.setSVar("StationX", "TappedCards$TapPowerValue");
|
||||
sa.setIntrinsic(intrinsic);
|
||||
inst.addSpellAbility(sa);
|
||||
} else if (keyword.startsWith("Encore")) {
|
||||
final String[] k = keyword.split(":");
|
||||
final String manacost = k[1];
|
||||
|
||||
@@ -421,13 +421,13 @@ public class CardLists {
|
||||
* @param cardList the list of creature cards for which to sum the power
|
||||
* @param crew for cards that crew with toughness rather than power
|
||||
*/
|
||||
public static int getTotalPower(Iterable<Card> cardList, SpellAbility sa) {
|
||||
public static int getTotalPower(Iterable<Card> cardList, CardTraitBase ctb) {
|
||||
int total = 0;
|
||||
for (final Card crd : cardList) {
|
||||
if (StaticAbilityTapPowerValue.withToughness(crd, sa)) {
|
||||
if (StaticAbilityTapPowerValue.withToughness(crd, ctb)) {
|
||||
total += Math.max(0, crd.getNetToughness());
|
||||
} else {
|
||||
int m = StaticAbilityTapPowerValue.getMod(crd, sa);
|
||||
int m = StaticAbilityTapPowerValue.getMod(crd, ctb);
|
||||
total += Math.max(0, crd.getNetPower() + m);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class CardState extends GameObject implements IHasSVars, ITranslatable {
|
||||
private String name = "";
|
||||
@@ -366,14 +367,16 @@ public class CardState extends GameObject implements IHasSVars, ITranslatable {
|
||||
public final FCollectionView<SpellAbility> getManaAbilities() {
|
||||
FCollection<SpellAbility> newCol = new FCollection<>();
|
||||
updateSpellAbilities(newCol, true);
|
||||
newCol.addAll(abilities.stream().filter(SpellAbility::isManaAbility).toList());
|
||||
// stream().toList() causes crash on Android, use Collectors.toList()
|
||||
newCol.addAll(abilities.stream().filter(SpellAbility::isManaAbility).collect(Collectors.toList()));
|
||||
card.updateSpellAbilities(newCol, this, true);
|
||||
return newCol;
|
||||
}
|
||||
public final FCollectionView<SpellAbility> getNonManaAbilities() {
|
||||
FCollection<SpellAbility> newCol = new FCollection<>();
|
||||
updateSpellAbilities(newCol, false);
|
||||
newCol.addAll(abilities.stream().filter(Predicate.not(SpellAbility::isManaAbility)).toList());
|
||||
// stream().toList() causes crash on Android, use Collectors.toList()
|
||||
newCol.addAll(abilities.stream().filter(Predicate.not(SpellAbility::isManaAbility)).collect(Collectors.toList()));
|
||||
card.updateSpellAbilities(newCol, this, false);
|
||||
return newCol;
|
||||
}
|
||||
@@ -385,7 +388,10 @@ public class CardState extends GameObject implements IHasSVars, ITranslatable {
|
||||
CardState leftState = getCard().getState(CardStateName.LeftSplit);
|
||||
Collection<SpellAbility> leftAbilities = leftState.abilities;
|
||||
if (null != mana) {
|
||||
leftAbilities = leftAbilities.stream().filter(mana ? SpellAbility::isManaAbility : Predicate.not(SpellAbility::isManaAbility)).toList();
|
||||
leftAbilities = leftAbilities.stream()
|
||||
.filter(mana ? SpellAbility::isManaAbility : Predicate.not(SpellAbility::isManaAbility))
|
||||
// stream().toList() causes crash on Android, use Collectors.toList()
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
newCol.addAll(leftAbilities);
|
||||
leftState.updateSpellAbilities(newCol, mana);
|
||||
@@ -394,7 +400,10 @@ public class CardState extends GameObject implements IHasSVars, ITranslatable {
|
||||
CardState rightState = getCard().getState(CardStateName.RightSplit);
|
||||
Collection<SpellAbility> rightAbilities = rightState.abilities;
|
||||
if (null != mana) {
|
||||
rightAbilities = rightAbilities.stream().filter(mana ? SpellAbility::isManaAbility : Predicate.not(SpellAbility::isManaAbility)).toList();
|
||||
rightAbilities = rightAbilities.stream()
|
||||
.filter(mana ? SpellAbility::isManaAbility : Predicate.not(SpellAbility::isManaAbility))
|
||||
// stream().toList() causes crash on Android, use Collectors.toList()
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
newCol.addAll(rightAbilities);
|
||||
rightState.updateSpellAbilities(newCol, mana);
|
||||
@@ -428,8 +437,7 @@ public class CardState extends GameObject implements IHasSVars, ITranslatable {
|
||||
CardTypeView type = getTypeWithChanges();
|
||||
if (type.isLand()) {
|
||||
if (landAbility == null) {
|
||||
landAbility = new LandAbility(card);
|
||||
landAbility.setCardState(this);
|
||||
landAbility = new LandAbility(card, this);
|
||||
}
|
||||
newCol.add(landAbility);
|
||||
} else if (type.isAura()) {
|
||||
|
||||
@@ -101,7 +101,6 @@ public class CardView extends GameEntityView {
|
||||
set(TrackableProperty.Controller, ownerAndController);
|
||||
set(TrackableProperty.ImageKey, imageKey);
|
||||
}
|
||||
|
||||
public PlayerView getOwner() {
|
||||
return get(TrackableProperty.Owner);
|
||||
}
|
||||
@@ -1061,6 +1060,8 @@ public class CardView extends GameEntityView {
|
||||
mergedCollection.add(card);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
set(TrackableProperty.MergedCards, null);
|
||||
}
|
||||
updateMergeCollections(mergedCollection);
|
||||
|
||||
@@ -1335,29 +1336,9 @@ public class CardView extends GameEntityView {
|
||||
}
|
||||
void updateImageKey(Card c) {
|
||||
set(TrackableProperty.ImageKey, c.getImageKey());
|
||||
// currently only works for Cards
|
||||
if (!c.getGamePieceType().equals(GamePieceType.CARD)) {
|
||||
return;
|
||||
}
|
||||
IPaperCard pc = c.getPaperCard();
|
||||
if (pc != null) {
|
||||
set(TrackableProperty.Artist, pc.getArtist());
|
||||
}
|
||||
}
|
||||
void updateImageKey(CardState c) {
|
||||
set(TrackableProperty.ImageKey, c.getImageKey());
|
||||
// currently only works for Cards
|
||||
if (!c.getCard().getGamePieceType().equals(GamePieceType.CARD)) {
|
||||
return;
|
||||
}
|
||||
IPaperCard pc = c.getCard().getPaperCard();
|
||||
if (pc != null) { // currently Artist is per Card
|
||||
set(TrackableProperty.Artist, pc.getArtist());
|
||||
}
|
||||
}
|
||||
|
||||
public String getArtist() {
|
||||
return get(TrackableProperty.Artist);
|
||||
}
|
||||
|
||||
public CardTypeView getType() {
|
||||
|
||||
@@ -282,7 +282,7 @@ public class CostPayment extends ManaConversionMatrix {
|
||||
private static List<Pair<Mana, Integer>> selectManaToPayFor(final ManaPool manapool, final ManaCostShard shard,
|
||||
final SpellAbility saBeingPaidFor, final byte colorsPaid, Map<String, Integer> xManaCostPaidByColor) {
|
||||
final List<Pair<Mana, Integer>> weightedOptions = new ArrayList<>();
|
||||
for (final Mana thisMana : manapool) {
|
||||
for (final Mana thisMana : Lists.newArrayList(manapool)) {
|
||||
if (shard == ManaCostShard.COLORED_X && !ManaCostBeingPaid.canColoredXShardBePaidByColor(MagicColor.toShortString(thisMana.getColor()), xManaCostPaidByColor)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -178,6 +178,7 @@ public enum Keyword {
|
||||
SQUAD("Squad", KeywordWithCost.class, false, "As an additional cost to cast this spell, you may pay %s any number of times. When this creature enters, create that many tokens that are copies of it."),
|
||||
START_YOUR_ENGINES("Start your engines", SimpleKeyword.class, true, "If you have no speed, it starts at 1. It increases once on each of your turns when an opponent loses life. Max speed is 4."),
|
||||
STARTING_INTENSITY("Starting intensity", KeywordWithAmount.class, true, null),
|
||||
STATION("Station", KeywordWithAmount.class, false, "Tap another creature you control: Put charge counters equal to its power on this Spacecraft. Station only as a sorcery. It’s an artifact creature at %d+."),
|
||||
STORM("Storm", SimpleKeyword.class, false, "When you cast this spell, copy it for each other spell that was cast before it this turn. You may choose new targets for the copies."),
|
||||
STRIVE("Strive", KeywordWithCost.class, false, "CARDNAME costs %s more to cast for each target beyond the first."),
|
||||
SUNBURST("Sunburst", SimpleKeyword.class, false, "This enters with either a +1/+1 or charge counter on it for each color of mana spent to cast it based on whether it's a creature."),
|
||||
|
||||
@@ -904,10 +904,8 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
runParams.put(AbilityKey.CounterAmount, oldValue + i + 1);
|
||||
getGame().getTriggerHandler().runTrigger(TriggerType.CounterAdded, AbilityKey.newMap(runParams), false);
|
||||
}
|
||||
if (addAmount > 0) {
|
||||
runParams.put(AbilityKey.CounterAmount, addAmount);
|
||||
getGame().getTriggerHandler().runTrigger(TriggerType.CounterAddedOnce, AbilityKey.newMap(runParams), false);
|
||||
}
|
||||
runParams.put(AbilityKey.CounterAmount, addAmount);
|
||||
getGame().getTriggerHandler().runTrigger(TriggerType.CounterAddedOnce, AbilityKey.newMap(runParams), false);
|
||||
if (table != null) {
|
||||
table.put(source, this, counterType, addAmount);
|
||||
}
|
||||
@@ -3605,7 +3603,7 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
}
|
||||
|
||||
String trigStr = "Mode$ Phase | Phase$ Main1 | ValidPlayer$ You | TriggerZones$ Command | TriggerDescription$ " +
|
||||
"At the beginning of your precombat main phase, if you have any rad counters, mill that many cards. For each nonland card milled this way, you lose 1 life and a rad counter.";
|
||||
"At the beginning of your precombat main phase, if you have any rad counters, mill that many cards. For each nonland card milled this way, you lose 1 life and a rad counter.";
|
||||
|
||||
Trigger tr = TriggerHandler.parseTrigger(trigStr, radiationEffect, true);
|
||||
SpellAbility sa = AbilityFactory.getAbility("DB$ InternalRadiation", radiationEffect);
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.esotericsoftware.minlog.Log;
|
||||
import forge.card.mana.ManaCost;
|
||||
import forge.game.Game;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardState;
|
||||
import forge.game.cost.Cost;
|
||||
|
||||
/**
|
||||
@@ -37,6 +38,9 @@ public abstract class Ability extends SpellAbility {
|
||||
protected Ability(final Card sourceCard, final ManaCost manaCost) {
|
||||
this(sourceCard, new Cost(manaCost, true), null);
|
||||
}
|
||||
protected Ability(final Card sourceCard, final ManaCost manaCost, final CardState state) {
|
||||
super(sourceCard, new Cost(manaCost, true), null, state);
|
||||
}
|
||||
protected Ability(final Card sourceCard, final ManaCost manaCost, SpellAbilityView view0) {
|
||||
this(sourceCard, new Cost(manaCost, true), view0);
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package forge.game.spellability;
|
||||
|
||||
import forge.card.mana.ManaCost;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardState;
|
||||
import forge.game.cost.Cost;
|
||||
|
||||
/**
|
||||
@@ -43,6 +44,9 @@ public abstract class AbilityStatic extends Ability implements Cloneable {
|
||||
public AbilityStatic(final Card sourceCard, final ManaCost manaCost) {
|
||||
super(sourceCard, manaCost);
|
||||
}
|
||||
public AbilityStatic(final Card sourceCard, final ManaCost manaCost, final CardState state) {
|
||||
super(sourceCard, manaCost, state);
|
||||
}
|
||||
|
||||
public AbilityStatic(final Card sourceCard, final Cost abCost, final TargetRestrictions tgt) {
|
||||
super(sourceCard, abCost);
|
||||
|
||||
@@ -21,6 +21,7 @@ import forge.card.CardStateName;
|
||||
import forge.card.mana.ManaCost;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCopyService;
|
||||
import forge.game.card.CardState;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.staticability.StaticAbility;
|
||||
import forge.game.zone.ZoneType;
|
||||
@@ -31,8 +32,8 @@ import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
public class LandAbility extends AbilityStatic {
|
||||
|
||||
public LandAbility(Card sourceCard) {
|
||||
super(sourceCard, ManaCost.NO_COST);
|
||||
public LandAbility(Card sourceCard, CardState state) {
|
||||
super(sourceCard, ManaCost.NO_COST, state);
|
||||
|
||||
getRestrictions().setZone(ZoneType.Hand);
|
||||
}
|
||||
|
||||
@@ -195,12 +195,18 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
||||
}
|
||||
|
||||
protected SpellAbility(final Card iSourceCard, final Cost toPay) {
|
||||
this(iSourceCard, toPay, null);
|
||||
this(iSourceCard, toPay, null, null);
|
||||
}
|
||||
protected SpellAbility(final Card iSourceCard, final Cost toPay, SpellAbilityView view0) {
|
||||
this(iSourceCard, toPay, view0, null);
|
||||
}
|
||||
protected SpellAbility(final Card iSourceCard, final Cost toPay, SpellAbilityView view0, CardState cs) {
|
||||
id = nextId();
|
||||
hostCard = iSourceCard;
|
||||
payCosts = toPay;
|
||||
if (cs != null) {
|
||||
cardState = cs;
|
||||
}
|
||||
if (view0 == null) {
|
||||
view0 = new SpellAbilityView(this);
|
||||
}
|
||||
@@ -1222,12 +1228,14 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
||||
try {
|
||||
clone = (SpellAbility) clone();
|
||||
clone.id = lki ? id : nextId();
|
||||
clone.view = new SpellAbilityView(clone, lki || host.getGame() == null ? null : host.getGame().getTracker());
|
||||
|
||||
// don't use setHostCard to not trigger the not copied parts yet
|
||||
|
||||
copyHelper(clone, host, lki || keepTextChanges);
|
||||
|
||||
// need CardState before View
|
||||
clone.view = new SpellAbilityView(clone, lki || host.getGame() == null ? null : host.getGame().getTracker());
|
||||
|
||||
// always set this to false, it is only set in CopyEffect
|
||||
clone.mayChooseNewTargets = false;
|
||||
|
||||
|
||||
@@ -133,18 +133,16 @@ public class SpellAbilityStackInstance implements IIdentifiable, IHasCardView {
|
||||
return ability.getTargets();
|
||||
}
|
||||
|
||||
public void updateTarget(TargetChoices target, Card cause) {
|
||||
if (target != null) {
|
||||
TargetChoices oldTarget = ability.getTargets();
|
||||
ability.setTargets(target);
|
||||
public void updateTarget(TargetChoices oldTC, Card cause) {
|
||||
if (oldTC != null) {
|
||||
stackDescription = ability.getStackDescription();
|
||||
view.updateTargetCards(this);
|
||||
view.updateTargetPlayers(this);
|
||||
view.updateText(this);
|
||||
|
||||
Set<GameObject> distinctObjects = Sets.newHashSet();
|
||||
for (final GameObject tgt : target) {
|
||||
if (oldTarget != null && oldTarget.contains(tgt)) {
|
||||
for (final GameObject tgt : ability.getTargets()) {
|
||||
if (oldTC.contains(tgt)) {
|
||||
// it was an old target, so don't trigger becomes target
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ import forge.util.TextUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@@ -273,7 +274,7 @@ public final class StaticAbilityContinuous {
|
||||
if (hostCard.hasChosenPlayer()) {
|
||||
Player cp = hostCard.getChosenPlayer();
|
||||
input = input.replaceAll("ChosenPlayerUID", String.valueOf(cp.getId()));
|
||||
input = input.replaceAll("ChosenPlayerName", cp.getName());
|
||||
input = input.replaceAll("ChosenPlayerName", Matcher.quoteReplacement(cp.getName()));
|
||||
}
|
||||
if (hostCard.hasNamedCard()) {
|
||||
final String chosenName = hostCard.getNamedCard().replace(",", ";");
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
package forge.game.staticability;
|
||||
|
||||
import forge.game.CardTraitBase;
|
||||
import forge.game.Game;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.zone.ZoneType;
|
||||
|
||||
public class StaticAbilityTapPowerValue {
|
||||
|
||||
public static boolean withToughness(final Card card, final SpellAbility sa) {
|
||||
public static boolean withToughness(final Card card, final CardTraitBase ctb) {
|
||||
final Game game = card.getGame();
|
||||
for (final Card ca : game.getCardsIn(ZoneType.STATIC_ABILITIES_SOURCE_ZONES)) {
|
||||
for (final StaticAbility stAb : ca.getStaticAbilities()) {
|
||||
if (!stAb.checkConditions(StaticAbilityMode.TapPowerValue)) {
|
||||
continue;
|
||||
}
|
||||
if (withToughness(stAb, card, sa)) {
|
||||
if (withToughness(stAb, card, ctb)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -22,20 +22,20 @@ public class StaticAbilityTapPowerValue {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean withToughness(final StaticAbility stAb, final Card card, final SpellAbility sa) {
|
||||
public static boolean withToughness(final StaticAbility stAb, final Card card, final CardTraitBase ctb) {
|
||||
if (!stAb.getParam("Value").equals("Toughness")) {
|
||||
return false;
|
||||
}
|
||||
if (!stAb.matchesValidParam("ValidCard", card)) {
|
||||
return false;
|
||||
}
|
||||
if (!stAb.matchesValidParam("ValidSA", sa)) {
|
||||
if (!stAb.matchesValidParam("ValidSA", ctb)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static int getMod(final Card card, SpellAbility sa) {
|
||||
public static int getMod(final Card card, final CardTraitBase ctb) {
|
||||
int i = 0;
|
||||
final Game game = card.getGame();
|
||||
for (final Card ca : game.getCardsIn(ZoneType.STATIC_ABILITIES_SOURCE_ZONES)) {
|
||||
@@ -46,11 +46,10 @@ public class StaticAbilityTapPowerValue {
|
||||
if (!stAb.matchesValidParam("ValidCard", card)) {
|
||||
continue;
|
||||
}
|
||||
if (!stAb.matchesValidParam("ValidSA", sa)) {
|
||||
if (!stAb.matchesValidParam("ValidSA", ctb)) {
|
||||
continue;
|
||||
}
|
||||
int t = Integer.parseInt(stAb.getParam("Value"));
|
||||
i = i + t;
|
||||
i += Integer.parseInt(stAb.getParam("Value"));
|
||||
}
|
||||
}
|
||||
return i;
|
||||
|
||||
@@ -83,7 +83,6 @@ public class TriggerBecomesTarget extends Trigger {
|
||||
public final void setTriggeringObjects(final SpellAbility sa, Map<AbilityKey, Object> runParams) {
|
||||
sa.setTriggeringObject(AbilityKey.Source, ((SpellAbility) runParams.get(AbilityKey.SourceSA)).getHostCard());
|
||||
sa.setTriggeringObjectsFrom(runParams, AbilityKey.SourceSA, AbilityKey.Target);
|
||||
sa.setTriggeringObject(AbilityKey.StackInstance, sa.getHostCard().getGame().getStack().getInstanceMatchingSpellAbilityID((SpellAbility) runParams.get(AbilityKey.SourceSA)));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -24,7 +24,6 @@ import java.util.Set;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import forge.card.ColorSet;
|
||||
import forge.game.Game;
|
||||
import forge.game.GameEntity;
|
||||
import forge.game.GameObject;
|
||||
import forge.game.ability.AbilityKey;
|
||||
@@ -35,7 +34,6 @@ import forge.game.card.CardUtil;
|
||||
import forge.game.mana.Mana;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.spellability.SpellAbilityStackInstance;
|
||||
import forge.game.spellability.TargetChoices;
|
||||
import forge.util.Expressions;
|
||||
import forge.util.Localizer;
|
||||
@@ -78,22 +76,9 @@ public class TriggerSpellAbilityCastOrCopy extends Trigger {
|
||||
return false;
|
||||
}
|
||||
final Card cast = spellAbility.getHostCard();
|
||||
final Game game = cast.getGame();
|
||||
final SpellAbilityStackInstance si = game.getStack().getInstanceMatchingSpellAbilityID(spellAbility);
|
||||
|
||||
if (!matchesValidParam("ValidPlayer", runParams.get(AbilityKey.Player))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hasParam("ValidActivatingPlayer")) {
|
||||
Player activator;
|
||||
if (spellAbility.isManaAbility()) {
|
||||
activator = (Player) runParams.get(AbilityKey.Activator);
|
||||
} else if (si == null) {
|
||||
return false;
|
||||
} else {
|
||||
activator = si.getSpellAbility().getActivatingPlayer();
|
||||
}
|
||||
Player activator = (Player) runParams.get(AbilityKey.Activator);
|
||||
|
||||
if (!matchesValidParam("ValidActivatingPlayer", activator)) {
|
||||
return false;
|
||||
@@ -143,9 +128,6 @@ public class TriggerSpellAbilityCastOrCopy extends Trigger {
|
||||
|
||||
if (hasParam("TargetsValid")) {
|
||||
SpellAbility sa = spellAbility;
|
||||
if (si != null) {
|
||||
sa = si.getSpellAbility();
|
||||
}
|
||||
|
||||
boolean validTgtFound = false;
|
||||
while (sa != null && !validTgtFound) {
|
||||
@@ -249,13 +231,10 @@ public class TriggerSpellAbilityCastOrCopy extends Trigger {
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public final void setTriggeringObjects(final SpellAbility sa, Map<AbilityKey, Object> runParams) {
|
||||
final SpellAbility castSA = (SpellAbility) runParams.get(AbilityKey.SpellAbility);
|
||||
final SpellAbilityStackInstance si = sa.getHostCard().getGame().getStack().getInstanceMatchingSpellAbilityID(castSA);
|
||||
final SpellAbility saForTargets = si != null ? si.getSpellAbility() : castSA;
|
||||
sa.setTriggeringObject(AbilityKey.Card, castSA.getHostCard());
|
||||
sa.setTriggeringObject(AbilityKey.SpellAbility, castSA);
|
||||
sa.setTriggeringObject(AbilityKey.StackInstance, si);
|
||||
final List<TargetChoices> allTgts = saForTargets.getAllTargetChoices();
|
||||
final SpellAbility cause = (SpellAbility) runParams.get(AbilityKey.SpellAbility);
|
||||
sa.setTriggeringObject(AbilityKey.Card, cause.getHostCard());
|
||||
sa.setTriggeringObject(AbilityKey.SpellAbility, cause);
|
||||
final List<TargetChoices> allTgts = cause.getAllTargetChoices();
|
||||
if (!allTgts.isEmpty()) {
|
||||
final FCollection<GameEntity> saTargets = new FCollection<>();
|
||||
for (TargetChoices tc : allTgts) {
|
||||
@@ -263,11 +242,10 @@ public class TriggerSpellAbilityCastOrCopy extends Trigger {
|
||||
}
|
||||
sa.setTriggeringObject(AbilityKey.SpellAbilityTargets, saTargets);
|
||||
}
|
||||
sa.setTriggeringObject(AbilityKey.LifeAmount, castSA.getAmountLifePaid());
|
||||
sa.setTriggeringObject(AbilityKey.LifeAmount, cause.getAmountLifePaid());
|
||||
sa.setTriggeringObjectsFrom(
|
||||
runParams,
|
||||
AbilityKey.CardLKI,
|
||||
AbilityKey.Player,
|
||||
AbilityKey.Activator,
|
||||
AbilityKey.CurrentStormCount,
|
||||
AbilityKey.CurrentCastSpells
|
||||
|
||||
@@ -267,7 +267,7 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
|
||||
addAbilityActivatedThisTurn(sp, source);
|
||||
}
|
||||
|
||||
Map<AbilityKey, Object> runParams = AbilityKey.mapFromPlayer(source.getController());
|
||||
Map<AbilityKey, Object> runParams = AbilityKey.newMap();
|
||||
runParams.put(AbilityKey.Activator, activator);
|
||||
runParams.put(AbilityKey.SpellAbility, sp);
|
||||
game.getTriggerHandler().runTrigger(TriggerType.SpellAbilityCast, runParams, true);
|
||||
@@ -355,7 +355,7 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
|
||||
si = push(sp, si, id);
|
||||
|
||||
// Copied spells aren't cast per se so triggers shouldn't run for them.
|
||||
Map<AbilityKey, Object> runParams = AbilityKey.mapFromPlayer(sp.getHostCard().getController());
|
||||
Map<AbilityKey, Object> runParams = AbilityKey.newMap();
|
||||
|
||||
if (sp.isSpell() && !sp.isCopied()) {
|
||||
final Card lki = CardCopyService.getLKICopy(sp.getHostCard());
|
||||
@@ -394,7 +394,7 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
|
||||
}
|
||||
|
||||
runParams.put(AbilityKey.Activator, sp.getActivatingPlayer());
|
||||
runParams.put(AbilityKey.SpellAbility, si.getSpellAbility());
|
||||
runParams.put(AbilityKey.SpellAbility, sp);
|
||||
runParams.put(AbilityKey.CurrentStormCount, thisTurnCast.size());
|
||||
runParams.put(AbilityKey.CurrentCastSpells, Lists.newArrayList(thisTurnCast));
|
||||
|
||||
|
||||
@@ -118,7 +118,6 @@ public enum TrackableProperty {
|
||||
|
||||
//Card State
|
||||
Name(TrackableTypes.StringType),
|
||||
Artist(TrackableTypes.StringType),
|
||||
Colors(TrackableTypes.ColorSetType),
|
||||
OriginalColors(TrackableTypes.ColorSetType),
|
||||
LeftSplitColors(TrackableTypes.ColorSetType),
|
||||
|
||||
@@ -40,7 +40,7 @@ import com.google.common.cache.CacheLoader.InvalidCacheLoadException;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.mortennobel.imagescaling.ResampleOp;
|
||||
|
||||
import forge.card.CardEdition;
|
||||
import forge.card.CardSplitType;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardView;
|
||||
import forge.game.player.PlayerView;
|
||||
@@ -56,8 +56,8 @@ import forge.model.FModel;
|
||||
import forge.toolbox.FSkin;
|
||||
import forge.toolbox.FSkin.SkinIcon;
|
||||
import forge.toolbox.imaging.FCardImageRenderer;
|
||||
import forge.util.Aggregates;
|
||||
import forge.util.ImageUtil;
|
||||
import forge.util.TextUtil;
|
||||
|
||||
/**
|
||||
* This class stores ALL card images in a cache with soft values. this means
|
||||
@@ -171,41 +171,65 @@ public class ImageCache {
|
||||
|
||||
IPaperCard ipc = null;
|
||||
boolean altState = imageKey.endsWith(ImageKeys.BACKFACE_POSTFIX);
|
||||
boolean useArtCrop = "Crop".equals(FModel.getPreferences().getPref(ForgePreferences.FPref.UI_CARD_ART_FORMAT));
|
||||
String fileName = imageKey;
|
||||
String specColor = "";
|
||||
if (imageKey.endsWith(ImageKeys.SPECFACE_W)) {
|
||||
specColor = "white";
|
||||
} else if (imageKey.endsWith(ImageKeys.SPECFACE_U)) {
|
||||
specColor = "blue";
|
||||
} else if (imageKey.endsWith(ImageKeys.SPECFACE_B)) {
|
||||
specColor = "black";
|
||||
} else if (imageKey.endsWith(ImageKeys.SPECFACE_R)) {
|
||||
specColor = "red";
|
||||
} else if (imageKey.endsWith(ImageKeys.SPECFACE_G)) {
|
||||
specColor = "green";
|
||||
}
|
||||
if (altState)
|
||||
imageKey = imageKey.substring(0, imageKey.length() - ImageKeys.BACKFACE_POSTFIX.length());
|
||||
if (!specColor.isEmpty())
|
||||
imageKey = imageKey.substring(0, imageKey.length() - ImageKeys.SPECFACE_W.length());
|
||||
if (imageKey.startsWith(ImageKeys.CARD_PREFIX)) {
|
||||
String[] tempdata = imageKey.substring(2).split("\\|"); //We want to check the edition first.
|
||||
|
||||
String name = tempdata[0];
|
||||
String setCode = tempdata.length > 1 ? tempdata[1] : CardEdition.UNKNOWN_CODE;
|
||||
String collectorNumber = tempdata.length > 3 ? tempdata[2] : IPaperCard.NO_COLLECTOR_NUMBER;
|
||||
|
||||
CardEdition edition = StaticData.instance().getEditions().get(setCode);
|
||||
|
||||
if (useArtCrop) {
|
||||
CardEdition.EditionEntry ee;
|
||||
if (!collectorNumber.isEmpty() && !collectorNumber.equals(IPaperCard.NO_COLLECTOR_NUMBER)) {
|
||||
ee = edition.getCardFromCollectorNumber(collectorNumber);
|
||||
if (ee != null) { // TODO handle Specialize Collector number
|
||||
ee = edition.getCardFromCollectorNumber(collectorNumber.substring(0, collectorNumber.length() - 1));
|
||||
ipc = ImageUtil.getPaperCardFromImageKey(imageKey);
|
||||
if (ipc != null) {
|
||||
if (altState) {
|
||||
imageKey = ipc.getCardAltImageKey();
|
||||
} else if (!specColor.isEmpty()) {
|
||||
switch (specColor) {
|
||||
case "white":
|
||||
imageKey = ipc.getCardWSpecImageKey();
|
||||
break;
|
||||
case "blue":
|
||||
imageKey = ipc.getCardUSpecImageKey();
|
||||
break;
|
||||
case "black":
|
||||
imageKey = ipc.getCardBSpecImageKey();
|
||||
break;
|
||||
case "red":
|
||||
imageKey = ipc.getCardRSpecImageKey();
|
||||
break;
|
||||
case "green":
|
||||
imageKey = ipc.getCardGSpecImageKey();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
ee = Aggregates.random(edition.getCardInSet(name));
|
||||
}
|
||||
|
||||
// Skip fetching if artist info is not available for art crop
|
||||
if (ee != null && ee.artistName().isEmpty()) {
|
||||
useArtCrop = false;
|
||||
imageKey = ipc.getCardImageKey();
|
||||
}
|
||||
if (StringUtils.isBlank(imageKey))
|
||||
return Pair.of(_defaultImage, true);
|
||||
}
|
||||
ipc = StaticData.instance().fetchCard(name, setCode, collectorNumber);
|
||||
|
||||
fileName = ImageUtil.getImageRelativePath(name, setCode, collectorNumber, useArtCrop);
|
||||
} // TODO add artCrop for Token
|
||||
}
|
||||
|
||||
// Replace .full to .artcrop if art crop is preferred
|
||||
// Only allow use art if the artist info is available
|
||||
boolean useArtCrop = "Crop".equals(FModel.getPreferences().getPref(ForgePreferences.FPref.UI_CARD_ART_FORMAT))
|
||||
&& ipc != null && !ipc.getArtist().isEmpty();
|
||||
String originalKey = imageKey;
|
||||
if (useArtCrop) {
|
||||
if (ipc != null && ipc.getRules().getSplitType() == CardSplitType.Flip) {
|
||||
// Art crop will always use front face as image key for flip cards
|
||||
imageKey = ipc.getCardImageKey();
|
||||
}
|
||||
imageKey = TextUtil.fastReplace(imageKey, ".full", ".artcrop");
|
||||
}
|
||||
|
||||
// Load from file and add to cache if not found in cache initially.
|
||||
BufferedImage original = getImage(imageKey);
|
||||
@@ -215,11 +239,16 @@ public class ImageCache {
|
||||
}
|
||||
|
||||
// if art crop is exist, check also if the full card image is also cached.
|
||||
if (useArtCrop && original != null) {
|
||||
BufferedImage cached = _CACHE.getIfPresent(originalKey);
|
||||
if (cached != null)
|
||||
return Pair.of(cached, false);
|
||||
}
|
||||
|
||||
boolean noBorder = !useArtCrop && !isPreferenceEnabled(ForgePreferences.FPref.UI_RENDER_BLACK_BORDERS);
|
||||
boolean fetcherEnabled = isPreferenceEnabled(ForgePreferences.FPref.UI_ENABLE_ONLINE_IMAGE_FETCHER);
|
||||
boolean isPlaceholder = (original == null) && fetcherEnabled;
|
||||
String setCode = fileName.split("/")[0].trim().toUpperCase();
|
||||
String setCode = imageKey.split("/")[0].trim().toUpperCase();
|
||||
|
||||
// If the user has indicated that they prefer Forge NOT render a black border, round the image corners
|
||||
// to account for JPEG images that don't have a transparency.
|
||||
@@ -272,7 +301,7 @@ public class ImageCache {
|
||||
CardView card = ipc != null ? Card.getCardForUi(ipc).getView() : cardView;
|
||||
String legalString = null;
|
||||
original = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
|
||||
if (art != null && ipc != null) {
|
||||
if (art != null) {
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(StaticData.instance().getCardEdition(ipc.getEdition()).getDate());
|
||||
int year = cal.get(Calendar.YEAR);
|
||||
|
||||
@@ -18,8 +18,7 @@ final class ImageLoader extends CacheLoader<String, BufferedImage> {
|
||||
if (FModel.getPreferences().getPrefBoolean(ForgePreferences.FPref.UI_DISABLE_CARD_IMAGES))
|
||||
return null;
|
||||
|
||||
boolean useArtCrop = "Crop".equals(FModel.getPreferences().getPref(ForgePreferences.FPref.UI_CARD_ART_FORMAT));
|
||||
File file = ImageKeys.getImageFile(key, useArtCrop);
|
||||
File file = ImageKeys.getImageFile(key);
|
||||
if (file != null) {
|
||||
if (!file.exists()) {
|
||||
return null;
|
||||
|
||||
@@ -77,11 +77,41 @@ public final class FImageUtil {
|
||||
}
|
||||
|
||||
boolean altState = key.endsWith(ImageKeys.BACKFACE_POSTFIX);
|
||||
String specColor = "";
|
||||
if (key.endsWith(ImageKeys.SPECFACE_W)) {
|
||||
specColor = "white";
|
||||
} else if (key.endsWith(ImageKeys.SPECFACE_U)) {
|
||||
specColor = "blue";
|
||||
} else if (key.endsWith(ImageKeys.SPECFACE_B)) {
|
||||
specColor = "black";
|
||||
} else if (key.endsWith(ImageKeys.SPECFACE_R)) {
|
||||
specColor = "red";
|
||||
} else if (key.endsWith(ImageKeys.SPECFACE_G)) {
|
||||
specColor = "green";
|
||||
}
|
||||
String imageKey = key;
|
||||
if (prefix.equals(ImageKeys.CARD_PREFIX)) {
|
||||
PaperCard card = ImageUtil.getPaperCardFromImageKey(key);
|
||||
if (altState) {
|
||||
imageKey = card.getCardAltImageKey();
|
||||
} else if (!specColor.isEmpty()) {
|
||||
switch (specColor) {
|
||||
case "white":
|
||||
imageKey = card.getCardWSpecImageKey();
|
||||
break;
|
||||
case "blue":
|
||||
imageKey = card.getCardUSpecImageKey();
|
||||
break;
|
||||
case "black":
|
||||
imageKey = card.getCardBSpecImageKey();
|
||||
break;
|
||||
case "red":
|
||||
imageKey = card.getCardRSpecImageKey();
|
||||
break;
|
||||
case "green":
|
||||
imageKey = card.getCardGSpecImageKey();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
imageKey = card.getCardImageKey();
|
||||
}
|
||||
@@ -89,6 +119,9 @@ public final class FImageUtil {
|
||||
if(altState) {
|
||||
imageKey = imageKey.substring(0, imageKey.length() - ImageKeys.BACKFACE_POSTFIX.length());
|
||||
imageKey += "full.jpg";
|
||||
} else if (!specColor.isEmpty()) {
|
||||
imageKey = imageKey.substring(0, imageKey.length() - ImageKeys.SPECFACE_W.length());
|
||||
imageKey += "full.jpg";
|
||||
}
|
||||
|
||||
File file = ImageKeys.getImageFile(imageKey);
|
||||
|
||||
@@ -34,14 +34,10 @@ public class SwingImageFetcher extends ImageFetcher {
|
||||
return false;
|
||||
}
|
||||
|
||||
String newdespath = destPath;
|
||||
if (!destPath.contains(".artcrop")) {
|
||||
newdespath = urlToDownload.contains(".fullborder.jpg") || urlToDownload.startsWith(ForgeConstants.URL_PIC_SCRYFALL_DOWNLOAD) ?
|
||||
TextUtil.fastReplace(destPath, ".full.jpg", ".fullborder.jpg") : destPath;
|
||||
if (!newdespath.contains(".full") && urlToDownload.startsWith(ForgeConstants.URL_PIC_SCRYFALL_DOWNLOAD) && !destPath.startsWith(ForgeConstants.CACHE_TOKEN_PICS_DIR))
|
||||
newdespath = newdespath.replace(".jpg", ".fullborder.jpg"); //fix planes/phenomenon for round border options
|
||||
}
|
||||
|
||||
String newdespath = urlToDownload.contains(".fullborder.jpg") || urlToDownload.startsWith(ForgeConstants.URL_PIC_SCRYFALL_DOWNLOAD) ?
|
||||
TextUtil.fastReplace(destPath, ".full.jpg", ".fullborder.jpg") : destPath;
|
||||
if (!newdespath.contains(".full") && urlToDownload.startsWith(ForgeConstants.URL_PIC_SCRYFALL_DOWNLOAD) && !destPath.startsWith(ForgeConstants.CACHE_TOKEN_PICS_DIR))
|
||||
newdespath = newdespath.replace(".jpg", ".fullborder.jpg"); //fix planes/phenomenon for round border options
|
||||
URL url = new URL(urlToDownload);
|
||||
System.out.println("Attempting to fetch: " + url);
|
||||
BufferedImage image = ImageIO.read(url);
|
||||
|
||||
@@ -274,6 +274,14 @@ public class Forge implements ApplicationListener {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean hasExternalInput() {
|
||||
return hasGamepad() || hasKeyboard();
|
||||
}
|
||||
|
||||
public static boolean hasKeyboard() {
|
||||
return !GuiBase.isAndroid();
|
||||
}
|
||||
|
||||
public static InputProcessor getInputProcessor() {
|
||||
return inputProcessor;
|
||||
}
|
||||
@@ -1235,7 +1243,15 @@ public class Forge implements ApplicationListener {
|
||||
if (keyInputAdapter != null) {
|
||||
return keyInputAdapter.keyUp(keyCode);
|
||||
}
|
||||
return false;
|
||||
// if no active key input adapter, give current screen or overlay a chance to handle key
|
||||
FContainer container = FOverlay.getTopOverlay();
|
||||
if (container == null) {
|
||||
container = currentScreen;
|
||||
if (container == null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return container.keyUp(keyCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -448,7 +448,7 @@ public class EnemySprite extends CharacterSprite implements Steerable<Vector2> {
|
||||
if (data.copyPlayerDeck && Current.latestDeck() != null) {
|
||||
List<PaperCard> paperCardList = Current.latestDeck().getMain().toFlatList().stream()
|
||||
.filter(paperCard -> !paperCard.isVeryBasicLand())
|
||||
.toList();
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (paperCardList.size() < 6) {
|
||||
// Player trying to cheese doppleganger and farm cards. Sorry, the fun police have arrived
|
||||
|
||||
@@ -35,7 +35,9 @@ import java.util.*;
|
||||
* Class that represents the player (not the player sprite)
|
||||
*/
|
||||
public class AdventurePlayer implements Serializable, SaveFileContent {
|
||||
public static final int NUMBER_OF_DECKS = 10;
|
||||
public static final int MIN_DECK_COUNT = 10;
|
||||
// this is a purely arbitrary limit, could be higher or lower; just meant as some sort of reasonable limit for the user
|
||||
public static final int MAX_DECK_COUNT = 20;
|
||||
// Player profile data.
|
||||
private String name;
|
||||
private int heroRace;
|
||||
@@ -45,7 +47,7 @@ public class AdventurePlayer implements Serializable, SaveFileContent {
|
||||
|
||||
// Deck data
|
||||
private Deck deck;
|
||||
private final Deck[] decks = new Deck[NUMBER_OF_DECKS];
|
||||
private final ArrayList<Deck> decks = new ArrayList<Deck>(MIN_DECK_COUNT);
|
||||
private int selectedDeckIndex = 0;
|
||||
private final DifficultyData difficultyData = new DifficultyData();
|
||||
|
||||
@@ -91,9 +93,13 @@ public class AdventurePlayer implements Serializable, SaveFileContent {
|
||||
return statistic;
|
||||
}
|
||||
|
||||
public int getDeckCount() { return decks.size(); }
|
||||
|
||||
private void clearDecks() {
|
||||
for (int i = 0; i < NUMBER_OF_DECKS; i++) decks[i] = new Deck(Forge.getLocalizer().getMessage("lblEmptyDeck"));
|
||||
deck = decks[0];
|
||||
decks.clear();
|
||||
for (int i = 0; i < MIN_DECK_COUNT; i++)
|
||||
decks.add(new Deck(Forge.getLocalizer().getMessage("lblEmptyDeck")));
|
||||
deck = decks.get(0);
|
||||
selectedDeckIndex = 0;
|
||||
}
|
||||
|
||||
@@ -140,7 +146,7 @@ public class AdventurePlayer implements Serializable, SaveFileContent {
|
||||
announceCustom = usingCustomDeck = isUsingCustomDeck;
|
||||
|
||||
deck = startingDeck;
|
||||
decks[0] = deck;
|
||||
decks.set(0, deck);
|
||||
|
||||
cards.addAllFlat(deck.getAllCardsInASinglePool().toFlatList());
|
||||
|
||||
@@ -173,9 +179,9 @@ public class AdventurePlayer implements Serializable, SaveFileContent {
|
||||
}
|
||||
|
||||
public void setSelectedDeckSlot(int slot) {
|
||||
if (slot >= 0 && slot < NUMBER_OF_DECKS) {
|
||||
if (slot >= 0 && slot < getDeckCount()) {
|
||||
selectedDeckIndex = slot;
|
||||
deck = decks[selectedDeckIndex];
|
||||
deck = decks.get(selectedDeckIndex);
|
||||
setColorIdentity(DeckProxy.getColorIdentity(deck));
|
||||
}
|
||||
}
|
||||
@@ -214,7 +220,7 @@ public class AdventurePlayer implements Serializable, SaveFileContent {
|
||||
}
|
||||
|
||||
public Deck getDeck(int index) {
|
||||
return decks[index];
|
||||
return decks.get(index);
|
||||
}
|
||||
|
||||
public CardPool getCards() {
|
||||
@@ -448,17 +454,44 @@ public class AdventurePlayer implements Serializable, SaveFileContent {
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < NUMBER_OF_DECKS; i++) {
|
||||
if (!data.containsKey("deck_name_" + i)) {
|
||||
if (i == 0) decks[i] = deck;
|
||||
else decks[i] = new Deck(Forge.getLocalizer().getMessage("lblEmptyDeck"));
|
||||
continue;
|
||||
// load decks
|
||||
// check if this save has dynamic deck count, use set-count load if not
|
||||
boolean hasDynamicDeckCount = data.containsKey("deckCount");
|
||||
if (hasDynamicDeckCount) {
|
||||
int dynamicDeckCount = data.readInt("deckCount");
|
||||
// in case the save had previously saved more decks than the current version allows (in case of the max being lowered)
|
||||
dynamicDeckCount = Math.min(MAX_DECK_COUNT, dynamicDeckCount);
|
||||
for (int i = 0; i < dynamicDeckCount; i++){
|
||||
// the first x elements are pre-created
|
||||
if (i < MIN_DECK_COUNT) {
|
||||
decks.set(i, new Deck(data.readString("deck_name_" + i)));
|
||||
}
|
||||
else {
|
||||
decks.add(new Deck(data.readString("deck_name_" + i)));
|
||||
}
|
||||
decks.get(i).getMain().addAll(CardPool.fromCardList(Lists.newArrayList((String[]) data.readObject("deck_" + i))));
|
||||
if (data.containsKey("sideBoardCards_" + i))
|
||||
decks.get(i).getOrCreate(DeckSection.Sideboard).addAll(CardPool.fromCardList(Lists.newArrayList((String[]) data.readObject("sideBoardCards_" + i))));
|
||||
}
|
||||
// in case we allow removing decks from the deck selection GUI, populate up to the minimum
|
||||
for (int i = dynamicDeckCount++; i < MIN_DECK_COUNT; i++) {
|
||||
decks.set(i, new Deck(Forge.getLocalizer().getMessage("lblEmptyDeck")));
|
||||
}
|
||||
// legacy load
|
||||
} else {
|
||||
for (int i = 0; i < MIN_DECK_COUNT; i++) {
|
||||
if (!data.containsKey("deck_name_" + i)) {
|
||||
if (i == 0) decks.set(i, deck);
|
||||
else decks.set(i, new Deck(Forge.getLocalizer().getMessage("lblEmptyDeck")));
|
||||
continue;
|
||||
}
|
||||
decks.set(i, new Deck(data.readString("deck_name_" + i)));
|
||||
decks.get(i).getMain().addAll(CardPool.fromCardList(Lists.newArrayList((String[]) data.readObject("deck_" + i))));
|
||||
if (data.containsKey("sideBoardCards_" + i))
|
||||
decks.get(i).getOrCreate(DeckSection.Sideboard).addAll(CardPool.fromCardList(Lists.newArrayList((String[]) data.readObject("sideBoardCards_" + i))));
|
||||
}
|
||||
decks[i] = new Deck(data.readString("deck_name_" + i));
|
||||
decks[i].getMain().addAll(CardPool.fromCardList(Lists.newArrayList((String[]) data.readObject("deck_" + i))));
|
||||
if (data.containsKey("sideBoardCards_" + i))
|
||||
decks[i].getOrCreate(DeckSection.Sideboard).addAll(CardPool.fromCardList(Lists.newArrayList((String[]) data.readObject("sideBoardCards_" + i))));
|
||||
}
|
||||
|
||||
setSelectedDeckSlot(data.readInt("selectedDeckIndex"));
|
||||
cards.addAll(CardPool.fromCardList(Lists.newArrayList((String[]) data.readObject("cards"))));
|
||||
|
||||
@@ -602,11 +635,14 @@ public class AdventurePlayer implements Serializable, SaveFileContent {
|
||||
data.storeObject("deckCards", deck.getMain().toCardList("\n").split("\n"));
|
||||
if (deck.get(DeckSection.Sideboard) != null)
|
||||
data.storeObject("sideBoardCards", deck.get(DeckSection.Sideboard).toCardList("\n").split("\n"));
|
||||
for (int i = 0; i < NUMBER_OF_DECKS; i++) {
|
||||
data.store("deck_name_" + i, decks[i].getName());
|
||||
data.storeObject("deck_" + i, decks[i].getMain().toCardList("\n").split("\n"));
|
||||
if (decks[i].get(DeckSection.Sideboard) != null)
|
||||
data.storeObject("sideBoardCards_" + i, decks[i].get(DeckSection.Sideboard).toCardList("\n").split("\n"));
|
||||
|
||||
// save decks dynamically
|
||||
data.store("deckCount", getDeckCount());
|
||||
for (int i = 0; i < getDeckCount(); i++) {
|
||||
data.store("deck_name_" + i, decks.get(i).getName());
|
||||
data.storeObject("deck_" + i, decks.get(i).getMain().toCardList("\n").split("\n"));
|
||||
if (decks.get(i).get(DeckSection.Sideboard) != null)
|
||||
data.storeObject("sideBoardCards_" + i, decks.get(i).get(DeckSection.Sideboard).toCardList("\n").split("\n"));
|
||||
}
|
||||
data.store("selectedDeckIndex", selectedDeckIndex);
|
||||
data.storeObject("cards", cards.toCardList("\n").split("\n"));
|
||||
@@ -933,7 +969,7 @@ public class AdventurePlayer implements Serializable, SaveFileContent {
|
||||
|
||||
public void renameDeck(String text) {
|
||||
deck = (Deck) deck.copyTo(text);
|
||||
decks[selectedDeckIndex] = deck;
|
||||
decks.set(selectedDeckIndex, deck);
|
||||
}
|
||||
|
||||
public int cardSellPrice(PaperCard card) {
|
||||
@@ -1182,10 +1218,23 @@ public class AdventurePlayer implements Serializable, SaveFileContent {
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a deck by replacing the current selected deck with a new deck
|
||||
* Clears a deck by replacing the current selected deck with a new deck
|
||||
*/
|
||||
public void deleteDeck() {
|
||||
deck = decks[selectedDeckIndex] = new Deck(Forge.getLocalizer().getMessage("lblEmptyDeck"));
|
||||
public void clearDeck() {
|
||||
deck = decks.set(selectedDeckIndex, new Deck(Forge.getLocalizer().getMessage("lblEmptyDeck")));
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually removes the deck from the list of decks.
|
||||
*/
|
||||
public void deleteDeck(){
|
||||
int oldIndex = selectedDeckIndex;
|
||||
this.setSelectedDeckSlot(0);
|
||||
decks.remove(oldIndex);
|
||||
}
|
||||
|
||||
public void addDeck(){
|
||||
decks.add(new Deck(Forge.getLocalizer().getMessage("lblEmptyDeck")));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1194,9 +1243,9 @@ public class AdventurePlayer implements Serializable, SaveFileContent {
|
||||
* @return int - index of new copy slot, or -1 if no slot was available
|
||||
*/
|
||||
public int copyDeck() {
|
||||
for (int i = 0; i < decks.length; i++) {
|
||||
for (int i = 0; i < MAX_DECK_COUNT; i++) {
|
||||
if (isEmptyDeck(i)) {
|
||||
decks[i] = (Deck) deck.copyTo(deck.getName() + " (" + Forge.getLocalizer().getMessage("lblCopy") + ")");
|
||||
decks.set(i, (Deck) deck.copyTo(deck.getName() + " (" + Forge.getLocalizer().getMessage("lblCopy") + ")"));
|
||||
return i;
|
||||
}
|
||||
}
|
||||
@@ -1205,7 +1254,7 @@ public class AdventurePlayer implements Serializable, SaveFileContent {
|
||||
}
|
||||
|
||||
public boolean isEmptyDeck(int deckIndex) {
|
||||
return decks[deckIndex].isEmpty() && decks[deckIndex].getName().equals(Forge.getLocalizer().getMessage("lblEmptyDeck"));
|
||||
return decks.get(deckIndex).isEmpty() && decks.get(deckIndex).getName().equals(Forge.getLocalizer().getMessage("lblEmptyDeck"));
|
||||
}
|
||||
|
||||
public void removeEvent(AdventureEventData completedEvent) {
|
||||
@@ -1227,8 +1276,8 @@ public class AdventurePlayer implements Serializable, SaveFileContent {
|
||||
|
||||
// 2. Count max cards across all decks in excess of unsellable
|
||||
Map<PaperCard, Integer> maxCardCounts = new HashMap<>();
|
||||
for (int i = 0; i < NUMBER_OF_DECKS; i++) {
|
||||
for (final Map.Entry<PaperCard, Integer> cp : decks[i].getAllCardsInASinglePool()) {
|
||||
for (int i = 0; i < getDeckCount(); i++) {
|
||||
for (final Map.Entry<PaperCard, Integer> cp : decks.get(i).getAllCardsInASinglePool()) {
|
||||
int count = cp.getValue();
|
||||
if (count > maxCardCounts.getOrDefault(cp.getKey(), 0)) {
|
||||
maxCardCounts.put(cp.getKey(), cp.getValue());
|
||||
|
||||
@@ -20,6 +20,7 @@ public class PointOfInterestChanges implements SaveFileContent {
|
||||
//private final java.util.Map<Integer, Float> shopModifiers = new HashMap<>();
|
||||
private final java.util.Map<Integer, Integer> reputation = new HashMap<>();
|
||||
private Boolean isBookmarked;
|
||||
private Boolean isVisited;
|
||||
|
||||
public static class Map extends HashMap<String,PointOfInterestChanges> implements SaveFileContent {
|
||||
@Override
|
||||
@@ -67,6 +68,7 @@ public class PointOfInterestChanges implements SaveFileContent {
|
||||
reputation.putAll((java.util.Map<Integer, Integer>) data.readObject("reputation"));
|
||||
}
|
||||
isBookmarked = (Boolean) data.readObject("isBookmarked");
|
||||
isVisited = (Boolean) data.readObject("isVisited");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -78,6 +80,7 @@ public class PointOfInterestChanges implements SaveFileContent {
|
||||
data.storeObject("shopSeeds", shopSeeds);
|
||||
data.storeObject("reputation", reputation);
|
||||
data.storeObject("isBookmarked", isBookmarked);
|
||||
data.storeObject("isVisited", isVisited);
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -177,4 +180,12 @@ public class PointOfInterestChanges implements SaveFileContent {
|
||||
// reset map when assigning as a quest target that needs enemies
|
||||
deletedObjects.clear();
|
||||
}
|
||||
public boolean isVisited() {
|
||||
if (isVisited ==null)
|
||||
return false;
|
||||
return isVisited;
|
||||
}
|
||||
public void visit() {
|
||||
isVisited = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -389,7 +389,7 @@ public class AdventureDeckEditor extends TabPageScreen<AdventureDeckEditor> {
|
||||
@Override
|
||||
public void onActivate() {
|
||||
decksUsingMyCards = new ItemPool<>(InventoryItem.class);
|
||||
for (int i = 0; i < AdventurePlayer.NUMBER_OF_DECKS; i++) {
|
||||
for (int i = 0; i < AdventurePlayer.current().getDeckCount(); i++) {
|
||||
final Deck deck = AdventurePlayer.current().getDeck(i);
|
||||
CardPool main = deck.getMain();
|
||||
for (final Map.Entry<PaperCard, Integer> e : main) {
|
||||
@@ -577,6 +577,9 @@ public class AdventureDeckEditor extends TabPageScreen<AdventureDeckEditor> {
|
||||
@Override
|
||||
protected void buildMenu() {
|
||||
addItem(new FMenuItem(Forge.getLocalizer().getMessage("btnCopyToClipboard"), Forge.hdbuttons ? FSkinImage.HDEXPORT : FSkinImage.BLANK, e1 -> FDeckViewer.copyDeckToClipboard(getDeck())));
|
||||
addItem(new FMenuItem(Forge.getLocalizer().getMessage("btnCopyCollectionToClipboard"), Forge.hdbuttons ? FSkinImage.HDEXPORT : FSkinImage.BLANK, e1 -> {
|
||||
FDeckViewer.copyCollectionToClipboard(AdventurePlayer.current().getCards());
|
||||
}));
|
||||
if (allowsAddBasic()) {
|
||||
FMenuItem addBasic = new FMenuItem(Forge.getLocalizer().getMessage("lblAddBasicLands"), FSkinImage.LANDLOGO, e1 -> launchBasicLandDialog());
|
||||
addItem(addBasic);
|
||||
@@ -649,6 +652,8 @@ public class AdventureDeckEditor extends TabPageScreen<AdventureDeckEditor> {
|
||||
Map<String, CardEdition> editionsByName = new HashMap<>();
|
||||
for (CardEdition e : FModel.getMagicDb().getEditions()) {
|
||||
editionsByName.put(e.getName().toLowerCase(), e);
|
||||
editionsByName.put(e.getName().replace(":", "").toLowerCase(), e);
|
||||
editionsByName.put(e.getName().replace("'", "").toLowerCase(), e);
|
||||
}
|
||||
|
||||
String sketchbookPrefix = "landscape sketchbook - ";
|
||||
@@ -1611,5 +1616,13 @@ public class AdventureDeckEditor extends TabPageScreen<AdventureDeckEditor> {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean keyUp(int keyCode) {
|
||||
if (keyCode == Input.Keys.ESCAPE) {
|
||||
return this.tabHeader.btnBack.trigger();
|
||||
}
|
||||
return super.keyUp(keyCode);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,11 +16,12 @@ import forge.adventure.util.Current;
|
||||
|
||||
public class DeckSelectScene extends UIScene {
|
||||
private final IntMap<TextraButton> buttons = new IntMap<>();
|
||||
private final IntMap<Label> labels = new IntMap<>();
|
||||
Color defColor;
|
||||
TextField textInput;
|
||||
Table layout;
|
||||
TextraLabel header;
|
||||
TextraButton back, edit, rename;
|
||||
TextraButton back, edit, rename, add;
|
||||
int currentSlot = 0;
|
||||
ScrollPane scrollPane;
|
||||
Dialog renameDialog;
|
||||
@@ -45,13 +46,13 @@ public class DeckSelectScene extends UIScene {
|
||||
root.add(header).colspan(2);
|
||||
root.row();
|
||||
root.add(scrollPane).expand().width(window.getWidth() - 20);
|
||||
for (int i = 0; i < AdventurePlayer.NUMBER_OF_DECKS; i++)
|
||||
addDeckSlot(Forge.getLocalizer().getMessage("lblDeck") + ": " + (i + 1), i);
|
||||
this.layoutDeckButtons();
|
||||
|
||||
textInput = Controls.newTextField("");
|
||||
back = ui.findActor("return");
|
||||
edit = ui.findActor("edit");
|
||||
rename = ui.findActor("rename");
|
||||
add = ui.findActor("add");
|
||||
ui.onButtonPress("return", DeckSelectScene.this::back);
|
||||
ui.onButtonPress("edit", DeckSelectScene.this::edit);
|
||||
ui.onButtonPress("rename", () -> {
|
||||
@@ -59,11 +60,44 @@ public class DeckSelectScene extends UIScene {
|
||||
showRenameDialog();
|
||||
});
|
||||
ui.onButtonPress("copy", DeckSelectScene.this::copy);
|
||||
ui.onButtonPress("delete", DeckSelectScene.this::maybeDelete);
|
||||
ui.onButtonPress("delete", DeckSelectScene.this::promptDelete);
|
||||
ui.onButtonPress("add", DeckSelectScene.this::addDeck);
|
||||
defColor = ui.findActor("return").getColor();
|
||||
window.add(root);
|
||||
}
|
||||
|
||||
private void refreshDeckButtons(){
|
||||
clearDeckButtons();
|
||||
layoutDeckButtons();
|
||||
}
|
||||
|
||||
private void clearDeckButtons(){
|
||||
int count = AdventurePlayer.current().getDeckCount();
|
||||
for (int i = count; i >= 0; i--){
|
||||
clearDeckButton(i);
|
||||
}
|
||||
layout.clearChildren();
|
||||
buttons.clear();
|
||||
labels.clear();
|
||||
}
|
||||
|
||||
private void layoutDeckButtons() {
|
||||
for (int i = 0; i < AdventurePlayer.current().getDeckCount(); i++)
|
||||
addDeckButton(Forge.getLocalizer().getMessage("lblDeck") + ": " + (i + 1), i);
|
||||
}
|
||||
|
||||
private void addDeck(){
|
||||
if (Current.player().getDeckCount() >= AdventurePlayer.MAX_DECK_COUNT){
|
||||
showDialog(createGenericDialog(Forge.getLocalizer().getMessage("lblAddDeck"), Forge.getLocalizer().getMessage("lblMaxDeckCountReached"),
|
||||
Forge.getLocalizer().getMessage("lblOK"), null, this::removeDialog, null));
|
||||
return;
|
||||
}
|
||||
|
||||
Current.player().addDeck();
|
||||
refreshDeckButtons();
|
||||
select(Current.player().getSelectedDeckIndex());
|
||||
}
|
||||
|
||||
private void copy() {
|
||||
if (Current.player().isEmptyDeck(currentSlot)) return;
|
||||
int index = Current.player().copyDeck();
|
||||
@@ -79,17 +113,32 @@ public class DeckSelectScene extends UIScene {
|
||||
}
|
||||
}
|
||||
|
||||
private void maybeDelete() {
|
||||
if (Current.player().isEmptyDeck(currentSlot)) return;
|
||||
private void promptDelete() {
|
||||
Dialog deleteDialog = createGenericDialog(Forge.getLocalizer().getMessage("lblDelete"), Forge.getLocalizer().getMessage("lblAreYouSureProceedDelete"),
|
||||
Forge.getLocalizer().getMessage("lblOK"),
|
||||
Forge.getLocalizer().getMessage("lblAbort"), this::delete, this::removeDialog);
|
||||
Forge.getLocalizer().getMessage("lblAbort"), this::clearOrDelete, this::removeDialog);
|
||||
|
||||
showDialog(deleteDialog);
|
||||
}
|
||||
|
||||
private void delete() {
|
||||
Current.player().deleteDeck();
|
||||
/**
|
||||
* Clears or deletes the currently selected deck.
|
||||
*/
|
||||
private void clearOrDelete(){
|
||||
if (currentSlot >= AdventurePlayer.MIN_DECK_COUNT){
|
||||
Current.player().deleteDeck();
|
||||
}
|
||||
else {
|
||||
Current.player().clearDeck();
|
||||
}
|
||||
|
||||
refreshDeckButtons();
|
||||
select(0);
|
||||
removeDialog();
|
||||
}
|
||||
|
||||
private void clear() {
|
||||
Current.player().clearDeck();
|
||||
updateDeckButton(currentSlot);
|
||||
removeDialog();
|
||||
}
|
||||
@@ -117,7 +166,18 @@ public class DeckSelectScene extends UIScene {
|
||||
showDialog(renameDialog);
|
||||
}
|
||||
|
||||
private TextraButton addDeckSlot(String name, int i) {
|
||||
/**
|
||||
* Not sure if this is strictly necessary in Java but wouldn't want to leak before clearing the layout table.
|
||||
*/
|
||||
private void clearDeckButton(int i){
|
||||
if (buttons.containsKey(i)) {
|
||||
TextraButton button = buttons.remove(i);
|
||||
button.clearListeners();
|
||||
button.clearActions();
|
||||
}
|
||||
}
|
||||
|
||||
private TextraButton addDeckButton(String name, int i) {
|
||||
TextraButton button = Controls.newTextButton("-");
|
||||
button.addListener(new ClickListener() {
|
||||
@Override
|
||||
@@ -131,9 +191,12 @@ public class DeckSelectScene extends UIScene {
|
||||
}
|
||||
});
|
||||
|
||||
button.setText(Current.player().getDeck(i).getName());
|
||||
Label label = Controls.newLabel(name);
|
||||
layout.add(Controls.newLabel(name)).pad(2);
|
||||
layout.add(button).fill(true, false).expand(true, false).align(Align.left).expandX().pad(2);
|
||||
buttons.put(i, button);
|
||||
labels.put(i, label);
|
||||
addToSelectable(new Selectable(button));
|
||||
layout.row();
|
||||
return button;
|
||||
@@ -158,13 +221,7 @@ public class DeckSelectScene extends UIScene {
|
||||
|
||||
@Override
|
||||
public void enter() {
|
||||
for (int i = 0; i < AdventurePlayer.NUMBER_OF_DECKS; i++) {
|
||||
if (buttons.containsKey(i)) {
|
||||
buttons.get(i).setText(Current.player().getDeck(i).getName());
|
||||
buttons.get(i).getTextraLabel().layout();
|
||||
buttons.get(i).layout();
|
||||
}
|
||||
}
|
||||
refreshDeckButtons();
|
||||
GameHUD.getInstance().switchAudio();
|
||||
select(Current.player().getSelectedDeckIndex());
|
||||
performTouch(scrollPane); //can use mouse wheel if available to scroll after selection
|
||||
@@ -175,9 +232,7 @@ public class DeckSelectScene extends UIScene {
|
||||
private void rename() {
|
||||
String text = textInput.getText();
|
||||
Current.player().renameDeck(text);
|
||||
buttons.get(currentSlot).setText(Current.player().getDeck(currentSlot).getName());
|
||||
buttons.get(currentSlot).getTextraLabel().layout();
|
||||
buttons.get(currentSlot).layout();
|
||||
updateDeckButton(currentSlot);
|
||||
}
|
||||
|
||||
private void edit() {
|
||||
|
||||
@@ -511,13 +511,11 @@ public class EventScene extends MenuScene implements IAfterMatch {
|
||||
|
||||
if (match.p1 instanceof AdventureEventData.AdventureEventHuman) {
|
||||
humanMatch = match;
|
||||
continue;
|
||||
} else if (match.p2 instanceof AdventureEventData.AdventureEventHuman) {
|
||||
AdventureEventData.AdventureEventParticipant placeholder = match.p1;
|
||||
match.p1 = match.p2;
|
||||
match.p2 = placeholder;
|
||||
humanMatch = match;
|
||||
continue;
|
||||
} else {
|
||||
//Todo: Actually run match simulation here
|
||||
if (MyRandom.percentTrue(50)) {
|
||||
@@ -530,7 +528,6 @@ public class EventScene extends MenuScene implements IAfterMatch {
|
||||
match.winner = match.p2;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (humanMatch != null && humanMatch.round != currentEvent.currentRound)
|
||||
@@ -539,14 +536,16 @@ public class EventScene extends MenuScene implements IAfterMatch {
|
||||
DuelScene duelScene = DuelScene.instance();
|
||||
EnemySprite enemy = humanMatch.p2.getSprite();
|
||||
currentEvent.nextOpponent = humanMatch.p2;
|
||||
advance.setDisabled(true);
|
||||
FThreads.invokeInEdtNowOrLater(() -> Forge.setTransitionScreen(new TransitionScreen(() -> {
|
||||
duelScene.initDuels(WorldStage.getInstance().getPlayerSprite(), enemy, false, currentEvent);
|
||||
advance.setDisabled(false);
|
||||
Forge.switchScene(duelScene);
|
||||
}, Forge.takeScreenshot(), true, false, false, false, "", Current.player().avatar(), enemy.getAtlasPath(), Current.player().getName(), enemy.getName(), humanMatch.p1.getRecord(), humanMatch.p2.getRecord())));
|
||||
} else {
|
||||
finishRound();
|
||||
advance.setDisabled(false);
|
||||
}
|
||||
advance.setDisabled(false);
|
||||
}
|
||||
|
||||
AdventureEventData.AdventureEventMatch humanMatch = null;
|
||||
|
||||
@@ -4,15 +4,20 @@ import com.badlogic.gdx.graphics.Texture;
|
||||
import com.badlogic.gdx.math.Vector2;
|
||||
import com.badlogic.gdx.scenes.scene2d.Actor;
|
||||
import com.badlogic.gdx.scenes.scene2d.Group;
|
||||
import com.badlogic.gdx.scenes.scene2d.InputEvent;
|
||||
import com.badlogic.gdx.scenes.scene2d.InputListener;
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Image;
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane;
|
||||
import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable;
|
||||
import com.badlogic.gdx.utils.SnapshotArray;
|
||||
import com.github.tommyettinger.textra.TextraButton;
|
||||
import com.github.tommyettinger.textra.TypingLabel;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
import forge.Forge;
|
||||
import forge.adventure.data.AdventureEventData;
|
||||
import forge.adventure.data.AdventureQuestData;
|
||||
import forge.adventure.player.AdventurePlayer;
|
||||
import forge.adventure.pointofintrest.PointOfInterest;
|
||||
import forge.adventure.stage.GameHUD;
|
||||
import forge.adventure.stage.WorldStage;
|
||||
@@ -37,6 +42,9 @@ public class MapViewScene extends UIScene {
|
||||
private int index = -1;
|
||||
private float avatarX = 0, avatarY = 0;
|
||||
private Set<Vector2> positions;
|
||||
private final List<TypingLabel> details;
|
||||
private final float maxZoom = 1.2f;
|
||||
private final float minZoom = 0.25f;
|
||||
private Set<PointOfInterest> bookmark;
|
||||
|
||||
public static MapViewScene instance() {
|
||||
@@ -49,7 +57,25 @@ public class MapViewScene extends UIScene {
|
||||
super(Forge.isLandscapeMode() ? "ui/map.json" : "ui/map_portrait.json");
|
||||
ui.onButtonPress("done", this::done);
|
||||
ui.onButtonPress("quest", this::scroll);
|
||||
scroll = ui.findActor("map");
|
||||
//TODO:Add Translations for buttons
|
||||
ui.onButtonPress("details", this::details);
|
||||
ui.onButtonPress("events", this::events);
|
||||
ui.onButtonPress("reputation", this::reputation);
|
||||
ui.onButtonPress("names", this::names);
|
||||
ui.onButtonPress("zoomIn", this::zoomIn);
|
||||
ui.onButtonPress("zoomOut", this::zoomOut);
|
||||
scroll = new ScrollPane(null,Controls.getSkin()) {
|
||||
@Override
|
||||
public void addScrollListener() {
|
||||
return;
|
||||
}
|
||||
};
|
||||
scroll.setName("map");
|
||||
scroll.setActor(Controls.newTextraLabel(""));
|
||||
scroll.setWidth(ui.findActor("map").getWidth());
|
||||
scroll.setHeight(ui.findActor("map").getHeight());
|
||||
ui.addActor(scroll);
|
||||
scroll.setZIndex(1);
|
||||
labels = Lists.newArrayList();
|
||||
positions = Sets.newHashSet();
|
||||
bookmark = Sets.newHashSet();
|
||||
@@ -60,16 +86,53 @@ public class MapViewScene extends UIScene {
|
||||
img.setPosition(0, 0);
|
||||
table.addActor(img);
|
||||
table.addActor(miniMapPlayer);
|
||||
miniMapPlayer.setZIndex(2);
|
||||
details = Lists.newArrayList();
|
||||
ui.addListener(new InputListener() {
|
||||
public boolean scrolled(InputEvent event, float x, float y, float scrollAmountX, float scrollAmountY) {
|
||||
event.cancel();
|
||||
scroll.setScrollbarsVisible(true);
|
||||
if (scrollAmountY > 0) {
|
||||
zoomOut();
|
||||
return true;
|
||||
} else if (scrollAmountY < 0) {
|
||||
zoomIn();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
stage.setScrollFocus(ui);
|
||||
|
||||
}
|
||||
|
||||
public void test() {
|
||||
img.setPosition((scroll.getScrollPercentX()*2334 +233)*0.1f + 0.9f*img.getX(),(2544-scroll.getScrollPercentY()*2544 +128)*0.1f + 0.9f*img.getY());
|
||||
img.setScale(img.getScaleX()*0.9f);
|
||||
miniMapPlayer.setPosition((scroll.getScrollPercentX()*2334 +233)*0.1f + 0.9f*miniMapPlayer.getX(),(2544-scroll.getScrollPercentY()*2544 +128)*0.1f + 0.9f*miniMapPlayer.getY());
|
||||
miniMapPlayer.setScale(miniMapPlayer.getScaleX()*0.9f);
|
||||
for(Actor actor : table.getChildren()) {
|
||||
if (actor instanceof TypingLabel) {
|
||||
actor.setPosition((scroll.getScrollPercentX() * 2334 + 233) * 0.1f + 0.9f * actor.getX(), (2544 - scroll.getScrollPercentY() * 2544 + 128) * 0.1f + 0.9f * actor.getY());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean done() {
|
||||
GameHUD.getInstance().getTouchpad().setVisible(false);
|
||||
for (Actor a : table.getChildren()) {
|
||||
if (a instanceof TypingLabel)
|
||||
a.remove();
|
||||
SnapshotArray<Actor> allActors = table.getChildren();
|
||||
for (int i = 0; i < allActors.size; i++) {
|
||||
if (allActors.get(i) instanceof TypingLabel) {
|
||||
allActors.get(i).remove();
|
||||
i--;
|
||||
}
|
||||
}
|
||||
labels.clear();
|
||||
positions.clear();
|
||||
details.clear();
|
||||
miniMapPlayer.setScale(1);
|
||||
img.setScale(1);
|
||||
img.setPosition(0,0);
|
||||
index = -1;
|
||||
Forge.switchToLast();
|
||||
return true;
|
||||
@@ -101,6 +164,134 @@ public class MapViewScene extends UIScene {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public void details() {
|
||||
TextraButton detailsButton = ui.findActor("details");
|
||||
if (detailsButton != null) {
|
||||
detailsButton.setVisible(false);
|
||||
detailsButton.setDisabled(true);
|
||||
}
|
||||
TextraButton eventButton = ui.findActor("events");
|
||||
if (eventButton != null) {
|
||||
eventButton.setVisible(true);
|
||||
eventButton.setDisabled(false);
|
||||
}
|
||||
List<PointOfInterest> allPois = Current.world().getAllPointOfInterest();
|
||||
for (PointOfInterest poi : allPois) {
|
||||
for (AdventureEventData data : AdventurePlayer.current().getEvents()) {
|
||||
if (data.sourceID.equals(poi.getID())) {
|
||||
TypingLabel label = Controls.newTypingLabel("[%?BLACKEN] " + data.getCardBlock());
|
||||
table.addActor(label);
|
||||
details.add(label);
|
||||
label.setPosition(img.getScaleX()*(getMapX(poi.getPosition().x) - label.getWidth() / 2) + img.getX(), img.getScaleY()*(getMapY(poi.getPosition().y) - label.getHeight() / 2) + img.getY());
|
||||
label.skipToTheEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void events() {
|
||||
TextraButton eventsButton = ui.findActor("events");
|
||||
if (eventsButton != null) {
|
||||
eventsButton.setVisible(false);
|
||||
eventsButton.setDisabled(true);
|
||||
}
|
||||
TextraButton repButton = ui.findActor("reputation");
|
||||
if (repButton != null) {
|
||||
repButton.setVisible(true);
|
||||
repButton.setDisabled(false);
|
||||
}
|
||||
for (TypingLabel detail : details) {
|
||||
table.removeActor(detail);
|
||||
}
|
||||
List<PointOfInterest> allPois = Current.world().getAllPointOfInterest();
|
||||
details.clear();
|
||||
for (PointOfInterest poi : allPois) {
|
||||
int rep = WorldSave.getCurrentSave().getPointOfInterestChanges(poi.getID()).getMapReputation();
|
||||
if (rep != 0) {
|
||||
TypingLabel label = Controls.newTypingLabel("[%?BLACKEN] " + rep);
|
||||
table.addActor(label);
|
||||
details.add(label);
|
||||
label.setPosition(img.getScaleX()*(getMapX(poi.getPosition().x) - label.getWidth() / 2) + img.getX(), img.getScaleY()*(getMapY(poi.getPosition().y) - label.getHeight() / 2) + img.getY());
|
||||
label.skipToTheEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void reputation() {
|
||||
TextraButton repButton = ui.findActor("reputation");
|
||||
if (repButton != null) {
|
||||
repButton.setVisible(false);
|
||||
repButton.setDisabled(true);
|
||||
}
|
||||
TextraButton namesButton = ui.findActor("names");
|
||||
if (namesButton != null) {
|
||||
namesButton.setVisible(true);
|
||||
namesButton.setDisabled(false);
|
||||
}
|
||||
for (TypingLabel detail : details) {
|
||||
table.removeActor(detail);
|
||||
}
|
||||
details.clear();
|
||||
List<PointOfInterest> allPois = Current.world().getAllPointOfInterest();
|
||||
for (PointOfInterest poi : allPois) {
|
||||
if (WorldSave.getCurrentSave().getPointOfInterestChanges(poi.getID()).isVisited()) {
|
||||
if ("cave".equalsIgnoreCase(poi.getData().type) || "dungeon".equalsIgnoreCase(poi.getData().type) || "castle".equalsIgnoreCase(poi.getData().type)) {
|
||||
TypingLabel label = Controls.newTypingLabel("[%?BLACKEN] " + poi.getDisplayName());
|
||||
table.addActor(label);
|
||||
details.add(label);
|
||||
label.setPosition(img.getScaleX()*(getMapX(poi.getPosition().x) - label.getWidth() / 2) + img.getX(), img.getScaleY()*(getMapY(poi.getPosition().y) - label.getHeight() / 2) + img.getY());
|
||||
label.skipToTheEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void names() {
|
||||
TextraButton namesButton = ui.findActor("names");
|
||||
if (namesButton != null) {
|
||||
namesButton.setVisible(false);
|
||||
namesButton.setDisabled(true);
|
||||
}
|
||||
TextraButton detailsButton = ui.findActor("details");
|
||||
if (detailsButton != null) {
|
||||
detailsButton.setVisible(true);
|
||||
detailsButton.setDisabled(false);
|
||||
}
|
||||
for (TypingLabel detail : details) {
|
||||
table.removeActor(detail);
|
||||
}
|
||||
details.clear();
|
||||
|
||||
}
|
||||
|
||||
public void zoomOut() {
|
||||
if (img.getScaleX()*0.9f > minZoom) {
|
||||
img.setPosition((scroll.getScrollX() + scroll.getWidth()/2) * 0.1f + 0.9f * img.getX(), (scroll.getMaxY() - scroll.getScrollY() + scroll.getHeight()/2) * 0.1f + 0.9f * img.getY());
|
||||
img.setScale(img.getScaleX() * 0.9f);
|
||||
miniMapPlayer.setPosition((scroll.getScrollX() + scroll.getWidth()/2) * 0.1f + 0.9f * miniMapPlayer.getX(), (scroll.getMaxY() - scroll.getScrollY() + scroll.getHeight()/2) * 0.1f + 0.9f * miniMapPlayer.getY());
|
||||
miniMapPlayer.setScale(miniMapPlayer.getScaleX() * 0.9f);
|
||||
for (Actor actor : table.getChildren()) {
|
||||
if (actor instanceof TypingLabel) {
|
||||
actor.setPosition((scroll.getScrollX() + scroll.getWidth()/2) * 0.1f + 0.9f * actor.getX(), (scroll.getMaxY() - scroll.getScrollY() + scroll.getHeight()/2) * 0.1f + 0.9f * actor.getY());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public void zoomIn() {
|
||||
if (img.getScaleX()*1.1f < maxZoom) {
|
||||
img.setPosition(-(scroll.getScrollX() + scroll.getWidth()/2) * 0.1f + 1.1f * img.getX(), -(scroll.getMaxY() - scroll.getScrollY() + scroll.getHeight()/2) * 0.1f + 1.1f * img.getY());
|
||||
img.setScale(img.getScaleX() * 1.1f);
|
||||
miniMapPlayer.setPosition(-(scroll.getScrollX() + scroll.getWidth()/2) * 0.1f + 1.1f * miniMapPlayer.getX(), -(scroll.getMaxY() - scroll.getScrollY() + scroll.getHeight()/2) * 0.1f + 1.1f * miniMapPlayer.getY());
|
||||
miniMapPlayer.setScale(miniMapPlayer.getScaleX() * 1.1f);
|
||||
for (Actor actor : table.getChildren()) {
|
||||
if (actor instanceof TypingLabel) {
|
||||
actor.setPosition(-(scroll.getScrollX() + scroll.getWidth()/2) * 0.1f + 1.1f * actor.getX(), -(scroll.getMaxY() - scroll.getScrollY() + scroll.getHeight()/2) * 0.1f + 1.1f * actor.getY());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enter() {
|
||||
if (miniMapTexture != null)
|
||||
@@ -135,6 +326,37 @@ public class MapViewScene extends UIScene {
|
||||
label.setPosition(getMapX(poi.getPosition().x) - label.getWidth() / 2, getMapY(poi.getPosition().y) - label.getHeight() / 2);
|
||||
label.skipToTheEnd();
|
||||
}
|
||||
|
||||
TextraButton detailsButton = ui.findActor("details");
|
||||
if (detailsButton != null) {
|
||||
detailsButton.setVisible(true);
|
||||
detailsButton.setDisabled(false);
|
||||
}
|
||||
TextraButton eventButton = ui.findActor("events");
|
||||
if (eventButton != null) {
|
||||
eventButton.setVisible(false);
|
||||
eventButton.setDisabled(true);
|
||||
}
|
||||
TextraButton repButton = ui.findActor("reputation");
|
||||
if (repButton != null) {
|
||||
repButton.setVisible(false);
|
||||
repButton.setDisabled(true);
|
||||
}
|
||||
TextraButton namesButton = ui.findActor("names");
|
||||
if (namesButton != null) {
|
||||
namesButton.setVisible(false);
|
||||
namesButton.setDisabled(true);
|
||||
}
|
||||
TextraButton zoomInButton = ui.findActor("zoomIn");
|
||||
if (zoomInButton != null) {
|
||||
zoomInButton.setVisible(true);
|
||||
zoomInButton.setDisabled(false);
|
||||
}
|
||||
TextraButton zoomOutButton = ui.findActor("zoomOut");
|
||||
if (zoomOutButton != null) {
|
||||
zoomOutButton.setVisible(true);
|
||||
zoomOutButton.setDisabled(false);
|
||||
}
|
||||
TextraButton questButton = ui.findActor("quest");
|
||||
if (questButton != null) {
|
||||
questButton.setDisabled(labels.isEmpty());
|
||||
|
||||
@@ -308,7 +308,7 @@ public class MenuScene extends UIScene {
|
||||
}
|
||||
dialog.show(stage, Actions.show());
|
||||
dialog.setPosition((stage.getWidth() - dialog.getWidth()) / 2, (stage.getHeight() - dialog.getHeight()) / 2);
|
||||
if (Forge.hasGamepad() && !dialogButtonMap.isEmpty())
|
||||
if (Forge.hasExternalInput() && !dialogButtonMap.isEmpty())
|
||||
stage.setKeyboardFocus(dialogButtonMap.first());
|
||||
}
|
||||
|
||||
|
||||
@@ -159,18 +159,15 @@ public class RewardScene extends UIScene {
|
||||
boolean done(boolean skipShowLoot) {
|
||||
GameHUD.getInstance().getTouchpad().setVisible(false);
|
||||
if (!skipShowLoot) {
|
||||
doneButton.setText("[+OK]");
|
||||
showLootOrDone();
|
||||
return true;
|
||||
}
|
||||
if (type != null) {
|
||||
switch (type) {
|
||||
case Shop:
|
||||
doneButton.setText("[+OK]");
|
||||
break;
|
||||
case QuestReward:
|
||||
case Loot:
|
||||
doneButton.setText("[+OK]");
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -217,7 +214,6 @@ public class RewardScene extends UIScene {
|
||||
@Override
|
||||
public void enter() {
|
||||
autoSell = false;
|
||||
doneButton.setText("[+OK]");
|
||||
updateDetailButton();
|
||||
super.enter();
|
||||
}
|
||||
@@ -252,7 +248,7 @@ public class RewardScene extends UIScene {
|
||||
reward.flip();
|
||||
}
|
||||
}, delay);
|
||||
delay += 0.15f;
|
||||
delay += 0.12f;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -342,6 +338,8 @@ public class RewardScene extends UIScene {
|
||||
this.shopActor = shopActor;
|
||||
this.changes = shopActor.getMapStage().getChanges();
|
||||
addToSelectable(restockButton);
|
||||
} else {
|
||||
doneButton.setText("[+OK]");
|
||||
}
|
||||
for (Actor actor : new Array.ArrayIterator<>(generated)) {
|
||||
actor.remove();
|
||||
@@ -406,7 +404,6 @@ public class RewardScene extends UIScene {
|
||||
|
||||
switch (type) {
|
||||
case Shop:
|
||||
doneButton.setText("[+OK]");
|
||||
String shopName = shopActor.getDescription();
|
||||
if ((shopName != null && !shopName.isEmpty())) {
|
||||
headerLabel.setVisible(true);
|
||||
@@ -425,19 +422,16 @@ public class RewardScene extends UIScene {
|
||||
headerLabel.setVisible(false);
|
||||
headerLabel.setText("");
|
||||
restockButton.setVisible(false);
|
||||
doneButton.setText("[+OK]");
|
||||
break;
|
||||
case Loot:
|
||||
headerLabel.skipToTheEnd();
|
||||
headerLabel.setPosition(restockButton.getX(), restockButton.getY());
|
||||
headerLabel.setVisible(true);
|
||||
headerLabel.setText("[%?SHINY][;]\u2610 " + Forge.getLocalizer().getMessage("lblAll"));
|
||||
headerLabel.skipToTheEnd();
|
||||
restockButton.setVisible(false);
|
||||
doneButton.setText("[+OK]");
|
||||
break;
|
||||
case RewardChoice:
|
||||
restockButton.setVisible(false);
|
||||
doneButton.setText("[+OK]");
|
||||
headerLabel.setVisible(remainingSelections > 0);
|
||||
headerLabel.setText(Forge.getLocalizer().getMessage("lblSelectRewards", remainingSelections));
|
||||
doneButton.setDisabled(remainingSelections > 0);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package forge.adventure.scene;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.Input;
|
||||
import com.badlogic.gdx.controllers.Controller;
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
import com.badlogic.gdx.graphics.g2d.TextureRegion;
|
||||
@@ -242,6 +243,7 @@ public class UIScene extends Scene {
|
||||
public Dialog createGenericDialog(String title, String label, String stringYes, String stringNo, Runnable runnableYes, Runnable runnableNo) {
|
||||
return createGenericDialog(title, label, stringYes, stringNo, runnableYes, runnableNo, false, "");
|
||||
}
|
||||
|
||||
public Dialog createGenericDialog(String title, String label, String stringYes, String stringNo, Runnable runnableYes, Runnable runnableNo, boolean cancelButton, String stringCancel) {
|
||||
Dialog dialog = new Dialog(title == null ? "" : title, Controls.getSkin());
|
||||
textboxOpen = true;
|
||||
@@ -340,16 +342,15 @@ public class UIScene extends Scene {
|
||||
}
|
||||
|
||||
public boolean keyPressed(int keycode) {
|
||||
Selectable selection = getSelected();
|
||||
ui.pressDown(keycode);
|
||||
|
||||
Selectable selection = getSelected();
|
||||
if (KeyBinding.Use.isPressed(keycode)) {
|
||||
if (selection != null) {
|
||||
selection.onPressDown(this);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
ui.pressDown(keycode);
|
||||
if (stage.getKeyboardFocus() instanceof SelectBox) {
|
||||
SelectBox box = (SelectBox) stage.getKeyboardFocus();
|
||||
if (box.getScrollPane().hasParent()) {
|
||||
@@ -379,17 +380,22 @@ public class UIScene extends Scene {
|
||||
scroll.setScrollY(scroll.getScrollY() + 20);
|
||||
}
|
||||
}
|
||||
if(!textboxOpen){
|
||||
if (KeyBinding.Down.isPressed(keycode))
|
||||
if (!textboxOpen) {
|
||||
//Allow letter S for TextField since this is binded on down keys
|
||||
if (KeyBinding.Down.isPressed(keycode, !(stage.getKeyboardFocus() instanceof TextField))
|
||||
|| KeyBinding.Down.isPressed(keycode, Input.Keys.S != keycode))
|
||||
selectNextDown();
|
||||
if (KeyBinding.Up.isPressed(keycode))
|
||||
//Allow letter W for TextField since this is binded on up keys
|
||||
if (KeyBinding.Up.isPressed(keycode, !(stage.getKeyboardFocus() instanceof TextField))
|
||||
|| KeyBinding.Up.isPressed(keycode, Input.Keys.W != keycode))
|
||||
selectNextUp();
|
||||
if (!(stage.getKeyboardFocus() instanceof Selector) && !(stage.getKeyboardFocus() instanceof TextField) && !(stage.getKeyboardFocus() instanceof Slider)) {
|
||||
if (KeyBinding.Right.isPressed(keycode))
|
||||
selectNextRight();
|
||||
if (KeyBinding.Left.isPressed(keycode))
|
||||
selectNextLeft();
|
||||
}
|
||||
// Allow Right & Left keybinds if not Selector, Slider or Textfield
|
||||
if (KeyBinding.Right.isPressed(keycode, !(stage.getKeyboardFocus() instanceof Selector)
|
||||
&& !(stage.getKeyboardFocus() instanceof TextField) && !(stage.getKeyboardFocus() instanceof Slider)))
|
||||
selectNextRight();
|
||||
if (KeyBinding.Left.isPressed(keycode, !(stage.getKeyboardFocus() instanceof Selector)
|
||||
&& !(stage.getKeyboardFocus() instanceof TextField) && !(stage.getKeyboardFocus() instanceof Slider)))
|
||||
selectNextLeft();
|
||||
}
|
||||
if (!dialogShowing()) {
|
||||
Button pressedButton = ui.buttonPressed(keycode);
|
||||
|
||||
@@ -75,6 +75,10 @@ public class Console extends Window {
|
||||
}
|
||||
|
||||
public void command(String text) {
|
||||
if (text.equalsIgnoreCase("exit")) {
|
||||
toggle();
|
||||
return;
|
||||
}
|
||||
Cell<Label> newLine=content.add(text);
|
||||
newLine.getActor().setColor(1,1,1,1);
|
||||
newLine.growX().align(Align.left|Align.bottom).row();
|
||||
|
||||
@@ -786,6 +786,7 @@ public class GameHUD extends Stage {
|
||||
console.toggle();
|
||||
if (console.isVisible()) {
|
||||
clearAbility();
|
||||
console.setZIndex(ui.getChildren().size);
|
||||
} else {
|
||||
updateAbility();
|
||||
}
|
||||
@@ -794,6 +795,12 @@ public class GameHUD extends Stage {
|
||||
@Override
|
||||
public boolean keyUp(int keycode) {
|
||||
ui.pressUp(keycode);
|
||||
|
||||
Button pressedButton = ui.buttonPressed(keycode);
|
||||
if (pressedButton != null) {
|
||||
pressedButton.fire(eventTouchUp);
|
||||
}
|
||||
|
||||
return super.keyUp(keycode);
|
||||
}
|
||||
|
||||
@@ -807,16 +814,17 @@ public class GameHUD extends Stage {
|
||||
toggleConsole();
|
||||
return true;
|
||||
}
|
||||
if (keycode == Input.Keys.BACK) {
|
||||
if (KeyBinding.Back.isPressed(keycode)) {
|
||||
if (console.isVisible()) {
|
||||
toggleConsole();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (console.isVisible())
|
||||
return true;
|
||||
Button pressedButton = ui.buttonPressed(keycode);
|
||||
if (pressedButton != null) {
|
||||
performTouch(pressedButton);
|
||||
pressedButton.fire(eventTouchDown);
|
||||
}
|
||||
return super.keyDown(keycode);
|
||||
}
|
||||
@@ -877,7 +885,7 @@ public class GameHUD extends Stage {
|
||||
dialogOnlyInput = true;
|
||||
gameStage.hudIsShowingDialog(true);
|
||||
MapStage.getInstance().hudIsShowingDialog(true);
|
||||
if (Forge.hasGamepad() && !dialogButtonMap.isEmpty())
|
||||
if (Forge.hasExternalInput() && !dialogButtonMap.isEmpty())
|
||||
this.setKeyboardFocus(dialogButtonMap.first());
|
||||
}
|
||||
|
||||
|
||||
@@ -110,7 +110,7 @@ public abstract class GameStage extends Stage {
|
||||
dialog.setPosition((dialogStage.getWidth() - dialog.getWidth()) / 2, (dialogStage.getHeight() - dialog.getHeight()) / 2);
|
||||
dialogOnlyInput = true;
|
||||
|
||||
if (Forge.hasGamepad() && !dialogButtonMap.isEmpty())
|
||||
if (Forge.hasExternalInput() && !dialogButtonMap.isEmpty())
|
||||
dialogStage.setKeyboardFocus(dialogButtonMap.first());
|
||||
}
|
||||
|
||||
@@ -577,9 +577,6 @@ public abstract class GameStage extends Stage {
|
||||
if (!player.isMoving())
|
||||
stop();
|
||||
}
|
||||
if (KeyBinding.Menu.isPressed(keycode)) {
|
||||
openMenu();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -1132,8 +1132,10 @@ public class MapStage extends GameStage {
|
||||
dialog.show(dialogStage, Actions.show());
|
||||
dialog.setPosition((dialogStage.getWidth() - dialog.getWidth()) / 2, (dialogStage.getHeight() - dialog.getHeight()) / 2);
|
||||
dialogOnlyInput = true;
|
||||
if (Forge.hasGamepad() && !dialogButtonMap.isEmpty())
|
||||
|
||||
if (Forge.hasExternalInput() && !dialogButtonMap.isEmpty()) {
|
||||
dialogStage.setKeyboardFocus(dialogButtonMap.first());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -216,6 +216,7 @@ public class WorldStage extends GameStage implements SaveFileContent {
|
||||
WorldSave.getCurrentSave().autoSave();
|
||||
loadPOI(point.getPointOfInterest());
|
||||
point.getMapSprite().checkOut();
|
||||
WorldSave.getCurrentSave().getPointOfInterestChanges(point.getPointOfInterest().getID()).visit();
|
||||
return true;
|
||||
} else {
|
||||
if (point == collidingPoint) {
|
||||
@@ -365,7 +366,7 @@ public class WorldStage extends GameStage implements SaveFileContent {
|
||||
super.draw();
|
||||
if (WorldSave.getCurrentSave().getPlayer().hasAnnounceFantasy()) {
|
||||
MapStage.getInstance().showDeckAwardDialog("{BLINK=WHITE;RED}Chaos Mode!{ENDBLINK}\n" +
|
||||
"Enemy will use Preconstructed or Random Generated Decks. Genetic AI Decks will be available to some enemies on Hard difficulty.",
|
||||
"Enemy will use Preconstructed or Random Generated Decks. Genetic AI Decks will be available to some enemies on Hard difficulty.",
|
||||
WorldSave.getCurrentSave().getPlayer().getSelectedDeck());
|
||||
WorldSave.getCurrentSave().getPlayer().clearAnnounceFantasy();
|
||||
} else if (WorldSave.getCurrentSave().getPlayer().hasAnnounceCustom()) {
|
||||
|
||||
@@ -48,6 +48,12 @@ public class Controls {
|
||||
this.setWidth(this.layout.getWidth() + (this.style != null && this.style.background != null ? this.style.background.getLeftWidth() + this.style.background.getRightWidth() : 0.0F));
|
||||
layout();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Batch batch, float parentAlpha) {
|
||||
batch.setColor(1f, 1f, 1f, 1f); // Set color before drawing each actor, per libGDX docs
|
||||
super.draw(batch, parentAlpha);
|
||||
}
|
||||
}
|
||||
|
||||
static class TextButtonFix extends TextraButton {
|
||||
@@ -249,6 +255,27 @@ public class Controls {
|
||||
return ret;
|
||||
}
|
||||
|
||||
static public SelectBox<Integer> newComboBox(Integer[] text, int item, Function<Object, Void> func) {
|
||||
SelectBox<Integer> ret = newComboBox();
|
||||
ret.getStyle().listStyle.selection.setTopHeight(4);
|
||||
ret.setItems(text);
|
||||
ret.addListener(new ChangeListener() {
|
||||
@Override
|
||||
public void changed(ChangeEvent event, Actor actor) {
|
||||
try {
|
||||
func.apply(((SelectBox) actor).getSelected());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
func.apply(item);
|
||||
ret.getList().setAlignment(Align.center);
|
||||
ret.setSelected(item);
|
||||
ret.setAlignment(Align.right);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static public TextField newTextField(String text) {
|
||||
return new TextField(text, getSkin());
|
||||
}
|
||||
@@ -485,10 +512,11 @@ public class Controls {
|
||||
private String currencyIcon;
|
||||
private boolean isShards;
|
||||
private int currencyAmount;
|
||||
private float animationDelay = 2f; //seconds to wait before replacing intermediate label
|
||||
private float intermediateDuration = 2f; // Seconds to wait before replacing intermediate label
|
||||
private final String NEGDECOR = "[RED]-";
|
||||
private final String POSDECOR = "[GREEN]+";
|
||||
private final Timer t = new Timer();
|
||||
private boolean isInitializing = true;
|
||||
|
||||
public AccountingLabel(TextraLabel target, boolean isShards) {
|
||||
target.setVisible(false);
|
||||
@@ -500,50 +528,72 @@ public class Controls {
|
||||
if (isShards) {
|
||||
currencyAmount = Current.player().getShards();
|
||||
currencyIcon = "[+Shards]";
|
||||
Current.player().onShardsChange(() -> update(AdventurePlayer.current().getShards(), true));
|
||||
Current.player().onShardsChange(() -> {
|
||||
if (!isInitializing) { // Avoid unwanted call to update() during scene initialization, triggering animation
|
||||
update(AdventurePlayer.current().getShards(), true);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
currencyAmount = Current.player().getGold();
|
||||
currencyIcon = "[+Gold] "; //fix space since gold sprite is wider than a single glyph
|
||||
Current.player().onGoldChange(() -> update(AdventurePlayer.current().getGold(), true));
|
||||
Current.player().onGoldChange(() -> {
|
||||
if (!isInitializing) { // Avoid unwanted call to update() during scene initialization, triggering animation
|
||||
update(AdventurePlayer.current().getGold(), true);
|
||||
}
|
||||
});
|
||||
}
|
||||
label.setText(getLabelText(currencyAmount));
|
||||
setName(label.getName());
|
||||
replaceLabel(label);
|
||||
|
||||
isInitializing = false; // Initialization complete
|
||||
}
|
||||
|
||||
public void setAnimationDelay(float animationDelay) {
|
||||
this.animationDelay = animationDelay;
|
||||
public void setIntermediateDuration(float intermediateDuration) {
|
||||
this.intermediateDuration = intermediateDuration;
|
||||
}
|
||||
|
||||
public float getAnimationDelay() {
|
||||
return animationDelay;
|
||||
public float getIntermediateDuration() {
|
||||
return intermediateDuration;
|
||||
}
|
||||
|
||||
public void update(int newAmount) {
|
||||
update(newAmount, false);
|
||||
}
|
||||
|
||||
public void update(int newAmount, boolean animate) {
|
||||
public void update(int newAmount, boolean animateIntermediate) {
|
||||
|
||||
if (animate) {
|
||||
if (animateIntermediate) {
|
||||
TextraLabel temporaryLabel = getUpdateLabel(newAmount);
|
||||
currencyAmount = newAmount;
|
||||
replaceLabel(temporaryLabel);
|
||||
|
||||
t.schedule(new AccountingLabelUpdater(temporaryLabel), animationDelay);
|
||||
// Add a quick 'bump' animation to the temporary label
|
||||
SequenceAction sequence = new SequenceAction();
|
||||
sequence.addAction(Actions.alpha(0.25f));
|
||||
sequence.addAction(Actions.parallel(
|
||||
Actions.alpha(1f, 0.05f, Interpolation.pow2Out),
|
||||
Actions.moveBy(0f, 2f, 0.05f, Interpolation.pow2Out)
|
||||
));
|
||||
sequence.addAction(Actions.moveBy(0f, -2f, 0.05f, Interpolation.pow2Out));
|
||||
|
||||
temporaryLabel.addAction(sequence);
|
||||
|
||||
t.schedule(new AccountingLabelUpdater(temporaryLabel), intermediateDuration);
|
||||
} else {
|
||||
currencyAmount = newAmount;
|
||||
drawFinalLabel(false);
|
||||
drawFinalLabel(true); // Draw final label with animation since the intermediate label was not used.
|
||||
}
|
||||
}
|
||||
|
||||
private void drawFinalLabel(boolean fadeIn) {
|
||||
private void drawFinalLabel(boolean animateFinal) {
|
||||
|
||||
TextraLabel finalLabel = getDefaultLabel();
|
||||
if (fadeIn) {
|
||||
if (animateFinal) {
|
||||
// Add a quick fade-in animation to the final label
|
||||
SequenceAction sequence = new SequenceAction();
|
||||
sequence.addAction(Actions.alpha(0.5f));
|
||||
sequence.addAction(Actions.alpha(1f, 2f, Interpolation.pow2Out));
|
||||
sequence.addAction(Actions.alpha(0.25f));
|
||||
sequence.addAction(Actions.alpha(1f, 0.1f, Interpolation.pow2Out));
|
||||
finalLabel.addAction(sequence);
|
||||
}
|
||||
replaceLabel(finalLabel);
|
||||
@@ -556,7 +606,7 @@ public class Controls {
|
||||
private TextraLabel getUpdateLabel(int newAmount) {
|
||||
int delta = newAmount - currencyAmount;
|
||||
String updateText = delta == 0 ? "" : (delta < 0 ? NEGDECOR + delta * -1 : POSDECOR + delta);
|
||||
return Controls.newTextraLabel(getLabelText(currencyAmount, updateText));
|
||||
return Controls.newTextraLabel(getLabelText(newAmount, updateText));
|
||||
}
|
||||
|
||||
private String getLabelText(int amount) {
|
||||
@@ -579,13 +629,15 @@ public class Controls {
|
||||
label.remove();
|
||||
label = newLabel;
|
||||
placeholder.getStage().addActor(label);
|
||||
|
||||
label.setZIndex(placeholder.getZIndex() - 1); // Ensure the new label is behind any tooltips that were present
|
||||
}
|
||||
|
||||
private class AccountingLabelUpdater extends Timer.Task {
|
||||
@Override
|
||||
public void run() {
|
||||
if (label.equals(target)) {
|
||||
drawFinalLabel(true);
|
||||
drawFinalLabel(false); // Passing false to avoid final animation since the intermediate label was animated
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,82 +8,80 @@ import forge.gui.GuiBase;
|
||||
|
||||
// The standard button has index 0, controller binding is 1. Others can be added if needed.
|
||||
public enum KeyBinding {
|
||||
Left("Left", new int[]{Input.Keys.LEFT,Input.Keys.DPAD_LEFT, Input.Keys.A}),
|
||||
Up("Up", new int[] {Input.Keys.UP,Input.Keys.DPAD_UP, Input.Keys.W}),
|
||||
Right("Right", new int[] {Input.Keys.RIGHT,Input.Keys.DPAD_RIGHT, Input.Keys.D}),
|
||||
Down("Down", new int[] {Input.Keys.DOWN,Input.Keys.DPAD_DOWN, Input.Keys.S}),
|
||||
Menu("Menu", new int[] {Input.Keys.ESCAPE,Input.Keys.BUTTON_START}),
|
||||
Inventory("Inventory", new int[] {Input.Keys.I,Input.Keys.BUTTON_X}),
|
||||
Status("Status", new int[] {Input.Keys.Q,Input.Keys.BUTTON_Y}),
|
||||
Deck("Deck", new int[] {Input.Keys.E,Input.Keys.BUTTON_A}),
|
||||
Map("Map", new int[] {Input.Keys.M,Input.Keys.BUTTON_SELECT}),
|
||||
Equip("Equip", new int[] {Input.Keys.E,Input.Keys.BUTTON_X}),
|
||||
ExitToWorldMap("ExitToWorldMap",new int[] {Input.Keys.F4,Input.Keys.BUTTON_L2}),
|
||||
Bookmark("Bookmark",new int[] {Input.Keys.B, Input.Keys.BUTTON_R2}),
|
||||
Use("Use", new int[] {Input.Keys.ENTER,Input.Keys.BUTTON_A}),
|
||||
Back("Back", new int[] {Input.Keys.ESCAPE,Input.Keys.BUTTON_B}),
|
||||
ScrollUp("ScrollUp", new int[] {Input.Keys.PAGE_UP,Input.Keys.BUTTON_L1}),
|
||||
ScrollDown("ScrollDown", new int[] {Input.Keys.PAGE_DOWN,Input.Keys.BUTTON_R1}),
|
||||
Left("Left", new int[]{Input.Keys.LEFT, Input.Keys.DPAD_LEFT, Input.Keys.A}),
|
||||
Up("Up", new int[]{Input.Keys.UP, Input.Keys.DPAD_UP, Input.Keys.W}),
|
||||
Right("Right", new int[]{Input.Keys.RIGHT, Input.Keys.DPAD_RIGHT, Input.Keys.D}),
|
||||
Down("Down", new int[]{Input.Keys.DOWN, Input.Keys.DPAD_DOWN, Input.Keys.S}),
|
||||
Menu("Menu", new int[]{Input.Keys.ESCAPE, Input.Keys.BUTTON_START}),
|
||||
Inventory("Inventory", new int[]{Input.Keys.I, Input.Keys.BUTTON_X}),
|
||||
Status("Status", new int[]{Input.Keys.Q, Input.Keys.BUTTON_Y}),
|
||||
Deck("Deck", new int[]{Input.Keys.E, Input.Keys.BUTTON_A}),
|
||||
Map("Map", new int[]{Input.Keys.M, Input.Keys.BUTTON_SELECT}),
|
||||
Equip("Equip", new int[]{Input.Keys.E, Input.Keys.BUTTON_X}),
|
||||
ExitToWorldMap("ExitToWorldMap", new int[]{Input.Keys.F4, Input.Keys.BUTTON_L2}),
|
||||
Bookmark("Bookmark", new int[]{Input.Keys.B, Input.Keys.BUTTON_R2}),
|
||||
Use("Use", new int[]{Input.Keys.ENTER, Input.Keys.BUTTON_A}),
|
||||
Back("Back", new int[]{Input.Keys.ESCAPE, Input.Keys.BUTTON_B, Input.Keys.BACK}),
|
||||
ScrollUp("ScrollUp", new int[]{Input.Keys.PAGE_UP, Input.Keys.BUTTON_L1}),
|
||||
ScrollDown("ScrollDown", new int[]{Input.Keys.PAGE_DOWN, Input.Keys.BUTTON_R1}),
|
||||
;
|
||||
String name;
|
||||
int [] bindings;
|
||||
final String name;
|
||||
final int[] bindings;
|
||||
|
||||
KeyBinding(String name, int [] bindings)
|
||||
{
|
||||
this.name=name;
|
||||
KeyBinding(String name, int[] bindings) {
|
||||
this.name = name;
|
||||
this.bindings = bindings;
|
||||
}
|
||||
public boolean isPressed(int key)
|
||||
{
|
||||
for(int i = 0; i < bindings.length; i++){
|
||||
if(key == bindings[i]) {
|
||||
return true;
|
||||
|
||||
public boolean isPressed(int key) {
|
||||
return isPressed(key, null);
|
||||
}
|
||||
|
||||
public boolean isPressed(int key, Boolean requiredCondition) {
|
||||
if (requiredCondition == null || requiredCondition) {
|
||||
for (int i = 0; i < bindings.length; i++) {
|
||||
if (key == bindings[i]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// The controller binding always has index 1.
|
||||
static String controllerPrefix="XBox_";
|
||||
final static String controllerPrefix = "XBox_";
|
||||
|
||||
public String getLabelText(boolean pressed) {
|
||||
if(Controllers.getCurrent()!=null)
|
||||
{
|
||||
return "[%120][+"+controllerPrefix+Input.Keys.toString(bindings[1]).replace(" Button","")+(pressed?"_pressed]":"]");
|
||||
}
|
||||
else
|
||||
{
|
||||
if(GuiBase.isAndroid())
|
||||
if (Controllers.getCurrent() != null) {
|
||||
return "[%120][+" + controllerPrefix + Input.Keys.toString(bindings[1]).replace(" Button", "") + (pressed ? "_pressed]" : "]");
|
||||
} else {
|
||||
if (GuiBase.isAndroid())
|
||||
return "";
|
||||
return "[%120][+"+Input.Keys.toString(bindings[0])+(pressed?"_pressed]":"]");
|
||||
return "[%120][+" + Input.Keys.toString(bindings[0]) + (pressed ? "_pressed]" : "]");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static int controllerButtonToKey(Controller controller,int key)
|
||||
{
|
||||
ControllerMapping map=controller.getMapping();
|
||||
if(key==map.buttonA) return Input.Keys.BUTTON_A;
|
||||
if(key==map.buttonB) return Input.Keys.BUTTON_B;
|
||||
if(key==map.buttonX) return Input.Keys.BUTTON_X;
|
||||
if(key==map.buttonY) return Input.Keys.BUTTON_Y;
|
||||
|
||||
if(key==map.buttonBack) return Input.Keys.BUTTON_SELECT;
|
||||
if(key==map.buttonStart) return Input.Keys.BUTTON_START;
|
||||
if(key==map.buttonL1) return Input.Keys.BUTTON_L1;
|
||||
if(key==map.buttonL2) return Input.Keys.BUTTON_L2;
|
||||
if(key==map.buttonR1) return Input.Keys.BUTTON_R1;
|
||||
if(key==map.buttonR2) return Input.Keys.BUTTON_R2;
|
||||
if(key==map.buttonDpadUp) return Input.Keys.DPAD_UP;
|
||||
if(key==map.buttonDpadDown) return Input.Keys.DPAD_DOWN;
|
||||
if(key==map.buttonDpadLeft) return Input.Keys.DPAD_LEFT;
|
||||
if(key==map.buttonDpadRight) return Input.Keys.DPAD_RIGHT;
|
||||
if(key==map.buttonLeftStick) return Input.Keys.BUTTON_THUMBL;
|
||||
if(key==map.buttonRightStick) return Input.Keys.BUTTON_THUMBR;
|
||||
public static int controllerButtonToKey(Controller controller, int key) {
|
||||
ControllerMapping map = controller.getMapping();
|
||||
if (key == map.buttonA) return Input.Keys.BUTTON_A;
|
||||
if (key == map.buttonB) return Input.Keys.BUTTON_B;
|
||||
if (key == map.buttonX) return Input.Keys.BUTTON_X;
|
||||
if (key == map.buttonY) return Input.Keys.BUTTON_Y;
|
||||
|
||||
if (key == map.buttonBack) return Input.Keys.BUTTON_SELECT;
|
||||
if (key == map.buttonStart) return Input.Keys.BUTTON_START;
|
||||
if (key == map.buttonL1) return Input.Keys.BUTTON_L1;
|
||||
if (key == map.buttonL2) return Input.Keys.BUTTON_L2;
|
||||
if (key == map.buttonR1) return Input.Keys.BUTTON_R1;
|
||||
if (key == map.buttonR2) return Input.Keys.BUTTON_R2;
|
||||
if (key == map.buttonDpadUp) return Input.Keys.DPAD_UP;
|
||||
if (key == map.buttonDpadDown) return Input.Keys.DPAD_DOWN;
|
||||
if (key == map.buttonDpadLeft) return Input.Keys.DPAD_LEFT;
|
||||
if (key == map.buttonDpadRight) return Input.Keys.DPAD_RIGHT;
|
||||
if (key == map.buttonLeftStick) return Input.Keys.BUTTON_THUMBL;
|
||||
if (key == map.buttonRightStick) return Input.Keys.BUTTON_THUMBR;
|
||||
|
||||
if(key==map.buttonDpadUp) return Input.Keys.DPAD_UP;
|
||||
if(key==map.buttonDpadDown) return Input.Keys.DPAD_DOWN;
|
||||
if(key==map.buttonDpadLeft) return Input.Keys.DPAD_LEFT;
|
||||
if(key==map.buttonDpadRight) return Input.Keys.DPAD_RIGHT;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,9 +14,12 @@ import com.badlogic.gdx.math.Vector3;
|
||||
import com.badlogic.gdx.scenes.scene2d.Actor;
|
||||
import com.badlogic.gdx.scenes.scene2d.Group;
|
||||
import com.badlogic.gdx.scenes.scene2d.InputEvent;
|
||||
import com.badlogic.gdx.scenes.scene2d.actions.Actions;
|
||||
import com.badlogic.gdx.scenes.scene2d.InputListener;
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Image;
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Label;
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Tooltip;
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.TooltipManager;
|
||||
import com.badlogic.gdx.scenes.scene2d.utils.ActorGestureListener;
|
||||
import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
|
||||
import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable;
|
||||
@@ -120,10 +123,10 @@ public class RewardActor extends Actor implements Disposable, ImageFetcher.Callb
|
||||
public void onImageFetched() {
|
||||
ImageCache.getInstance().clear();
|
||||
|
||||
|
||||
|
||||
if(reward.type.equals(Reward.Type.Card)) {
|
||||
imageKey = reward.getCard().getImageKey(false);
|
||||
PaperCard card = ImageUtil.getPaperCardFromImageKey(imageKey);
|
||||
imageKey = card.getCardImageKey();
|
||||
|
||||
|
||||
int count = 0;
|
||||
@@ -220,6 +223,7 @@ public class RewardActor extends Actor implements Disposable, ImageFetcher.Callb
|
||||
reward.setAutoSell(!reward.isAutoSell());
|
||||
String c = reward.isAutoSell() ? "[%85][GREEN]" : "[%85][GRAY]";
|
||||
autoSell.setText(c + "\uFF04");
|
||||
autoSell.getColor().a = reward.isAutoSell() ? 1f : 0.7f;
|
||||
}
|
||||
|
||||
public RewardActor(Reward reward, boolean flippable, RewardScene.Type type, boolean showOverlay) {
|
||||
@@ -235,6 +239,17 @@ public class RewardActor extends Actor implements Disposable, ImageFetcher.Callb
|
||||
case Card: {
|
||||
if (!reward.isNoSell) {
|
||||
autoSell = Controls.newTextButton("[%85][GRAY]\uFF04");
|
||||
autoSell.getColor().a = 0.7f; // semi-transparent by default
|
||||
autoSell.addListener(new InputListener() {
|
||||
@Override
|
||||
public void enter(InputEvent event, float x, float y, int pointer, Actor fromActor) {
|
||||
if (!reward.isAutoSell()) autoSell.getColor().a = 1f;
|
||||
}
|
||||
@Override
|
||||
public void exit(InputEvent event, float x, float y, int pointer, Actor toActor) {
|
||||
if (!reward.isAutoSell()) autoSell.getColor().a = 0.7f;
|
||||
}
|
||||
});
|
||||
float scale = autoSell.getWidth();
|
||||
autoSell.setSize(scale, scale * 1.2f);
|
||||
autoSell.addListener(new ClickListener() {
|
||||
@@ -246,7 +261,8 @@ public class RewardActor extends Actor implements Disposable, ImageFetcher.Callb
|
||||
hasbackface = reward.getCard().hasBackFace();
|
||||
if (ImageCache.getInstance().imageKeyFileExists(reward.getCard().getImageKey(false)) && !Forge.enableUIMask.equals("Art")) {
|
||||
int count = 0;
|
||||
File frontFace = ImageKeys.getImageFile(reward.getCard().getImageKey(false));
|
||||
PaperCard card = ImageUtil.getPaperCardFromImageKey(reward.getCard().getImageKey(false));
|
||||
File frontFace = ImageKeys.getImageFile(card.getCardImageKey());
|
||||
if (frontFace != null) {
|
||||
try {
|
||||
Texture front = Forge.getAssets().manager().get(frontFace.getPath(), Texture.class, false);
|
||||
@@ -272,7 +288,8 @@ public class RewardActor extends Actor implements Disposable, ImageFetcher.Callb
|
||||
//preload card back for performance
|
||||
if (hasbackface) {
|
||||
if (ImageCache.getInstance().imageKeyFileExists(reward.getCard().getImageKey(true))) {
|
||||
File backFace = ImageKeys.getImageFile(reward.getCard().getImageKey(true));
|
||||
PaperCard cardBack = ImageUtil.getPaperCardFromImageKey(reward.getCard().getImageKey(true));
|
||||
File backFace = ImageKeys.getImageFile(cardBack.getCardAltImageKey());
|
||||
if (backFace != null) {
|
||||
try {
|
||||
Texture back = Forge.getAssets().manager().get(backFace.getPath(), Texture.class, false);
|
||||
@@ -396,7 +413,8 @@ public class RewardActor extends Actor implements Disposable, ImageFetcher.Callb
|
||||
if (imageKey != "") {
|
||||
isBooster = true;
|
||||
File file = new File(IMAGE_LIST_QUEST_BOOSTERS_FILE);
|
||||
try (Scanner scanner = new Scanner(file)) {
|
||||
try {
|
||||
Scanner scanner = new Scanner(file);
|
||||
String boosterPath = "";
|
||||
while(scanner.hasNextLine())
|
||||
{
|
||||
@@ -564,8 +582,8 @@ public class RewardActor extends Actor implements Disposable, ImageFetcher.Callb
|
||||
TextureRegionDrawable drawable = new TextureRegionDrawable(ImageCache.getInstance().croppedBorderImage(texture));
|
||||
float origW = texture.getWidth();
|
||||
float origH = texture.getHeight();
|
||||
float boundW = Scene.getIntendedWidth() * 0.95f;
|
||||
float boundH = Scene.getIntendedHeight() * 0.95f;
|
||||
float boundW = GuiBase.isAndroid() ? Scene.getIntendedWidth() * 0.95f : Scene.getIntendedWidth() * 0.7f; // Use smaller size for Desktop
|
||||
float boundH = GuiBase.isAndroid() ? Scene.getIntendedHeight() * 0.95f : Scene.getIntendedHeight() * 0.7f; // Use smaller size for Desktop
|
||||
float newW = origW;
|
||||
float newH = origH;
|
||||
if (origW > boundW) {
|
||||
@@ -843,7 +861,7 @@ public class RewardActor extends Actor implements Disposable, ImageFetcher.Callb
|
||||
super.act(delta);
|
||||
if (clicked) {
|
||||
if (flipProcess < 1)
|
||||
flipProcess += delta * 2.4;
|
||||
flipProcess += delta * 4;
|
||||
else
|
||||
flipProcess = 1;
|
||||
|
||||
@@ -1109,11 +1127,17 @@ public class RewardActor extends Actor implements Disposable, ImageFetcher.Callb
|
||||
public TextraLabel getStoredLabel() {
|
||||
return cLabel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Batch batch, float parentAlpha) {
|
||||
batch.setColor(1f, 1f, 1f, 1f); // Set color before drawing each actor, per libGDX docs
|
||||
super.draw(batch, parentAlpha);
|
||||
}
|
||||
}
|
||||
|
||||
class ImageToolTip extends Tooltip<ComplexTooltip> {
|
||||
public ImageToolTip(ComplexTooltip contents) {
|
||||
super(contents);
|
||||
super(contents, RewardTooltipManager.getInstance());
|
||||
}
|
||||
|
||||
public Image getImage() {
|
||||
@@ -1253,4 +1277,30 @@ public class RewardActor extends Actor implements Disposable, ImageFetcher.Callb
|
||||
super.draw(batch, parentAlpha);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend and override TooltipManager to avoid the built-in default animations.
|
||||
*/
|
||||
static class RewardTooltipManager extends TooltipManager {
|
||||
private static RewardTooltipManager instance;
|
||||
|
||||
public static RewardTooltipManager getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new RewardTooltipManager();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void showAction(Tooltip tooltip) {
|
||||
// Overriding showAction for instant tooltip display
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void hideAction(Tooltip tooltip) {
|
||||
tooltip.getContainer().addAction(Actions.sequence(
|
||||
Actions.removeActor() // Remove tooltip without animation
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,6 +247,9 @@ public class ImageCache {
|
||||
imageKey = imageKey.substring(0, imageKey.length() - ImageKeys.BACKFACE_POSTFIX.length());
|
||||
}
|
||||
if (imageKey.startsWith(ImageKeys.CARD_PREFIX)) {
|
||||
PaperCard card = ImageUtil.getPaperCardFromImageKey(imageKey);
|
||||
if (card != null)
|
||||
imageKey = altState ? card.getCardAltImageKey() : card.getCardImageKey();
|
||||
if (StringUtils.isBlank(imageKey)) {
|
||||
if (useDefaultIfNotFound)
|
||||
return getDefaultImage();
|
||||
@@ -292,7 +295,7 @@ public class ImageCache {
|
||||
image fetcher to update automatically after the card image/s are downloaded*/
|
||||
imageLoaded = false;
|
||||
if (image != null && imageRecord.get().get(image.toString()) == null)
|
||||
imageRecord.get().put(image.toString(), new ImageRecord(Color.valueOf("#171717").toString(), false, getRadius(image))); //black border
|
||||
imageRecord.get().put(image.toString(), new ImageRecord(Color.valueOf("#171717").toString(), false, getRadius(image), image.toString().contains(".fullborder.") || image.toString().contains("tokens"))); //black border
|
||||
}
|
||||
}
|
||||
return image;
|
||||
@@ -346,7 +349,7 @@ public class ImageCache {
|
||||
radius = 22;
|
||||
updateImageRecord(cardTexture.toString(),
|
||||
borderless ? Color.valueOf("#171717").toString() : isCloserToWhite(getpixelColor(cardTexture)).getLeft(),
|
||||
!borderless && isCloserToWhite(getpixelColor(cardTexture)).getRight(), radius);
|
||||
!borderless && isCloserToWhite(getpixelColor(cardTexture)).getRight(), radius, cardTexture.toString().contains(".fullborder.") || cardTexture.toString().contains("tokens"));
|
||||
}
|
||||
return cardTexture;
|
||||
}
|
||||
@@ -441,8 +444,8 @@ public class ImageCache {
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
public void updateImageRecord(String textureString, String colorValue, Boolean isClosertoWhite, int radius) {
|
||||
imageRecord.get().put(textureString, new ImageRecord(colorValue, isClosertoWhite, radius));
|
||||
public void updateImageRecord(String textureString, String colorValue, Boolean isClosertoWhite, int radius, boolean fullborder) {
|
||||
imageRecord.get().put(textureString, new ImageRecord(colorValue, isClosertoWhite, radius, fullborder));
|
||||
}
|
||||
|
||||
public int getRadius(Texture t) {
|
||||
@@ -457,6 +460,15 @@ public class ImageCache {
|
||||
return i;
|
||||
}
|
||||
|
||||
public boolean isFullBorder(Texture image) {
|
||||
if (image == null)
|
||||
return false;
|
||||
ImageRecord record = imageRecord.get().get(image.toString());
|
||||
if (record == null)
|
||||
return false;
|
||||
return record.isFullBorder;
|
||||
}
|
||||
|
||||
public FImage getBorder(String textureString) {
|
||||
ImageRecord record = imageRecord.get().get(textureString);
|
||||
if (record == null)
|
||||
@@ -541,11 +553,13 @@ public class ImageCache {
|
||||
String colorValue;
|
||||
Boolean isCloserToWhite;
|
||||
Integer cardRadius;
|
||||
boolean isFullBorder;
|
||||
|
||||
ImageRecord(String colorString, Boolean closetoWhite, int radius) {
|
||||
ImageRecord(String colorString, Boolean closetoWhite, int radius, boolean fullborder) {
|
||||
colorValue = colorString;
|
||||
isCloserToWhite = closetoWhite;
|
||||
cardRadius = radius;
|
||||
isFullBorder = fullborder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,21 +34,22 @@ public class CardImage implements FImage {
|
||||
|
||||
@Override
|
||||
public void draw(Graphics g, float x, float y, float w, float h) {
|
||||
CardView cv = CardView.getCardForUi(card);
|
||||
if (image == null) { //attempt to retrieve card image if needed
|
||||
image = ImageCache.getInstance().getImage(card);
|
||||
if (image == null) {
|
||||
if (!Forge.enableUIMask.equals("Off")) //render this if mask is still loading
|
||||
CardImageRenderer.drawCardImage(g, CardView.getCardForUi(card), false, x, y, w, h, CardStackPosition.Top, Forge.enableUIMask.equals("Art"), true);
|
||||
CardImageRenderer.drawCardImage(g, cv, false, x, y, w, h, CardStackPosition.Top, Forge.enableUIMask.equals("Art"), true);
|
||||
|
||||
return; //can't draw anything if can't be loaded yet
|
||||
}
|
||||
}
|
||||
|
||||
if (image == ImageCache.getInstance().getDefaultImage() || Forge.enableUIMask.equals("Art")) {
|
||||
CardImageRenderer.drawCardImage(g, CardView.getCardForUi(card), false, x, y, w, h, CardStackPosition.Top, true, true);
|
||||
CardImageRenderer.drawCardImage(g, cv, false, x, y, w, h, CardStackPosition.Top, true, true);
|
||||
} else {
|
||||
if (Forge.enableUIMask.equals("Full")) {
|
||||
if (image.toString().contains(".fullborder."))
|
||||
if (ImageCache.getInstance().isFullBorder(image))
|
||||
g.drawCardRoundRect(image, null, x, y, w, h, false, false);
|
||||
else {
|
||||
float radius = (h - w) / 8;
|
||||
|
||||
@@ -9,9 +9,9 @@ import java.util.List;
|
||||
|
||||
import forge.ImageKeys;
|
||||
import forge.assets.*;
|
||||
import forge.item.PaperCard;
|
||||
import forge.util.ImageUtil;
|
||||
import forge.util.TextBounds;
|
||||
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.badlogic.gdx.graphics.Color;
|
||||
@@ -156,7 +156,13 @@ public class CardImageRenderer {
|
||||
}
|
||||
//space for artist
|
||||
textBoxHeight -= 2 * PT_FONT.getCapHeight();
|
||||
String artist = ObjectUtils.firstNonNull(state.getArtist(), "WOTC");
|
||||
PaperCard paperCard = null;
|
||||
try {
|
||||
paperCard = ImageUtil.getPaperCardFromImageKey(state.getImageKey());
|
||||
} catch (Exception e) {}
|
||||
String artist = "WOTC";
|
||||
if (paperCard != null && !paperCard.getArtist().isEmpty())
|
||||
artist = paperCard.getArtist();
|
||||
float minTextBoxHeight = 2 * headerHeight;
|
||||
if (textBoxHeight < minTextBoxHeight) {
|
||||
artHeight -= (minTextBoxHeight - textBoxHeight); //subtract from art height if text box not big enough otherwise
|
||||
@@ -808,7 +814,7 @@ public class CardImageRenderer {
|
||||
}
|
||||
if (rotatePlane && (card.getCurrentState().isPhenomenon() || card.getCurrentState().isPlane() || (card.getCurrentState().isBattle() && !altState) || (card.getAlternateState() != null && card.getAlternateState().isBattle() && altState))) {
|
||||
if (Forge.enableUIMask.equals("Full")) {
|
||||
if (image.toString().contains(".fullborder."))
|
||||
if (ImageCache.getInstance().isFullBorder(image))
|
||||
g.drawCardRoundRect(image, new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, -90);
|
||||
else {
|
||||
g.drawRotatedImage(FSkin.getBorders().get(0), new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, -90);
|
||||
@@ -821,7 +827,7 @@ public class CardImageRenderer {
|
||||
} else if (rotateSplit && isCurrentCard && card.isSplitCard() && canshow && !card.isFaceDown()) {
|
||||
boolean isAftermath = card.getText().contains("Aftermath") || card.getAlternateState().getOracleText().contains("Aftermath");
|
||||
if (Forge.enableUIMask.equals("Full")) {
|
||||
if (image.toString().contains(".fullborder."))
|
||||
if (ImageCache.getInstance().isFullBorder(image))
|
||||
g.drawCardRoundRect(image, new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, isAftermath ? 90 : -90);
|
||||
else {
|
||||
g.drawRotatedImage(FSkin.getBorders().get(ImageCache.getInstance().getFSkinBorders(card)), new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, isAftermath ? 90 : -90);
|
||||
@@ -837,7 +843,7 @@ public class CardImageRenderer {
|
||||
if (card.isSplitCard() && rotateSplit && isCurrentCard) {
|
||||
boolean isAftermath = card.getText().contains("Aftermath") || card.getAlternateState().getOracleText().contains("Aftermath");
|
||||
if (Forge.enableUIMask.equals("Full")) {
|
||||
if (image.toString().contains(".fullborder."))
|
||||
if (ImageCache.getInstance().isFullBorder(image))
|
||||
g.drawCardRoundRect(image, new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, isAftermath ? 90 : -90);
|
||||
else {
|
||||
g.drawRotatedImage(FSkin.getBorders().get(ImageCache.getInstance().getFSkinBorders(card)), new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, isAftermath ? 90 : -90);
|
||||
@@ -849,7 +855,7 @@ public class CardImageRenderer {
|
||||
g.drawRotatedImage(image, new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, isAftermath ? 90 : -90);
|
||||
} else {
|
||||
if (Forge.enableUIMask.equals("Full")) {
|
||||
if (image.toString().contains(".fullborder."))
|
||||
if (ImageCache.getInstance().isFullBorder(image))
|
||||
g.drawCardRoundRect(image, null, x, y, w, h, false, false);
|
||||
else {
|
||||
g.drawImage(ImageCache.getInstance().getBorderImage(image.toString()), ImageCache.getInstance().borderColor(image), x, y, w, h);
|
||||
@@ -866,7 +872,7 @@ public class CardImageRenderer {
|
||||
g.drawImage(sleeves, x, y, w, h);
|
||||
}
|
||||
} else if (Forge.enableUIMask.equals("Full") && canshow) {
|
||||
if (image.toString().contains(".fullborder."))
|
||||
if (ImageCache.getInstance().isFullBorder(image))
|
||||
g.drawCardRoundRect(image, null, x, y, w, h, false, false);
|
||||
else {
|
||||
g.drawImage(ImageCache.getInstance().getBorderImage(image.toString()), ImageCache.getInstance().borderColor(image), x, y, w, h);
|
||||
|
||||
@@ -597,6 +597,7 @@ public class CardRenderer {
|
||||
|
||||
public static void drawCard(Graphics g, IPaperCard pc, float x, float y, float w, float h, CardStackPosition pos) {
|
||||
Texture image = new RendererCachedCardImage(pc, false).getImage();
|
||||
final CardView card = CardView.getCardForUi(pc);
|
||||
float radius = (h - w) / 8;
|
||||
float croppedArea = isModernFrame(pc) ? CROP_MULTIPLIER : 0.97f;
|
||||
float minusxy = isModernFrame(pc) ? 0.0f : 0.13f * radius;
|
||||
@@ -609,7 +610,7 @@ public class CardRenderer {
|
||||
CardImageRenderer.drawCardImage(g, CardView.getCardForUi(pc), false, x, y, w, h, pos, true, true);
|
||||
} else {
|
||||
if (Forge.enableUIMask.equals("Full")) {
|
||||
if (image.toString().contains(".fullborder."))
|
||||
if (ImageCache.getInstance().isFullBorder(image))
|
||||
g.drawCardRoundRect(image, null, x, y, w, h, false, false);
|
||||
else {
|
||||
//tint the border
|
||||
@@ -622,7 +623,6 @@ public class CardRenderer {
|
||||
g.drawImage(image, x, y, w, h);
|
||||
}
|
||||
if (pc.isFoil()) { //draw foil effect if needed
|
||||
final CardView card = CardView.getCardForUi(pc);
|
||||
if (card.getCurrentState().getFoilIndex() == 0) { //if foil finish not yet established, assign a random one
|
||||
card.getCurrentState().setFoilIndexOverride(-1);
|
||||
}
|
||||
@@ -630,7 +630,7 @@ public class CardRenderer {
|
||||
}
|
||||
} else {
|
||||
//if card has invalid or no texture due to sudden changes in ImageCache, draw CardImageRenderer instead and wait for it to refresh automatically
|
||||
CardImageRenderer.drawCardImage(g, CardView.getCardForUi(pc), false, x, y, w, h, pos, true, true);
|
||||
CardImageRenderer.drawCardImage(g, card, false, x, y, w, h, pos, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -663,7 +663,7 @@ public class CardRenderer {
|
||||
if (FModel.getPreferences().getPrefBoolean(ForgePreferences.FPref.UI_ROTATE_PLANE_OR_PHENOMENON)
|
||||
&& (card.getCurrentState().isPhenomenon() || card.getCurrentState().isPlane() || (card.getCurrentState().isBattle() && !showAltState) || (card.getAlternateState() != null && card.getAlternateState().isBattle() && showAltState)) && rotate) {
|
||||
if (Forge.enableUIMask.equals("Full")) {
|
||||
if (image.toString().contains(".fullborder."))
|
||||
if (ImageCache.getInstance().isFullBorder(image))
|
||||
g.drawCardRoundRect(image, x, y, w, h, x + w / 2, y + h / 2, -90);
|
||||
else {
|
||||
g.drawRotatedImage(FSkin.getBorders().get(0), x, y, w, h, x + w / 2, y + h / 2, -90);
|
||||
@@ -675,7 +675,7 @@ public class CardRenderer {
|
||||
g.drawRotatedImage(image, x, y, w, h, x + w / 2, y + h / 2, -90);
|
||||
} else {
|
||||
if (Forge.enableUIMask.equals("Full") && canshow) {
|
||||
if (image.toString().contains(".fullborder."))
|
||||
if (ImageCache.getInstance().isFullBorder(image))
|
||||
g.drawCardRoundRect(image, crack_overlay, x, y, w, h, drawGray(card), magnify ? false : card.getDamage() > 0);
|
||||
else {
|
||||
//boolean t = (card.getCurrentState().getOriginalColors() != card.getCurrentState().getColors()) || card.getCurrentState().hasChangeColors();
|
||||
|
||||
@@ -103,22 +103,27 @@ public class CardZoom extends FOverlay {
|
||||
if (pc != null) {
|
||||
Card cardW = Card.fromPaperCard(pc, null);
|
||||
cardW.setState(CardStateName.SpecializeW, true);
|
||||
cardW.setImageKey(pc, CardStateName.SpecializeW);
|
||||
list.add(cardW.getView());
|
||||
|
||||
Card cardU = Card.fromPaperCard(pc, null);
|
||||
cardU.setState(CardStateName.SpecializeU, true);;
|
||||
cardU.setState(CardStateName.SpecializeU, true);
|
||||
cardU.setImageKey(pc, CardStateName.SpecializeU);
|
||||
list.add(cardU.getView());
|
||||
|
||||
Card cardB = Card.fromPaperCard(pc, null);
|
||||
cardB.setState(CardStateName.SpecializeB, true);
|
||||
cardB.setImageKey(pc, CardStateName.SpecializeB);
|
||||
list.add(cardB.getView());
|
||||
|
||||
Card cardR = Card.fromPaperCard(pc, null);
|
||||
cardR.setState(CardStateName.SpecializeR, true);
|
||||
cardR.setImageKey(pc, CardStateName.SpecializeR);
|
||||
list.add(cardR.getView());
|
||||
|
||||
Card cardG = Card.fromPaperCard(pc, null);
|
||||
cardG.setState(CardStateName.SpecializeG, true);
|
||||
cardG.setImageKey(pc, CardStateName.SpecializeG);
|
||||
list.add(cardG.getView());
|
||||
}
|
||||
if (!list.isEmpty())
|
||||
|
||||
@@ -1997,7 +1997,9 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
|
||||
@Override
|
||||
public void refresh() {
|
||||
BoosterDraft draft = parentScreen.getDraft();
|
||||
if (draft == null) { return; }
|
||||
if (draft == null || !draft.hasNextChoice()) {
|
||||
return;
|
||||
}
|
||||
|
||||
CardPool pool = draft.nextChoice();
|
||||
int packNumber = draft.getCurrentBoosterIndex() + 1;
|
||||
|
||||
@@ -72,6 +72,21 @@ public class FDeckViewer extends FScreen {
|
||||
FOptionPane.showMessageDialog(Forge.getLocalizer().getMessage("lblDeckListCopiedClipboard", deck.getName()));
|
||||
}
|
||||
|
||||
public static void copyCollectionToClipboard(CardPool pool) {
|
||||
final String nl = System.lineSeparator();
|
||||
final StringBuilder collectionList = new StringBuilder();
|
||||
Set<String> accounted = new HashSet<>();
|
||||
for (final Entry<PaperCard, Integer> entry : pool) {
|
||||
String cardName = entry.getKey().getCardName();
|
||||
if (!accounted.contains(cardName)) {
|
||||
collectionList.append(pool.countByName(cardName)).append(" ").append(cardName).append(nl);
|
||||
accounted.add(cardName);
|
||||
}
|
||||
}
|
||||
Forge.getClipboard().setContents(collectionList.toString());
|
||||
FOptionPane.showMessageDialog(Forge.getLocalizer().getMessage("lblCollectionCopiedClipboard"));
|
||||
}
|
||||
|
||||
private final Deck deck;
|
||||
private final CardManager cardManager;
|
||||
private DeckSection currentSection;
|
||||
|
||||
@@ -190,4 +190,8 @@ public abstract class FDisplayObject {
|
||||
public boolean keyDown(int keyCode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean keyUp(int keyCode) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ Name=kavu
|
||||
1 Kavu Primarch|TSR|1
|
||||
4 Mountain|MH2|1
|
||||
3 Mountain|MH2|2
|
||||
2 Punishing Fire|C11|1
|
||||
2 Punishing Fire|CMD|1
|
||||
2 Raging Kavu|INV|1
|
||||
2 Ram Through|IKO|1
|
||||
2 Sacred Foundry|GRN|1
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
<object id="53" template="../../obj/treasure.tx" x="194" y="125"/>
|
||||
<object id="54" template="../../obj/enemy.tx" x="201.253" y="161.473">
|
||||
<properties>
|
||||
<property name="effect" value="{ "startBattleWithCard": [ "Dreadhorde Invasion" }"/>
|
||||
<property name="effect" value="{ "startBattleWithCard": ["Dreadhorde Invasion"]}"/>
|
||||
<property name="enemy" value="Big Zombie"/>
|
||||
<property name="speedModifier" type="float" value="3"/>
|
||||
<property name="threatRange" type="int" value="60"/>
|
||||
|
||||
@@ -184,7 +184,7 @@
|
||||
"type": "item",
|
||||
"count": 1,
|
||||
"probability": 1,
|
||||
"itemName": "Lightbringers Boots'"
|
||||
"itemName": "Lightbringers Boots"
|
||||
}
|
||||
]</property>
|
||||
<property name="spawn.Easy" type="bool" value="true"/>
|
||||
|
||||
@@ -74,7 +74,7 @@
|
||||
</object>
|
||||
<object id="58" template="../../obj/enemy.tx" x="153" y="516">
|
||||
<properties>
|
||||
<property name="effect" value="{ "startBattleWithCard": [ "Exquisite Blood" }"/>
|
||||
<property name="effect" value="{ "startBattleWithCard": [ "Exquisite Blood" ] }"/>
|
||||
<property name="enemy" value="Fallen Angel"/>
|
||||
<property name="threatRange" type="int" value="60"/>
|
||||
</properties>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<tileset firstgid="1" source="../../tileset/main.tsx"/>
|
||||
<tileset firstgid="10113" source="../../tileset/buildings.tsx"/>
|
||||
<tileset firstgid="11905" source="../../tileset/dungeon.tsx"/>
|
||||
<tileset firstgid="16001" source="../../tileset/main-nocollide.tsx"/>
|
||||
<tileset firstgid="16385" source="../../tileset/main-nocollide.tsx"/>
|
||||
<layer id="1" name="Background" width="35" height="25">
|
||||
<data encoding="base64" compression="zlib">
|
||||
eJybxsnAMG0AMDYwEO4YCm6hBxhIu9HBqFuwg8HmloFOsyMFqLIPtAsgAOSOUbdggsHijlEwCgYzmCM60C4YBaNg6AIAYHk9+Q==
|
||||
@@ -24,12 +24,12 @@
|
||||
</layer>
|
||||
<layer id="5" name="AboveSprites" width="35" height="25">
|
||||
<data encoding="base64" compression="zlib">
|
||||
eJzNlktuwjAQhp22Ujelq8SqVJUdN2BTIagESH0seyV6jKqAaLsADsAtOEDLgoPwGAtHHqYe26Qh5Je+lfP4PBk7FqLYDBKesnj4iICzQH6kEL/AQv4d28Tue88LrE3f845hiV0iUrNTugyT/TFbHl/L44LzeSfEBQNNE/XzKNnNCffuB1kvDWmuaeh7H6Tb5a2+TwfoAt8wVrs0+ILr4qsh57KUBpWediraRWV+a+ih+nAunNupXK5grAJco77K4xt9JYa0V2wumDGMTYAp49Ik+3aoC+1dri6Yp6oQz8BL1e6CWcdhLm14Voehq99Dv1NIv2BC9passdXJ5rKO/XvKsUJdsq4lmsjRd+rs0JKG9Do1/2O43Ou6zm520IScm961I/5X/Od/7XNxudl6Nksfr2K3iyt5u6j6pv1waPJ24faKkPM+t/fis8Mh0DWRxaUoXNkCTvTB1g==
|
||||
eJzNlk9OAjEUhztI4gZczTQmBHbcwI0hSCIslC1HgmMQlYAu1AN4A5ccAFlwENHX0Ekfz762jMMwv+Rbdf58ffPaqRDF5inhKYuHjwioBLKSQnwBa/l37Cd233tWYG0ePe+YldglIjU7pcss2R+z5XNUHhecRVOIKgNNF/XzPNnNCffuA1kvHWmu6eh7e9LtMrnapw8MgBcYa58bfMF18dWQc9lIg8pYOxXtorJsGMaoPpwL53YqlxqM1YEL1Fd5fKPnxJD2is0F8wpjb8A749Il+3aoC+1dri6Yu5YQ98CwZXfBbOMwl1t4Vp9hoN9Dv1NIv2BC9passdXJ5rKN/XvKsUJdsq4lmsjRd+rscCMN6XVq/sdwudZ1/bjcQRNybppqR/yv+M//2uficrP1bJY+/o7dLq7k7aLqm/bDocnbhdsrQs773N6Lzw6HQNdEFpeicOUXefTD2g==
|
||||
</data>
|
||||
</layer>
|
||||
<layer id="6" name="Clutter" width="35" height="25">
|
||||
<data encoding="base64" compression="zlib">
|
||||
eJxjYBgF1Ab/RUgTpxSsEIVgEMgF2rEcyA6XhPAXimLXAxK/5s/AcB2Ib/hTzy0HJfDIhSDYK5HchcuN1HDLSjz+J0WcEjAHaCYTElYOQeXbiaFimLi9GKZZPcHUdx8yQHcLDHPI4tYD8xMhQIwaXKDCgoGh0oJ8/dQEO4Du2Eklt5QHUMccEBDGkff2KTAw7FdA0LiAaCDt3RKjyMAQq4igcYHNFLoFOb0QGy6q7PjNZCazbEBOL8SGCyG30KrMBAGQ3eiYEKBVuUSqOwiBRBFMTAmgpr+p5abBDiipB2gNqFkeUwqoWR5TCigtj0cB5QAAqOk3GA==
|
||||
eJxjYBgF1Ab/RUgTpxSsEIVgEMgF2rEcyA6XhPAXimLXAxIPCwSqA+KIQOq55aAEbjnHMAR7JZK7cLmRGm5Zicf/pIhTAuYAzWRCwotDUfl2YqgYJm4vhmkWTyj13YcM0N0CwxyyuPXA/EQIEKMGF6iwYGCotCBfPzXBDqA7dlLJLd+pmPeEceS9fQoMDPsVEDQuMDWI9m6JUWRgiFVE0LiAcTBl9iOnF2LDRZUdv5nMZJYNyOmF2HAh5BZalZkgALIbHRMCtCqXSHUHIZAogokpAdT0N7XcNNgBJfUArQE1y2NKATXLY0oBpeXxKKAcAABkFTay
|
||||
</data>
|
||||
</layer>
|
||||
<layer id="7" name="AboveAboveClutter" width="35" height="25">
|
||||
@@ -52,7 +52,7 @@
|
||||
<object id="49" template="../../obj/gold.tx" x="48.3333" y="126.333"/>
|
||||
<object id="50" template="../../obj/enemy.tx" x="298.667" y="182">
|
||||
<properties>
|
||||
<property name="effect" value="{ "startBattleWithCard": [ "Black Market" }"/>
|
||||
<property name="effect" value="{ "startBattleWithCard": ["Black Market"]}"/>
|
||||
<property name="enemy" value="Cosmic Horror"/>
|
||||
<property name="threatRange" type="int" value="40"/>
|
||||
</properties>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<map version="1.10" tiledversion="1.10.1" orientation="orthogonal" renderorder="right-down" width="30" height="17" tilewidth="16" tileheight="16" infinite="0" nextlayerid="8" nextobjectid="113">
|
||||
<map version="1.10" tiledversion="1.10.1" orientation="orthogonal" renderorder="right-down" width="30" height="17" tilewidth="16" tileheight="16" infinite="0" nextlayerid="8" nextobjectid="138">
|
||||
<editorsettings>
|
||||
<export target="wastetown..tmx" format="tmx"/>
|
||||
</editorsettings>
|
||||
@@ -106,36 +106,37 @@
|
||||
<property name="threatRange" type="int" value="30"/>
|
||||
</properties>
|
||||
</object>
|
||||
<object id="87" template="../../obj/enemy.tx" x="278.667" y="203.667">
|
||||
<object id="87" template="../../obj/enemy.tx" x="276.711" y="206.275">
|
||||
<properties>
|
||||
<property name="enemy" value="Monk"/>
|
||||
<property name="threatRange" type="int" value="40"/>
|
||||
</properties>
|
||||
</object>
|
||||
<object id="88" template="../../obj/exit.tx" x="162.521" y="212.637" width="24.2714" height="6.20943"/>
|
||||
<object id="89" template="../../obj/exit.tx" x="163.046" y="180.468" width="24.2714" height="6.20943"/>
|
||||
<object id="90" template="../../obj/exit.tx" x="114.792" y="180.818" width="24.2714" height="6.20943"/>
|
||||
<object id="91" template="../../obj/exit.tx" x="114.792" y="116.48" width="24.2714" height="6.20943"/>
|
||||
<object id="92" template="../../obj/exit.tx" x="115.142" y="148.299" width="24.2714" height="6.20943"/>
|
||||
<object id="93" template="../../obj/exit.tx" x="162.696" y="117.179" width="24.2714" height="6.20943"/>
|
||||
<object id="94" template="../../obj/exit.tx" x="162.696" y="85.36" width="24.2714" height="6.20943"/>
|
||||
<object id="95" template="../../obj/exit.tx" x="114.792" y="85.36" width="24.2714" height="6.20943"/>
|
||||
<object id="96" template="../../obj/exit.tx" x="162.696" y="149.348" width="24.2714" height="6.20943"/>
|
||||
<object id="97" template="../../obj/exit.tx" x="227.034" y="148.299" width="24.2714" height="6.20943"/>
|
||||
<object id="98" template="../../obj/exit.tx" x="255.357" y="51.7924" width="15.5298" height="6.20943"/>
|
||||
<object id="99" template="../../obj/exit.tx" x="273.714" y="84.6607" width="24.621" height="6.20943"/>
|
||||
<object id="100" template="../../obj/exit.tx" x="274.413" y="117.179" width="24.621" height="6.20943"/>
|
||||
<object id="101" template="../../obj/exit.tx" x="226.16" y="212.288" width="24.621" height="6.20943"/>
|
||||
<object id="102" template="../../obj/exit.tx" x="275.113" y="148.299" width="24.621" height="6.20943"/>
|
||||
<object id="103" template="../../obj/exit.tx" x="227.558" y="180.468" width="24.621" height="6.20943"/>
|
||||
<object id="104" template="../../obj/exit.tx" x="226.16" y="116.83" width="24.621" height="6.20943"/>
|
||||
<object id="105" template="../../obj/exit.tx" x="225.81" y="84.6607" width="24.621" height="6.20943"/>
|
||||
<object id="106" template="../../obj/exit.tx" x="193.291" y="31.5119" width="29.5163" height="35.5811"/>
|
||||
<object id="107" template="../../obj/exit.tx" x="323.016" y="98.6472" width="8.18684" height="24.0422"/>
|
||||
<object id="108" template="../../obj/exit.tx" x="323.191" y="145.677" width="8.18684" height="24.0422"/>
|
||||
<object id="109" template="../../obj/exit.tx" x="98.3579" y="98.8221" width="8.18684" height="24.0422"/>
|
||||
<object id="110" template="../../obj/exit.tx" x="98.0082" y="148.474" width="8.18684" height="24.0422"/>
|
||||
<object id="111" template="../../obj/waypoint.tx" x="201.099" y="238.512"/>
|
||||
<object id="112" template="../../obj/waypoint.tx" x="201.448" y="105.641"/>
|
||||
<object id="113" template="../../obj/collision.tx" x="226.244" y="149.837" width="24.0078" height="5.4"/>
|
||||
<object id="114" template="../../obj/collision.tx" x="226.196" y="181.7" width="24.0078" height="5.4"/>
|
||||
<object id="115" template="../../obj/collision.tx" x="226.996" y="213.5" width="24.0078" height="5.4"/>
|
||||
<object id="116" template="../../obj/collision.tx" x="275.396" y="181.9" width="24.0078" height="5.4"/>
|
||||
<object id="117" template="../../obj/collision.tx" x="274.596" y="117.3" width="24.0078" height="5.4"/>
|
||||
<object id="118" template="../../obj/collision.tx" x="274.596" y="148.9" width="24.0078" height="5.4"/>
|
||||
<object id="119" template="../../obj/collision.tx" x="226.596" y="116.7" width="24.0078" height="5.4"/>
|
||||
<object id="120" template="../../obj/collision.tx" x="227.196" y="85.7" width="24.0078" height="5.4"/>
|
||||
<object id="121" template="../../obj/collision.tx" x="274.996" y="85.3" width="24.0078" height="5.4"/>
|
||||
<object id="122" template="../../obj/collision.tx" x="324.196" y="99.5" width="7.0078" height="23"/>
|
||||
<object id="123" template="../../obj/collision.tx" x="323.896" y="147.7" width="7.0078" height="23"/>
|
||||
<object id="124" template="../../obj/collision.tx" x="99.0961" y="147.9" width="7.0078" height="23"/>
|
||||
<object id="125" template="../../obj/collision.tx" x="98.4961" y="100.3" width="7.0078" height="23"/>
|
||||
<object id="126" template="../../obj/collision.tx" x="115.896" y="116.9" width="22.6078" height="6.6"/>
|
||||
<object id="127" template="../../obj/collision.tx" x="115.496" y="148.7" width="22.6078" height="6.6"/>
|
||||
<object id="128" template="../../obj/collision.tx" x="115.696" y="180.9" width="22.6078" height="6.6"/>
|
||||
<object id="129" template="../../obj/collision.tx" x="163.296" y="212.9" width="22.6078" height="6.6"/>
|
||||
<object id="130" template="../../obj/collision.tx" x="163.296" y="180.5" width="22.6078" height="6.6"/>
|
||||
<object id="131" template="../../obj/collision.tx" x="163.696" y="148.5" width="22.6078" height="6.6"/>
|
||||
<object id="132" template="../../obj/collision.tx" x="163.496" y="116.3" width="22.6078" height="6.6"/>
|
||||
<object id="133" template="../../obj/collision.tx" x="163.296" y="84.1" width="22.6078" height="6.6"/>
|
||||
<object id="134" template="../../obj/collision.tx" x="115.696" y="84.1" width="22.6078" height="6.6"/>
|
||||
<object id="135" template="../../obj/collision.tx" x="256.296" y="51.1" width="16.2078" height="8.4"/>
|
||||
<object id="136" template="../../obj/collision.tx" x="192.496" y="31.9" width="31.2078" height="36.8"/>
|
||||
</objectgroup>
|
||||
</map>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<map version="1.10" tiledversion="1.10.2" orientation="orthogonal" renderorder="right-down" width="60" height="50" tilewidth="16" tileheight="16" infinite="0" nextlayerid="13" nextobjectid="119">
|
||||
<map version="1.10" tiledversion="1.10.1" orientation="orthogonal" renderorder="right-down" width="60" height="50" tilewidth="16" tileheight="16" infinite="0" nextlayerid="13" nextobjectid="119">
|
||||
<editorsettings>
|
||||
<export target="wastetown..tmx" format="tmx"/>
|
||||
</editorsettings>
|
||||
@@ -173,7 +173,7 @@
|
||||
<property name="enemy" value="Greater Sandwurm"/>
|
||||
<property name="hidden" type="bool" value="true"/>
|
||||
<property name="speedModifier" type="float" value="40"/>
|
||||
<property name="threatRange" type="int" value="30"/>
|
||||
<property name="threatRange" type="int" value="80"/>
|
||||
</properties>
|
||||
</object>
|
||||
<object id="115" template="../../obj/enemy.tx" x="855.333" y="361.333">
|
||||
@@ -181,7 +181,7 @@
|
||||
<property name="enemy" value="Greater Sandwurm"/>
|
||||
<property name="hidden" type="bool" value="true"/>
|
||||
<property name="speedModifier" type="float" value="40"/>
|
||||
<property name="threatRange" type="int" value="30"/>
|
||||
<property name="threatRange" type="int" value="70"/>
|
||||
</properties>
|
||||
</object>
|
||||
<object id="116" template="../../obj/enemy.tx" x="294" y="367.333">
|
||||
@@ -197,7 +197,7 @@
|
||||
<property name="enemy" value="Scarab"/>
|
||||
<property name="hidden" type="bool" value="true"/>
|
||||
<property name="speedModifier" type="float" value="40"/>
|
||||
<property name="threatRange" type="int" value="50"/>
|
||||
<property name="threatRange" type="int" value="60"/>
|
||||
</properties>
|
||||
</object>
|
||||
<object id="118" template="../../obj/booster.tx" x="4" y="641.333">
|
||||
|
||||
@@ -83,7 +83,7 @@
|
||||
]
|
||||
}
|
||||
]</property>
|
||||
<property name="displayNameOverride" value="Tibalt's Pet Torturer"/>
|
||||
<property name="displayNameOverride" value="Tibalt's Torturer"/>
|
||||
<property name="effect">{ "startBattleWithCard": [ "Phyrexian Arena","Xander's Wake"]
|
||||
}</property>
|
||||
<property name="enemy" value="Torturer"/>
|
||||
|
||||
@@ -162,8 +162,8 @@
|
||||
"minHeight": 16,
|
||||
"rightWidth": 4,
|
||||
"leftWidth": 4,
|
||||
"bottomHeight": 7,
|
||||
"topHeight": 4,
|
||||
"bottomHeight": 8,
|
||||
"topHeight": 5,
|
||||
"offsetX": 0,
|
||||
"offsetY": 0,
|
||||
"offsetXspeed": 0,
|
||||
@@ -566,7 +566,7 @@
|
||||
"downFontColor": "RGBA_255_255_255_255",
|
||||
"overFontColor": "RGBA_255_255_255_255",
|
||||
"focusedFontColor": "RGBA_255_255_255_255",
|
||||
"disabledFontColor": "RGBA_255_255_255_255",
|
||||
"disabledFontColor": "RGBA_204_200_200_255",
|
||||
"checkedFontColor": "RGBA_255_255_255_255",
|
||||
"checkedDownFontColor": "RGBA_255_255_255_255",
|
||||
"checkedOverFontColor": "RGBA_255_255_255_255",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Deathknightidle.png
|
||||
deathknightidle.png
|
||||
size: 80,16
|
||||
format: RGBA8888
|
||||
filter: Nearest,Nearest
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
"width": 100,
|
||||
"height": 30,
|
||||
"x": 365,
|
||||
"y": 60
|
||||
"y": 50
|
||||
},
|
||||
{
|
||||
"type": "TextButton",
|
||||
@@ -44,7 +44,7 @@
|
||||
"width": 100,
|
||||
"height": 30,
|
||||
"x": 365,
|
||||
"y": 110
|
||||
"y": 90
|
||||
},
|
||||
{
|
||||
"type": "TextButton",
|
||||
@@ -53,7 +53,7 @@
|
||||
"width": 100,
|
||||
"height": 30,
|
||||
"x": 365,
|
||||
"y": 160
|
||||
"y": 130
|
||||
},
|
||||
{
|
||||
"type": "TextButton",
|
||||
@@ -62,6 +62,15 @@
|
||||
"width": 100,
|
||||
"height": 30,
|
||||
"x": 365,
|
||||
"y": 170
|
||||
},
|
||||
{
|
||||
"type": "TextButton",
|
||||
"name": "add",
|
||||
"text": "tr(lblAddDeck)",
|
||||
"width": 100,
|
||||
"height": 30,
|
||||
"x": 365,
|
||||
"y": 210
|
||||
}
|
||||
]
|
||||
|
||||
@@ -15,33 +15,42 @@
|
||||
"x": 4,
|
||||
"y": 4,
|
||||
"width": 262,
|
||||
"height": 414
|
||||
"height": 384
|
||||
},
|
||||
{
|
||||
"type": "TextButton",
|
||||
"name": "add",
|
||||
"text": "tr(lblAddDeck)",
|
||||
"width": 130,
|
||||
"height": 30,
|
||||
"x": 4,
|
||||
"y": 388
|
||||
},
|
||||
{
|
||||
"type": "TextButton",
|
||||
"name": "delete",
|
||||
"text": "tr(lblDelete)",
|
||||
"width": 86,
|
||||
"width": 130,
|
||||
"height": 30,
|
||||
"x": 136,
|
||||
"y": 388
|
||||
},
|
||||
{
|
||||
"type": "TextButton",
|
||||
"name": "copy",
|
||||
"text": "tr(lblCopy)",
|
||||
"width": 130,
|
||||
"height": 30,
|
||||
"x": 4,
|
||||
"y": 418
|
||||
},
|
||||
{
|
||||
"type": "TextButton",
|
||||
"name": "copy",
|
||||
"text": "tr(lblCopy)",
|
||||
"width": 86,
|
||||
"height": 30,
|
||||
"x": 92,
|
||||
"y": 418
|
||||
},
|
||||
{
|
||||
"type": "TextButton",
|
||||
"name": "rename",
|
||||
"text": "tr(lblRename)",
|
||||
"width": 86,
|
||||
"width": 130,
|
||||
"height": 30,
|
||||
"x": 180,
|
||||
"x": 136,
|
||||
"y": 418
|
||||
},
|
||||
{
|
||||
|
||||
@@ -37,10 +37,10 @@
|
||||
{
|
||||
"type": "TextButton",
|
||||
"name": "done",
|
||||
"text": "[+OK]",
|
||||
"text": "Back",
|
||||
"width": 48,
|
||||
"height": 30,
|
||||
"binding": "Use",
|
||||
"binding": "Back",
|
||||
"x": 420,
|
||||
"y": 120
|
||||
} ,
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
"width": 480,
|
||||
"height": 270,
|
||||
"yDown": true,
|
||||
"elements": [
|
||||
"elements": [
|
||||
{
|
||||
"type": "Scroll",
|
||||
"name": "map",
|
||||
"name": "map",
|
||||
"width": 480,
|
||||
"height": 270
|
||||
},
|
||||
@@ -28,6 +28,60 @@
|
||||
"height": 20,
|
||||
"x": 415,
|
||||
"y": 245
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "TextButton",
|
||||
"name": "details",
|
||||
"text": "[%80] Details",
|
||||
"width": 60,
|
||||
"height": 20,
|
||||
"x": 210,
|
||||
"y": 245
|
||||
},
|
||||
{
|
||||
"type": "TextButton",
|
||||
"name": "events",
|
||||
"text": "[%80] Events",
|
||||
"width": 60,
|
||||
"height": 20,
|
||||
"x": 210,
|
||||
"y": 245
|
||||
},
|
||||
{
|
||||
"type": "TextButton",
|
||||
"name": "reputation",
|
||||
"text": "[%80] Reputation",
|
||||
"width": 60,
|
||||
"height": 20,
|
||||
"x": 210,
|
||||
"y": 245
|
||||
},
|
||||
{
|
||||
"type": "TextButton",
|
||||
"name": "names",
|
||||
"text": "[%80] Names",
|
||||
"width": 60,
|
||||
"height": 20,
|
||||
"x": 210,
|
||||
"y": 245
|
||||
},
|
||||
{
|
||||
"type": "TextButton",
|
||||
"name": "zoomIn",
|
||||
"text": "[%80]+",
|
||||
"width": 20,
|
||||
"height": 20,
|
||||
"x": 5,
|
||||
"y": 5
|
||||
},
|
||||
{
|
||||
"type": "TextButton",
|
||||
"name": "zoomOut",
|
||||
"text": "[%80]-",
|
||||
"width": 20,
|
||||
"height": 20,
|
||||
"x": 455,
|
||||
"y": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -26,6 +26,60 @@
|
||||
"height": 20,
|
||||
"x": 205,
|
||||
"y": 455
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "TextButton",
|
||||
"name": "details",
|
||||
"text": "[%80] Details",
|
||||
"width": 60,
|
||||
"height": 20,
|
||||
"x": 105,
|
||||
"y": 245
|
||||
},
|
||||
{
|
||||
"type": "TextButton",
|
||||
"name": "events",
|
||||
"text": "[%80] Events",
|
||||
"width": 60,
|
||||
"height": 20,
|
||||
"x": 105,
|
||||
"y": 245
|
||||
},
|
||||
{
|
||||
"type": "TextButton",
|
||||
"name": "reputation",
|
||||
"text": "[%80] Reputation",
|
||||
"width": 60,
|
||||
"height": 20,
|
||||
"x": 105,
|
||||
"y": 245
|
||||
},
|
||||
{
|
||||
"type": "TextButton",
|
||||
"name": "names",
|
||||
"text": "[%80] Names",
|
||||
"width": 60,
|
||||
"height": 20,
|
||||
"x": 105,
|
||||
"y": 245
|
||||
},
|
||||
{
|
||||
"type": "TextButton",
|
||||
"name": "zoomIn",
|
||||
"text": "[%80]+",
|
||||
"width": 20,
|
||||
"height": 20,
|
||||
"x": 5,
|
||||
"y": 5
|
||||
},
|
||||
{
|
||||
"type": "TextButton",
|
||||
"name": "zoomOut",
|
||||
"text": "[%80]-",
|
||||
"width": 20,
|
||||
"height": 20,
|
||||
"x": 245,
|
||||
"y": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -141,3 +141,4 @@ Pioneer Masters, 3/6/PIO, PIO
|
||||
Innistrad Remastered, 3/6/INR, INR
|
||||
Aetherdrift, 3/6/DFT, DFT
|
||||
Tarkir Dragonstorm, 3/6/TDM, TDM
|
||||
Final Fantasy, 3/6/FIN, FIN
|
||||
|
||||
@@ -5,8 +5,8 @@ PT:3/2
|
||||
K:Flying
|
||||
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigMill | TriggerDescription$ At the beginning of your upkeep, mill a card. If an instant or sorcery card was milled this way, transform CARDNAME.
|
||||
SVar:TrigMill:DB$ Mill | Defined$ You | RememberMilled$ True | SubAbility$ DBTransform
|
||||
SVar:DBTransform:DB$ SetState | Defined$ Self | ConditionDefined$ Remembered | ConditionPresent$ Card.Instant,Card.Sorcery | SubAbility$ Cleanup | Mode$ Transform
|
||||
SVar:Cleanup:DB$ Cleanup | ClearRemembered$ True
|
||||
SVar:DBTransform:DB$ SetState | Defined$ Self | ConditionDefined$ Remembered | ConditionPresent$ Card.Instant,Card.Sorcery | SubAbility$ DBCleanup | Mode$ Transform
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||
DeckHints:Ability$Delirium & Type$Instant|Sorcery
|
||||
AlternateMode:DoubleFaced
|
||||
Oracle:Flying\nAt the beginning of your upkeep, mill a card. If an instant or sorcery card was milled this way, transform Aberrant Researcher.
|
||||
|
||||
@@ -5,4 +5,4 @@ A:SP$ Charm | Choices$ DBTokens,DBTap | CharmNum$ 1
|
||||
SVar:DBTokens:DB$ Token | TokenScript$ c_1_1_hero | TokenOwner$ You | TokenAmount$ 3 | SpellDescription$ Take the Elevator — Create three 1/1 colorless Hero creature tokens.
|
||||
SVar:DBTap:DB$ Tap | ValidTgts$ Creature | TargetMin$ 0 | TargetMax$ 3 | TgtPrompt$ Select up to three target creatures | SubAbility$ DBPutCounter | SpellDescription$ Take 59 Flights of Stairs — Tap up to three target creatures. Put a stun counter on one of them.
|
||||
SVar:DBPutCounter:DB$ PutCounter | Choices$ Creature.targetedBy | CounterType$ Stun | CounterNum$ 1
|
||||
Oracle:Choose one—\n• Take the Elevator — Create three 1/1 colorless Hero creature tokens.\n• Take 59 Flights of Stairs — Tap up to three target creatures. Put a stun counter on one of them. (If a permanent with a stun counter would become untapped, remove one from it instead.)
|
||||
Oracle:Choose one —\n• Take the Elevator — Create three 1/1 colorless Hero creature tokens.\n• Take 59 Flights of Stairs — Tap up to three target creatures. Put a stun counter on one of them. (If a permanent with a stun counter would become untapped, remove one from it instead.)
|
||||
@@ -4,6 +4,6 @@ Types:Legendary Creature Spirit Soldier
|
||||
PT:3/4
|
||||
K:Vigilance
|
||||
T:Mode$ BecomesTarget | ValidTarget$ Card.Self | ValidSource$ Ability.numTargets EQ1 | TriggerZones$ Battlefield | Execute$ TrigCopy | TriggerDescription$ Whenever CARDNAME becomes the target of an ability that targets only it, you may pay {1}{R/W}. If you do, copy that ability for each other creature you control that ability could target. Each copy targets a different one of those creatures. ({R/W} can be paid with either {R} or {W}.)
|
||||
SVar:TrigCopy:AB$ CopySpellAbility | Cost$ 1 RW | Defined$ TriggeredStackInstance | CopyForEachCanTarget$ Creature.YouCtrl
|
||||
SVar:TrigCopy:AB$ CopySpellAbility | Cost$ 1 RW | Defined$ TriggeredSpellAbility | CopyForEachCanTarget$ Creature.YouCtrl
|
||||
AI:RemoveDeck:Random
|
||||
Oracle:Vigilance\nWhenever Agrus Kos, Eternal Soldier becomes the target of an ability that targets only it, you may pay {1}{R/W}. If you do, copy that ability for each other creature you control that ability could target. Each copy targets a different one of those creatures. ({R/W} can be paid with either {R} or {W}.)
|
||||
|
||||
@@ -9,5 +9,5 @@ SVar:Mana:AB$ Mana | Cost$ T Sac<1/CARDNAME/this artifact> | Produced$ Any | Amo
|
||||
K:Class:3:4 R:AddTrigger$ TriggerExplosion
|
||||
SVar:TriggerExplosion:Mode$ SpellCast | ValidCard$ Card | ValidActivatingPlayer$ You | TriggerZones$ Battlefield | ValidSA$ Spell.ManaFromTreasure | Execute$ TrigDamage | TriggerDescription$ Whenever you cast a spell, if mana from a Treasure was spent to cast it, this Class deals damage equal to that spell's mana value to each opponent.
|
||||
SVar:TrigDamage:DB$ DealDamage | Defined$ Opponent | NumDmg$ X
|
||||
SVar:X:TriggeredStackInstance$CardManaCostLKI
|
||||
SVar:X:TriggeredSpellAbility$CardManaCostLKI
|
||||
Oracle:(Gain the next level as a sorcery to add its ability.)\nWhen Alchemist's Talent enters, create two tapped Treasure tokens.\n{1}{R}: Level 2\nTreasures you control have "{T}, Sacrifice this artifact: Add two mana of any one color."\n{4}{R}: Level 3\nWhenever you cast a spell, if mana from a Treasure was spent to cast it, this Class deals damage equal to that spell's mana value to each opponent.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user