mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-13 17:27:46 +00:00
Merge branch 'adventure' into 'master'
Add Adventure mode See merge request core-developers/forge!5318
This commit is contained in:
6
.gitlab-ci.yml
Normal file
6
.gitlab-ci.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
deploy:
|
||||
image: maven:3.6-jdk-8
|
||||
script:
|
||||
- 'mvn -U -B clean -P windows-linux install'
|
||||
only:
|
||||
- master
|
||||
BIN
forge-adventure/fallback_skin/bg_splash.png
Normal file
BIN
forge-adventure/fallback_skin/bg_splash.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 208 KiB |
BIN
forge-adventure/fallback_skin/bg_texture.jpg
Normal file
BIN
forge-adventure/fallback_skin/bg_texture.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 28 KiB |
BIN
forge-adventure/fallback_skin/font1.ttf
Normal file
BIN
forge-adventure/fallback_skin/font1.ttf
Normal file
Binary file not shown.
BIN
forge-adventure/libs/gdx-backend-lwjgl-natives.jar
Normal file
BIN
forge-adventure/libs/gdx-backend-lwjgl-natives.jar
Normal file
Binary file not shown.
BIN
forge-adventure/libs/gdx-backend-lwjgl-sources.jar
Normal file
BIN
forge-adventure/libs/gdx-backend-lwjgl-sources.jar
Normal file
Binary file not shown.
BIN
forge-adventure/libs/gdx-backend-lwjgl.jar
Normal file
BIN
forge-adventure/libs/gdx-backend-lwjgl.jar
Normal file
Binary file not shown.
BIN
forge-adventure/libs/gdx-freetype-natives.jar
Normal file
BIN
forge-adventure/libs/gdx-freetype-natives.jar
Normal file
Binary file not shown.
BIN
forge-adventure/libs/gdx-natives.jar
Normal file
BIN
forge-adventure/libs/gdx-natives.jar
Normal file
Binary file not shown.
188
forge-adventure/pom.xml
Normal file
188
forge-adventure/pom.xml
Normal file
@@ -0,0 +1,188 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>forge</artifactId>
|
||||
<groupId>forge</groupId>
|
||||
<version>1.6.45-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>forge-adventure</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>Forge Adventure</name>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>jitpack.io</id>
|
||||
<url>https://jitpack.io</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
<build>
|
||||
<sourceDirectory>src</sourceDirectory>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.8</source>
|
||||
<target>1.8</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>com.akathist.maven.plugins.launch4j</groupId>
|
||||
<artifactId>launch4j-maven-plugin</artifactId>
|
||||
<version>1.7.25</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>l4j-adv</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>launch4j</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<headerType>gui</headerType>
|
||||
<outfile>${project.build.directory}/forge-adventure.exe</outfile>
|
||||
<jar>${project.build.finalName}-jar-with-dependencies.jar</jar>
|
||||
<dontWrapJar>true</dontWrapJar>
|
||||
<errTitle>forge</errTitle>
|
||||
<icon>src/main/config/forge-adventure.ico</icon>
|
||||
<classPath>
|
||||
<mainClass>forge.adventure.Main</mainClass>
|
||||
<addDependencies>false</addDependencies>
|
||||
<preCp>anything</preCp>
|
||||
</classPath>
|
||||
<jre>
|
||||
<minVersion>1.8.0</minVersion>
|
||||
<maxHeapSize>4096</maxHeapSize>
|
||||
<opts>
|
||||
<opt>-Dfile.encoding=UTF-8</opt>
|
||||
</opts>
|
||||
</jre>
|
||||
<versionInfo>
|
||||
<fileVersion>
|
||||
1.0.0.0
|
||||
</fileVersion>
|
||||
<txtFileVersion>
|
||||
1.0.0.0
|
||||
</txtFileVersion>
|
||||
<fileDescription>Forge</fileDescription>
|
||||
<copyright>Forge</copyright>
|
||||
<productVersion>
|
||||
1.0.0.0
|
||||
</productVersion>
|
||||
<txtProductVersion>
|
||||
1.0.0.0
|
||||
</txtProductVersion>
|
||||
<productName>forge-adventure</productName>
|
||||
<internalName>forge-adventure</internalName>
|
||||
<originalFilename>forge-adventure.exe</originalFilename>
|
||||
</versionInfo>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<configuration>
|
||||
<attach>false</attach>
|
||||
<descriptorRefs>
|
||||
<descriptorRef>jar-with-dependencies</descriptorRef>
|
||||
</descriptorRefs>
|
||||
<archive>
|
||||
<manifest>
|
||||
<mainClass>forge.adventure.Main</mainClass>
|
||||
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
|
||||
</manifest>
|
||||
</archive>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>make-assembly</id>
|
||||
<!-- this is used for inheritance merges -->
|
||||
<phase>package</phase>
|
||||
<!-- bind to the packaging phase -->
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.github.jetopto1</groupId>
|
||||
<artifactId>cling</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.badlogicgames.gdx</groupId>
|
||||
<artifactId>gdx</artifactId>
|
||||
<version>1.10.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.badlogicgames.gdx</groupId>
|
||||
<artifactId>gdx-platform</artifactId>
|
||||
<version>1.10.0</version>
|
||||
<classifier>natives-desktop</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.badlogicgames.gdx</groupId>
|
||||
<artifactId>gdx-freetype</artifactId>
|
||||
<version>1.10.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.badlogicgames.gdx</groupId>
|
||||
<artifactId>gdx-backend-lwjgl3</artifactId>
|
||||
<version>1.10.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.badlogicgames.gdx</groupId>
|
||||
<artifactId>gdx-freetype-platform</artifactId>
|
||||
<version>1.10.0</version>
|
||||
<classifier>natives-desktop</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>forge</groupId>
|
||||
<artifactId>forge-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>forge</groupId>
|
||||
<artifactId>forge-game</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>forge</groupId>
|
||||
<artifactId>forge-ai</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>forge</groupId>
|
||||
<artifactId>forge-gui</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.raeleus.TenPatch</groupId>
|
||||
<artifactId>tenpatch</artifactId>
|
||||
<version>5.2.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains</groupId>
|
||||
<artifactId>annotations</artifactId>
|
||||
<version>RELEASE</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
<maven.compiler.target>8</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
</project>
|
||||
14
forge-adventure/sentry.properties
Normal file
14
forge-adventure/sentry.properties
Normal file
@@ -0,0 +1,14 @@
|
||||
# ideally this should be using HTTPS, but this is fine for now
|
||||
dsn=http://a0b8dbad9b8a49cfa51bf65d462e8dae@sentry.cardforge.org:9000/2
|
||||
stacktrace.app.packages=forge
|
||||
|
||||
# where to store events if offline or can't reach the above server
|
||||
buffer.dir=sentry-events
|
||||
buffer.size=100
|
||||
|
||||
# allow ample time for graceful shutdown
|
||||
buffer.shutdowntimeout=5000
|
||||
async.shutdowntimeout=5000
|
||||
|
||||
# allow longer messages
|
||||
maxmessagelength=1500
|
||||
BIN
forge-adventure/src/main/config/forge-adventure.ico
Normal file
BIN
forge-adventure/src/main/config/forge-adventure.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 90 KiB |
BIN
forge-adventure/src/main/config/forge.ico
Normal file
BIN
forge-adventure/src/main/config/forge.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 222 KiB |
@@ -0,0 +1,195 @@
|
||||
package forge.adventure;
|
||||
|
||||
import com.badlogic.gdx.ApplicationAdapter;
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
import com.badlogic.gdx.graphics.Pixmap;
|
||||
import com.badlogic.gdx.graphics.Texture;
|
||||
import com.badlogic.gdx.graphics.g2d.Batch;
|
||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
|
||||
import com.badlogic.gdx.graphics.g2d.TextureRegion;
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import com.badlogic.gdx.utils.ScreenUtils;
|
||||
import forge.adventure.libgdxgui.Graphics;
|
||||
import forge.adventure.scene.ForgeScene;
|
||||
import forge.adventure.scene.Scene;
|
||||
import forge.adventure.scene.SceneType;
|
||||
import forge.adventure.util.Config;
|
||||
|
||||
/**
|
||||
* Application adapter the handle switching and fading between scenes
|
||||
*/
|
||||
public class AdventureApplicationAdapter extends ApplicationAdapter {
|
||||
public static AdventureApplicationAdapter instance;
|
||||
Scene currentScene = null;
|
||||
Array<Scene> lastScene = new Array<>();
|
||||
private int currentWidth;
|
||||
private int currentHeight;
|
||||
private float animationTimeout;
|
||||
Batch animationBatch;
|
||||
Texture transitionTexture;
|
||||
TextureRegion lastScreenTexture;
|
||||
private boolean sceneWasSwapped =false;
|
||||
private Graphics graphics;
|
||||
|
||||
public Graphics getGraphics()
|
||||
{
|
||||
if(graphics==null)
|
||||
graphics=new Graphics();
|
||||
return graphics;
|
||||
}
|
||||
|
||||
public TextureRegion getLastScreenTexture() {
|
||||
return lastScreenTexture;
|
||||
}
|
||||
public AdventureApplicationAdapter() {
|
||||
instance = this;
|
||||
}
|
||||
|
||||
public int getCurrentWidth() {
|
||||
return currentWidth;
|
||||
}
|
||||
|
||||
public int getCurrentHeight() {
|
||||
return currentHeight;
|
||||
}
|
||||
|
||||
|
||||
public Scene getCurrentScene() {
|
||||
return currentScene;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resize(int w, int h) {
|
||||
currentWidth = w;
|
||||
currentHeight = h;
|
||||
StartAdventure.app.resize(w, h);
|
||||
super.resize(w, h);
|
||||
}
|
||||
|
||||
public boolean switchScene(Scene newScene) {
|
||||
|
||||
if (currentScene != null) {
|
||||
if (!currentScene.leave())
|
||||
return false;
|
||||
lastScene.add(currentScene);
|
||||
}
|
||||
storeScreen();
|
||||
sceneWasSwapped =true;
|
||||
currentScene = newScene;
|
||||
currentScene.enter();
|
||||
return true;
|
||||
}
|
||||
|
||||
private void storeScreen() {
|
||||
if(!(currentScene instanceof ForgeScene))
|
||||
lastScreenTexture = ScreenUtils.getFrameBufferTexture();
|
||||
|
||||
|
||||
}
|
||||
|
||||
public void resLoaded() {
|
||||
for (forge.adventure.scene.SceneType entry : SceneType.values()) {
|
||||
entry.instance.resLoaded();
|
||||
}
|
||||
//AdventureApplicationAdapter.CurrentAdapter.switchScene(SceneType.RewardScene.instance);
|
||||
|
||||
|
||||
switchScene(SceneType.StartScene.instance);
|
||||
animationBatch=new SpriteBatch();
|
||||
transitionTexture =new Texture(Config.instance().getFile("ui/transition.png"));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void create() {
|
||||
|
||||
Pixmap pm = new Pixmap(Config.instance().getFile("skin/cursor.png"));
|
||||
Gdx.graphics.setCursor(Gdx.graphics.newCursor(pm, 0, 0));
|
||||
pm.dispose();
|
||||
for (forge.adventure.scene.SceneType entry : SceneType.values()) {
|
||||
entry.instance.create();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render() {
|
||||
float delta=Gdx.graphics.getDeltaTime();
|
||||
float transitionTime = 0.2f;
|
||||
if(sceneWasSwapped)
|
||||
{
|
||||
sceneWasSwapped =false;
|
||||
animationTimeout= transitionTime;
|
||||
Gdx.gl.glClearColor(0, 0, 0, 1);
|
||||
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
|
||||
return;
|
||||
}
|
||||
if(animationTimeout>=0)
|
||||
{
|
||||
Gdx.gl.glClearColor(0, 0, 0, 1);
|
||||
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
|
||||
animationBatch.begin();
|
||||
animationTimeout-=delta;
|
||||
animationBatch.setColor(1,1,1,1);
|
||||
animationBatch.draw(lastScreenTexture,0,0, Gdx.graphics.getWidth(),Gdx.graphics.getHeight());
|
||||
animationBatch.setColor(1,1,1,1-(1/ transitionTime)*animationTimeout);
|
||||
animationBatch.draw(transitionTexture,0,0, Gdx.graphics.getWidth(),Gdx.graphics.getHeight());
|
||||
animationBatch.draw(transitionTexture,0,0, Gdx.graphics.getWidth(),Gdx.graphics.getHeight());
|
||||
animationBatch.end();
|
||||
if(animationTimeout<0)
|
||||
{
|
||||
currentScene.render();
|
||||
storeScreen();
|
||||
Gdx.gl.glClearColor(0, 0, 0, 1);
|
||||
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
if(animationTimeout>=-transitionTime)
|
||||
{
|
||||
Gdx.gl.glClearColor(0, 0, 0, 1);
|
||||
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
|
||||
animationBatch.begin();
|
||||
animationTimeout-=delta;
|
||||
animationBatch.setColor(1,1,1,1);
|
||||
animationBatch.draw(lastScreenTexture,0,0, Gdx.graphics.getWidth(),Gdx.graphics.getHeight());
|
||||
animationBatch.setColor(1,1,1,(1/ transitionTime)*(animationTimeout+ transitionTime));
|
||||
animationBatch.draw(transitionTexture,0,0, Gdx.graphics.getWidth(),Gdx.graphics.getHeight());
|
||||
animationBatch.draw(transitionTexture,0,0, Gdx.graphics.getWidth(),Gdx.graphics.getHeight());
|
||||
animationBatch.end();
|
||||
return;
|
||||
}
|
||||
currentScene.render();
|
||||
currentScene.act(delta);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
for (forge.adventure.scene.SceneType entry : SceneType.values()) {
|
||||
entry.instance.dispose();
|
||||
}
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
private Scene getLastScene() {
|
||||
return lastScene.size==0?null: lastScene.get(lastScene.size-1);
|
||||
}
|
||||
|
||||
public Scene switchToLast() {
|
||||
|
||||
if(lastScene.size!=0)
|
||||
{
|
||||
storeScreen();
|
||||
currentScene = lastScene.get(lastScene.size-1);
|
||||
currentScene.enter();
|
||||
sceneWasSwapped =true;
|
||||
lastScene.removeIndex(lastScene.size-1);
|
||||
return currentScene;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package forge.adventure;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import forge.interfaces.IDeviceAdapter;
|
||||
import forge.util.FileUtil;
|
||||
import forge.util.OperatingSystem;
|
||||
import forge.util.RestartUtil;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* Adapter for desktop usage
|
||||
*/
|
||||
public class DesktopAdapter implements IDeviceAdapter {
|
||||
private final String switchOrientationFile;
|
||||
|
||||
public DesktopAdapter(String switchOrientationFile0) {
|
||||
switchOrientationFile = switchOrientationFile0;
|
||||
}
|
||||
|
||||
//just assume desktop always connected to wifi
|
||||
@Override
|
||||
public boolean isConnectedToInternet() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConnectedToWifi() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDownloadsDir() {
|
||||
return System.getProperty("user.home") + "/Downloads/";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean openFile(String filename) {
|
||||
try {
|
||||
Desktop.getDesktop().open(new File(filename));
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restart() {
|
||||
if (RestartUtil.prepareForRestart()) {
|
||||
Gdx.app.exit();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exit() {
|
||||
Gdx.app.exit(); //can just use Gdx.app.exit for desktop
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTablet() {
|
||||
return true; //treat desktop the same as a tablet
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLandscapeMode(boolean landscapeMode) {
|
||||
//create file to indicate that landscape mode should be used
|
||||
if (landscapeMode) {
|
||||
FileUtil.writeFile(switchOrientationFile, "1");
|
||||
} else {
|
||||
FileUtil.deleteFile(switchOrientationFile);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preventSystemSleep(boolean preventSleep) {
|
||||
OperatingSystem.preventSystemSleep(preventSleep);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void convertToJPEG(InputStream input, OutputStream output) throws IOException {
|
||||
BufferedImage image = ImageIO.read(input);
|
||||
ImageIO.write(image, "jpg", output);
|
||||
}
|
||||
}
|
||||
299
forge-adventure/src/main/java/forge/adventure/Main.java
Normal file
299
forge-adventure/src/main/java/forge/adventure/Main.java
Normal file
@@ -0,0 +1,299 @@
|
||||
package forge.adventure;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.Input;
|
||||
import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application;
|
||||
import com.badlogic.gdx.backends.lwjgl3.Lwjgl3ApplicationConfiguration;
|
||||
import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Clipboard;
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
import com.badlogic.gdx.utils.Clipboard;
|
||||
import forge.adventure.libgdxgui.Forge;
|
||||
import forge.adventure.libgdxgui.FrameRate;
|
||||
import forge.adventure.libgdxgui.GuiMobile;
|
||||
import forge.adventure.libgdxgui.assets.AssetsDownloader;
|
||||
import forge.adventure.libgdxgui.assets.FSkin;
|
||||
import forge.adventure.libgdxgui.assets.FSkinFont;
|
||||
import forge.adventure.libgdxgui.assets.ImageCache;
|
||||
import forge.adventure.libgdxgui.screens.FScreen;
|
||||
import forge.adventure.libgdxgui.screens.SplashScreen;
|
||||
import forge.adventure.scene.SettingsScene;
|
||||
import forge.adventure.util.Config;
|
||||
import forge.error.ExceptionHandler;
|
||||
import forge.gui.FThreads;
|
||||
import forge.gui.GuiBase;
|
||||
import forge.interfaces.IDeviceAdapter;
|
||||
import forge.localinstance.properties.ForgeConstants;
|
||||
import forge.localinstance.properties.ForgePreferences;
|
||||
import forge.model.FModel;
|
||||
import forge.sound.MusicPlaylist;
|
||||
import forge.sound.SoundSystem;
|
||||
import forge.util.BuildInfo;
|
||||
import forge.util.CardTranslation;
|
||||
import forge.util.FileUtil;
|
||||
import forge.util.Localizer;
|
||||
import io.sentry.Sentry;
|
||||
import io.sentry.SentryClient;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Deque;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Wrapper to start forge first (splash screen and resources loading)
|
||||
*
|
||||
*/
|
||||
class StartAdventure extends AdventureApplicationAdapter {
|
||||
private static final int continuousRenderingCount = 1; //initialize to 1 since continuous rendering is the default
|
||||
private static final Deque<FScreen> Dscreens = new ArrayDeque<>();
|
||||
private static final boolean isloadingaMatch = false;
|
||||
public static String extrawide = "default";
|
||||
public static float heigtModifier = 0.0f;
|
||||
public static boolean showFPS = false;
|
||||
public static boolean altPlayerLayout = false;
|
||||
public static boolean altZoneTabs = false;
|
||||
public static String enableUIMask = "Crop";
|
||||
public static boolean enablePreloadExtendedArt = false;
|
||||
public static boolean isTabletDevice = false;
|
||||
public static String locale = "en-US";
|
||||
public static boolean hdbuttons = false;
|
||||
public static boolean hdstart = false;
|
||||
public static boolean isPortraitMode = false;
|
||||
public static boolean gameInProgress = false;
|
||||
public static boolean disposeTextures = false;
|
||||
public static int cacheSize = 400;
|
||||
public static int totalDeviceRAM = 0;
|
||||
public static int androidVersion = 0;
|
||||
public static boolean autoCache = false;
|
||||
public static int lastButtonIndex = 0;
|
||||
public static String CJK_Font = "";
|
||||
public static Forge app;
|
||||
private static Clipboard clipboard;
|
||||
private static IDeviceAdapter deviceAdapter;
|
||||
private static FrameRate frameRate;
|
||||
private static FScreen currentScreen;
|
||||
private static SplashScreen splashScreen;
|
||||
private static Forge.KeyInputAdapter keyInputAdapter;
|
||||
private static boolean exited;
|
||||
private static boolean textureFiltering = false;
|
||||
private static boolean destroyThis = false;
|
||||
|
||||
public StartAdventure() {
|
||||
|
||||
super();
|
||||
Forge.isTabletDevice = true;
|
||||
Forge.isPortraitMode = false;
|
||||
Forge.hdbuttons = true;
|
||||
Forge.hdstart = true;
|
||||
|
||||
String path= Files.exists(Paths.get("./res"))?"./":"../forge-gui/";
|
||||
|
||||
app = (Forge) Forge.getApp(new Lwjgl3Clipboard(), new DesktopAdapter(""), path, true, false, 0, true, 0, "", "");
|
||||
|
||||
clipboard = new Lwjgl3Clipboard();
|
||||
GuiBase.setUsingAppDirectory(false); //obb directory on android uses the package name as entrypoint
|
||||
GuiBase.setInterface(new GuiMobile(path));
|
||||
GuiBase.enablePropertyConfig(true);
|
||||
isPortraitMode = true;
|
||||
totalDeviceRAM = 0;
|
||||
GuiBase.setDeviceInfo("", "", 0, 0);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render() {
|
||||
if (splashScreen != null) {
|
||||
Gdx.gl.glClearColor(1, 0, 1, 1);
|
||||
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); // Clear the screen.
|
||||
getGraphics().begin(getCurrentWidth(), getCurrentHeight());
|
||||
splashScreen.setSize(getCurrentWidth(), getCurrentHeight());
|
||||
splashScreen.screenPos.setSize(getCurrentWidth(), getCurrentHeight());
|
||||
if (splashScreen.getRotate180()) {
|
||||
getGraphics().startRotateTransform(getCurrentWidth() / 2f, getCurrentHeight() / 2f, 180);
|
||||
}
|
||||
splashScreen.draw(getGraphics());
|
||||
if (splashScreen.getRotate180()) {
|
||||
getGraphics().endTransform();
|
||||
}
|
||||
|
||||
getGraphics().end();
|
||||
} else {
|
||||
super.render();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resize(int width, int height) {
|
||||
super.resize(width, height);
|
||||
if (splashScreen != null)
|
||||
splashScreen.setSize(width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void create() {
|
||||
//install our error handler
|
||||
ExceptionHandler.registerErrorHandling();
|
||||
splashScreen = new SplashScreen();
|
||||
frameRate = new FrameRate();
|
||||
/*
|
||||
Set CatchBackKey here and exit the app when you hit the
|
||||
back button while the textures,fonts,etc are still loading,
|
||||
to prevent rendering issue when you try to restart
|
||||
the app again (seems it doesnt dispose correctly...?!?)
|
||||
*/
|
||||
Gdx.input.setCatchKey(Input.Keys.BACK, true);
|
||||
destroyThis = true; //Prevent back()
|
||||
ForgePreferences prefs = SettingsScene.Preference = new ForgePreferences();
|
||||
|
||||
|
||||
String skinName;
|
||||
if (FileUtil.doesFileExist(ForgeConstants.MAIN_PREFS_FILE)) {
|
||||
skinName = prefs.getPref(ForgePreferences.FPref.UI_SKIN);
|
||||
} else {
|
||||
skinName = "default"; //use default skin if preferences file doesn't exist yet
|
||||
}
|
||||
FSkin.loadLight(skinName, splashScreen);
|
||||
|
||||
textureFiltering = prefs.getPrefBoolean(ForgePreferences.FPref.UI_LIBGDX_TEXTURE_FILTERING);
|
||||
showFPS = prefs.getPrefBoolean(ForgePreferences.FPref.UI_SHOW_FPS);
|
||||
altPlayerLayout = prefs.getPrefBoolean(ForgePreferences.FPref.UI_ALT_PLAYERINFOLAYOUT);
|
||||
altZoneTabs = prefs.getPrefBoolean(ForgePreferences.FPref.UI_ALT_PLAYERZONETABS);
|
||||
enableUIMask = prefs.getPref(ForgePreferences.FPref.UI_ENABLE_BORDER_MASKING);
|
||||
if (prefs.getPref(ForgePreferences.FPref.UI_ENABLE_BORDER_MASKING).equals("true")) //override old settings if not updated
|
||||
enableUIMask = "Full";
|
||||
else if (prefs.getPref(ForgePreferences.FPref.UI_ENABLE_BORDER_MASKING).equals("false"))
|
||||
enableUIMask = "Off";
|
||||
enablePreloadExtendedArt = prefs.getPrefBoolean(ForgePreferences.FPref.UI_ENABLE_PRELOAD_EXTENDED_ART);
|
||||
locale = prefs.getPref(ForgePreferences.FPref.UI_LANGUAGE);
|
||||
autoCache = prefs.getPrefBoolean(ForgePreferences.FPref.UI_AUTO_CACHE_SIZE);
|
||||
disposeTextures = prefs.getPrefBoolean(ForgePreferences.FPref.UI_ENABLE_DISPOSE_TEXTURES);
|
||||
CJK_Font = prefs.getPref(ForgePreferences.FPref.UI_CJK_FONT);
|
||||
|
||||
if (autoCache) {
|
||||
//increase cacheSize for devices with RAM more than 5GB, default is 400. Some phones have more than 10GB RAM (Mi 10, OnePlus 8, S20, etc..)
|
||||
if (totalDeviceRAM > 5000) //devices with more than 10GB RAM will have 800 Cache size, 600 Cache size for morethan 5GB RAM
|
||||
cacheSize = totalDeviceRAM > 10000 ? 800 : 600;
|
||||
}
|
||||
//init cache
|
||||
ImageCache.initCache(cacheSize);
|
||||
final Localizer localizer = Localizer.getInstance();
|
||||
|
||||
//load model on background thread (using progress bar to report progress)
|
||||
super.create();
|
||||
FThreads.invokeInBackgroundThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
//see if app or assets need updating
|
||||
AssetsDownloader.checkForUpdates(splashScreen);
|
||||
if (exited) {
|
||||
return;
|
||||
} //don't continue if user chose to exit or couldn't download required assets
|
||||
|
||||
FModel.initialize(splashScreen.getProgressBar(), null);
|
||||
|
||||
splashScreen.getProgressBar().setDescription(localizer.getMessage("lblLoadingFonts"));
|
||||
FSkinFont.preloadAll(locale);
|
||||
|
||||
splashScreen.getProgressBar().setDescription(localizer.getMessage("lblLoadingCardTranslations"));
|
||||
CardTranslation.preloadTranslation(locale, ForgeConstants.LANG_DIR);
|
||||
|
||||
splashScreen.getProgressBar().setDescription(localizer.getMessage("lblFinishingStartup"));
|
||||
|
||||
//add reminder to preload
|
||||
if (enablePreloadExtendedArt) {
|
||||
if (autoCache)
|
||||
splashScreen.getProgressBar().setDescription(localizer.getMessage("lblPreloadExtendedArt") + "\nDetected RAM: " + totalDeviceRAM + "MB. Cache size: " + cacheSize);
|
||||
else
|
||||
splashScreen.getProgressBar().setDescription(localizer.getMessage("lblPreloadExtendedArt"));
|
||||
} else {
|
||||
if (autoCache)
|
||||
splashScreen.getProgressBar().setDescription(localizer.getMessage("lblFinishingStartup") + "\nDetected RAM: " + totalDeviceRAM + "MB. Cache size: " + cacheSize);
|
||||
else
|
||||
splashScreen.getProgressBar().setDescription(localizer.getMessage("lblFinishingStartup"));
|
||||
}
|
||||
|
||||
Gdx.app.postRunnable(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
FSkin.loadFull(splashScreen);
|
||||
|
||||
SoundSystem.instance.setBackgroundMusic(MusicPlaylist.MENUS); //start background music
|
||||
destroyThis = false; //Allow back()
|
||||
Gdx.input.setCatchKey(Input.Keys.MENU, true);
|
||||
//openHomeScreen(-1); //default for startup
|
||||
splashScreen = null;
|
||||
|
||||
|
||||
//adjust height modifier
|
||||
|
||||
//update landscape mode preference if it doesn't match what the app loaded as
|
||||
if (!FModel.getPreferences().getPrefBoolean(ForgePreferences.FPref.UI_LANDSCAPE_MODE)) {
|
||||
FModel.getPreferences().setPref(ForgePreferences.FPref.UI_LANDSCAPE_MODE, true);
|
||||
FModel.getPreferences().save();
|
||||
}
|
||||
|
||||
resLoaded();
|
||||
if (!enablePreloadExtendedArt)
|
||||
return;
|
||||
List<String> borderlessCardlistkeys = FileUtil.readFile(ForgeConstants.BORDERLESS_CARD_LIST_FILE);
|
||||
if (borderlessCardlistkeys.isEmpty())
|
||||
return;
|
||||
List<String> filteredkeys = new ArrayList<>();
|
||||
for (String cardname : borderlessCardlistkeys) {
|
||||
File image = new File(ForgeConstants.CACHE_CARD_PICS_DIR + ForgeConstants.PATH_SEPARATOR + cardname + ".jpg");
|
||||
if (image.exists())
|
||||
filteredkeys.add(cardname);
|
||||
}
|
||||
if (!filteredkeys.isEmpty())
|
||||
ImageCache.preloadCache(filteredkeys);
|
||||
/* call preloadExtendedArt here, if we put it above we will *
|
||||
* get error: No OpenGL context found in the current thread. */
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
* Main entry point
|
||||
*/
|
||||
public class Main {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
Sentry.init();
|
||||
SentryClient sentryClient = Sentry.getStoredClient();
|
||||
sentryClient.setRelease(BuildInfo.getVersionString());
|
||||
sentryClient.setEnvironment(System.getProperty("os.name"));
|
||||
sentryClient.addTag("Java Version", System.getProperty("java.version"));
|
||||
|
||||
// HACK - temporary solution to "Comparison method violates it's general contract!" crash
|
||||
System.setProperty("java.util.Arrays.useLegacyMergeSort", "true");
|
||||
|
||||
//Turn off the Java 2D system's use of Direct3D to improve rendering speed (particularly when Full Screen)
|
||||
System.setProperty("sun.java2d.d3d", "false");
|
||||
|
||||
|
||||
Lwjgl3ApplicationConfiguration config = new Lwjgl3ApplicationConfiguration();
|
||||
config.setResizable(false);
|
||||
StartAdventure start=new StartAdventure();
|
||||
|
||||
if (Config.instance().getSettingData().fullScreen)
|
||||
{
|
||||
config.setFullscreenMode(Lwjgl3ApplicationConfiguration.getDisplayMode());
|
||||
} else {
|
||||
config.setWindowedMode(Config.instance().getSettingData().width, Config.instance().getSettingData().height);
|
||||
}
|
||||
|
||||
config.setWindowIcon(Config.instance().getFilePath("forge-adventure.png"));
|
||||
new Lwjgl3Application(start, config);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
package forge.adventure.character;
|
||||
|
||||
import com.badlogic.gdx.graphics.Texture;
|
||||
import com.badlogic.gdx.graphics.g2d.*;
|
||||
import com.badlogic.gdx.math.Vector2;
|
||||
import com.badlogic.gdx.scenes.scene2d.Actor;
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import com.badlogic.gdx.utils.ObjectSet;
|
||||
import forge.adventure.stage.SpriteGroup;
|
||||
import forge.adventure.util.Config;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* CharacterSprite base class for animated sprites on the map
|
||||
*/
|
||||
|
||||
public class CharacterSprite extends MapActor {
|
||||
private final HashMap<AnimationTypes, HashMap<AnimationDirections, Animation<TextureRegion>>> animations = new HashMap<>();
|
||||
float timer;
|
||||
private Animation<TextureRegion> currentAnimation = null;
|
||||
private AnimationTypes currentAnimationType = AnimationTypes.Idle;
|
||||
private AnimationDirections currentAnimationDir = AnimationDirections.None;
|
||||
private Sprite avatar;
|
||||
|
||||
public CharacterSprite(String path) {
|
||||
collisionHeight=0.4f;
|
||||
load(path);
|
||||
}
|
||||
|
||||
protected void load(String path) {
|
||||
TextureAtlas atlas = Config.instance().getAtlas(path);
|
||||
for (Texture texture : new ObjectSet.ObjectSetIterator<>( atlas.getTextures()))
|
||||
texture.setFilter(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest);
|
||||
animations.clear();
|
||||
for (AnimationTypes stand : AnimationTypes.values()) {
|
||||
if (stand == AnimationTypes.Avatar) {
|
||||
avatar = atlas.createSprite(stand.toString());
|
||||
continue;
|
||||
}
|
||||
HashMap<AnimationDirections, Animation<TextureRegion>> dirs = new HashMap<>();
|
||||
for (AnimationDirections dir : AnimationDirections.values()) {
|
||||
|
||||
Array<Sprite> anim;
|
||||
if (dir == AnimationDirections.None)
|
||||
anim = atlas.createSprites(stand.toString());
|
||||
else
|
||||
anim = atlas.createSprites(stand.toString() + dir.toString());
|
||||
if (anim.size != 0) {
|
||||
dirs.put(dir, new Animation<>(0.2f, anim));
|
||||
}
|
||||
}
|
||||
animations.put(stand, dirs);
|
||||
|
||||
}
|
||||
|
||||
|
||||
for (AnimationTypes stand : AnimationTypes.values()) {
|
||||
if (stand == AnimationTypes.Avatar) {
|
||||
continue;
|
||||
}
|
||||
HashMap<AnimationDirections, Animation<TextureRegion>> dirs = animations.get(stand);
|
||||
|
||||
if (!dirs.containsKey(AnimationDirections.None) && dirs.containsKey(AnimationDirections.Right)) {
|
||||
dirs.put(AnimationDirections.None, (dirs.get(AnimationDirections.Right)));
|
||||
}
|
||||
if (!dirs.containsKey(AnimationDirections.Right) && dirs.containsKey(AnimationDirections.None)) {
|
||||
dirs.put(AnimationDirections.Right, (dirs.get(AnimationDirections.None)));
|
||||
}
|
||||
if (!dirs.containsKey(AnimationDirections.Left) && dirs.containsKey(AnimationDirections.Right)) {
|
||||
dirs.put(AnimationDirections.Left, FlipAnimation(dirs.get(AnimationDirections.Right)));
|
||||
}
|
||||
if (dirs.containsKey(AnimationDirections.Left) && !dirs.containsKey(AnimationDirections.Right)) {
|
||||
dirs.put(AnimationDirections.Right, FlipAnimation(dirs.get(AnimationDirections.Left)));
|
||||
}
|
||||
if (!dirs.containsKey(AnimationDirections.LeftUp) && dirs.containsKey(AnimationDirections.Left)) {
|
||||
dirs.put(AnimationDirections.LeftUp, dirs.get(AnimationDirections.Left));
|
||||
}
|
||||
if (!dirs.containsKey(AnimationDirections.LeftDown) && dirs.containsKey(AnimationDirections.Left)) {
|
||||
dirs.put(AnimationDirections.LeftDown, dirs.get(AnimationDirections.Left));
|
||||
}
|
||||
if (!dirs.containsKey(AnimationDirections.RightDown) && dirs.containsKey(AnimationDirections.Right)) {
|
||||
dirs.put(AnimationDirections.RightDown, dirs.get(AnimationDirections.Right));
|
||||
}
|
||||
if (!dirs.containsKey(AnimationDirections.RightUp) && dirs.containsKey(AnimationDirections.Right)) {
|
||||
dirs.put(AnimationDirections.RightUp, dirs.get(AnimationDirections.Right));
|
||||
}
|
||||
if (!dirs.containsKey(AnimationDirections.Up) && dirs.containsKey(AnimationDirections.Right)) {
|
||||
dirs.put(AnimationDirections.Up, dirs.get(AnimationDirections.Right));
|
||||
}
|
||||
if (!dirs.containsKey(AnimationDirections.Down) && dirs.containsKey(AnimationDirections.Left)) {
|
||||
dirs.put(AnimationDirections.Down, dirs.get(AnimationDirections.Left));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
setAnimation(AnimationTypes.Idle);
|
||||
setDirection(AnimationDirections.Right);
|
||||
}
|
||||
|
||||
static public Animation<TextureRegion> FlipAnimation(Animation<TextureRegion> anim) {
|
||||
TextureRegion[] texReg = anim.getKeyFrames();
|
||||
Array<TextureRegion> newReg = new Array<>();
|
||||
for (TextureRegion reg : texReg) {
|
||||
TextureRegion cpy = new TextureRegion(reg);
|
||||
cpy.flip(true, false);
|
||||
newReg.add(cpy);
|
||||
}
|
||||
return new Animation<>(anim.getFrameDuration(), newReg);
|
||||
}
|
||||
|
||||
public void setAnimation(AnimationTypes type) {
|
||||
if (currentAnimationType != type) {
|
||||
currentAnimationType = type;
|
||||
updateAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateAnimation() {
|
||||
AnimationTypes aniType = currentAnimationType;
|
||||
AnimationDirections aniDir = currentAnimationDir;
|
||||
if (!animations.containsKey(aniType)) {
|
||||
aniType = AnimationTypes.Idle;
|
||||
}
|
||||
if (!animations.containsKey(aniType)) {
|
||||
return;
|
||||
}
|
||||
HashMap<AnimationDirections, Animation<TextureRegion>> dirs = animations.get(aniType);
|
||||
|
||||
if (!dirs.containsKey(aniDir)) {
|
||||
aniDir = AnimationDirections.Right;
|
||||
}
|
||||
if (!dirs.containsKey(aniDir)) {
|
||||
return;
|
||||
}
|
||||
currentAnimation = dirs.get(aniDir);
|
||||
}
|
||||
|
||||
public void setDirection(AnimationDirections dir) {
|
||||
if (currentAnimationDir != dir) {
|
||||
currentAnimationDir = dir;
|
||||
updateAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void positionChanged() {
|
||||
Actor parent = getParent();
|
||||
if (parent instanceof SpriteGroup) {
|
||||
((SpriteGroup) parent).UpdateActorZ(this);
|
||||
}
|
||||
super.positionChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveBy(float x, float y) {
|
||||
super.moveBy(x, y);
|
||||
if (x == 0 && y == 0) {
|
||||
return;
|
||||
}
|
||||
Vector2 vec = new Vector2(x, y);
|
||||
float degree = vec.angleDeg();
|
||||
|
||||
setAnimation(AnimationTypes.Walk);
|
||||
if (degree < 22.5)
|
||||
setDirection(AnimationDirections.Right);
|
||||
else if (degree < 22.5 + 45)
|
||||
setDirection(AnimationDirections.RightUp);
|
||||
else if (degree < 22.5 + 45 * 2)
|
||||
setDirection(AnimationDirections.Up);
|
||||
else if (degree < 22.5 + 45 * 3)
|
||||
setDirection(AnimationDirections.LeftUp);
|
||||
else if (degree < 22.5 + 45 * 4)
|
||||
setDirection(AnimationDirections.Left);
|
||||
else if (degree < 22.5 + 45 * 5)
|
||||
setDirection(AnimationDirections.LeftDown);
|
||||
else if (degree < 22.5 + 45 * 6)
|
||||
setDirection(AnimationDirections.Down);
|
||||
else if (degree < 22.5 + 45 * 7)
|
||||
setDirection(AnimationDirections.RightDown);
|
||||
else
|
||||
setDirection(AnimationDirections.Right);
|
||||
|
||||
}
|
||||
|
||||
public Vector2 pos() {
|
||||
return new Vector2(getX(), getY());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void act(float delta) {
|
||||
timer += delta;
|
||||
super.act(delta);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Batch batch, float parentAlpha) {
|
||||
if (currentAnimation == null)
|
||||
return;
|
||||
TextureRegion currentFrame = currentAnimation.getKeyFrame(timer, true);
|
||||
setHeight(currentFrame.getRegionHeight());
|
||||
setWidth(currentFrame.getRegionWidth());
|
||||
batch.draw(currentFrame, getX(), getY());
|
||||
super.draw(batch,parentAlpha);
|
||||
//batch.draw(getDebugTexture(),getX(),getY());
|
||||
|
||||
}
|
||||
|
||||
public Sprite getAvatar() {
|
||||
return avatar;
|
||||
}
|
||||
|
||||
public enum AnimationTypes {
|
||||
Idle,
|
||||
Walk,
|
||||
Death,
|
||||
Attack,
|
||||
Hit,
|
||||
Avatar
|
||||
}
|
||||
|
||||
public enum AnimationDirections {
|
||||
|
||||
None,
|
||||
Right,
|
||||
RightDown,
|
||||
Down,
|
||||
LeftDown,
|
||||
Left,
|
||||
LeftUp,
|
||||
Up,
|
||||
RightUp
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package forge.adventure.character;
|
||||
|
||||
import com.badlogic.gdx.math.Vector2;
|
||||
import com.badlogic.gdx.scenes.scene2d.Actor;
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import forge.adventure.data.EnemyData;
|
||||
import forge.adventure.data.RewardData;
|
||||
import forge.adventure.util.Current;
|
||||
import forge.adventure.util.Reward;
|
||||
|
||||
/**
|
||||
* EnemySprite
|
||||
* Character sprite that represents an Enemy
|
||||
*/
|
||||
public class EnemySprite extends CharacterSprite {
|
||||
EnemyData data;
|
||||
private int id;
|
||||
|
||||
public EnemySprite(EnemyData enemyData) {
|
||||
super(enemyData.sprite);
|
||||
|
||||
data = enemyData;
|
||||
}
|
||||
|
||||
public EnemySprite(int id, EnemyData enemyData) {
|
||||
this(enemyData);
|
||||
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public void moveTo(Actor other, float delta) {
|
||||
Vector2 diff = new Vector2(other.getX(), other.getY()).sub(pos());
|
||||
|
||||
diff.setLength(data.speed*delta);
|
||||
moveBy(diff.x, diff.y);
|
||||
}
|
||||
|
||||
public EnemyData getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
public Array<Reward> getRewards() {
|
||||
Array<Reward> ret=new Array<Reward>();
|
||||
if(data.rewards==null)
|
||||
return ret;
|
||||
for(RewardData rdata:data.rewards)
|
||||
{
|
||||
ret.addAll(rdata.generate(false,Current.latestDeck()!=null? Current.latestDeck().getMain().toFlatList():null));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
package forge.adventure.character;
|
||||
|
||||
import forge.adventure.scene.SceneType;
|
||||
import forge.adventure.scene.TileMapScene;
|
||||
import forge.adventure.stage.MapStage;
|
||||
|
||||
/**
|
||||
* EntryActor
|
||||
* Used to teleport the player in and out of the map
|
||||
*/
|
||||
public class EntryActor extends MapActor{
|
||||
private final MapStage stage;
|
||||
private final int id;
|
||||
String targetMap;
|
||||
|
||||
public EntryActor(MapStage stage,String sourceMap, int id,String targetMap,float x,float y,float w,float h,String direction)
|
||||
{
|
||||
this.stage = stage;
|
||||
this.id = id;
|
||||
this.targetMap = targetMap;
|
||||
|
||||
|
||||
if((targetMap==null||targetMap.isEmpty()&&sourceMap.isEmpty())||//if target is null and "from world"
|
||||
!sourceMap.isEmpty()&&targetMap.equals(sourceMap)) //or if source is this target
|
||||
{
|
||||
switch(direction)
|
||||
{
|
||||
case "up":
|
||||
stage.GetPlayer().setPosition(x,y+h);
|
||||
break;
|
||||
case "down":
|
||||
stage.GetPlayer().setPosition(x,y-stage.GetPlayer().getHeight());
|
||||
break;
|
||||
case "right":
|
||||
stage.GetPlayer().setPosition(x-stage.GetPlayer().getWidth(),y);
|
||||
break;
|
||||
case "left":
|
||||
stage.GetPlayer().setPosition(x+w,y);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public MapStage getMapStage()
|
||||
{
|
||||
return stage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerCollide()
|
||||
{
|
||||
if(targetMap==null||targetMap.isEmpty())
|
||||
{
|
||||
stage.exit();
|
||||
}
|
||||
else
|
||||
{
|
||||
((TileMapScene)SceneType.TileMapScene.instance).loadNext(targetMap);
|
||||
}
|
||||
}
|
||||
|
||||
public int getObjectID() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
package forge.adventure.character;
|
||||
|
||||
import com.badlogic.gdx.graphics.Pixmap;
|
||||
import com.badlogic.gdx.graphics.Texture;
|
||||
import com.badlogic.gdx.graphics.g2d.Batch;
|
||||
import com.badlogic.gdx.math.Rectangle;
|
||||
import com.badlogic.gdx.scenes.scene2d.Actor;
|
||||
|
||||
/**
|
||||
* Map Actor base class for Actors on the map
|
||||
* implements collision detection.
|
||||
*/
|
||||
public class MapActor extends Actor {
|
||||
|
||||
|
||||
Texture debugTexture;
|
||||
float collisionHeight=1.0f;
|
||||
private Texture getDebugTexture() {
|
||||
if (debugTexture == null) {
|
||||
Pixmap pixmap = new Pixmap((int) getWidth(), (int) getHeight(), Pixmap.Format.RGBA8888);
|
||||
pixmap.setColor(1.0f,0,0,0.5f);
|
||||
pixmap.fillRectangle(0, (int) getHeight()- (int)boundingRect.getHeight(), (int) boundingRect.getWidth(), (int) boundingRect.getHeight());
|
||||
debugTexture = new Texture(pixmap);
|
||||
pixmap.dispose();
|
||||
|
||||
}
|
||||
return debugTexture;
|
||||
}
|
||||
Rectangle boundingRect;
|
||||
|
||||
boolean isCollidingWithPlayer=false;
|
||||
protected void onPlayerCollide()
|
||||
{
|
||||
|
||||
}
|
||||
boolean boundDebug=false;
|
||||
public void setBoundDebug(boolean debug)
|
||||
{
|
||||
boundDebug=debug;
|
||||
}
|
||||
@Override
|
||||
public void draw(Batch batch, float alpha) {
|
||||
|
||||
if(boundDebug)
|
||||
batch.draw(getDebugTexture(),getX(),getY());
|
||||
}
|
||||
@Override
|
||||
protected void positionChanged() {
|
||||
|
||||
updateBoundingRect();
|
||||
super.positionChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void sizeChanged() {
|
||||
super.sizeChanged();
|
||||
updateBoundingRect();
|
||||
}
|
||||
|
||||
void updateBoundingRect() {
|
||||
boundingRect = new Rectangle(getX(), getY(), getWidth(), getHeight()*collisionHeight);
|
||||
}
|
||||
|
||||
public Rectangle boundingRect() {
|
||||
return boundingRect;
|
||||
}
|
||||
public boolean collideWithPlayer(PlayerSprite other) {
|
||||
|
||||
|
||||
boolean newIsColliding= collideWith(other);
|
||||
if(newIsColliding)
|
||||
{
|
||||
if(!isCollidingWithPlayer)
|
||||
onPlayerCollide();
|
||||
isCollidingWithPlayer=true;
|
||||
}
|
||||
else
|
||||
{
|
||||
isCollidingWithPlayer=false;
|
||||
}
|
||||
return isCollidingWithPlayer;
|
||||
}
|
||||
public boolean collideWith(Rectangle other) {
|
||||
return boundingRect().overlaps(other);
|
||||
|
||||
}
|
||||
|
||||
public boolean collideWith(MapActor other) {
|
||||
return collideWith(other.boundingRect());
|
||||
}
|
||||
|
||||
public boolean collideWith(Actor other) {
|
||||
return boundingRect.x < other.getX() + other.getWidth() && boundingRect.x + boundingRect.width > other.getX() && boundingRect.y < other.getY() + other.getHeight() && boundingRect.y + boundingRect.height > other.getY();
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package forge.adventure.character;
|
||||
|
||||
/**
|
||||
* Designed to add anonymous class for a single action on collision
|
||||
*/
|
||||
public class OnCollide extends MapActor {
|
||||
|
||||
Runnable onCollide;
|
||||
public OnCollide(Runnable func) {
|
||||
onCollide = func;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPlayerCollide() {
|
||||
try {
|
||||
onCollide.run();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package forge.adventure.character;
|
||||
|
||||
import com.badlogic.gdx.math.Vector2;
|
||||
import forge.adventure.stage.GameStage;
|
||||
import forge.adventure.util.Config;
|
||||
import forge.adventure.util.Current;
|
||||
import forge.adventure.world.AdventurePlayer;
|
||||
|
||||
/**
|
||||
* Class that will represent the player sprite on the map
|
||||
*/
|
||||
public class PlayerSprite extends CharacterSprite {
|
||||
private final float playerSpeed;
|
||||
private final Vector2 direction = Vector2.Zero.cpy();
|
||||
private float playerSpeedModifier = 1f;
|
||||
GameStage gameStage;
|
||||
public PlayerSprite(GameStage gameStage) {
|
||||
super(AdventurePlayer.current().spriteName());
|
||||
this.gameStage=gameStage;
|
||||
setOriginX(getWidth() / 2);
|
||||
Current.player().onPlayerChanged(()->updatePlayer());
|
||||
playerSpeed=Config.instance().getConfigData().playerBaseSpeed;
|
||||
}
|
||||
|
||||
private void updatePlayer() {
|
||||
load(AdventurePlayer.current().spriteName());
|
||||
}
|
||||
|
||||
public void LoadPos() {
|
||||
setPosition(AdventurePlayer.current().getWorldPosX(), AdventurePlayer.current().getWorldPosY());
|
||||
}
|
||||
|
||||
public void storePos() {
|
||||
AdventurePlayer.current().setWorldPosX(getX());
|
||||
AdventurePlayer.current().setWorldPosY(getY());
|
||||
}
|
||||
|
||||
public Vector2 getMovementDirection() {
|
||||
return direction;
|
||||
}
|
||||
|
||||
public void setMovementDirection(final Vector2 dir) {
|
||||
direction.set(dir);
|
||||
}
|
||||
|
||||
public void setMoveModifier(float speed) {
|
||||
playerSpeedModifier = speed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void act(float delta) {
|
||||
super.act(delta);
|
||||
direction.setLength(playerSpeed * delta * playerSpeedModifier);
|
||||
|
||||
if(!direction.isZero())
|
||||
{
|
||||
|
||||
gameStage.prepareCollision(pos(),direction,boundingRect);
|
||||
direction.set(gameStage.adjustMovement(direction,boundingRect));
|
||||
moveBy(direction.x, direction.y);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public boolean isMoving() {
|
||||
return !direction.isZero();
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
direction.setZero();
|
||||
setAnimation(AnimationTypes.Idle);
|
||||
}
|
||||
|
||||
public void setPosition(Vector2 oldPosition) {
|
||||
setPosition(oldPosition.x,oldPosition.y);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package forge.adventure.character;
|
||||
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import forge.adventure.AdventureApplicationAdapter;
|
||||
import forge.adventure.scene.RewardScene;
|
||||
import forge.adventure.scene.SceneType;
|
||||
import forge.adventure.stage.MapStage;
|
||||
import forge.adventure.util.Reward;
|
||||
|
||||
/**
|
||||
* Map actor that will open the Shop on collision
|
||||
*/
|
||||
public class ShopActor extends MapActor{
|
||||
private final MapStage stage;
|
||||
private final int id;
|
||||
Array<Reward> rewardData;
|
||||
|
||||
public ShopActor(MapStage stage, int id, Array<Reward> rewardData)
|
||||
{
|
||||
this.stage = stage;
|
||||
this.id = id;
|
||||
this.rewardData = rewardData;
|
||||
|
||||
}
|
||||
|
||||
public MapStage getMapStage()
|
||||
{
|
||||
return stage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerCollide()
|
||||
{
|
||||
|
||||
stage.GetPlayer().stop();
|
||||
((RewardScene) SceneType.RewardScene.instance).loadRewards(rewardData, RewardScene.Type.Shop,this);
|
||||
AdventureApplicationAdapter.instance.switchScene(SceneType.RewardScene.instance);
|
||||
}
|
||||
|
||||
public int getObjectID() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package forge.adventure.character;
|
||||
|
||||
import com.badlogic.gdx.graphics.g2d.Batch;
|
||||
import com.badlogic.gdx.graphics.g2d.TextureRegion;
|
||||
|
||||
/**
|
||||
* Class to add sprites to a map
|
||||
*/
|
||||
public class TextureSprite extends MapActor{
|
||||
|
||||
private final TextureRegion region;
|
||||
|
||||
public TextureSprite(TextureRegion region)
|
||||
{
|
||||
|
||||
this.region = region;
|
||||
setWidth(region.getRegionWidth());
|
||||
setHeight(region.getRegionHeight());
|
||||
}
|
||||
@Override
|
||||
public void draw (Batch batch, float parentAlpha) {
|
||||
batch.draw(region,getX(),getY(),getWidth(),getHeight());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package forge.adventure.data;
|
||||
|
||||
import com.badlogic.gdx.graphics.Color;
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* Data class that will be used to read Json configuration files
|
||||
* BiomeData
|
||||
* contains the information for the biomes
|
||||
*/
|
||||
public class BiomeData implements Serializable {
|
||||
private final Random rand = new Random();
|
||||
public float startPointX;
|
||||
public float startPointY;
|
||||
public float noiseWeight;
|
||||
public float distWeight;
|
||||
public String name;
|
||||
public String tilesetAtlas;
|
||||
public String tilesetName;
|
||||
public BiomeTerrainData[] terrain;
|
||||
public float width;
|
||||
public float height;
|
||||
public String color;
|
||||
public boolean invertHeight;
|
||||
public String[] spriteNames;
|
||||
public List<String> enemies;
|
||||
public List<String> pointsOfInterest;
|
||||
|
||||
private ArrayList<EnemyData> enemyList;
|
||||
private ArrayList<PointOfInterestData> pointOfInterestList;
|
||||
|
||||
public Color GetColor() {
|
||||
return Color.valueOf(color);
|
||||
}
|
||||
|
||||
public ArrayList<EnemyData> getEnemyList() {
|
||||
if (enemyList == null) {
|
||||
enemyList = new ArrayList<EnemyData>();
|
||||
if (enemies == null)
|
||||
return enemyList;
|
||||
for (EnemyData data : new Array.ArrayIterator<>(WorldData.getAllEnemies())) {
|
||||
if (enemies.contains(data.name)) {
|
||||
enemyList.add(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
return enemyList;
|
||||
}
|
||||
|
||||
public ArrayList<PointOfInterestData> getPointsOfInterest() {
|
||||
if (pointOfInterestList == null) {
|
||||
pointOfInterestList = new ArrayList<PointOfInterestData>();
|
||||
if(pointsOfInterest==null)
|
||||
return pointOfInterestList;
|
||||
Array<PointOfInterestData> allTowns = PointOfInterestData.getAllPointOfInterest();
|
||||
for (PointOfInterestData data : new Array.ArrayIterator<>(allTowns)) {
|
||||
if (pointsOfInterest.contains(data.name)) {
|
||||
pointOfInterestList.add(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
return pointOfInterestList;
|
||||
}
|
||||
|
||||
public EnemyData getEnemy(float difficultyFactor) {
|
||||
EnemyData bestData=null;
|
||||
float biggestNumber=0;
|
||||
for (EnemyData data : enemyList) {
|
||||
float newNumber=data.spawnRate *rand.nextFloat()*difficultyFactor;
|
||||
if (newNumber>biggestNumber) {
|
||||
biggestNumber=newNumber;
|
||||
bestData=data;
|
||||
}
|
||||
}
|
||||
return bestData;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package forge.adventure.data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Data class that will be used to read Json configuration files
|
||||
* BiomeSpriteData
|
||||
* contains the information for the sprites on the map like trees and rocks
|
||||
*/
|
||||
public class BiomeSpriteData implements Serializable {
|
||||
public String name;
|
||||
public double startArea;
|
||||
public double endArea;
|
||||
public double density;
|
||||
public double resolution;
|
||||
public int layer;
|
||||
|
||||
public String key() {
|
||||
return "BiomeSprite&" + name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package forge.adventure.data;
|
||||
|
||||
|
||||
/**
|
||||
* Data class that will be used to read Json configuration files
|
||||
* BiomeData
|
||||
* contains the information for the terrain distribution
|
||||
*/
|
||||
public class BiomeTerrainData {
|
||||
//sprite name in the biome atlas file
|
||||
public String spriteName;
|
||||
//minimum noise value where to place the terrain
|
||||
public float min;
|
||||
//maximum noise value where to place the terrain
|
||||
public float max;
|
||||
// factor for the noise resolution
|
||||
public float resolution;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package forge.adventure.data;
|
||||
|
||||
|
||||
/**
|
||||
* Data class that will be used to read Json configuration files
|
||||
* BiomeData
|
||||
* contains general information about the game
|
||||
*/
|
||||
public class ConfigData {
|
||||
public int screenWidth;
|
||||
public int screenHeight;
|
||||
public String skin;
|
||||
public String font;
|
||||
public String fontColor;
|
||||
public int minDeckSize;
|
||||
public float playerBaseSpeed;
|
||||
public String[] starterDecks;
|
||||
public DifficultyData[] difficulties;
|
||||
public RewardData legalCards;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package forge.adventure.data;
|
||||
|
||||
/**
|
||||
* Data class that will be used to read Json configuration files
|
||||
* BiomeData
|
||||
* contains the information for the difficulties
|
||||
*/
|
||||
public class DifficultyData {
|
||||
public String name="";
|
||||
public int startingLife=10;
|
||||
public int staringMoney=10;
|
||||
public float enemyLifeFactor=1;
|
||||
public boolean startingDifficulty;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package forge.adventure.data;
|
||||
|
||||
import forge.adventure.util.CardUtil;
|
||||
import forge.deck.Deck;
|
||||
|
||||
/**
|
||||
* Data class that will be used to read Json configuration files
|
||||
* BiomeData
|
||||
* contains the information of enemies
|
||||
*/
|
||||
public class EnemyData {
|
||||
public String name;
|
||||
public String sprite;
|
||||
public String deck;
|
||||
public float spawnRate;
|
||||
public float difficulty;
|
||||
public float speed;
|
||||
public int life;
|
||||
public RewardData[] rewards;
|
||||
|
||||
public EnemyData()
|
||||
{
|
||||
|
||||
}
|
||||
public EnemyData(EnemyData enemyData) {
|
||||
name =enemyData.name;
|
||||
sprite =enemyData.sprite;
|
||||
deck =enemyData.deck;
|
||||
spawnRate =enemyData.spawnRate;
|
||||
difficulty =enemyData.difficulty ;
|
||||
speed =enemyData.speed;
|
||||
life =enemyData.life;
|
||||
if(enemyData.rewards==null)
|
||||
{
|
||||
rewards=null;
|
||||
}
|
||||
else
|
||||
{
|
||||
rewards =new RewardData[enemyData.rewards.length];
|
||||
for(int i=0;i<rewards.length;i++)
|
||||
rewards[i]=new RewardData(enemyData.rewards[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public Deck generateDeck() {
|
||||
return CardUtil.getDeck(deck);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package forge.adventure.data;
|
||||
|
||||
|
||||
/**
|
||||
* Data class that will be used to read Json configuration files
|
||||
* BiomeData
|
||||
* contains the information for a generated deck
|
||||
*
|
||||
* if the template is null then it will use just the reward information of mainDeck and sideBoard
|
||||
*/
|
||||
public class GeneratedDeckData {
|
||||
public String name;
|
||||
public GeneratedDeckTemplateData template;
|
||||
public RewardData[] mainDeck;
|
||||
public RewardData[] sideBoard;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package forge.adventure.data;
|
||||
|
||||
/**
|
||||
* Data class that will be used to read Json configuration files
|
||||
* BiomeData
|
||||
* contains the information for the generated deck
|
||||
*/
|
||||
public class GeneratedDeckTemplateData {
|
||||
public String[] colors;
|
||||
public int count;
|
||||
public float rares;
|
||||
public String tribe;
|
||||
public float tribeSynergyCards=0.6f;
|
||||
public float tribeCards=1.0f;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package forge.adventure.data;
|
||||
|
||||
/**
|
||||
* Data class that will be used to read Json configuration files
|
||||
* BiomeData
|
||||
* contains the information possible hero sprite
|
||||
*/
|
||||
public class HeroData {
|
||||
public String name;
|
||||
public String female;
|
||||
public String male;
|
||||
public String femaleAvatar;
|
||||
public String maleAvatar;
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package forge.adventure.data;
|
||||
|
||||
import com.badlogic.gdx.files.FileHandle;
|
||||
import com.badlogic.gdx.graphics.Texture;
|
||||
import com.badlogic.gdx.graphics.g2d.Sprite;
|
||||
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
|
||||
import com.badlogic.gdx.graphics.g2d.TextureRegion;
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import com.badlogic.gdx.utils.Json;
|
||||
import forge.adventure.util.Config;
|
||||
import forge.adventure.util.Paths;
|
||||
|
||||
/**
|
||||
* Data class that will be used to read Json configuration files
|
||||
* BiomeData
|
||||
* contains the a list of all heroes
|
||||
*/
|
||||
public class HeroListData {
|
||||
static private HeroListData instance;
|
||||
public HeroData[] heroes;
|
||||
public String avatar;
|
||||
private TextureAtlas avatarSprites;
|
||||
|
||||
static private HeroListData read() {
|
||||
Json json = new Json();
|
||||
FileHandle handle = Config.instance().getFile(Paths.HEROES);
|
||||
if (handle.exists()) {
|
||||
instance = json.fromJson(HeroListData.class, handle);
|
||||
instance.avatarSprites = Config.instance().getAtlas(instance.avatar);
|
||||
|
||||
instance.avatarSprites.getTextures().first().setFilter(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest);
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
static public String getHero(int raceIndex, boolean female) {
|
||||
if (instance == null)
|
||||
instance = read();
|
||||
HeroData data = instance.heroes[raceIndex];
|
||||
|
||||
if (female)
|
||||
return data.female;
|
||||
return data.male;
|
||||
|
||||
}
|
||||
|
||||
public static TextureRegion getAvatar(int heroRace, boolean isFemale, int avatarIndex) {
|
||||
|
||||
if (instance == null)
|
||||
instance = read();
|
||||
HeroData data = instance.heroes[heroRace];
|
||||
Array<Sprite> sprites;
|
||||
if (isFemale)
|
||||
sprites = instance.avatarSprites.createSprites(data.femaleAvatar);
|
||||
else
|
||||
sprites = instance.avatarSprites.createSprites(data.maleAvatar);
|
||||
avatarIndex %= sprites.size;
|
||||
if (avatarIndex < 0) {
|
||||
avatarIndex += sprites.size;
|
||||
}
|
||||
return sprites.get(avatarIndex);
|
||||
}
|
||||
|
||||
public static Array<String> getRaces() {
|
||||
if (instance == null)
|
||||
instance = read();
|
||||
Array<String> ret = new Array<>();
|
||||
for (HeroData hero : instance.heroes) {
|
||||
ret.add(hero.name);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package forge.adventure.data;
|
||||
|
||||
import com.badlogic.gdx.files.FileHandle;
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import com.badlogic.gdx.utils.Json;
|
||||
import forge.adventure.util.Config;
|
||||
import forge.adventure.util.Paths;
|
||||
|
||||
/**
|
||||
* Data class that will be used to read Json configuration files
|
||||
* BiomeData
|
||||
* contains the information for the point of interests like towns and dungeons
|
||||
*/
|
||||
public class PointOfInterestData {
|
||||
public String name;
|
||||
public String type;
|
||||
public int count;
|
||||
public String spriteAtlas;
|
||||
public String sprite;
|
||||
public String map;
|
||||
public float radiusFactor;
|
||||
|
||||
|
||||
|
||||
private static Array<PointOfInterestData> pointOfInterestList;
|
||||
public static Array<PointOfInterestData> getAllPointOfInterest() {
|
||||
if (pointOfInterestList == null) {
|
||||
Json json = new Json();
|
||||
FileHandle handle = Config.instance().getFile(Paths.POINTS_OF_INTEREST);
|
||||
if (handle.exists()) {
|
||||
Array readJson = json.fromJson(Array.class, PointOfInterestData.class, handle);
|
||||
pointOfInterestList = readJson;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
return pointOfInterestList;
|
||||
}
|
||||
public static PointOfInterestData getPointOfInterest(String name) {
|
||||
for(PointOfInterestData data: new Array.ArrayIterator<>(getAllPointOfInterest()))
|
||||
{
|
||||
if(data.name.equals(name))
|
||||
return data;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
package forge.adventure.data;
|
||||
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import com.google.common.collect.Iterables;
|
||||
import forge.StaticData;
|
||||
import forge.adventure.util.CardUtil;
|
||||
import forge.adventure.util.Config;
|
||||
import forge.adventure.util.Reward;
|
||||
import forge.adventure.world.WorldSave;
|
||||
import forge.item.PaperCard;
|
||||
import forge.model.FModel;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Data class that will be used to read Json configuration files
|
||||
* BiomeData
|
||||
* contains the information for a "reward"
|
||||
* that can be a random card, gold or items.
|
||||
* Also used for deck generation and shops
|
||||
*/
|
||||
public class RewardData {
|
||||
public String type;
|
||||
public float probability;
|
||||
public int count;
|
||||
public int addMaxCount;
|
||||
public String cardName;
|
||||
public String itemName;
|
||||
public String[] editions;
|
||||
public String[] colors;
|
||||
public String[] rarity;
|
||||
public String[] subTypes;
|
||||
public String[] cardTypes;
|
||||
public String[] superTypes;
|
||||
public int[] manaCosts;
|
||||
public String[] keyWords;
|
||||
public String colorType;
|
||||
public String cardText;
|
||||
public boolean matchAllSubTypes;
|
||||
|
||||
|
||||
public RewardData()
|
||||
{
|
||||
|
||||
}
|
||||
public RewardData(RewardData rewardData) {
|
||||
type =rewardData.type;
|
||||
probability =rewardData.probability;
|
||||
count =rewardData.count;
|
||||
addMaxCount =rewardData.addMaxCount;
|
||||
cardName =rewardData.cardName;
|
||||
itemName =rewardData.itemName;
|
||||
editions =rewardData.editions==null?null:rewardData.editions.clone();
|
||||
colors =rewardData.colors==null?null:rewardData.colors.clone();
|
||||
rarity =rewardData.rarity==null?null:rewardData.rarity.clone();
|
||||
subTypes =rewardData.subTypes==null?null:rewardData.subTypes.clone();
|
||||
cardTypes =rewardData.cardTypes==null?null:rewardData.cardTypes.clone();
|
||||
superTypes =rewardData.superTypes==null?null:rewardData.superTypes.clone();
|
||||
manaCosts =rewardData.manaCosts==null?null:rewardData.manaCosts.clone();
|
||||
keyWords =rewardData.keyWords==null?null:rewardData.keyWords.clone();
|
||||
colorType =rewardData.colorType;
|
||||
cardText =rewardData.cardText;
|
||||
matchAllSubTypes =rewardData.matchAllSubTypes;
|
||||
}
|
||||
|
||||
private static Iterable<PaperCard> allCards;
|
||||
private static Iterable<PaperCard> allEnemyCards;
|
||||
public Array<Reward> generate(boolean isForEnemy)
|
||||
{
|
||||
return generate(isForEnemy,null);
|
||||
}
|
||||
public Array<Reward> generate(boolean isForEnemy,Iterable<PaperCard> cards)
|
||||
{
|
||||
if(allCards==null)
|
||||
{
|
||||
RewardData legals=Config.instance().getConfigData().legalCards;
|
||||
if(legals==null)
|
||||
{
|
||||
allCards = FModel.getMagicDb().getCommonCards().getUniqueCardsNoAlt();
|
||||
}
|
||||
else
|
||||
{
|
||||
allCards = Iterables.filter(FModel.getMagicDb().getCommonCards().getUniqueCardsNoAlt(), new CardUtil.CardPredicate(legals, true));
|
||||
}
|
||||
allEnemyCards=Iterables.filter(allCards, input -> {
|
||||
if(input==null)return false;
|
||||
return !input.getRules().getAiHints().getRemAIDecks();
|
||||
});
|
||||
}
|
||||
Array<Reward> ret=new Array<>();
|
||||
if(probability==0|| WorldSave.getCurrentSave().getWorld().getRandom().nextFloat()<=probability)
|
||||
{
|
||||
if(type==null||type.isEmpty())
|
||||
type="randomCard";
|
||||
int addedCount=(int)((float)(addMaxCount)* WorldSave.getCurrentSave().getWorld().getRandom().nextFloat());
|
||||
|
||||
switch(type)
|
||||
{
|
||||
case "card":
|
||||
case "randomCard":
|
||||
if(cardName!=null&&!cardName.isEmpty())
|
||||
{
|
||||
for(int i=0;i<count+addedCount;i++)
|
||||
{
|
||||
ret.add(new Reward(StaticData.instance().getCommonCards().getCard(cardName)));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(PaperCard card:CardUtil.generateCards(isForEnemy?allEnemyCards:allCards,this, count+addedCount))
|
||||
{
|
||||
ret.add(new Reward(card));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "deckCard":
|
||||
if(cards==null)return ret;
|
||||
for(PaperCard card: CardUtil.generateCards(cards,this, count+addedCount))
|
||||
{
|
||||
ret.add(new Reward(card));
|
||||
}
|
||||
break;
|
||||
case "gold":
|
||||
ret.add(new Reward(count+addedCount));
|
||||
break;
|
||||
case "life":
|
||||
ret.add(new Reward(Reward.Type.Life, count+addedCount));
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static public List<PaperCard> generateAllCards(Iterable<RewardData> dataList, boolean isForEnemy)
|
||||
{
|
||||
|
||||
return rewardsToCards(generateAll(dataList, isForEnemy));
|
||||
}
|
||||
static public Iterable<Reward> generateAll(Iterable<RewardData> dataList, boolean isForEnemy)
|
||||
{
|
||||
Array<Reward> ret=new Array<Reward>();
|
||||
for (RewardData data:dataList)
|
||||
ret.addAll(data.generate(isForEnemy));
|
||||
return ret;
|
||||
}
|
||||
static public List<PaperCard> rewardsToCards(Iterable<Reward> dataList)
|
||||
{
|
||||
ArrayList<PaperCard> ret=new ArrayList<PaperCard>();
|
||||
for (Reward data:dataList)
|
||||
{
|
||||
ret.add(data.getCard());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package forge.adventure.data;
|
||||
|
||||
|
||||
/**
|
||||
* Data class that will be used to read Json configuration files
|
||||
* SettingData
|
||||
* contains settings outside of the chosen adventure
|
||||
*/
|
||||
public class SettingData {
|
||||
|
||||
public int width;
|
||||
public int height;
|
||||
public String plane;
|
||||
public boolean fullScreen;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package forge.adventure.data;
|
||||
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
|
||||
/**
|
||||
* Data class that will be used to read Json configuration files
|
||||
* SettingData
|
||||
* contains data for a Shop on the map
|
||||
*/
|
||||
public class ShopData {
|
||||
|
||||
public String name;
|
||||
public String spriteAtlas;
|
||||
public String sprite;
|
||||
public Array<RewardData> rewards;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package forge.adventure.data;
|
||||
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import com.badlogic.gdx.utils.OrderedMap;
|
||||
|
||||
/**
|
||||
* Data class that will be used to read Json configuration files
|
||||
* UIData
|
||||
* contains a GUI definition, used for most user interfaces to customize the UI
|
||||
*/
|
||||
public class UIData {
|
||||
public int width;
|
||||
public int height;
|
||||
public boolean yDown;
|
||||
public Array<OrderedMap<String,String>> elements;
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package forge.adventure.data;
|
||||
|
||||
import com.badlogic.gdx.files.FileHandle;
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import com.badlogic.gdx.utils.Json;
|
||||
import forge.adventure.util.Config;
|
||||
import forge.adventure.util.Paths;
|
||||
import forge.adventure.world.BiomeSprites;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
/**
|
||||
* Data class that will be used to read Json configuration files
|
||||
* UIData
|
||||
* contains the definition of the world
|
||||
*/
|
||||
public class WorldData implements Serializable {
|
||||
|
||||
static Array<EnemyData> allEnemies;
|
||||
public int width;
|
||||
public int height;
|
||||
public float playerStartPosX;
|
||||
public float playerStartPosY;
|
||||
public float noiseZoomBiome;
|
||||
public int tileSize;
|
||||
public List<String> biomesNames;
|
||||
public BiomeData roadTileset;
|
||||
public String biomesSprites;
|
||||
public float maxRoadDistance;
|
||||
private BiomeSprites sprites;
|
||||
private List<BiomeData> biomes;
|
||||
private static Array<ShopData> shopList;
|
||||
|
||||
|
||||
public static Array<ShopData> getShopList() {
|
||||
if (shopList == null) {
|
||||
shopList = new Array<>();
|
||||
Json json = new Json();
|
||||
FileHandle handle = Config.instance().getFile(Paths.SHOPS);
|
||||
if (handle.exists())
|
||||
{
|
||||
|
||||
Array readList = json.fromJson(Array.class, ShopData.class, handle);
|
||||
shopList = readList;
|
||||
}
|
||||
}
|
||||
return shopList;
|
||||
}
|
||||
static public Array<EnemyData> getAllEnemies() {
|
||||
if (allEnemies == null) {
|
||||
Json json = new Json();
|
||||
FileHandle handle = Config.instance().getFile(Paths.ENEMIES);
|
||||
if (handle.exists())
|
||||
{
|
||||
|
||||
Array readList = json.fromJson(Array.class, EnemyData.class, handle);
|
||||
allEnemies = readList;
|
||||
}
|
||||
}
|
||||
return allEnemies;
|
||||
}
|
||||
|
||||
public static EnemyData getEnemy(String enemy) {
|
||||
for(EnemyData data: new Array.ArrayIterator<>(getAllEnemies()))
|
||||
{
|
||||
if(data.name.equals(enemy))
|
||||
return data;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public BiomeSprites GetBiomeSprites() {
|
||||
if (sprites == null) {
|
||||
Json json = new Json();
|
||||
sprites = (json.fromJson(BiomeSprites.class, Config.instance().getFile(biomesSprites)));
|
||||
}
|
||||
return sprites;
|
||||
}
|
||||
|
||||
public List<BiomeData> GetBiomes() {
|
||||
if (biomes == null) {
|
||||
biomes = new ArrayList<BiomeData>();
|
||||
Json json = new Json();
|
||||
for (String name : biomesNames) {
|
||||
biomes.add(json.fromJson(BiomeData.class, Config.instance().getFile(name)));
|
||||
}
|
||||
}
|
||||
return biomes;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package forge.adventure.editor;
|
||||
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
|
||||
/**
|
||||
* Editor class to edit configuration, maybe moved or removed
|
||||
*/
|
||||
public class DocumentChangeListener implements DocumentListener {
|
||||
private final Runnable run;
|
||||
|
||||
public DocumentChangeListener(Runnable run)
|
||||
{
|
||||
this.run = run;
|
||||
}
|
||||
@Override
|
||||
public void insertUpdate(DocumentEvent e) {
|
||||
|
||||
changedUpdate(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUpdate(DocumentEvent e) {
|
||||
changedUpdate(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changedUpdate(DocumentEvent e) {
|
||||
run.run();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package forge.adventure.editor;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
/**
|
||||
* Editor class to edit configuration, maybe moved or removed
|
||||
*/
|
||||
public class EditorMainWindow extends JFrame {
|
||||
JTabbedPane tabs =new JTabbedPane();
|
||||
|
||||
public EditorMainWindow()
|
||||
{
|
||||
BorderLayout layout=new BorderLayout();
|
||||
setLayout(layout);
|
||||
add(tabs);
|
||||
tabs.addTab("Enemies",new EnemyEditor());
|
||||
setVisible(true);
|
||||
setSize(800,600);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package forge.adventure.editor;
|
||||
|
||||
import forge.adventure.data.EnemyData;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
/**
|
||||
* Editor class to edit configuration, maybe moved or removed
|
||||
*/
|
||||
public class EnemyEdit extends JComponent {
|
||||
EnemyData currentData;
|
||||
|
||||
|
||||
JTextField nameField=new JTextField();
|
||||
JSpinner lifeFiled= new JSpinner(new SpinnerNumberModel(0, 0, 1000, 1));
|
||||
JSpinner spawnRate= new JSpinner(new SpinnerNumberModel(0.0, 0., 1, 0.1));
|
||||
JSpinner difficulty= new JSpinner(new SpinnerNumberModel(0.0, 0., 1, 0.1));
|
||||
JSpinner speed= new JSpinner(new SpinnerNumberModel(0.0, 0., 100., 1.0));
|
||||
FilePicker deck=new FilePicker(new String[]{"dck","json"});
|
||||
FilePicker atlas=new FilePicker(new String[]{"atlas"});
|
||||
RewardsEditor rewards=new RewardsEditor();
|
||||
SwingAtlasPreview preview=new SwingAtlasPreview();
|
||||
private boolean updating=false;
|
||||
|
||||
public EnemyEdit()
|
||||
{
|
||||
|
||||
JComponent center=new JComponent() { };
|
||||
center.setLayout(new GridLayout(8,2));
|
||||
|
||||
center.add(new JLabel("Name:")); center.add(nameField);
|
||||
center.add(new JLabel("Life:")); center.add(lifeFiled);
|
||||
center.add(new JLabel("Spawn rate:")); center.add(spawnRate);
|
||||
center.add(new JLabel("Difficulty:")); center.add(difficulty);
|
||||
center.add(new JLabel("Speed:")); center.add(speed);
|
||||
center.add(new JLabel("Deck:")); center.add(deck);
|
||||
center.add(new JLabel("Sprite:")); center.add(atlas);
|
||||
BorderLayout layout=new BorderLayout();
|
||||
setLayout(layout);
|
||||
add(center,BorderLayout.PAGE_START);
|
||||
add(rewards,BorderLayout.CENTER);
|
||||
add(preview,BorderLayout.LINE_START);
|
||||
|
||||
atlas.getEdit().getDocument().addDocumentListener(new DocumentChangeListener(()->updateEnemy()));
|
||||
nameField.getDocument().addDocumentListener(new DocumentChangeListener(()->updateEnemy()));
|
||||
deck.getEdit().getDocument().addDocumentListener(new DocumentChangeListener(()->updateEnemy()));
|
||||
lifeFiled.addChangeListener(e -> updateEnemy());
|
||||
speed.addChangeListener(e -> updateEnemy());
|
||||
difficulty.addChangeListener(e -> updateEnemy());
|
||||
spawnRate.addChangeListener(e -> updateEnemy());
|
||||
rewards.addChangeListener(e -> updateEnemy());
|
||||
lifeFiled.addChangeListener(e -> updateEnemy());
|
||||
refresh();
|
||||
}
|
||||
|
||||
private void updateEnemy() {
|
||||
if(currentData==null||updating)
|
||||
return;
|
||||
currentData.name=nameField.getText();
|
||||
currentData.life= (int) lifeFiled.getValue();
|
||||
currentData.sprite= atlas.getEdit().getText();
|
||||
currentData.speed= ((Double) speed.getValue()).floatValue();
|
||||
currentData.spawnRate=((Double) spawnRate.getValue()).floatValue();
|
||||
currentData.difficulty=((Double) difficulty.getValue()).floatValue();
|
||||
currentData.deck= deck.getEdit().getText();
|
||||
currentData.rewards= rewards.getRewards();
|
||||
preview.setSpritePath(currentData.sprite);
|
||||
}
|
||||
|
||||
public void setCurrentEnemy(EnemyData data)
|
||||
{
|
||||
currentData=data;
|
||||
refresh();
|
||||
}
|
||||
|
||||
private void refresh() {
|
||||
setEnabled(currentData!=null);
|
||||
if(currentData==null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
updating=true;
|
||||
nameField.setText(currentData.name);
|
||||
lifeFiled.setValue(currentData.life);
|
||||
atlas.getEdit().setText(currentData.sprite);
|
||||
deck.getEdit().setText(currentData.deck);
|
||||
speed.setValue(new Float(currentData.speed).doubleValue());
|
||||
spawnRate.setValue(new Float(currentData.spawnRate).doubleValue());
|
||||
difficulty.setValue(new Float(currentData.difficulty).doubleValue());
|
||||
rewards.setRewards(currentData.rewards);
|
||||
preview.setSpritePath(currentData.sprite);
|
||||
updating=false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
package forge.adventure.editor;
|
||||
|
||||
import com.badlogic.gdx.files.FileHandle;
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import com.badlogic.gdx.utils.Json;
|
||||
import com.badlogic.gdx.utils.JsonWriter;
|
||||
import forge.adventure.data.EnemyData;
|
||||
import forge.adventure.util.Config;
|
||||
import forge.adventure.util.Paths;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionListener;
|
||||
|
||||
/**
|
||||
* Editor class to edit configuration, maybe moved or removed
|
||||
*/
|
||||
public class EnemyEditor extends JComponent {
|
||||
DefaultListModel<EnemyData> model = new DefaultListModel<>();
|
||||
JList<EnemyData> list = new JList<>(model);
|
||||
JToolBar toolBar = new JToolBar("toolbar");
|
||||
EnemyEdit edit=new EnemyEdit();
|
||||
|
||||
|
||||
|
||||
public class EnemyDataRenderer extends DefaultListCellRenderer {
|
||||
@Override
|
||||
public Component getListCellRendererComponent(
|
||||
JList list, Object value, int index,
|
||||
boolean isSelected, boolean cellHasFocus) {
|
||||
JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
|
||||
if(!(value instanceof EnemyData))
|
||||
return label;
|
||||
EnemyData enemy=(EnemyData) value;
|
||||
// Get the renderer component from parent class
|
||||
|
||||
label.setText(enemy.name);
|
||||
SwingAtlas atlas=new SwingAtlas(Config.instance().getFile(enemy.sprite));
|
||||
if(atlas.has("Avatar"))
|
||||
label.setIcon(atlas.get("Avatar"));
|
||||
else
|
||||
{
|
||||
ImageIcon img=atlas.getAny();
|
||||
if(img!=null)
|
||||
label.setIcon(img);
|
||||
}
|
||||
return label;
|
||||
}
|
||||
}
|
||||
public void addButton(String name,ActionListener action)
|
||||
{
|
||||
JButton newButton=new JButton(name);
|
||||
newButton.addActionListener(action);
|
||||
toolBar.add(newButton);
|
||||
|
||||
}
|
||||
public EnemyEditor()
|
||||
{
|
||||
|
||||
list.setCellRenderer(new EnemyDataRenderer());
|
||||
list.addListSelectionListener(e -> updateEdit());
|
||||
addButton("add",e->addEnemy());
|
||||
addButton("remove",e->remove());
|
||||
addButton("copy",e->copy());
|
||||
addButton("load",e->load());
|
||||
addButton("save",e->save());
|
||||
BorderLayout layout=new BorderLayout();
|
||||
setLayout(layout);
|
||||
add(new JScrollPane(list), BorderLayout.LINE_START);
|
||||
add(toolBar, BorderLayout.PAGE_START);
|
||||
add(edit,BorderLayout.CENTER);
|
||||
load();
|
||||
}
|
||||
private void copy() {
|
||||
|
||||
int selected=list.getSelectedIndex();
|
||||
if(selected<0)
|
||||
return;
|
||||
EnemyData data=new EnemyData(model.get(selected));
|
||||
model.add(model.size(),data);
|
||||
}
|
||||
private void updateEdit() {
|
||||
|
||||
int selected=list.getSelectedIndex();
|
||||
if(selected<0)
|
||||
return;
|
||||
edit.setCurrentEnemy(model.get(selected));
|
||||
}
|
||||
|
||||
void save()
|
||||
{
|
||||
Array<EnemyData> allEnemies=new Array<>();
|
||||
for(int i=0;i<model.getSize();i++)
|
||||
allEnemies.add(model.get(i));
|
||||
Json json = new Json(JsonWriter.OutputType.json);
|
||||
FileHandle handle = Config.instance().getFile(Paths.ENEMIES);
|
||||
handle.writeString(json.prettyPrint(json.toJson(allEnemies,Array.class, EnemyData.class)),false);
|
||||
|
||||
}
|
||||
void load()
|
||||
{
|
||||
model.clear();
|
||||
Array<EnemyData> allEnemies=new Array<>();
|
||||
Json json = new Json();
|
||||
FileHandle handle = Config.instance().getFile(Paths.ENEMIES);
|
||||
if (handle.exists())
|
||||
{
|
||||
Array readEnemies=json.fromJson(Array.class, EnemyData.class, handle);
|
||||
allEnemies = readEnemies;
|
||||
}
|
||||
for (int i=0;i<allEnemies.size;i++) {
|
||||
model.add(i,allEnemies.get(i));
|
||||
}
|
||||
}
|
||||
void addEnemy()
|
||||
{
|
||||
EnemyData data=new EnemyData();
|
||||
data.name="Enemy "+model.getSize();
|
||||
model.add(model.size(),data);
|
||||
}
|
||||
void remove()
|
||||
{
|
||||
int selected=list.getSelectedIndex();
|
||||
if(selected<0)
|
||||
return;
|
||||
model.remove(selected);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package forge.adventure.editor;
|
||||
|
||||
import forge.adventure.util.Config;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.filechooser.FileNameExtensionFilter;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Editor class to edit configuration, maybe moved or removed
|
||||
*/
|
||||
public class FilePicker extends Box {
|
||||
JTextField edit=new JTextField();
|
||||
JButton findButton=new JButton(UIManager.getIcon("FileView.directoryIcon"));
|
||||
private final String[] fileEndings;
|
||||
|
||||
public FilePicker(String[] fileEndings) {
|
||||
super(BoxLayout.X_AXIS);
|
||||
this.fileEndings = fileEndings;
|
||||
|
||||
findButton.addActionListener(e->find());
|
||||
|
||||
add(edit);
|
||||
add(findButton);
|
||||
|
||||
}
|
||||
JTextField getEdit()
|
||||
{
|
||||
return edit;
|
||||
}
|
||||
|
||||
private void find() {
|
||||
JFileChooser fc = new JFileChooser();
|
||||
fc.setCurrentDirectory(new File(Config.instance().getFilePath(edit.getText())));
|
||||
fc.setFileFilter( new FileNameExtensionFilter("Pick File",fileEndings));
|
||||
fc.setMultiSelectionEnabled(false);
|
||||
if (fc.showOpenDialog(this) ==
|
||||
JFileChooser.APPROVE_OPTION) {
|
||||
File selected = fc.getSelectedFile();
|
||||
|
||||
try {
|
||||
if (selected != null&&selected.getCanonicalPath().startsWith(new File(Config.instance().getFilePath("")).getCanonicalPath())) {
|
||||
edit.setText(selected.getCanonicalPath().substring(new File(Config.instance().getFilePath("")).getCanonicalPath().length()+1).replace('\\','/'));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package forge.adventure.editor;
|
||||
|
||||
import forge.adventure.util.Config;
|
||||
|
||||
/**
|
||||
* Editor class to edit configuration, maybe moved or removed
|
||||
*/
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
Config.instance();
|
||||
new EditorMainWindow();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
package forge.adventure.editor;
|
||||
|
||||
import forge.adventure.data.RewardData;
|
||||
import forge.card.CardType;
|
||||
import forge.game.keyword.Keyword;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
import java.awt.*;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Editor class to edit configuration, maybe moved or removed
|
||||
*/
|
||||
public class RewardEdit extends JComponent {
|
||||
RewardData currentData;
|
||||
|
||||
JComboBox typeField =new JComboBox(new String[] { "card", "gold", "life", "deckCard", "item"});
|
||||
JSpinner probability = new JSpinner(new SpinnerNumberModel(0f, 0, 1, 0.1f));
|
||||
JSpinner count = new JSpinner(new SpinnerNumberModel(0, 0, 1000, 1));
|
||||
JSpinner addMaxCount = new JSpinner(new SpinnerNumberModel(0, 0, 1000, 1));
|
||||
JTextField cardName =new JTextField();
|
||||
JTextField itemName =new JTextField();
|
||||
TextListEdit editions =new TextListEdit();
|
||||
TextListEdit colors =new TextListEdit(new String[] { "White", "Blue", "Black", "Red", "Green" });
|
||||
TextListEdit rarity =new TextListEdit(new String[] { "Basic Land", "Common", "Uncommon", "Rare", "Mythic Rare" });
|
||||
TextListEdit subTypes =new TextListEdit();
|
||||
TextListEdit cardTypes =new TextListEdit(Arrays.asList(CardType.CoreType.values()).stream().map(CardType.CoreType::toString).toArray(String[]::new));
|
||||
TextListEdit superTypes =new TextListEdit(Arrays.asList(CardType.Supertype.values()).stream().map(CardType.Supertype::toString).toArray(String[]::new));
|
||||
TextListEdit manaCosts =new TextListEdit();
|
||||
TextListEdit keyWords =new TextListEdit(Arrays.asList(Keyword.values()).stream().map(Keyword::toString).toArray(String[]::new));
|
||||
JComboBox colorType =new JComboBox(new String[] { "Any", "Colorless", "MultiColor", "MonoColor"});
|
||||
JTextField cardText =new JTextField();
|
||||
private boolean updating=false;
|
||||
|
||||
public RewardEdit()
|
||||
{
|
||||
setLayout(new GridLayout(16,2));
|
||||
|
||||
add(new JLabel("Type:")); add(typeField);
|
||||
add(new JLabel("probability:")); add(probability);
|
||||
add(new JLabel("count:")); add(count);
|
||||
add(new JLabel("addMaxCount:")); add(addMaxCount);
|
||||
add(new JLabel("cardName:")); add(cardName);
|
||||
add(new JLabel("itemName:")); add(itemName);
|
||||
add(new JLabel("editions:")); add(editions);
|
||||
add(new JLabel("colors:")); add(colors);
|
||||
add(new JLabel("rarity:")); add(rarity);
|
||||
add(new JLabel("subTypes:")); add(subTypes);
|
||||
add(new JLabel("cardTypes:")); add(cardTypes);
|
||||
add(new JLabel("superTypes:")); add(superTypes);
|
||||
add(new JLabel("manaCosts:")); add(manaCosts);
|
||||
add(new JLabel("keyWords:")); add(keyWords);
|
||||
add(new JLabel("colorType:")); add(colorType);
|
||||
add(new JLabel("cardText:")); add(cardText);
|
||||
|
||||
|
||||
typeField.addActionListener(((e)->updateReward()));
|
||||
probability.addChangeListener(e->updateReward());
|
||||
count.addChangeListener(e->updateReward());
|
||||
addMaxCount.addChangeListener(e->updateReward());
|
||||
cardName.getDocument().addDocumentListener(new DocumentChangeListener(()->updateReward()));
|
||||
itemName.getDocument().addDocumentListener(new DocumentChangeListener(()->updateReward()));
|
||||
editions.getEdit().getDocument().addDocumentListener(new DocumentChangeListener(()->updateReward()));
|
||||
colors.getEdit().getDocument().addDocumentListener(new DocumentChangeListener(()->updateReward()));
|
||||
rarity.getEdit().getDocument().addDocumentListener(new DocumentChangeListener(()->updateReward()));
|
||||
subTypes.getEdit().getDocument().addDocumentListener(new DocumentChangeListener(()->updateReward()));
|
||||
cardTypes.getEdit().getDocument().addDocumentListener(new DocumentChangeListener(()->updateReward()));
|
||||
superTypes.getEdit().getDocument().addDocumentListener(new DocumentChangeListener(()->updateReward()));
|
||||
manaCosts.getEdit().getDocument().addDocumentListener(new DocumentChangeListener(()->updateReward()));
|
||||
keyWords.getEdit().getDocument().addDocumentListener(new DocumentChangeListener(()->updateReward()));
|
||||
colorType.addActionListener(((e)->updateReward()));
|
||||
cardText.getDocument().addDocumentListener(new DocumentChangeListener(()->updateReward()));
|
||||
|
||||
}
|
||||
|
||||
private void updateReward() {
|
||||
if(currentData==null||updating)
|
||||
return;
|
||||
|
||||
|
||||
currentData.type=typeField.getSelectedItem()==null?null:typeField.getSelectedItem().toString();
|
||||
currentData.probability=((Double)probability.getValue()).floatValue();
|
||||
currentData.count= (int) count.getValue();
|
||||
currentData.addMaxCount= (int) addMaxCount.getValue();
|
||||
currentData.cardName = cardName.getText().isEmpty()?null:cardName.getText();
|
||||
currentData.itemName = itemName.getText().isEmpty()?null:itemName.getText();
|
||||
currentData.editions = editions.getList();
|
||||
currentData.colors = colors.getList();
|
||||
currentData.rarity = rarity.getList();
|
||||
currentData.subTypes = subTypes.getList();
|
||||
currentData.cardTypes = cardTypes.getList();
|
||||
currentData.superTypes = superTypes.getList();
|
||||
currentData.manaCosts = manaCosts.getListAsInt();
|
||||
currentData.keyWords = keyWords.getList();
|
||||
currentData.colorType=colorType.getSelectedItem()==null?null:colorType.getSelectedItem().toString();
|
||||
currentData.cardText = cardText.getText().isEmpty()?null:cardText.getText();
|
||||
|
||||
ChangeListener[] listeners = listenerList.getListeners(ChangeListener.class);
|
||||
if (listeners != null && listeners.length > 0) {
|
||||
ChangeEvent evt = new ChangeEvent(this);
|
||||
for (ChangeListener listener : listeners) {
|
||||
listener.stateChanged(evt);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
public void addChangeListener(ChangeListener l) {
|
||||
listenerList.add(ChangeListener.class, l);
|
||||
}
|
||||
public void setCurrentReward(RewardData data)
|
||||
{
|
||||
currentData=data;
|
||||
refresh();
|
||||
}
|
||||
|
||||
private void refresh() {
|
||||
setEnabled(currentData!=null);
|
||||
if(currentData==null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
updating=true;
|
||||
typeField.setSelectedItem(currentData.type);
|
||||
|
||||
probability.setValue(new Double(currentData.probability));
|
||||
count.setValue(currentData.count);
|
||||
addMaxCount.setValue(currentData.addMaxCount);
|
||||
cardName.setText(currentData.cardName);
|
||||
itemName.setText(currentData.itemName);
|
||||
editions.setText(currentData.editions);
|
||||
colors.setText(currentData.colors);
|
||||
rarity.setText(currentData.rarity);
|
||||
subTypes.setText(currentData.subTypes);
|
||||
cardTypes.setText(currentData.cardTypes);
|
||||
superTypes.setText(currentData.superTypes);
|
||||
manaCosts.setText(currentData.manaCosts);
|
||||
keyWords.setText(currentData.keyWords);
|
||||
colorType.setSelectedItem(currentData.colorType);
|
||||
cardText.setText(currentData.cardText);
|
||||
|
||||
|
||||
updating=false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
package forge.adventure.editor;
|
||||
|
||||
import forge.adventure.data.RewardData;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionListener;
|
||||
|
||||
/**
|
||||
* Editor class to edit configuration, maybe moved or removed
|
||||
*/
|
||||
public class RewardsEditor extends JComponent{
|
||||
DefaultListModel<RewardData> model = new DefaultListModel<>();
|
||||
JList<RewardData> list = new JList<>(model);
|
||||
JToolBar toolBar = new JToolBar("toolbar");
|
||||
RewardEdit edit=new RewardEdit();
|
||||
|
||||
|
||||
|
||||
public class RewardDataRenderer extends DefaultListCellRenderer {
|
||||
@Override
|
||||
public Component getListCellRendererComponent(
|
||||
JList list, Object value, int index,
|
||||
boolean isSelected, boolean cellHasFocus) {
|
||||
JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
|
||||
if(!(value instanceof RewardData))
|
||||
return label;
|
||||
RewardData reward=(RewardData) value;
|
||||
StringBuilder builder=new StringBuilder();
|
||||
if(reward.type==null||reward.type.isEmpty())
|
||||
builder.append("Reward");
|
||||
else
|
||||
builder.append(reward.type);
|
||||
builder.append(" ");
|
||||
builder.append(reward.count);
|
||||
if(reward.addMaxCount>0)
|
||||
{
|
||||
builder.append("-");
|
||||
builder.append(reward.count+reward.addMaxCount);
|
||||
}
|
||||
label.setText(builder.toString());
|
||||
return label;
|
||||
}
|
||||
}
|
||||
public void addButton(String name, ActionListener action)
|
||||
{
|
||||
JButton newButton=new JButton(name);
|
||||
newButton.addActionListener(action);
|
||||
toolBar.add(newButton);
|
||||
|
||||
}
|
||||
|
||||
public RewardsEditor()
|
||||
{
|
||||
|
||||
list.setCellRenderer(new RewardsEditor.RewardDataRenderer());
|
||||
list.addListSelectionListener(e -> updateEdit());
|
||||
addButton("add",e->addReward());
|
||||
addButton("remove",e->remove());
|
||||
addButton("copy",e->copy());
|
||||
BorderLayout layout=new BorderLayout();
|
||||
setLayout(layout);
|
||||
add(list, BorderLayout.LINE_START);
|
||||
add(toolBar, BorderLayout.PAGE_START);
|
||||
add(edit,BorderLayout.CENTER);
|
||||
|
||||
|
||||
edit.addChangeListener(new ChangeListener() {
|
||||
@Override
|
||||
public void stateChanged(ChangeEvent e) {
|
||||
emitChanged();
|
||||
}
|
||||
});
|
||||
}
|
||||
protected void emitChanged() {
|
||||
ChangeListener[] listeners = listenerList.getListeners(ChangeListener.class);
|
||||
if (listeners != null && listeners.length > 0) {
|
||||
ChangeEvent evt = new ChangeEvent(this);
|
||||
for (ChangeListener listener : listeners) {
|
||||
listener.stateChanged(evt);
|
||||
}
|
||||
}
|
||||
}
|
||||
private void copy() {
|
||||
|
||||
int selected=list.getSelectedIndex();
|
||||
if(selected<0)
|
||||
return;
|
||||
RewardData data=new RewardData(model.get(selected));
|
||||
model.add(model.size(),data);
|
||||
}
|
||||
|
||||
private void updateEdit() {
|
||||
|
||||
int selected=list.getSelectedIndex();
|
||||
if(selected<0)
|
||||
return;
|
||||
edit.setCurrentReward(model.get(selected));
|
||||
}
|
||||
|
||||
void addReward()
|
||||
{
|
||||
RewardData data=new RewardData();
|
||||
model.add(model.size(),data);
|
||||
}
|
||||
void remove()
|
||||
{
|
||||
int selected=list.getSelectedIndex();
|
||||
if(selected<0)
|
||||
return;
|
||||
model.remove(selected);
|
||||
}
|
||||
public void setRewards(RewardData[] rewards) {
|
||||
|
||||
model.clear();
|
||||
for (int i=0;i<rewards.length;i++) {
|
||||
model.add(i,rewards[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public RewardData[] getRewards() {
|
||||
|
||||
RewardData[] rewards= new RewardData[model.getSize()];
|
||||
for(int i=0;i<model.getSize();i++)
|
||||
{
|
||||
rewards[i]=model.get(i);
|
||||
}
|
||||
return rewards;
|
||||
}
|
||||
public void addChangeListener(ChangeListener listener) {
|
||||
listenerList.add(ChangeListener.class, listener);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package forge.adventure.editor;
|
||||
|
||||
import com.badlogic.gdx.files.FileHandle;
|
||||
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.swing.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
import static java.awt.Image.SCALE_FAST;
|
||||
|
||||
|
||||
/**
|
||||
* Editor class to edit configuration, maybe moved or removed
|
||||
*/
|
||||
public class SwingAtlas {
|
||||
|
||||
HashMap<String, ArrayList<ImageIcon>> images=new HashMap<>();
|
||||
public HashMap<String, ArrayList<ImageIcon>> getImages()
|
||||
{
|
||||
return images;
|
||||
}
|
||||
public SwingAtlas(FileHandle path)
|
||||
{
|
||||
if(!path.exists())
|
||||
return;
|
||||
TextureAtlas.TextureAtlasData data=new TextureAtlas.TextureAtlasData(path,path.parent(),false);
|
||||
for(TextureAtlas.TextureAtlasData.Region region: new Array.ArrayIterator<>(data.getRegions()))
|
||||
{
|
||||
String name=region.name;
|
||||
if(!images.containsKey(name))
|
||||
{
|
||||
images.put(name,new ArrayList<>());
|
||||
}
|
||||
ArrayList<ImageIcon> imageList=images.get(name);
|
||||
try {
|
||||
imageList.add(spriteToImage(region));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ImageIcon spriteToImage(TextureAtlas.TextureAtlasData.Region sprite) throws IOException {
|
||||
BufferedImage img = ImageIO.read(sprite.page.textureFile.file());
|
||||
return new ImageIcon(img.getSubimage(sprite.left,sprite.top, sprite.width, sprite.height).getScaledInstance(32,32,SCALE_FAST));
|
||||
}
|
||||
|
||||
public ImageIcon get(String name) {
|
||||
return images.get(name).get(0);
|
||||
}
|
||||
|
||||
public boolean has(String name) {
|
||||
return images.containsKey(name);
|
||||
}
|
||||
|
||||
public ImageIcon getAny() {
|
||||
if(images.isEmpty())
|
||||
return null;
|
||||
ArrayList<ImageIcon> imageList= images.get(images.keySet().iterator().next());
|
||||
if(imageList.isEmpty())
|
||||
return null;
|
||||
return imageList.get(0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package forge.adventure.editor;
|
||||
|
||||
import forge.adventure.util.Config;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Editor class to edit configuration, maybe moved or removed
|
||||
*/
|
||||
public class SwingAtlasPreview extends Box {
|
||||
private String sprite="";
|
||||
Timer timer;
|
||||
public SwingAtlasPreview() {
|
||||
super(BoxLayout.Y_AXIS);
|
||||
|
||||
timer = new Timer(200, new AbstractAction() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
counter++;
|
||||
for (Pair<JLabel, ArrayList<ImageIcon>> element : labels) {
|
||||
element.getKey().setIcon(element.getValue().get(counter % element.getValue().size()));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
int counter=0;
|
||||
List<Pair<JLabel,ArrayList<ImageIcon>>> labels=new ArrayList<>();
|
||||
public void setSpritePath(String sprite) {
|
||||
|
||||
removeAll();
|
||||
counter=0;
|
||||
labels.clear();
|
||||
if(this.sprite.equals(sprite))
|
||||
return;
|
||||
this.sprite=sprite;
|
||||
SwingAtlas atlas=new SwingAtlas(Config.instance().getFile(sprite));
|
||||
for(Map.Entry<String, ArrayList<ImageIcon>> element:atlas.getImages().entrySet())
|
||||
{
|
||||
JLabel image=new JLabel(element.getValue().get(0));
|
||||
add(new JLabel(element.getKey()));
|
||||
add(image);
|
||||
labels.add(Pair.of(image, element.getValue()));
|
||||
}
|
||||
timer.restart();
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package forge.adventure.editor;
|
||||
|
||||
import forge.adventure.util.Config;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Editor class to edit configuration, maybe moved or removed
|
||||
*/
|
||||
public class TextListEdit extends Box {
|
||||
JTextField edit=new JTextField();
|
||||
JButton findButton=new JButton(UIManager.getIcon("add"));
|
||||
JComboBox elements;
|
||||
public TextListEdit(String[] possibleElements) {
|
||||
super(BoxLayout.X_AXIS);
|
||||
|
||||
findButton.addActionListener(e->find());
|
||||
|
||||
add(edit);
|
||||
//add(findButton);
|
||||
elements= new JComboBox(possibleElements);
|
||||
add(elements);
|
||||
|
||||
}
|
||||
public TextListEdit()
|
||||
{
|
||||
this(new String[0]);
|
||||
|
||||
}
|
||||
JTextField getEdit()
|
||||
{
|
||||
return edit;
|
||||
}
|
||||
|
||||
private void find() {
|
||||
JFileChooser fc = new JFileChooser();
|
||||
fc.setCurrentDirectory(new File(Config.instance().getFilePath("")));
|
||||
fc.setMultiSelectionEnabled(false);
|
||||
if (fc.showOpenDialog(this) ==
|
||||
JFileChooser.APPROVE_OPTION) {
|
||||
File selected = fc.getSelectedFile();
|
||||
|
||||
try {
|
||||
if (selected != null&&selected.getCanonicalPath().startsWith(new File(Config.instance().getFilePath("")).getCanonicalPath())) {
|
||||
edit.setText(selected.getCanonicalPath().substring(new File(Config.instance().getFilePath("")).getCanonicalPath().length()+1));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setText(String[] itemName) {
|
||||
if(itemName==null)
|
||||
edit.setText("");
|
||||
else
|
||||
edit.setText(String.join(";",itemName));
|
||||
}
|
||||
|
||||
public void setText(int[] intValues) {
|
||||
if(intValues==null)
|
||||
{
|
||||
edit.setText("");
|
||||
return;
|
||||
}
|
||||
StringBuilder values= new StringBuilder();
|
||||
for(int i=0;i<intValues.length;i++)
|
||||
{
|
||||
values.append(intValues[i]);
|
||||
if(intValues.length>i+2)
|
||||
values.append(";");
|
||||
}
|
||||
edit.setText(values.toString());
|
||||
}
|
||||
|
||||
public String[] getList() {
|
||||
return edit.getText().isEmpty()?null:edit.getText().split(";");
|
||||
}
|
||||
|
||||
public int[] getListAsInt() {
|
||||
if(edit.getText().isEmpty())
|
||||
return null;
|
||||
String[] stringList=getList();
|
||||
int[] retList=new int[stringList.length];
|
||||
for(int i=0;i<retList.length;i++)
|
||||
{
|
||||
String intName=stringList[i];
|
||||
try
|
||||
{
|
||||
retList[i] = Integer.valueOf(intName);
|
||||
}
|
||||
catch (NumberFormatException e)
|
||||
{
|
||||
retList[i] =0;
|
||||
}
|
||||
}
|
||||
return retList;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package forge.adventure.libgdxgui;
|
||||
|
||||
import com.badlogic.gdx.graphics.Texture;
|
||||
import forge.adventure.libgdxgui.screens.match.MatchController;
|
||||
import forge.adventure.libgdxgui.assets.ImageCache;
|
||||
import forge.game.card.CardView;
|
||||
import forge.gui.GuiBase;
|
||||
import forge.item.InventoryItem;
|
||||
import forge.util.ImageFetcher;
|
||||
|
||||
public abstract class CachedCardImage implements ImageFetcher.Callback {
|
||||
protected final String key;
|
||||
static final ImageFetcher fetcher = GuiBase.getInterface().getImageFetcher();
|
||||
|
||||
public CachedCardImage(final CardView card) {
|
||||
key = card.getCurrentState().getImageKey(MatchController.instance.getLocalPlayers());
|
||||
fetch();
|
||||
}
|
||||
|
||||
public CachedCardImage(final InventoryItem ii) {
|
||||
key = ii.getImageKey(false);
|
||||
fetch();
|
||||
}
|
||||
|
||||
public CachedCardImage(String key) {
|
||||
this.key = key;
|
||||
fetch();
|
||||
}
|
||||
|
||||
public void fetch() {
|
||||
if (!ImageCache.imageKeyFileExists(key)) {
|
||||
fetcher.fetchImage(key, this);
|
||||
}
|
||||
}
|
||||
|
||||
public Texture getImage() {
|
||||
return ImageCache.getImage(key, true);
|
||||
}
|
||||
|
||||
public Texture getImage(String mykey) {
|
||||
return ImageCache.getImage(mykey, true);
|
||||
}
|
||||
|
||||
public abstract void onImageFetched();
|
||||
}
|
||||
@@ -0,0 +1,920 @@
|
||||
package forge.adventure.libgdxgui;
|
||||
|
||||
import com.badlogic.gdx.Application;
|
||||
import com.badlogic.gdx.ApplicationListener;
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.Input.Keys;
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
import com.badlogic.gdx.utils.Clipboard;
|
||||
import forge.adventure.libgdxgui.animation.ForgeAnimation;
|
||||
import forge.adventure.libgdxgui.assets.AssetsDownloader;
|
||||
import forge.adventure.libgdxgui.assets.FSkin;
|
||||
import forge.adventure.libgdxgui.assets.FSkinFont;
|
||||
import forge.adventure.libgdxgui.assets.ImageCache;
|
||||
import forge.adventure.libgdxgui.screens.FScreen;
|
||||
import forge.adventure.libgdxgui.screens.SplashScreen;
|
||||
import forge.adventure.libgdxgui.screens.match.MatchController;
|
||||
import forge.adventure.libgdxgui.toolbox.*;
|
||||
import forge.adventure.libgdxgui.util.Utils;
|
||||
import forge.error.ExceptionHandler;
|
||||
import forge.gui.FThreads;
|
||||
import forge.gui.GuiBase;
|
||||
import forge.gui.error.BugReporter;
|
||||
import forge.interfaces.IDeviceAdapter;
|
||||
import forge.localinstance.properties.ForgeConstants;
|
||||
import forge.localinstance.properties.ForgePreferences;
|
||||
import forge.localinstance.properties.ForgePreferences.FPref;
|
||||
import forge.model.FModel;
|
||||
import forge.sound.MusicPlaylist;
|
||||
import forge.sound.SoundSystem;
|
||||
import forge.util.Callback;
|
||||
import forge.util.CardTranslation;
|
||||
import forge.util.FileUtil;
|
||||
import forge.util.Localizer;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Deque;
|
||||
import java.util.List;
|
||||
|
||||
public class Forge implements ApplicationListener {
|
||||
public static final String CURRENT_VERSION = "1.6.42.001";
|
||||
|
||||
public static final Forge app = new Forge();
|
||||
private static final Deque<FScreen> Dscreens = new ArrayDeque<>();
|
||||
public static String extrawide = "default";
|
||||
public static float heigtModifier = 0.0f;
|
||||
public static boolean showFPS = false;
|
||||
public static boolean altPlayerLayout = false;
|
||||
public static boolean altZoneTabs = false;
|
||||
public static String enableUIMask = "Full";
|
||||
public static boolean enablePreloadExtendedArt = false;
|
||||
public static boolean isTabletDevice = false;
|
||||
public static String locale = "en-US";
|
||||
public static boolean hdbuttons = false;
|
||||
public static boolean hdstart = false;
|
||||
public static boolean isPortraitMode = false;
|
||||
public static boolean gameInProgress = false;
|
||||
public static boolean disposeTextures = false;
|
||||
public static int cacheSize = 400;
|
||||
public static int totalDeviceRAM = 0;
|
||||
public static int androidVersion = 0;
|
||||
public static boolean autoCache = false;
|
||||
public static int lastButtonIndex = 0;
|
||||
public static String CJK_Font = "";
|
||||
private static Clipboard clipboard;
|
||||
private static IDeviceAdapter deviceAdapter;
|
||||
private static int screenWidth;
|
||||
private static int screenHeight;
|
||||
private static forge.adventure.libgdxgui.Graphics graphics;
|
||||
private static forge.adventure.libgdxgui.FrameRate frameRate;
|
||||
private static FScreen currentScreen;
|
||||
private static SplashScreen splashScreen;
|
||||
private static KeyInputAdapter keyInputAdapter;
|
||||
private static boolean exited;
|
||||
private static int continuousRenderingCount = 1; //initialize to 1 since continuous rendering is the default
|
||||
private static boolean textureFiltering = false;
|
||||
private static boolean destroyThis = false;
|
||||
private static boolean isloadingaMatch = false;
|
||||
private MainInputProcessor input;
|
||||
|
||||
private Forge() {
|
||||
}
|
||||
|
||||
public static ApplicationListener getApp(Clipboard clipboard0, IDeviceAdapter deviceAdapter0, String assetDir0, boolean value, boolean androidOrientation, int totalRAM, boolean isTablet, int AndroidAPI, String AndroidRelease, String deviceName) {
|
||||
if (GuiBase.getInterface() == null) {
|
||||
clipboard = clipboard0;
|
||||
deviceAdapter = deviceAdapter0;
|
||||
GuiBase.setUsingAppDirectory(assetDir0.contains("forge.app")); //obb directory on android uses the package name as entrypoint
|
||||
GuiBase.setInterface(new forge.adventure.libgdxgui.GuiMobile(assetDir0));
|
||||
GuiBase.enablePropertyConfig(value);
|
||||
isPortraitMode = androidOrientation;
|
||||
totalDeviceRAM = totalRAM;
|
||||
isTabletDevice = isTablet;
|
||||
androidVersion = AndroidAPI;
|
||||
}
|
||||
GuiBase.setDeviceInfo(deviceName, AndroidRelease, AndroidAPI, totalRAM);
|
||||
return app;
|
||||
}
|
||||
|
||||
public static void openHomeScreen(int index) {
|
||||
}
|
||||
|
||||
public static Clipboard getClipboard() {
|
||||
return clipboard;
|
||||
}
|
||||
|
||||
public static IDeviceAdapter getDeviceAdapter() {
|
||||
return deviceAdapter;
|
||||
}
|
||||
|
||||
public static void startContinuousRendering() {
|
||||
if (++continuousRenderingCount == 1) {
|
||||
//only set continuous rendering to true if needed
|
||||
Gdx.graphics.setContinuousRendering(true);
|
||||
}
|
||||
}
|
||||
|
||||
public static void stopContinuousRendering() {
|
||||
if (continuousRenderingCount > 0 && --continuousRenderingCount == 0) {
|
||||
//only set continuous rendering to false if all continuous rendering requests have been ended
|
||||
Gdx.graphics.setContinuousRendering(false);
|
||||
}
|
||||
}
|
||||
|
||||
public static float getHeightModifier() {
|
||||
return heigtModifier;
|
||||
}
|
||||
|
||||
public static void setHeightModifier(float height) {
|
||||
heigtModifier = height;
|
||||
}
|
||||
|
||||
public static void adjustHeightModifier(float DisplayW, float DisplayH) {
|
||||
if (isLandscapeMode()) {//TODO: Fullscreen support for Display without screen controls
|
||||
float aspectratio = DisplayW / DisplayH;
|
||||
if (aspectratio > 1.82f) {/* extra wide */
|
||||
setHeightModifier(200.0f);
|
||||
extrawide = "extrawide";
|
||||
} else if (aspectratio > 1.7f) {/* wide */
|
||||
setHeightModifier(100.0f);
|
||||
extrawide = "wide";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void showMenu() {
|
||||
if (currentScreen == null) {
|
||||
return;
|
||||
}
|
||||
endKeyInput(); //end key input before menu shown
|
||||
if (FOverlay.getTopOverlay() == null) { //don't show menu if overlay open
|
||||
currentScreen.showMenu();
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean onHomeScreen() {
|
||||
return Dscreens.size() == 1;
|
||||
}
|
||||
|
||||
public static void back() {
|
||||
|
||||
}
|
||||
|
||||
//set screen that will be gone to on pressing Back before going to current Back screen
|
||||
public static void setBackScreen(final FScreen screen0, boolean replace) {
|
||||
Dscreens.remove(screen0); //remove screen from previous position in navigation history
|
||||
int index = Dscreens.size() - 1;
|
||||
if (index > 0) {
|
||||
Dscreens.addLast(screen0);
|
||||
if (replace) { //remove previous back screen if replacing back screen
|
||||
Dscreens.removeFirst();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void restart(boolean silent) {
|
||||
if (exited) {
|
||||
return;
|
||||
} //don't allow exiting multiple times
|
||||
|
||||
Callback<Boolean> callback = new Callback<Boolean>() {
|
||||
@Override
|
||||
public void run(Boolean result) {
|
||||
if (result) {
|
||||
exited = true;
|
||||
deviceAdapter.restart();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
final Localizer localizer = Localizer.getInstance();
|
||||
|
||||
if (silent) {
|
||||
callback.run(true);
|
||||
} else {
|
||||
FOptionPane.showConfirmDialog(
|
||||
localizer.getMessage("lblAreYouSureYouWishRestartForge"), localizer.getMessage("lblRestartForge"),
|
||||
localizer.getMessage("lblRestart"), localizer.getMessage("lblCancel"), callback);
|
||||
}
|
||||
}
|
||||
|
||||
public static void exit(boolean silent) {
|
||||
if (exited) {
|
||||
return;
|
||||
} //don't allow exiting multiple times
|
||||
|
||||
Callback<Boolean> callback = new Callback<Boolean>() {
|
||||
@Override
|
||||
public void run(Boolean result) {
|
||||
if (result) {
|
||||
exited = true;
|
||||
deviceAdapter.exit();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
final Localizer localizer = Localizer.getInstance();
|
||||
|
||||
if (silent) {
|
||||
callback.run(true);
|
||||
} else {
|
||||
FOptionPane.showConfirmDialog(
|
||||
localizer.getMessage("lblAreYouSureYouWishExitForge"), localizer.getMessage("lblExitForge"),
|
||||
localizer.getMessage("lblExit"), localizer.getMessage("lblCancel"), callback);
|
||||
}
|
||||
}
|
||||
|
||||
public static void openScreen(final FScreen screen0) {
|
||||
openScreen(screen0, false);
|
||||
}
|
||||
|
||||
public static void openScreen(final FScreen screen0, final boolean replaceBackScreen) {
|
||||
if (currentScreen == screen0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentScreen == null) {
|
||||
Dscreens.addFirst(screen0);
|
||||
setCurrentScreen(screen0);
|
||||
return;
|
||||
}
|
||||
|
||||
currentScreen.onSwitchAway(new Callback<Boolean>() {
|
||||
@Override
|
||||
public void run(Boolean result) {
|
||||
if (result) {
|
||||
if (replaceBackScreen && !Dscreens.isEmpty()) {
|
||||
Dscreens.removeFirst();
|
||||
}
|
||||
if (Dscreens.peekFirst() != screen0) { //prevent screen being its own back screen
|
||||
Dscreens.addFirst(screen0);
|
||||
}
|
||||
setCurrentScreen(screen0);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static boolean isTextureFilteringEnabled() {
|
||||
return textureFiltering;
|
||||
}
|
||||
|
||||
public static boolean isLandscapeMode() {
|
||||
if (GuiBase.isAndroid())
|
||||
return !isPortraitMode;
|
||||
return screenWidth > screenHeight;
|
||||
}
|
||||
|
||||
public static boolean isLoadingaMatch() {
|
||||
return isloadingaMatch;
|
||||
}
|
||||
|
||||
public static void setLoadingaMatch(boolean value) {
|
||||
isloadingaMatch = value;
|
||||
}
|
||||
|
||||
public static int getScreenWidth() {
|
||||
return screenWidth;
|
||||
}
|
||||
|
||||
public static int getScreenHeight() {
|
||||
return screenHeight;
|
||||
}
|
||||
|
||||
public static FScreen getCurrentScreen() {
|
||||
return currentScreen;
|
||||
}
|
||||
|
||||
private static void setCurrentScreen(FScreen screen0) {
|
||||
String toNewScreen = screen0 != null ? screen0.toString() : "";
|
||||
String previousScreen = currentScreen != null ? currentScreen.toString() : "";
|
||||
|
||||
gameInProgress = toNewScreen.toLowerCase().contains("match") || previousScreen.toLowerCase().contains("match");
|
||||
boolean dispose = toNewScreen.toLowerCase().contains("homescreen") && disposeTextures;
|
||||
try {
|
||||
endKeyInput(); //end key input before switching screens
|
||||
ForgeAnimation.endAll(); //end all active animations before switching screens
|
||||
|
||||
currentScreen = screen0;
|
||||
currentScreen.setSize(screenWidth, screenHeight);
|
||||
currentScreen.onActivate();
|
||||
/*keep Dscreens growing
|
||||
if (Dscreens.size() > 3) {
|
||||
for(int x = Dscreens.size(); x > 3; x--) {
|
||||
Dscreens.removeLast();
|
||||
}
|
||||
}*/
|
||||
/* for checking only
|
||||
if (!Dscreens.isEmpty()) {
|
||||
int x = 0;
|
||||
for(FScreen fScreen : Dscreens) {
|
||||
System.out.println("Screen ["+x+"]: "+fScreen.toString());
|
||||
x++;
|
||||
}
|
||||
System.out.println("---------------");
|
||||
}*/
|
||||
} catch (Exception ex) {
|
||||
graphics.end();
|
||||
//check if sentry is enabled, if not it will call the gui interface but here we end the graphics so we only send it via sentry..
|
||||
if (BugReporter.isSentryEnabled())
|
||||
BugReporter.reportException(ex);
|
||||
} finally {
|
||||
if (dispose)
|
||||
ImageCache.disposeTexture();
|
||||
}
|
||||
}
|
||||
|
||||
//log message to Forge.log file
|
||||
public static void log(Object message) {
|
||||
System.out.println(message);
|
||||
}
|
||||
|
||||
public static void startKeyInput(KeyInputAdapter adapter) {
|
||||
if (keyInputAdapter == adapter) {
|
||||
return;
|
||||
}
|
||||
if (keyInputAdapter != null) {
|
||||
keyInputAdapter.onInputEnd(); //make sure previous adapter is ended
|
||||
}
|
||||
keyInputAdapter = adapter;
|
||||
Gdx.input.setOnscreenKeyboardVisible(true);
|
||||
}
|
||||
|
||||
public static boolean endKeyInput() {
|
||||
if (keyInputAdapter == null) {
|
||||
return false;
|
||||
}
|
||||
keyInputAdapter.onInputEnd();
|
||||
keyInputAdapter = null;
|
||||
MainInputProcessor.keyTyped = false;
|
||||
MainInputProcessor.lastKeyTyped = '\0';
|
||||
Gdx.input.setOnscreenKeyboardVisible(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void create() {
|
||||
//install our error handler
|
||||
ExceptionHandler.registerErrorHandling();
|
||||
|
||||
GuiBase.setIsAndroid(Gdx.app.getType() == Application.ApplicationType.Android);
|
||||
|
||||
graphics = new Graphics();
|
||||
splashScreen = new SplashScreen();
|
||||
frameRate = new FrameRate();
|
||||
input=new MainInputProcessor();
|
||||
Gdx.input.setInputProcessor(input);
|
||||
/*
|
||||
Set CatchBackKey here and exit the app when you hit the
|
||||
back button while the textures,fonts,etc are still loading,
|
||||
to prevent rendering issue when you try to restart
|
||||
the app again (seems it doesnt dispose correctly...?!?)
|
||||
*/
|
||||
Gdx.input.setCatchKey(Keys.BACK, true);
|
||||
destroyThis = true; //Prevent back()
|
||||
ForgePreferences prefs = new ForgePreferences();
|
||||
|
||||
String skinName;
|
||||
if (FileUtil.doesFileExist(ForgeConstants.MAIN_PREFS_FILE)) {
|
||||
skinName = prefs.getPref(FPref.UI_SKIN);
|
||||
} else {
|
||||
skinName = "default"; //use default skin if preferences file doesn't exist yet
|
||||
}
|
||||
FSkin.loadLight(skinName, splashScreen);
|
||||
|
||||
textureFiltering = prefs.getPrefBoolean(FPref.UI_LIBGDX_TEXTURE_FILTERING);
|
||||
showFPS = prefs.getPrefBoolean(FPref.UI_SHOW_FPS);
|
||||
altPlayerLayout = prefs.getPrefBoolean(FPref.UI_ALT_PLAYERINFOLAYOUT);
|
||||
altZoneTabs = prefs.getPrefBoolean(FPref.UI_ALT_PLAYERZONETABS);
|
||||
enableUIMask = prefs.getPref(FPref.UI_ENABLE_BORDER_MASKING);
|
||||
if (prefs.getPref(FPref.UI_ENABLE_BORDER_MASKING).equals("true")) //override old settings if not updated
|
||||
enableUIMask = "Full";
|
||||
else if (prefs.getPref(FPref.UI_ENABLE_BORDER_MASKING).equals("false"))
|
||||
enableUIMask = "Off";
|
||||
enableUIMask = "Full";
|
||||
enablePreloadExtendedArt = prefs.getPrefBoolean(FPref.UI_ENABLE_PRELOAD_EXTENDED_ART);
|
||||
locale = prefs.getPref(FPref.UI_LANGUAGE);
|
||||
autoCache = prefs.getPrefBoolean(FPref.UI_AUTO_CACHE_SIZE);
|
||||
disposeTextures = prefs.getPrefBoolean(FPref.UI_ENABLE_DISPOSE_TEXTURES);
|
||||
CJK_Font = prefs.getPref(FPref.UI_CJK_FONT);
|
||||
|
||||
if (autoCache) {
|
||||
//increase cacheSize for devices with RAM more than 5GB, default is 400. Some phones have more than 10GB RAM (Mi 10, OnePlus 8, S20, etc..)
|
||||
if (totalDeviceRAM > 5000) //devices with more than 10GB RAM will have 800 Cache size, 600 Cache size for morethan 5GB RAM
|
||||
cacheSize = totalDeviceRAM > 10000 ? 800 : 600;
|
||||
}
|
||||
//init cache
|
||||
ImageCache.initCache(cacheSize);
|
||||
final Localizer localizer = Localizer.getInstance();
|
||||
|
||||
//load model on background thread (using progress bar to report progress)
|
||||
FThreads.invokeInBackgroundThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
//see if app or assets need updating
|
||||
AssetsDownloader.checkForUpdates(splashScreen);
|
||||
if (exited) {
|
||||
return;
|
||||
} //don't continue if user chose to exit or couldn't download required assets
|
||||
|
||||
FModel.initialize(splashScreen.getProgressBar(), null);
|
||||
|
||||
splashScreen.getProgressBar().setDescription(localizer.getMessage("lblLoadingFonts"));
|
||||
FSkinFont.preloadAll(locale);
|
||||
|
||||
splashScreen.getProgressBar().setDescription(localizer.getMessage("lblLoadingCardTranslations"));
|
||||
CardTranslation.preloadTranslation(locale, ForgeConstants.LANG_DIR);
|
||||
|
||||
splashScreen.getProgressBar().setDescription(localizer.getMessage("lblFinishingStartup"));
|
||||
|
||||
//add reminder to preload
|
||||
if (enablePreloadExtendedArt) {
|
||||
if (autoCache)
|
||||
splashScreen.getProgressBar().setDescription(localizer.getMessage("lblPreloadExtendedArt") + "\nDetected RAM: " + totalDeviceRAM + "MB. Cache size: " + cacheSize);
|
||||
else
|
||||
splashScreen.getProgressBar().setDescription(localizer.getMessage("lblPreloadExtendedArt"));
|
||||
} else {
|
||||
if (autoCache)
|
||||
splashScreen.getProgressBar().setDescription(localizer.getMessage("lblFinishingStartup") + "\nDetected RAM: " + totalDeviceRAM + "MB. Cache size: " + cacheSize);
|
||||
else
|
||||
splashScreen.getProgressBar().setDescription(localizer.getMessage("lblFinishingStartup"));
|
||||
}
|
||||
|
||||
Gdx.app.postRunnable(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
afterDbLoaded();
|
||||
/* call preloadExtendedArt here, if we put it above we will *
|
||||
* get error: No OpenGL context found in the current thread. */
|
||||
preloadExtendedArt();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void preloadExtendedArt() {
|
||||
if (!enablePreloadExtendedArt)
|
||||
return;
|
||||
List<String> borderlessCardlistkeys = FileUtil.readFile(ForgeConstants.BORDERLESS_CARD_LIST_FILE);
|
||||
if (borderlessCardlistkeys.isEmpty())
|
||||
return;
|
||||
List<String> filteredkeys = new ArrayList<>();
|
||||
for (String cardname : borderlessCardlistkeys) {
|
||||
File image = new File(ForgeConstants.CACHE_CARD_PICS_DIR + ForgeConstants.PATH_SEPARATOR + cardname + ".jpg");
|
||||
if (image.exists())
|
||||
filteredkeys.add(cardname);
|
||||
}
|
||||
if (!filteredkeys.isEmpty())
|
||||
ImageCache.preloadCache(filteredkeys);
|
||||
}
|
||||
|
||||
private void afterDbLoaded() {
|
||||
stopContinuousRendering(); //save power consumption by disabling continuous rendering once assets loaded
|
||||
|
||||
FSkin.loadFull(splashScreen);
|
||||
|
||||
SoundSystem.instance.setBackgroundMusic(MusicPlaylist.MENUS); //start background music
|
||||
destroyThis = false; //Allow back()
|
||||
Gdx.input.setCatchKey(Keys.MENU, true);
|
||||
openHomeScreen(-1); //default for startup
|
||||
splashScreen = null;
|
||||
|
||||
boolean isLandscapeMode = isLandscapeMode();
|
||||
|
||||
|
||||
//adjust height modifier
|
||||
adjustHeightModifier(getScreenWidth(), getScreenHeight());
|
||||
|
||||
//update landscape mode preference if it doesn't match what the app loaded as
|
||||
if (FModel.getPreferences().getPrefBoolean(FPref.UI_LANDSCAPE_MODE) != isLandscapeMode) {
|
||||
FModel.getPreferences().setPref(FPref.UI_LANDSCAPE_MODE, isLandscapeMode);
|
||||
FModel.getPreferences().save();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render() {
|
||||
if (showFPS)
|
||||
frameRate.update();
|
||||
|
||||
try {
|
||||
ImageCache.allowSingleLoad();
|
||||
ForgeAnimation.advanceAll();
|
||||
|
||||
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); // Clear the screen.
|
||||
|
||||
FContainer screen = currentScreen;
|
||||
if (screen == null) {
|
||||
screen = splashScreen;
|
||||
if (screen == null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
graphics.begin(screenWidth, screenHeight);
|
||||
screen.screenPos.setSize(screenWidth, screenHeight);
|
||||
if (screen.getRotate180()) {
|
||||
graphics.startRotateTransform(screenWidth / 2, screenHeight / 2, 180);
|
||||
}
|
||||
screen.draw(graphics);
|
||||
if (screen.getRotate180()) {
|
||||
graphics.endTransform();
|
||||
}
|
||||
for (FOverlay overlay : FOverlay.getOverlays()) {
|
||||
if (overlay.isVisibleOnScreen(currentScreen)) {
|
||||
overlay.screenPos.setSize(screenWidth, screenHeight);
|
||||
overlay.setSize(screenWidth, screenHeight); //update overlay sizes as they're rendered
|
||||
if (overlay.getRotate180()) {
|
||||
graphics.startRotateTransform(screenWidth / 2, screenHeight / 2, 180);
|
||||
}
|
||||
overlay.draw(graphics);
|
||||
if (overlay.getRotate180()) {
|
||||
graphics.endTransform();
|
||||
}
|
||||
}
|
||||
}
|
||||
graphics.end();
|
||||
} catch (Exception ex) {
|
||||
graphics.end();
|
||||
//check if sentry is enabled, if not it will call the gui interface but here we end the graphics so we only send it via sentry..
|
||||
if (BugReporter.isSentryEnabled())
|
||||
BugReporter.reportException(ex);
|
||||
}
|
||||
if (showFPS)
|
||||
frameRate.render();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resize(int width, int height) {
|
||||
try {
|
||||
screenWidth = width;
|
||||
screenHeight = height;
|
||||
if (currentScreen != null) {
|
||||
currentScreen.setSize(width, height);
|
||||
} else if (splashScreen != null) {
|
||||
splashScreen.setSize(width, height);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
graphics.end();
|
||||
//check if sentry is enabled, if not it will call the gui interface but here we end the graphics so we only send it via sentry..
|
||||
if (BugReporter.isSentryEnabled())
|
||||
BugReporter.reportException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pause() {
|
||||
if (MatchController.getHostedMatch() != null) {
|
||||
MatchController.getHostedMatch().pause();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resume() {
|
||||
if (MatchController.getHostedMatch() != null) {
|
||||
MatchController.getHostedMatch().resume();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
if (currentScreen != null) {
|
||||
FOverlay.hideAll();
|
||||
currentScreen.onClose(null);
|
||||
currentScreen = null;
|
||||
}
|
||||
Dscreens.clear();
|
||||
graphics.dispose();
|
||||
SoundSystem.instance.dispose();
|
||||
try {
|
||||
ExceptionHandler.unregisterErrorHandling();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
|
||||
public static abstract class KeyInputAdapter {
|
||||
public static boolean isCtrlKeyDown() {
|
||||
return Gdx.input.isKeyPressed(Keys.CONTROL_LEFT) || Gdx.input.isKeyPressed(Keys.CONTROL_RIGHT);
|
||||
}
|
||||
|
||||
public static boolean isShiftKeyDown() {
|
||||
return Gdx.input.isKeyPressed(Keys.SHIFT_LEFT) || Gdx.input.isKeyPressed(Keys.SHIFT_RIGHT);
|
||||
}
|
||||
|
||||
public static boolean isAltKeyDown() {
|
||||
return Gdx.input.isKeyPressed(Keys.ALT_LEFT) || Gdx.input.isKeyPressed(Keys.ALT_RIGHT);
|
||||
}
|
||||
|
||||
public static boolean isModifierKey(int keyCode) {
|
||||
switch (keyCode) {
|
||||
case Keys.CONTROL_LEFT:
|
||||
case Keys.CONTROL_RIGHT:
|
||||
case Keys.SHIFT_LEFT:
|
||||
case Keys.SHIFT_RIGHT:
|
||||
case Keys.ALT_LEFT:
|
||||
case Keys.ALT_RIGHT:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public abstract FDisplayObject getOwner();
|
||||
|
||||
public abstract boolean allowTouchInput();
|
||||
|
||||
public abstract boolean keyTyped(char ch);
|
||||
|
||||
public abstract boolean keyDown(int keyCode);
|
||||
|
||||
public abstract void onInputEnd();
|
||||
|
||||
//also allow handling of keyUp but don't require it
|
||||
public boolean keyUp(int keyCode) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static class MainInputProcessor extends FGestureAdapter {
|
||||
private static final List<FDisplayObject> potentialListeners = new ArrayList<>();
|
||||
private static char lastKeyTyped;
|
||||
private static boolean keyTyped, shiftKeyDown;
|
||||
//mouseMoved and scrolled events for desktop version
|
||||
private int mouseMovedX, mouseMovedY;
|
||||
|
||||
@Override
|
||||
public boolean keyDown(int keyCode) {
|
||||
if (keyCode == Keys.MENU) {
|
||||
showMenu();
|
||||
return true;
|
||||
}
|
||||
if (keyCode == Keys.SHIFT_LEFT || keyCode == Keys.SHIFT_RIGHT) {
|
||||
shiftKeyDown = true;
|
||||
}
|
||||
|
||||
// Cursor keys emulate swipe gestures
|
||||
// First we touch the screen and later swipe (fling) in the direction of the key pressed
|
||||
if (keyCode == Keys.LEFT) {
|
||||
touchDown(0, 0, 0, 0);
|
||||
return fling(1000, 0);
|
||||
}
|
||||
if (keyCode == Keys.RIGHT) {
|
||||
touchDown(0, 0, 0, 0);
|
||||
return fling(-1000, 0);
|
||||
}
|
||||
if (keyCode == Keys.UP) {
|
||||
touchDown(0, 0, 0, 0);
|
||||
return fling(0, -1000);
|
||||
}
|
||||
if (keyCode == Keys.DOWN) {
|
||||
touchDown(0, 0, 0, 0);
|
||||
return fling(0, 1000);
|
||||
}
|
||||
if (keyCode == Keys.BACK) {
|
||||
if (destroyThis)
|
||||
deviceAdapter.exit();
|
||||
else if (onHomeScreen() && isLandscapeMode())
|
||||
back();
|
||||
}
|
||||
if (keyInputAdapter == null) {
|
||||
if (KeyInputAdapter.isModifierKey(keyCode)) {
|
||||
return false; //don't process modifiers keys for unknown adapter
|
||||
}
|
||||
//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.keyDown(keyCode);
|
||||
}
|
||||
return keyInputAdapter.keyDown(keyCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean keyUp(int keyCode) {
|
||||
keyTyped = false; //reset on keyUp
|
||||
if (keyCode == Keys.SHIFT_LEFT || keyCode == Keys.SHIFT_RIGHT) {
|
||||
shiftKeyDown = false;
|
||||
}
|
||||
if (keyInputAdapter != null) {
|
||||
return keyInputAdapter.keyUp(keyCode);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean keyTyped(char ch) {
|
||||
if (keyInputAdapter != null) {
|
||||
if (ch >= ' ' && ch <= '~') { //only process this event if character is printable
|
||||
//prevent firing this event more than once for the same character on the same key down, otherwise it fires too often
|
||||
if (lastKeyTyped != ch || !keyTyped) {
|
||||
keyTyped = true;
|
||||
lastKeyTyped = ch;
|
||||
return keyInputAdapter.keyTyped(ch);
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void updatePotentialListeners(int x, int y) {
|
||||
potentialListeners.clear();
|
||||
|
||||
//base potential listeners on object containing touch down point
|
||||
for (FOverlay overlay : FOverlay.getOverlaysTopDown()) {
|
||||
if (overlay.isVisibleOnScreen(currentScreen)) {
|
||||
overlay.buildTouchListeners(x, y, potentialListeners);
|
||||
if (overlay.preventInputBehindOverlay()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (currentScreen != null) {
|
||||
currentScreen.buildTouchListeners(x, y, potentialListeners);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean touchDown(int x, int y, int pointer, int button) {
|
||||
if (pointer == 0) { //don't change listeners when second finger goes down for zoom
|
||||
updatePotentialListeners(x, y);
|
||||
if (keyInputAdapter != null) {
|
||||
if (!keyInputAdapter.allowTouchInput() || !potentialListeners.contains(keyInputAdapter.getOwner())) {
|
||||
endKeyInput(); //end key input if needed
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.touchDown(x, y, pointer, button);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean press(float x, float y) {
|
||||
try {
|
||||
for (FDisplayObject listener : potentialListeners) {
|
||||
if (listener.press(listener.screenToLocalX(x), listener.screenToLocalY(y))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} catch (Exception ex) {
|
||||
BugReporter.reportException(ex);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean release(float x, float y) {
|
||||
try {
|
||||
for (FDisplayObject listener : potentialListeners) {
|
||||
if (listener.release(listener.screenToLocalX(x), listener.screenToLocalY(y))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} catch (Exception ex) {
|
||||
BugReporter.reportException(ex);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean longPress(float x, float y) {
|
||||
try {
|
||||
for (FDisplayObject listener : potentialListeners) {
|
||||
if (listener.longPress(listener.screenToLocalX(x), listener.screenToLocalY(y))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} catch (Exception ex) {
|
||||
BugReporter.reportException(ex);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean tap(float x, float y, int count) {
|
||||
if (shiftKeyDown && flick(x, y)) {
|
||||
return true; //give flick logic a chance to handle Shift+click
|
||||
}
|
||||
try {
|
||||
for (FDisplayObject listener : potentialListeners) {
|
||||
if (listener.tap(listener.screenToLocalX(x), listener.screenToLocalY(y), count)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} catch (Exception ex) {
|
||||
BugReporter.reportException(ex);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean flick(float x, float y) {
|
||||
try {
|
||||
for (FDisplayObject listener : potentialListeners) {
|
||||
if (listener.flick(listener.screenToLocalX(x), listener.screenToLocalY(y))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} catch (Exception ex) {
|
||||
BugReporter.reportException(ex);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean fling(float velocityX, float velocityY) {
|
||||
try {
|
||||
for (FDisplayObject listener : potentialListeners) {
|
||||
if (listener.fling(velocityX, velocityY)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} catch (Exception ex) {
|
||||
BugReporter.reportException(ex);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean pan(float x, float y, float deltaX, float deltaY, boolean moreVertical) {
|
||||
try {
|
||||
for (FDisplayObject listener : potentialListeners) {
|
||||
if (listener.pan(listener.screenToLocalX(x), listener.screenToLocalY(y), deltaX, deltaY, moreVertical)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} catch (Exception ex) {
|
||||
BugReporter.reportException(ex);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean panStop(float x, float y) {
|
||||
try {
|
||||
for (FDisplayObject listener : potentialListeners) {
|
||||
if (listener.panStop(listener.screenToLocalX(x), listener.screenToLocalY(y))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} catch (Exception ex) {
|
||||
BugReporter.reportException(ex);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean zoom(float x, float y, float amount) {
|
||||
try {
|
||||
for (FDisplayObject listener : potentialListeners) {
|
||||
if (listener.zoom(listener.screenToLocalX(x), listener.screenToLocalY(y), amount)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} catch (Exception ex) {
|
||||
BugReporter.reportException(ex);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseMoved(int x, int y) {
|
||||
mouseMovedX = x;
|
||||
mouseMovedY = y;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean scrolled(float amountX, float amountY) {
|
||||
updatePotentialListeners(mouseMovedX, mouseMovedY);
|
||||
|
||||
if (KeyInputAdapter.isCtrlKeyDown()) { //zoom in or out based on amount
|
||||
return zoom(mouseMovedX, mouseMovedY, -Utils.AVG_FINGER_WIDTH * amountY);
|
||||
}
|
||||
|
||||
boolean handled;
|
||||
if (KeyInputAdapter.isShiftKeyDown()) {
|
||||
handled = pan(mouseMovedX, mouseMovedY, -Utils.AVG_FINGER_WIDTH * amountX, 0, false);
|
||||
} else {
|
||||
handled = pan(mouseMovedX, mouseMovedY, 0, -Utils.AVG_FINGER_HEIGHT * amountY, true);
|
||||
}
|
||||
if (panStop(mouseMovedX, mouseMovedY)) {
|
||||
handled = true;
|
||||
}
|
||||
return handled;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package forge.adventure.libgdxgui;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.graphics.OrthographicCamera;
|
||||
import com.badlogic.gdx.graphics.g2d.BitmapFont;
|
||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
|
||||
import com.badlogic.gdx.utils.Disposable;
|
||||
import com.badlogic.gdx.utils.TimeUtils;
|
||||
|
||||
/**
|
||||
* A nicer class for showing framerate that doesn't spam the console
|
||||
* like Logger.log()
|
||||
*
|
||||
* @author William Hartman
|
||||
*/
|
||||
|
||||
public class FrameRate implements Disposable{
|
||||
long lastTimeCounted;
|
||||
private float sinceChange;
|
||||
private float frameRate;
|
||||
private final BitmapFont font;
|
||||
private final SpriteBatch batch;
|
||||
private OrthographicCamera cam;
|
||||
|
||||
public FrameRate() {
|
||||
lastTimeCounted = TimeUtils.millis();
|
||||
sinceChange = 0;
|
||||
frameRate = Gdx.graphics.getFramesPerSecond();
|
||||
font = new BitmapFont();
|
||||
batch = new SpriteBatch();
|
||||
cam = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
|
||||
}
|
||||
|
||||
public void resize(int screenWidth, int screenHeight) {
|
||||
cam = new OrthographicCamera(screenWidth, screenHeight);
|
||||
cam.translate(screenWidth / 2, screenHeight / 2);
|
||||
cam.update();
|
||||
batch.setProjectionMatrix(cam.combined);
|
||||
}
|
||||
|
||||
public void update() {
|
||||
long delta = TimeUtils.timeSinceMillis(lastTimeCounted);
|
||||
lastTimeCounted = TimeUtils.millis();
|
||||
sinceChange += delta;
|
||||
if(sinceChange >= 1000) {
|
||||
sinceChange = 0;
|
||||
frameRate = Gdx.graphics.getFramesPerSecond();
|
||||
}
|
||||
}
|
||||
|
||||
public void render() {
|
||||
batch.begin();
|
||||
font.draw(batch, (int)frameRate + " fps", 3, Gdx.graphics.getHeight() - 3);
|
||||
batch.end();
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
font.dispose();
|
||||
batch.dispose();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,887 @@
|
||||
package forge.adventure.libgdxgui;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.graphics.Color;
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
import com.badlogic.gdx.graphics.Texture;
|
||||
import com.badlogic.gdx.graphics.g2d.Batch;
|
||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
|
||||
import com.badlogic.gdx.graphics.g2d.TextureRegion;
|
||||
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
|
||||
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
|
||||
import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType;
|
||||
import com.badlogic.gdx.math.Matrix4;
|
||||
import com.badlogic.gdx.math.Rectangle;
|
||||
import com.badlogic.gdx.math.Vector2;
|
||||
import com.badlogic.gdx.math.Vector3;
|
||||
import com.badlogic.gdx.scenes.scene2d.utils.ScissorStack;
|
||||
import forge.adventure.libgdxgui.assets.FImage;
|
||||
import forge.adventure.libgdxgui.assets.FSkinColor;
|
||||
import forge.adventure.libgdxgui.assets.FSkinFont;
|
||||
import forge.adventure.libgdxgui.toolbox.FDisplayObject;
|
||||
import forge.adventure.libgdxgui.util.TextBounds;
|
||||
import forge.adventure.libgdxgui.util.Utils;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
|
||||
public class Graphics {
|
||||
private static final int GL_BLEND = GL20.GL_BLEND;
|
||||
private static final int GL_LINE_SMOOTH = 2848; //create constant here since not in GL20
|
||||
|
||||
private final Batch batch;
|
||||
private final ShapeRenderer shapeRenderer = new ShapeRenderer();
|
||||
private final Deque<Matrix4> Dtransforms = new ArrayDeque<>();
|
||||
private final Vector3 tmp = new Vector3();
|
||||
private float regionHeight;
|
||||
private Rectangle bounds;
|
||||
private Rectangle visibleBounds;
|
||||
private int failedClipCount;
|
||||
private float alphaComposite = 1;
|
||||
private int transformCount = 0;
|
||||
private final String sVertex = "uniform mat4 u_projTrans;\n" +
|
||||
"\n" +
|
||||
"attribute vec4 a_position;\n" +
|
||||
"attribute vec2 a_texCoord0;\n" +
|
||||
"attribute vec4 a_color;\n" +
|
||||
"\n" +
|
||||
"varying vec4 v_color;\n" +
|
||||
"varying vec2 v_texCoord;\n" +
|
||||
"\n" +
|
||||
"uniform vec2 u_viewportInverse;\n" +
|
||||
"\n" +
|
||||
"void main() {\n" +
|
||||
" gl_Position = u_projTrans * a_position;\n" +
|
||||
" v_texCoord = a_texCoord0;\n" +
|
||||
" v_color = a_color;\n" +
|
||||
"}";
|
||||
private final String sFragment = "#ifdef GL_ES\n" +
|
||||
"precision mediump float;\n" +
|
||||
"precision mediump int;\n" +
|
||||
"#endif\n" +
|
||||
"\n" +
|
||||
"uniform sampler2D u_texture;\n" +
|
||||
"\n" +
|
||||
"// The inverse of the viewport dimensions along X and Y\n" +
|
||||
"uniform vec2 u_viewportInverse;\n" +
|
||||
"\n" +
|
||||
"// Color of the outline\n" +
|
||||
"uniform vec3 u_color;\n" +
|
||||
"\n" +
|
||||
"// Thickness of the outline\n" +
|
||||
"uniform float u_offset;\n" +
|
||||
"\n" +
|
||||
"// Step to check for neighbors\n" +
|
||||
"uniform float u_step;\n" +
|
||||
"\n" +
|
||||
"varying vec4 v_color;\n" +
|
||||
"varying vec2 v_texCoord;\n" +
|
||||
"\n" +
|
||||
"#define ALPHA_VALUE_BORDER 0.5\n" +
|
||||
"\n" +
|
||||
"void main() {\n" +
|
||||
" vec2 T = v_texCoord.xy;\n" +
|
||||
"\n" +
|
||||
" float alpha = 0.0;\n" +
|
||||
" bool allin = true;\n" +
|
||||
" for( float ix = -u_offset; ix < u_offset; ix += u_step )\n" +
|
||||
" {\n" +
|
||||
" for( float iy = -u_offset; iy < u_offset; iy += u_step )\n" +
|
||||
" {\n" +
|
||||
" float newAlpha = texture2D(u_texture, T + vec2(ix, iy) * u_viewportInverse).a;\n" +
|
||||
" allin = allin && newAlpha > ALPHA_VALUE_BORDER;\n" +
|
||||
" if (newAlpha > ALPHA_VALUE_BORDER && newAlpha >= alpha)\n" +
|
||||
" {\n" +
|
||||
" alpha = newAlpha;\n" +
|
||||
" }\n" +
|
||||
" }\n" +
|
||||
" }\n" +
|
||||
" if (allin)\n" +
|
||||
" {\n" +
|
||||
" alpha = 0.0;\n" +
|
||||
" }\n" +
|
||||
"\n" +
|
||||
" gl_FragColor = vec4(u_color,alpha);\n" +
|
||||
"}";
|
||||
|
||||
private final ShaderProgram shaderOutline = new ShaderProgram(sVertex, sFragment);
|
||||
|
||||
public Graphics() {
|
||||
batch = new SpriteBatch();
|
||||
ShaderProgram.pedantic = false;
|
||||
}
|
||||
public Graphics(Batch batch,float regionWidth0, float regionHeight0) {
|
||||
this.batch=batch;bound(regionWidth0, regionHeight0);
|
||||
}
|
||||
public void bound(float regionWidth0, float regionHeight0) {
|
||||
bounds = new Rectangle(0, 0, regionWidth0, regionHeight0);
|
||||
regionHeight = regionHeight0;
|
||||
visibleBounds = new Rectangle(bounds);
|
||||
}
|
||||
|
||||
public void begin(float regionWidth0, float regionHeight0) {
|
||||
batch.begin();
|
||||
bounds = new Rectangle(0, 0, regionWidth0, regionHeight0);
|
||||
regionHeight = regionHeight0;
|
||||
visibleBounds = new Rectangle(bounds);
|
||||
}
|
||||
|
||||
public void end() {
|
||||
if (batch.isDrawing()) {
|
||||
batch.end();
|
||||
}
|
||||
if (shapeRenderer.getCurrentType() != null) {
|
||||
shapeRenderer.end();
|
||||
}
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
batch.dispose();
|
||||
shapeRenderer.dispose();
|
||||
shaderOutline.dispose();
|
||||
}
|
||||
|
||||
public SpriteBatch getBatch() {
|
||||
return (SpriteBatch)batch;
|
||||
}
|
||||
|
||||
public boolean startClip() {
|
||||
return startClip(0, 0, bounds.width, bounds.height);
|
||||
}
|
||||
public boolean startClip(float x, float y, float w, float h) {
|
||||
batch.flush(); //must flush batch to prevent other things not rendering
|
||||
|
||||
Rectangle clip = new Rectangle(adjustX(x), adjustY(y, h), w, h);
|
||||
if (!Dtransforms.isEmpty()) { //transform position if needed
|
||||
tmp.set(clip.x, clip.y, 0);
|
||||
tmp.mul(batch.getTransformMatrix());
|
||||
float minX = tmp.x;
|
||||
float maxX = minX;
|
||||
float minY = tmp.y;
|
||||
float maxY = minY;
|
||||
tmp.set(clip.x + clip.width, clip.y, 0);
|
||||
tmp.mul(batch.getTransformMatrix());
|
||||
if (tmp.x < minX) { minX = tmp.x; }
|
||||
else if (tmp.x > maxX) { maxX = tmp.x; }
|
||||
if (tmp.y < minY) { minY = tmp.y; }
|
||||
else if (tmp.y > maxY) { maxY = tmp.y; }
|
||||
tmp.set(clip.x + clip.width, clip.y + clip.height, 0);
|
||||
tmp.mul(batch.getTransformMatrix());
|
||||
if (tmp.x < minX) { minX = tmp.x; }
|
||||
else if (tmp.x > maxX) { maxX = tmp.x; }
|
||||
if (tmp.y < minY) { minY = tmp.y; }
|
||||
else if (tmp.y > maxY) { maxY = tmp.y; }
|
||||
tmp.set(clip.x, clip.y + clip.height, 0);
|
||||
tmp.mul(batch.getTransformMatrix());
|
||||
if (tmp.x < minX) { minX = tmp.x; }
|
||||
else if (tmp.x > maxX) { maxX = tmp.x; }
|
||||
if (tmp.y < minY) { minY = tmp.y; }
|
||||
else if (tmp.y > maxY) { maxY = tmp.y; }
|
||||
|
||||
clip.set(minX, minY, maxX - minX, maxY - minY);
|
||||
}
|
||||
if (!ScissorStack.pushScissors(clip)) {
|
||||
failedClipCount++; //tracked failed clips to prevent calling popScissors on endClip
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
public void endClip() {
|
||||
if (failedClipCount == 0) {
|
||||
batch.flush(); //must flush batch to ensure stuffed rendered during clip respects that clip
|
||||
ScissorStack.popScissors();
|
||||
}
|
||||
else {
|
||||
failedClipCount--;
|
||||
}
|
||||
}
|
||||
|
||||
public void draw(FDisplayObject displayObj) {
|
||||
if (displayObj.getWidth() <= 0 || displayObj.getHeight() <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Rectangle parentBounds = bounds;
|
||||
bounds = new Rectangle(parentBounds.x + displayObj.getLeft(), parentBounds.y + displayObj.getTop(), displayObj.getWidth(), displayObj.getHeight());
|
||||
if (!Dtransforms.isEmpty()) { //transform screen position if needed by applying transform matrix to rectangle
|
||||
updateScreenPosForRotation(displayObj);
|
||||
}
|
||||
else {
|
||||
displayObj.screenPos.set(bounds);
|
||||
}
|
||||
|
||||
Rectangle intersection = Utils.getIntersection(bounds, visibleBounds);
|
||||
if (intersection != null) { //avoid drawing object if it's not within visible region
|
||||
final Rectangle backup = visibleBounds;
|
||||
visibleBounds = intersection;
|
||||
|
||||
if (displayObj.getRotate90()) { //use top-right corner of bounds as pivot point
|
||||
startRotateTransform(displayObj.getWidth(), 0, -90);
|
||||
updateScreenPosForRotation(displayObj);
|
||||
}
|
||||
else if (displayObj.getRotate180()) { //use center of bounds as pivot point
|
||||
startRotateTransform(displayObj.getWidth() / 2, displayObj.getHeight() / 2, 180);
|
||||
//screen position won't change for this object from a 180 degree rotation
|
||||
}
|
||||
|
||||
displayObj.draw(this);
|
||||
|
||||
if (displayObj.getRotate90() || displayObj.getRotate180()) {
|
||||
endTransform();
|
||||
}
|
||||
|
||||
visibleBounds = backup;
|
||||
}
|
||||
|
||||
bounds = parentBounds;
|
||||
}
|
||||
|
||||
private void updateScreenPosForRotation(FDisplayObject displayObj) {
|
||||
tmp.set(bounds.x, regionHeight - bounds.y, 0);
|
||||
tmp.mul(batch.getTransformMatrix());
|
||||
tmp.y = regionHeight - tmp.y;
|
||||
float minX = tmp.x;
|
||||
float maxX = minX;
|
||||
float minY = tmp.y;
|
||||
float maxY = minY;
|
||||
tmp.set(bounds.x + bounds.width, regionHeight - bounds.y, 0);
|
||||
tmp.mul(batch.getTransformMatrix());
|
||||
tmp.y = regionHeight - tmp.y;
|
||||
if (tmp.x < minX) { minX = tmp.x; }
|
||||
else if (tmp.x > maxX) { maxX = tmp.x; }
|
||||
if (tmp.y < minY) { minY = tmp.y; }
|
||||
else if (tmp.y > maxY) { maxY = tmp.y; }
|
||||
tmp.set(bounds.x + bounds.width, regionHeight - bounds.y - bounds.height, 0);
|
||||
tmp.mul(batch.getTransformMatrix());
|
||||
tmp.y = regionHeight - tmp.y;
|
||||
if (tmp.x < minX) { minX = tmp.x; }
|
||||
else if (tmp.x > maxX) { maxX = tmp.x; }
|
||||
if (tmp.y < minY) { minY = tmp.y; }
|
||||
else if (tmp.y > maxY) { maxY = tmp.y; }
|
||||
tmp.set(bounds.x, regionHeight - bounds.y - bounds.height, 0);
|
||||
tmp.mul(batch.getTransformMatrix());
|
||||
tmp.y = regionHeight - tmp.y;
|
||||
if (tmp.x < minX) { minX = tmp.x; }
|
||||
else if (tmp.x > maxX) { maxX = tmp.x; }
|
||||
if (tmp.y < minY) { minY = tmp.y; }
|
||||
else if (tmp.y > maxY) { maxY = tmp.y; }
|
||||
|
||||
displayObj.screenPos.set(minX, minY, maxX - minX, maxY - minY);
|
||||
}
|
||||
|
||||
public void drawLine(float thickness, FSkinColor skinColor, float x1, float y1, float x2, float y2) {
|
||||
drawLine(thickness, skinColor.getColor(), x1, y1, x2, y2);
|
||||
}
|
||||
public void drawLine(float thickness, Color color, float x1, float y1, float x2, float y2) {
|
||||
batch.end(); //must pause batch while rendering shapes
|
||||
|
||||
if (thickness > 1) {
|
||||
Gdx.gl.glLineWidth(thickness);
|
||||
}
|
||||
if (alphaComposite < 1) {
|
||||
color = FSkinColor.alphaColor(color, color.a * alphaComposite);
|
||||
}
|
||||
boolean needSmoothing = (x1 != x2 && y1 != y2);
|
||||
if (color.a < 1 || needSmoothing) { //enable blending so alpha colored shapes work properly
|
||||
Gdx.gl.glEnable(GL_BLEND);
|
||||
}
|
||||
if (needSmoothing) {
|
||||
Gdx.gl.glEnable(GL_LINE_SMOOTH);
|
||||
}
|
||||
|
||||
startShape(ShapeType.Line);
|
||||
shapeRenderer.setColor(color);
|
||||
shapeRenderer.line(adjustX(x1), adjustY(y1, 0), adjustX(x2), adjustY(y2, 0));
|
||||
endShape();
|
||||
|
||||
if (needSmoothing) {
|
||||
Gdx.gl.glDisable(GL_LINE_SMOOTH);
|
||||
}
|
||||
if (color.a < 1 || needSmoothing) {
|
||||
Gdx.gl.glDisable(GL_BLEND);
|
||||
}
|
||||
if (thickness > 1) {
|
||||
Gdx.gl.glLineWidth(1);
|
||||
}
|
||||
|
||||
batch.begin();
|
||||
}
|
||||
|
||||
public void drawArrow(float borderThickness, float arrowThickness, float arrowSize, FSkinColor skinColor, float x1, float y1, float x2, float y2) {
|
||||
drawArrow(borderThickness, arrowThickness, arrowSize, skinColor.getColor(), x1, y1, x2, y2);
|
||||
}
|
||||
public void drawArrow(float borderThickness, float arrowThickness, float arrowSize, Color color, float x1, float y1, float x2, float y2) {
|
||||
batch.end(); //must pause batch while rendering shapes
|
||||
|
||||
if (alphaComposite < 1) {
|
||||
color = FSkinColor.alphaColor(color, color.a * alphaComposite);
|
||||
}
|
||||
Gdx.gl.glEnable(GL_BLEND);
|
||||
Gdx.gl.glEnable(GL_LINE_SMOOTH);
|
||||
|
||||
float angle = new Vector2(x2 - x1, y2 - y1).angleRad();
|
||||
float perpRotation = (float)(Math.PI * 0.5f);
|
||||
float arrowHeadRotation = (float)(Math.PI * 0.8f);
|
||||
float arrowTipAngle = (float)(Math.PI - arrowHeadRotation);
|
||||
float halfThickness = arrowThickness / 2;
|
||||
|
||||
int index = 0;
|
||||
float[] vertices = new float[14];
|
||||
Vector2 arrowCorner1 = new Vector2(x2 + arrowSize * (float)Math.cos(angle + arrowHeadRotation), y2 + arrowSize * (float)Math.sin(angle + arrowHeadRotation));
|
||||
Vector2 arrowCorner2 = new Vector2(x2 + arrowSize * (float)Math.cos(angle - arrowHeadRotation), y2 + arrowSize * (float)Math.sin(angle - arrowHeadRotation));
|
||||
float arrowCornerLen = (arrowCorner1.dst(arrowCorner2) - arrowThickness) / 2;
|
||||
float arrowHeadLen = arrowSize * (float)Math.cos(arrowTipAngle);
|
||||
index = addVertex(arrowCorner1.x, arrowCorner1.y, vertices, index);
|
||||
index = addVertex(x2, y2, vertices, index);
|
||||
index = addVertex(arrowCorner2.x, arrowCorner2.y, vertices, index);
|
||||
index = addVertex(arrowCorner2.x + arrowCornerLen * (float)Math.cos(angle + perpRotation), arrowCorner2.y + arrowCornerLen * (float)Math.sin(angle + perpRotation), vertices, index);
|
||||
index = addVertex(x1 + halfThickness * (float)Math.cos(angle - perpRotation), y1 + halfThickness * (float)Math.sin(angle - perpRotation), vertices, index);
|
||||
index = addVertex(x1 + halfThickness * (float)Math.cos(angle + perpRotation), y1 + halfThickness * (float)Math.sin(angle + perpRotation), vertices, index);
|
||||
index = addVertex(arrowCorner1.x + arrowCornerLen * (float)Math.cos(angle - perpRotation), arrowCorner1.y + arrowCornerLen * (float)Math.sin(angle - perpRotation), vertices, index);
|
||||
|
||||
//draw arrow tail
|
||||
startShape(ShapeType.Filled);
|
||||
shapeRenderer.setColor(color);
|
||||
shapeRenderer.rectLine(adjustX(x1), adjustY(y1, 0),
|
||||
adjustX(x2 - arrowHeadLen * (float)Math.cos(angle)), //shorten tail to make room for arrow head
|
||||
adjustY(y2 - arrowHeadLen * (float)Math.sin(angle), 0), arrowThickness);
|
||||
|
||||
//draw arrow head
|
||||
shapeRenderer.triangle(vertices[0], vertices[1], vertices[2], vertices[3], vertices[4], vertices[5]);
|
||||
endShape();
|
||||
|
||||
//draw border around arrow
|
||||
if (borderThickness > 1) {
|
||||
Gdx.gl.glLineWidth(borderThickness);
|
||||
}
|
||||
startShape(ShapeType.Line);
|
||||
shapeRenderer.setColor(Color.BLACK);
|
||||
shapeRenderer.polygon(vertices);
|
||||
endShape();
|
||||
if (borderThickness > 1) {
|
||||
Gdx.gl.glLineWidth(1);
|
||||
}
|
||||
|
||||
Gdx.gl.glDisable(GL_LINE_SMOOTH);
|
||||
Gdx.gl.glDisable(GL_BLEND);
|
||||
|
||||
batch.begin();
|
||||
}
|
||||
|
||||
private int addVertex(float x, float y, float[] vertices, int index) {
|
||||
vertices[index] = adjustX(x);
|
||||
vertices[index + 1] = adjustY(y, 0);
|
||||
return index + 2;
|
||||
}
|
||||
|
||||
public void drawfillBorder(float thickness, Color color, float x, float y, float w, float h, float cornerRadius) {
|
||||
drawRoundRect(thickness, color, x, y, w, h, cornerRadius);
|
||||
fillRoundRect(color, x, y, w, h, cornerRadius);
|
||||
}
|
||||
|
||||
public void drawRoundRect(float thickness, FSkinColor skinColor, float x, float y, float w, float h, float cornerRadius) {
|
||||
drawRoundRect(thickness, skinColor.getColor(), x, y, w, h, cornerRadius);
|
||||
}
|
||||
public void drawRoundRect(float thickness, Color color, float x, float y, float w, float h, float cornerRadius) {
|
||||
batch.end(); //must pause batch while rendering shapes
|
||||
|
||||
if (thickness > 1) {
|
||||
Gdx.gl.glLineWidth(thickness);
|
||||
}
|
||||
if (alphaComposite < 1) {
|
||||
color = FSkinColor.alphaColor(color, color.a * alphaComposite);
|
||||
}
|
||||
if (color.a < 1 || cornerRadius > 0) { //enable blending so alpha colored shapes work properly
|
||||
Gdx.gl.glEnable(GL_BLEND);
|
||||
}
|
||||
if (cornerRadius > 0) {
|
||||
Gdx.gl.glEnable(GL_LINE_SMOOTH);
|
||||
}
|
||||
|
||||
//adjust width/height so rectangle covers equivalent filled area
|
||||
w = Math.round(w + 1);
|
||||
h = Math.round(h + 1);
|
||||
|
||||
startShape(ShapeType.Line);
|
||||
shapeRenderer.setColor(color);
|
||||
|
||||
shapeRenderer.arc(adjustX(x) + cornerRadius, adjustY(y + cornerRadius, 0), cornerRadius, 90f, 90f);
|
||||
shapeRenderer.arc(adjustX(x) + w - cornerRadius, adjustY(y + cornerRadius, 0), cornerRadius, 0f, 90f);
|
||||
shapeRenderer.arc(adjustX(x) + w - cornerRadius, adjustY(y + h - cornerRadius, 0), cornerRadius, 270, 90f);
|
||||
shapeRenderer.arc(adjustX(x) + cornerRadius, adjustY(y + h - cornerRadius, 0), cornerRadius, 180, 90f);
|
||||
shapeRenderer.rect(adjustX(x), adjustY(y+cornerRadius, h-cornerRadius*2), w, h-cornerRadius*2);
|
||||
shapeRenderer.rect(adjustX(x+cornerRadius), adjustY(y, h), w-cornerRadius*2, h);
|
||||
|
||||
endShape();
|
||||
|
||||
if (cornerRadius > 0) {
|
||||
Gdx.gl.glDisable(GL_LINE_SMOOTH);
|
||||
}
|
||||
if (color.a < 1 || cornerRadius > 0) {
|
||||
Gdx.gl.glDisable(GL_BLEND);
|
||||
}
|
||||
if (thickness > 1) {
|
||||
Gdx.gl.glLineWidth(1);
|
||||
}
|
||||
|
||||
batch.begin();
|
||||
}
|
||||
|
||||
public void fillRoundRect(FSkinColor skinColor, float x, float y, float w, float h, float cornerRadius) {
|
||||
fillRoundRect(skinColor.getColor(), x, y, w, h, cornerRadius);
|
||||
}
|
||||
public void fillRoundRect(Color color, float x, float y, float w, float h, float cornerRadius) {
|
||||
batch.end(); //must pause batch while rendering shapes
|
||||
if (alphaComposite < 1) {
|
||||
color = FSkinColor.alphaColor(color, color.a * alphaComposite);
|
||||
}
|
||||
if (color.a < 1) { //enable blending so alpha colored shapes work properly
|
||||
Gdx.gl.glEnable(GL_BLEND);
|
||||
}
|
||||
startShape(ShapeType.Filled);
|
||||
shapeRenderer.setColor(color);
|
||||
shapeRenderer.arc(adjustX(x) + cornerRadius, adjustY(y + cornerRadius, 0), cornerRadius, 90f, 90f);
|
||||
shapeRenderer.arc(adjustX(x) + w - cornerRadius, adjustY(y + cornerRadius, 0), cornerRadius, 0f, 90f);
|
||||
shapeRenderer.arc(adjustX(x) + w - cornerRadius, adjustY(y + h - cornerRadius, 0), cornerRadius, 270, 90f);
|
||||
shapeRenderer.arc(adjustX(x) + cornerRadius, adjustY(y + h - cornerRadius, 0), cornerRadius, 180, 90f);
|
||||
shapeRenderer.rect(adjustX(x), adjustY(y+cornerRadius, h-cornerRadius*2), w, h-cornerRadius*2);
|
||||
shapeRenderer.rect(adjustX(x+cornerRadius), adjustY(y, h), w-cornerRadius*2, h);
|
||||
endShape();
|
||||
if (color.a < 1) {
|
||||
Gdx.gl.glDisable(GL_BLEND);
|
||||
}
|
||||
batch.begin();
|
||||
}
|
||||
|
||||
public void drawRect(float thickness, FSkinColor skinColor, float x, float y, float w, float h) {
|
||||
drawRect(thickness, skinColor.getColor(), x, y, w, h);
|
||||
}
|
||||
public void drawRect(float thickness, Color color, float x, float y, float w, float h) {
|
||||
batch.end(); //must pause batch while rendering shapes
|
||||
|
||||
if (thickness > 1) {
|
||||
Gdx.gl.glLineWidth(thickness);
|
||||
}
|
||||
if (alphaComposite < 1) {
|
||||
color = FSkinColor.alphaColor(color, color.a * alphaComposite);
|
||||
}
|
||||
Gdx.gl.glEnable(GL_BLEND);
|
||||
Gdx.gl.glEnable(GL_LINE_SMOOTH); //must be smooth to ensure edges aren't missed
|
||||
|
||||
startShape(ShapeType.Line);
|
||||
shapeRenderer.setColor(color);
|
||||
shapeRenderer.rect(adjustX(x), adjustY(y, h), w, h);
|
||||
endShape();
|
||||
|
||||
Gdx.gl.glDisable(GL_LINE_SMOOTH);
|
||||
Gdx.gl.glDisable(GL_BLEND);
|
||||
if (thickness > 1) {
|
||||
Gdx.gl.glLineWidth(1);
|
||||
}
|
||||
|
||||
batch.begin();
|
||||
}
|
||||
|
||||
public void fillRect(FSkinColor skinColor, float x, float y, float w, float h) {
|
||||
fillRect(skinColor.getColor(), x, y, w, h);
|
||||
}
|
||||
public void fillRect(Color color, float x, float y, float w, float h) {
|
||||
batch.end(); //must pause batch while rendering shapes
|
||||
|
||||
if (alphaComposite < 1) {
|
||||
color = FSkinColor.alphaColor(color, color.a * alphaComposite);
|
||||
}
|
||||
if (color.a < 1) { //enable blending so alpha colored shapes work properly
|
||||
Gdx.gl.glEnable(GL_BLEND);
|
||||
}
|
||||
|
||||
startShape(ShapeType.Filled);
|
||||
shapeRenderer.setColor(color);
|
||||
shapeRenderer.rect(adjustX(x), adjustY(y, h), w, h);
|
||||
endShape();
|
||||
|
||||
if (color.a < 1) {
|
||||
Gdx.gl.glDisable(GL_BLEND);
|
||||
}
|
||||
|
||||
batch.begin();
|
||||
}
|
||||
|
||||
public void drawCircle(float thickness, FSkinColor skinColor, float x, float y, float radius) {
|
||||
drawCircle(thickness, skinColor.getColor(), x, y, radius);
|
||||
}
|
||||
public void drawCircle(float thickness, Color color, float x, float y, float radius) {
|
||||
batch.end(); //must pause batch while rendering shapes
|
||||
|
||||
if (thickness > 1) {
|
||||
Gdx.gl.glLineWidth(thickness);
|
||||
}
|
||||
if (alphaComposite < 1) {
|
||||
color = FSkinColor.alphaColor(color, color.a * alphaComposite);
|
||||
}
|
||||
Gdx.gl.glEnable(GL_BLEND);
|
||||
Gdx.gl.glEnable(GL_LINE_SMOOTH);
|
||||
|
||||
startShape(ShapeType.Line);
|
||||
shapeRenderer.setColor(color);
|
||||
shapeRenderer.circle(adjustX(x), adjustY(y, 0), radius);
|
||||
endShape();
|
||||
|
||||
Gdx.gl.glDisable(GL_LINE_SMOOTH);
|
||||
Gdx.gl.glDisable(GL_BLEND);
|
||||
if (thickness > 1) {
|
||||
Gdx.gl.glLineWidth(1);
|
||||
}
|
||||
|
||||
batch.begin();
|
||||
}
|
||||
|
||||
public void fillCircle(FSkinColor skinColor, float x, float y, float radius) {
|
||||
fillCircle(skinColor.getColor(), x, y, radius);
|
||||
}
|
||||
public void fillCircle(Color color, float x, float y, float radius) {
|
||||
batch.end(); //must pause batch while rendering shapes
|
||||
|
||||
if (alphaComposite < 1) {
|
||||
color = FSkinColor.alphaColor(color, color.a * alphaComposite);
|
||||
}
|
||||
if (color.a < 1) { //enable blending so alpha colored shapes work properly
|
||||
Gdx.gl.glEnable(GL_BLEND);
|
||||
}
|
||||
|
||||
startShape(ShapeType.Filled);
|
||||
shapeRenderer.setColor(color);
|
||||
shapeRenderer.circle(adjustX(x), adjustY(y, 0), radius); //TODO: Make smoother
|
||||
endShape();
|
||||
|
||||
if (color.a < 1) {
|
||||
Gdx.gl.glDisable(GL_BLEND);
|
||||
}
|
||||
|
||||
batch.begin();
|
||||
}
|
||||
|
||||
public void fillTriangle(FSkinColor skinColor, float x1, float y1, float x2, float y2, float x3, float y3) {
|
||||
fillTriangle(skinColor.getColor(), x1, y1, x2, y2, x3, y3);
|
||||
}
|
||||
public void fillTriangle(Color color, float x1, float y1, float x2, float y2, float x3, float y3) {
|
||||
batch.end(); //must pause batch while rendering shapes
|
||||
|
||||
if (alphaComposite < 1) {
|
||||
color = FSkinColor.alphaColor(color, color.a * alphaComposite);
|
||||
}
|
||||
if (color.a < 1) { //enable blending so alpha colored shapes work properly
|
||||
Gdx.gl.glEnable(GL_BLEND);
|
||||
}
|
||||
|
||||
startShape(ShapeType.Filled);
|
||||
shapeRenderer.setColor(color);
|
||||
shapeRenderer.triangle(adjustX(x1), adjustY(y1, 0), adjustX(x2), adjustY(y2, 0), adjustX(x3), adjustY(y3, 0));
|
||||
endShape();
|
||||
|
||||
if (color.a < 1) {
|
||||
Gdx.gl.glDisable(GL_BLEND);
|
||||
}
|
||||
|
||||
batch.begin();
|
||||
}
|
||||
|
||||
public void fillGradientRect(FSkinColor skinColor1, FSkinColor skinColor2, boolean vertical, float x, float y, float w, float h) {
|
||||
fillGradientRect(skinColor1.getColor(), skinColor2.getColor(), vertical, x, y, w, h);
|
||||
}
|
||||
public void fillGradientRect(FSkinColor skinColor1, Color color2, boolean vertical, float x, float y, float w, float h) {
|
||||
fillGradientRect(skinColor1.getColor(), color2, vertical, x, y, w, h);
|
||||
}
|
||||
public void fillGradientRect(Color color1, FSkinColor skinColor2, boolean vertical, float x, float y, float w, float h) {
|
||||
fillGradientRect(color1, skinColor2.getColor(), vertical, x, y, w, h);
|
||||
}
|
||||
public void fillGradientRect(Color color1, Color color2, boolean vertical, float x, float y, float w, float h) {
|
||||
batch.end(); //must pause batch while rendering shapes
|
||||
|
||||
if (alphaComposite < 1) {
|
||||
color1 = FSkinColor.alphaColor(color1, color1.a * alphaComposite);
|
||||
color2 = FSkinColor.alphaColor(color2, color2.a * alphaComposite);
|
||||
}
|
||||
boolean needBlending = (color1.a < 1 || color2.a < 1);
|
||||
if (needBlending) { //enable blending so alpha colored shapes work properly
|
||||
Gdx.gl.glEnable(GL_BLEND);
|
||||
}
|
||||
|
||||
Color topLeftColor = color1;
|
||||
Color topRightColor = vertical ? color1 : color2;
|
||||
Color bottomLeftColor = vertical ? color2 : color1;
|
||||
Color bottomRightColor = color2;
|
||||
|
||||
startShape(ShapeType.Filled);
|
||||
shapeRenderer.rect(adjustX(x), adjustY(y, h), w, h, bottomLeftColor, bottomRightColor, topRightColor, topLeftColor);
|
||||
endShape();
|
||||
|
||||
if (needBlending) {
|
||||
Gdx.gl.glDisable(GL_BLEND);
|
||||
}
|
||||
|
||||
batch.begin();
|
||||
}
|
||||
|
||||
private void startShape(ShapeType shapeType) {
|
||||
if (!Dtransforms.isEmpty()) {
|
||||
//must copy matrix before starting shape if transformed
|
||||
shapeRenderer.setTransformMatrix(batch.getTransformMatrix());
|
||||
}
|
||||
shapeRenderer.begin(shapeType);
|
||||
}
|
||||
|
||||
private void endShape() {
|
||||
shapeRenderer.end();
|
||||
}
|
||||
|
||||
public void setAlphaComposite(float alphaComposite0) {
|
||||
alphaComposite = alphaComposite0;
|
||||
batch.setColor(new Color(1, 1, 1, alphaComposite));
|
||||
}
|
||||
public void resetAlphaComposite() {
|
||||
alphaComposite = 1;
|
||||
batch.setColor(Color.WHITE);
|
||||
}
|
||||
public float getfloatAlphaComposite() { return alphaComposite; }
|
||||
|
||||
public void drawBorderImage(FImage image, Color borderColor, Color tintColor, float x, float y, float w, float h, boolean tint) {
|
||||
float oldalpha = alphaComposite;
|
||||
if(tint && !tintColor.equals(borderColor)){
|
||||
drawRoundRect(2f, borderLining(borderColor.toString()), x, y, w, h, (h-w)/12);
|
||||
fillRoundRect(tintColor, x, y, w, h, (h-w)/12);
|
||||
} else {
|
||||
image.draw(this, x, y, w, h);
|
||||
fillRoundRect(borderColor, x, y, w, h, (h-w)/10);//show corners edges
|
||||
}
|
||||
setAlphaComposite(oldalpha);
|
||||
}
|
||||
public void drawborderImage(Color borderColor, float x, float y, float w, float h) {
|
||||
float oldalpha = alphaComposite;
|
||||
fillRoundRect(borderColor, x, y, w, h, (h-w)/12);
|
||||
setAlphaComposite(oldalpha);
|
||||
}
|
||||
public void drawImage(FImage image, Color borderColor, float x, float y, float w, float h) {
|
||||
image.draw(this, x, y, w, h);
|
||||
fillRoundRect(borderColor, x+1, y+1, w-1.5f, h-1.5f, (h-w)/10);//used by zoom let some edges show...
|
||||
}
|
||||
public void drawImage(FImage image, float x, float y, float w, float h) {
|
||||
drawImage(image, x, y, w, h, false);
|
||||
}
|
||||
public void drawImage(FImage image, float x, float y, float w, float h, boolean withDarkOverlay) {
|
||||
image.draw(this, x, y, w, h);
|
||||
if(withDarkOverlay){
|
||||
float oldalpha = alphaComposite;
|
||||
setAlphaComposite(0.4f);
|
||||
fillRect(Color.BLACK, x, y, w, h);
|
||||
setAlphaComposite(oldalpha);
|
||||
}
|
||||
}
|
||||
public void drawImage(Texture image, float x, float y, float w, float h) {
|
||||
batch.draw(image, adjustX(x), adjustY(y, h), w, h);
|
||||
}
|
||||
public void drawImage(TextureRegion image, float x, float y, float w, float h) {
|
||||
if (image != null)
|
||||
batch.draw(image, adjustX(x), adjustY(y, h), w, h);
|
||||
}
|
||||
public void drawImage(TextureRegion image, TextureRegion glowImageReference, float x, float y, float w, float h, Color glowColor, boolean selected) {
|
||||
if (image == null || glowImageReference == null)
|
||||
return;
|
||||
//1st image is the image on top of the shader, 2nd image is for the outline reference for the shader glow...
|
||||
// if the 1st image don't have transparency in the middle (only on the sides, top and bottom, use the 1st image as outline reference...
|
||||
if (!selected) {
|
||||
batch.draw(image, adjustX(x), adjustY(y, h), w, h);
|
||||
} else {
|
||||
batch.end();
|
||||
shaderOutline.bind();
|
||||
shaderOutline.setUniformf("u_viewportInverse", new Vector2(1f / w, 1f / h));
|
||||
shaderOutline.setUniformf("u_offset", 3f);
|
||||
shaderOutline.setUniformf("u_step", Math.min(1f, w / 70f));
|
||||
shaderOutline.setUniformf("u_color", new Vector3(glowColor.r, glowColor.g, glowColor.b));
|
||||
batch.setShader(shaderOutline);
|
||||
batch.begin();
|
||||
//glow
|
||||
batch.draw(glowImageReference, adjustX(x), adjustY(y, h), w, h);
|
||||
batch.end();
|
||||
batch.setShader(null);
|
||||
batch.begin();
|
||||
//img
|
||||
batch.draw(image, adjustX(x), adjustY(y, h), w, h);
|
||||
}
|
||||
}
|
||||
public void drawDeckBox(FImage cardArt, float scale, TextureRegion image, TextureRegion glowImageReference, float x, float y, float w, float h, Color glowColor, boolean selected) {
|
||||
if (image == null || glowImageReference == null)
|
||||
return;
|
||||
float yBox = y-(h*0.25f);
|
||||
if (!selected) {
|
||||
cardArt.draw(this,x+((w-w*scale)/2), y+((h-h*scale)/3f), w*scale, h*scale/1.85f);
|
||||
batch.draw(image, adjustX(x), adjustY(yBox, h), w, h);
|
||||
} else {
|
||||
batch.end();
|
||||
shaderOutline.bind();
|
||||
shaderOutline.setUniformf("u_viewportInverse", new Vector2(1f / w, 1f / h));
|
||||
shaderOutline.setUniformf("u_offset", 3f);
|
||||
shaderOutline.setUniformf("u_step", Math.min(1f, w / 70f));
|
||||
shaderOutline.setUniformf("u_color", new Vector3(glowColor.r, glowColor.g, glowColor.b));
|
||||
batch.setShader(shaderOutline);
|
||||
batch.begin();
|
||||
//glow
|
||||
batch.draw(glowImageReference, adjustX(x), adjustY(yBox, h), w, h);
|
||||
batch.end();
|
||||
batch.setShader(null);
|
||||
batch.begin();
|
||||
//cardart
|
||||
cardArt.draw(this,x+((w-w*scale)/2), y+((h-h*scale)/3f), w*scale, h*scale/1.85f);
|
||||
//deckbox
|
||||
batch.draw(image, adjustX(x), adjustY(yBox, h), w, h);
|
||||
}
|
||||
}
|
||||
|
||||
public void drawRepeatingImage(Texture image, float x, float y, float w, float h) {
|
||||
if (startClip(x, y, w, h)) { //only render if clip successful, otherwise it will escape bounds
|
||||
int tilesW = (int)(w / image.getWidth()) + 1;
|
||||
int tilesH = (int)(h / image.getHeight()) + 1;
|
||||
batch.draw(image, adjustX(x), adjustY(y, h),
|
||||
image.getWidth() * tilesW,
|
||||
image.getHeight() * tilesH,
|
||||
0, tilesH, tilesW, 0);
|
||||
}
|
||||
endClip();
|
||||
}
|
||||
|
||||
//draw vertically flipped image
|
||||
public void drawFlippedImage(Texture image, float x, float y, float w, float h) {
|
||||
batch.draw(image, adjustX(x), adjustY(y, h), w, h, 0, 0, image.getWidth(), image.getHeight(), false, true);
|
||||
}
|
||||
|
||||
public void drawImageWithTransforms(TextureRegion image, float x, float y, float w, float h, float rotation, boolean flipX, boolean flipY) {
|
||||
float originX = x + w / 2;
|
||||
float originY = y + h / 2;
|
||||
batch.draw(image.getTexture(), adjustX(x), adjustY(y, h), originX - x, h - (originY - y), w, h, 1, 1, rotation, image.getRegionX(), image.getRegionY(), image.getRegionWidth(), image.getRegionHeight(), flipX, flipY);
|
||||
}
|
||||
|
||||
public void setProjectionMatrix(Matrix4 matrix) {
|
||||
batch.setProjectionMatrix(matrix);
|
||||
shapeRenderer.setProjectionMatrix(matrix);
|
||||
}
|
||||
|
||||
public void startRotateTransform(float originX, float originY, float rotation) {
|
||||
batch.end();
|
||||
Dtransforms.addFirst(new Matrix4(batch.getTransformMatrix().idt())); //startshape is using this above as reference
|
||||
transformCount++;
|
||||
batch.getTransformMatrix().idt().translate(adjustX(originX), adjustY(originY, 0), 0).rotate(Vector3.Z, rotation).translate(-adjustX(originX), -adjustY(originY, 0), 0);
|
||||
batch.begin();
|
||||
}
|
||||
|
||||
public void endTransform() {
|
||||
batch.end();
|
||||
shapeRenderer.setTransformMatrix(batch.getTransformMatrix().idt());
|
||||
Dtransforms.removeFirst();
|
||||
transformCount--;
|
||||
if(transformCount != Dtransforms.size()) {
|
||||
System.err.println(String.format("Stack count: %d, transformCount: %d", Dtransforms.size(), transformCount));
|
||||
transformCount = 0;
|
||||
Dtransforms.clear();
|
||||
}
|
||||
batch.getTransformMatrix().idt(); //reset
|
||||
shapeRenderer.getTransformMatrix().idt(); //reset
|
||||
batch.begin();
|
||||
}
|
||||
|
||||
public void drawRotatedImage(Texture image, float x, float y, float w, float h, float originX, float originY, float rotation) {
|
||||
drawRotatedImage(image, x, y, w, h, originX, originY, 0, 0, image.getWidth(), image.getHeight(), rotation);
|
||||
}
|
||||
public void drawRotatedImage(TextureRegion image, float x, float y, float w, float h, float originX, float originY, float rotation) {
|
||||
drawRotatedImage(image.getTexture(), x, y, w, h, originX, originY, image.getRegionX(), image.getRegionY(), image.getRegionWidth(), image.getRegionHeight(), rotation);
|
||||
}
|
||||
public void drawRotatedImage(Texture image, float x, float y, float w, float h, float originX, float originY, int srcX, int srcY, int srcWidth, int srcHeight, float rotation) {
|
||||
batch.draw(image, adjustX(x), adjustY(y, h), originX - x, h - (originY - y), w, h, 1, 1, rotation, srcX, srcY, srcWidth, srcHeight, false, false);
|
||||
}
|
||||
|
||||
public void drawText(String text, FSkinFont font, FSkinColor skinColor, float x, float y, float w, float h, boolean wrap, int horzAlignment, boolean centerVertically) {
|
||||
drawText(text, font, skinColor.getColor(), x, y, w, h, wrap, horzAlignment, centerVertically);
|
||||
}
|
||||
public void drawText(String text, FSkinFont font, Color color, float x, float y, float w, float h, boolean wrap, int horzAlignment, boolean centerVertically) {
|
||||
if (text == null)
|
||||
return;
|
||||
if (alphaComposite < 1) {
|
||||
color = FSkinColor.alphaColor(color, color.a * alphaComposite);
|
||||
}
|
||||
if (color.a < 1) { //enable blending so alpha colored shapes work properly
|
||||
Gdx.gl.glEnable(GL_BLEND);
|
||||
}
|
||||
|
||||
TextBounds textBounds;
|
||||
if (wrap) {
|
||||
textBounds = font.getWrappedBounds(text, w);
|
||||
}
|
||||
else {
|
||||
textBounds = font.getMultiLineBounds(text);
|
||||
}
|
||||
|
||||
boolean needClip = false;
|
||||
|
||||
while (textBounds.width > w || textBounds.height > h) {
|
||||
if (font.canShrink()) { //shrink font to fit if possible
|
||||
font = font.shrink();
|
||||
if (wrap) {
|
||||
textBounds = font.getWrappedBounds(text, w);
|
||||
}
|
||||
else {
|
||||
textBounds = font.getMultiLineBounds(text);
|
||||
}
|
||||
}
|
||||
else {
|
||||
needClip = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (needClip) { //prevent text flowing outside region if couldn't shrink it to fit
|
||||
startClip(x, y, w, h);
|
||||
}
|
||||
|
||||
float textHeight = textBounds.height;
|
||||
if (h > textHeight && centerVertically) {
|
||||
y += (h - textHeight) / 2;
|
||||
}
|
||||
|
||||
font.draw(batch, text, color, adjustX(x), adjustY(y, 0), w, wrap, horzAlignment);
|
||||
|
||||
if (needClip) {
|
||||
endClip();
|
||||
}
|
||||
|
||||
if (color.a < 1) {
|
||||
Gdx.gl.glDisable(GL_BLEND);
|
||||
}
|
||||
}
|
||||
|
||||
//use nifty trick with multiple text renders to draw outlined text
|
||||
public void drawOutlinedText(String text, FSkinFont skinFont, Color textColor, Color outlineColor, float x, float y, float w, float h, boolean wrap, int horzAlignment, boolean centerVertically) {
|
||||
drawText(text, skinFont, outlineColor, x - 1, y, w, h, wrap, horzAlignment, centerVertically);
|
||||
drawText(text, skinFont, outlineColor, x, y - 1, w, h, wrap, horzAlignment, centerVertically);
|
||||
drawText(text, skinFont, outlineColor, x - 1, y - 1, w, h, wrap, horzAlignment, centerVertically);
|
||||
drawText(text, skinFont, outlineColor, x + 1, y, w, h, wrap, horzAlignment, centerVertically);
|
||||
drawText(text, skinFont, outlineColor, x, y + 1, w, h, wrap, horzAlignment, centerVertically);
|
||||
drawText(text, skinFont, outlineColor, x + 1, y + 1, w, h, wrap, horzAlignment, centerVertically);
|
||||
drawText(text, skinFont, textColor, x, y, w, h, wrap, horzAlignment, centerVertically);
|
||||
}
|
||||
|
||||
public float adjustX(float x) {
|
||||
return x + bounds.x;
|
||||
}
|
||||
|
||||
public float adjustY(float y, float height) {
|
||||
return regionHeight - y - bounds.y - height; //flip y-axis
|
||||
}
|
||||
public Color borderLining(String c){
|
||||
if (c == null || c == "")
|
||||
return Color.valueOf("#fffffd");
|
||||
int c_r = Integer.parseInt(c.substring(0,2),16);
|
||||
int c_g = Integer.parseInt(c.substring(2,4),16);
|
||||
int c_b = Integer.parseInt(c.substring(4,6),16);
|
||||
int brightness = ((c_r * 299) + (c_g * 587) + (c_b * 114)) / 1000;
|
||||
return brightness > 155 ? Color.valueOf("#171717") : Color.valueOf("#fffffd");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,329 @@
|
||||
package forge.adventure.libgdxgui;
|
||||
|
||||
import com.badlogic.gdx.Application.ApplicationType;
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.graphics.Texture;
|
||||
import com.google.common.base.Function;
|
||||
import forge.adventure.libgdxgui.assets.*;
|
||||
import forge.adventure.libgdxgui.card.CardRenderer;
|
||||
import forge.adventure.libgdxgui.deck.FDeckViewer;
|
||||
import forge.adventure.libgdxgui.error.BugReportDialog;
|
||||
import forge.adventure.libgdxgui.screens.LoadingOverlay;
|
||||
import forge.adventure.libgdxgui.screens.match.MatchController;
|
||||
import forge.adventure.libgdxgui.screens.settings.GuiDownloader;
|
||||
import forge.adventure.libgdxgui.sound.AudioClip;
|
||||
import forge.adventure.libgdxgui.sound.AudioMusic;
|
||||
import forge.adventure.libgdxgui.toolbox.FOptionPane;
|
||||
import forge.adventure.libgdxgui.toolbox.GuiChoose;
|
||||
import forge.adventure.libgdxgui.util.LibGDXImageFetcher;
|
||||
import forge.deck.Deck;
|
||||
import forge.gamemodes.match.HostedMatch;
|
||||
import forge.gui.download.GuiDownloadService;
|
||||
import forge.gui.interfaces.IGuiBase;
|
||||
import forge.gui.interfaces.IGuiGame;
|
||||
import forge.item.PaperCard;
|
||||
import forge.localinstance.properties.ForgeConstants;
|
||||
import forge.localinstance.skin.FSkinProp;
|
||||
import forge.localinstance.skin.ISkinImage;
|
||||
import forge.sound.IAudioClip;
|
||||
import forge.sound.IAudioMusic;
|
||||
import forge.util.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public class GuiMobile implements IGuiBase {
|
||||
private final String assetsDir;
|
||||
private final ImageFetcher imageFetcher = new LibGDXImageFetcher();
|
||||
|
||||
public GuiMobile(final String assetsDir0) {
|
||||
assetsDir = assetsDir0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRunningOnDesktop() {
|
||||
if(Gdx.app==null)
|
||||
return true;
|
||||
return Gdx.app.getType() == ApplicationType.Desktop;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLibgdxPort() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCurrentVersion() {
|
||||
return Forge.CURRENT_VERSION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAssetsDir() {
|
||||
return assetsDir;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImageFetcher getImageFetcher() {
|
||||
return imageFetcher;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invokeInEdtNow(final Runnable proc) {
|
||||
proc.run();
|
||||
Gdx.graphics.requestRendering(); //must request rendering in case this procedure wasn't triggered by a local event
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invokeInEdtLater(final Runnable proc) {
|
||||
Gdx.app.postRunnable(proc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invokeInEdtAndWait(final Runnable proc) {
|
||||
if (isGuiThread()) {
|
||||
proc.run();
|
||||
}
|
||||
else {
|
||||
new WaitRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
proc.run();
|
||||
}
|
||||
}.invokeAndWait();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGuiThread() {
|
||||
return !ThreadUtil.isGameThread();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ISkinImage getSkinIcon(final FSkinProp skinProp) {
|
||||
if (skinProp == null) { return null; }
|
||||
return FSkin.getImages().get(skinProp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ISkinImage getUnskinnedIcon(final String path) {
|
||||
if (isGuiThread()) {
|
||||
return new FTextureImage(new Texture(Gdx.files.absolute(path)));
|
||||
}
|
||||
|
||||
//use a delay load image to avoid an error if called from background thread
|
||||
return new FDelayLoadImage(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ISkinImage getCardArt(final PaperCard card) {
|
||||
return CardRenderer.getCardArt(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ISkinImage getCardArt(final PaperCard card, final boolean backFace) {
|
||||
return CardRenderer.getCardArt(card, backFace);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ISkinImage createLayeredImage(final FSkinProp background, final String overlayFilename, final float opacity) {
|
||||
return new FBufferedImage(background.getWidth(), background.getHeight(), opacity) {
|
||||
@Override
|
||||
protected void draw(final Graphics g, final float w, final float h) {
|
||||
g.drawImage(FSkin.getImages().get(background), 0, 0, background.getWidth(), background.getHeight());
|
||||
|
||||
if (FileUtil.doesFileExist(overlayFilename)) {
|
||||
try {
|
||||
final Texture overlay = new Texture(Gdx.files.absolute(overlayFilename));
|
||||
g.drawImage(overlay, (background.getWidth() - overlay.getWidth()) / 2, (background.getHeight() - overlay.getHeight()) / 2, overlay.getWidth(), overlay.getHeight());
|
||||
} catch (final Exception e) {
|
||||
}
|
||||
}
|
||||
|
||||
Gdx.graphics.requestRendering(); //ensure image appears right away
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showImageDialog(final ISkinImage image, final String message, final String title) {
|
||||
new WaitCallback<Integer>() {
|
||||
@Override
|
||||
public void run() {
|
||||
FOptionPane.showMessageDialog(message, title, (FImage)image, this);
|
||||
}
|
||||
}.invokeAndWait();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int showOptionDialog(final String message, final String title, final FSkinProp icon, final List<String> options, final int defaultOption) {
|
||||
return new WaitCallback<Integer>() {
|
||||
@Override
|
||||
public void run() {
|
||||
FOptionPane.showOptionDialog(message, title, icon == null ? null : FSkin.getImages().get(icon), options, defaultOption, this);
|
||||
}
|
||||
}.invokeAndWait();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String showInputDialog(final String message, final String title, final FSkinProp icon, final String initialInput, final List<String> inputOptions) {
|
||||
return new WaitCallback<String>() {
|
||||
@Override
|
||||
public void run() {
|
||||
FOptionPane.showInputDialog(message, title, initialInput, inputOptions, this);
|
||||
}
|
||||
}.invokeAndWait();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<T> getChoices(final String message, final int min, final int max, final Collection<T> choices, final T selected, final Function<T, String> display) {
|
||||
return new WaitCallback<List<T>>() {
|
||||
@Override
|
||||
public void run() {
|
||||
GuiChoose.getChoices(message, min, max, choices, selected, display, this);
|
||||
}
|
||||
}.invokeAndWait();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<T> order(final String title, final String top, final int remainingObjectsMin, final int remainingObjectsMax,
|
||||
final List<T> sourceChoices, final List<T> destChoices) {
|
||||
return new WaitCallback<List<T>>() {
|
||||
@Override
|
||||
public void run() {
|
||||
GuiChoose.order(title, top, remainingObjectsMin, remainingObjectsMax, sourceChoices, destChoices, null, this);
|
||||
}
|
||||
}.invokeAndWait();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showBugReportDialog(final String title, final String text, final boolean showExitAppBtn) {
|
||||
BugReportDialog.show(title, text, showExitAppBtn);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showCardList(final String title, final String message, final List<PaperCard> list) {
|
||||
final Deck deck = new Deck(title + " - " + message);
|
||||
deck.getMain().addAllFlat(list);
|
||||
FDeckViewer.show(deck, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean showBoxedProduct(final String title, final String message, final List<PaperCard> list) {
|
||||
final Deck deck = new Deck(title + " - " + message); //TODO: Make this nicer
|
||||
deck.getMain().addAllFlat(list);
|
||||
FDeckViewer.show(deck);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PaperCard chooseCard(final String title, final String message, final List<PaperCard> list) {
|
||||
return new WaitCallback<PaperCard>() {
|
||||
@Override
|
||||
public void run() {
|
||||
GuiChoose.one(title + " - " + message, list, this);
|
||||
}
|
||||
}.invokeAndWait();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAvatarCount() {
|
||||
if (FSkin.isLoaded()) {
|
||||
return FSkin.getAvatars().size();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSleevesCount() {
|
||||
if (FSkin.isLoaded()) {
|
||||
return FSkin.getSleeves().size();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String showFileDialog(final String title, final String defaultDir) {
|
||||
return ForgeConstants.USER_GAMES_DIR + "Test.fgs"; //TODO: Show dialog
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getSaveFile(final File defaultFile) {
|
||||
return defaultFile; //TODO: Show dialog
|
||||
}
|
||||
|
||||
@Override
|
||||
public void download(final GuiDownloadService service, final Callback<Boolean> callback) {
|
||||
new GuiDownloader(service, callback).show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshSkin() {
|
||||
//todo refresh skin selector
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copyToClipboard(final String text) {
|
||||
Forge.getClipboard().setContents(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void browseToUrl(final String url) {
|
||||
Gdx.net.openURI(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IAudioClip createAudioClip(final String filename) {
|
||||
return AudioClip.createClip(ForgeConstants.SOUND_DIR + filename);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IAudioMusic createAudioMusic(final String filename) {
|
||||
return new AudioMusic(filename);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startAltSoundSystem(final String filename, final boolean isSynchronized) {
|
||||
//TODO: Support alt sound system
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearImageCache() {
|
||||
ImageCache.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showSpellShop() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showBazaar() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public IGuiGame getNewGuiGame() {
|
||||
return MatchController.instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HostedMatch hostMatch() {
|
||||
return MatchController.hostMatch();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runBackgroundTask(String message, Runnable task) {
|
||||
LoadingOverlay.runBackgroundTask(message, task);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encodeSymbols(String str, boolean formatReminderText) {
|
||||
return str; //not needed for mobile
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preventSystemSleep(boolean preventSleep) {
|
||||
Forge.getDeviceAdapter().preventSystemSleep(preventSleep);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package forge.adventure.libgdxgui.animation;
|
||||
|
||||
import forge.adventure.libgdxgui.Graphics;
|
||||
import forge.adventure.libgdxgui.sound.AudioClip;
|
||||
import forge.localinstance.properties.ForgeConstants;
|
||||
import forge.localinstance.properties.ForgePreferences;
|
||||
import forge.model.FModel;
|
||||
|
||||
public enum AbilityEffect {
|
||||
LIGHTNING("lightning.gif", "lightning.wav");
|
||||
|
||||
private final String gif, wav;
|
||||
private forge.adventure.libgdxgui.animation.GifAnimation animation;
|
||||
private AudioClip soundClip;
|
||||
|
||||
AbilityEffect(String gif0, String wav0) {
|
||||
gif = gif0;
|
||||
wav = wav0;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
if (animation == null) {
|
||||
animation = new GifAnimation(ForgeConstants.EFFECTS_DIR + gif);
|
||||
}
|
||||
if (soundClip == null) {
|
||||
soundClip = AudioClip.createClip(ForgeConstants.EFFECTS_DIR + wav);
|
||||
}
|
||||
soundClip.play(FModel.getPreferences().getPrefInt(ForgePreferences.FPref.UI_VOL_SOUNDS)/100f);
|
||||
animation.start();
|
||||
}
|
||||
|
||||
public void draw(Graphics g, float x, float y, float w, float h) {
|
||||
if (animation != null) {
|
||||
animation.draw(g, x, y, w, h);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package forge.adventure.libgdxgui.animation;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import forge.adventure.libgdxgui.Forge;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class ForgeAnimation {
|
||||
private static final List<ForgeAnimation> activeAnimations = new ArrayList<>();
|
||||
// A guard against inspecting activeAnimations while it's in the process of being edited
|
||||
private static boolean changingActiveAnimations = false;
|
||||
|
||||
public void start() {
|
||||
if (activeAnimations.contains(this)) { return; } //prevent starting the same animation multiple times
|
||||
|
||||
activeAnimations.add(this);
|
||||
if (activeAnimations.size() == 1 && !changingActiveAnimations) { //if first animation being started, ensure continuous rendering turned on
|
||||
Forge.startContinuousRendering();
|
||||
}
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
if (!activeAnimations.contains(this)) { return; } //prevent stopping the same animation multiple times
|
||||
|
||||
activeAnimations.remove(this);
|
||||
onEnd(false);
|
||||
if (activeAnimations.isEmpty()) { //when all animations have stopped, turn continuous rendering back off
|
||||
Forge.stopContinuousRendering();
|
||||
}
|
||||
}
|
||||
|
||||
public static void advanceAll() {
|
||||
if (activeAnimations.isEmpty()) { return; }
|
||||
|
||||
float dt = Gdx.graphics.getDeltaTime();
|
||||
for (int i = 0; i < activeAnimations.size(); i++) {
|
||||
if (!activeAnimations.get(i).advance(dt)) {
|
||||
// Without this guard, there is leaky behavior when a new animation is started
|
||||
// via the onEnd callback of a finishing animation; this is because the length
|
||||
// of the list is in the process of changing from 1 to 0 to 1 again, so
|
||||
// stopContinuousRendering() won't be called in this function (so it's
|
||||
// important to not allow startContinuousRendering() to be called either).
|
||||
changingActiveAnimations = true;
|
||||
activeAnimations.remove(i).onEnd(false);
|
||||
changingActiveAnimations = false;
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
if (activeAnimations.isEmpty()) { //when all animations have ended, turn continuous rendering back off
|
||||
Forge.stopContinuousRendering();
|
||||
}
|
||||
}
|
||||
|
||||
public static void endAll() {
|
||||
if (activeAnimations.isEmpty()) { return; }
|
||||
|
||||
for (ForgeAnimation animation : activeAnimations) {
|
||||
animation.onEnd(true);
|
||||
}
|
||||
activeAnimations.clear();
|
||||
Forge.stopContinuousRendering();
|
||||
}
|
||||
|
||||
//return true if animation should continue, false to stop the animation
|
||||
protected abstract boolean advance(float dt);
|
||||
protected abstract void onEnd(boolean endingAll);
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package forge.adventure.libgdxgui.animation;
|
||||
|
||||
import com.badlogic.gdx.math.Rectangle;
|
||||
import forge.adventure.libgdxgui.Graphics;
|
||||
import forge.adventure.libgdxgui.toolbox.FDisplayObject;
|
||||
import forge.adventure.libgdxgui.toolbox.FOverlay;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
|
||||
public class ForgeTransition extends ForgeAnimation {
|
||||
private static final FOverlay overlay = new FOverlay(null) {
|
||||
@Override protected void doLayout(final float width, final float height) {
|
||||
}
|
||||
};
|
||||
private static final Map<FDisplayObject, TransitionObject> transitionLookup = new LinkedHashMap<>();
|
||||
|
||||
public static void queue(final FDisplayObject obj, final Rectangle destBounds, final float duration, final Runnable onFinished) {
|
||||
queue(obj, destBounds, duration, 0, false, onFinished);
|
||||
}
|
||||
public static void queue(final FDisplayObject obj, final Rectangle destBounds, final float duration, final float arcAmount, final boolean arcOriginBelow, final Runnable onFinished) {
|
||||
TransitionObject transitionObj = transitionLookup.get(obj);
|
||||
if (transitionObj == null) {
|
||||
transitionObj = new TransitionObject(obj);
|
||||
transitionLookup.put(obj, transitionObj);
|
||||
overlay.add(transitionObj);
|
||||
obj.setVisible(false); //hide original object while transition in progress
|
||||
}
|
||||
final ForgeTransition transition = new ForgeTransition(transitionObj, destBounds, duration, arcAmount, arcOriginBelow, onFinished);
|
||||
transitionObj.transitions.add(transition);
|
||||
if (transitionObj.transitions.size() == 1) {
|
||||
transition.start(); //start transition right away if first transition added
|
||||
overlay.setVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
private final TransitionObject obj;
|
||||
/*private final Rectangle destBounds;
|
||||
private final float duration;
|
||||
private final float arcAmount;
|
||||
private final boolean arcOriginBelow;*/
|
||||
private final Runnable onFinished;
|
||||
|
||||
private ForgeTransition(final TransitionObject obj0, final Rectangle destBounds0, final float duration0, final float arcAmount0, final boolean arcOriginBelow0, final Runnable onFinished0) {
|
||||
obj = obj0;
|
||||
/*destBounds = destBounds0;
|
||||
duration = duration0;
|
||||
arcAmount = arcAmount0;
|
||||
arcOriginBelow = arcOriginBelow0;*/
|
||||
onFinished = onFinished0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean advance(final float dt) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onEnd(final boolean endingAll) {
|
||||
if (onFinished != null) {
|
||||
onFinished.run();
|
||||
}
|
||||
|
||||
if (endingAll) {
|
||||
transitionLookup.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
final int index = obj.transitions.indexOf(this);
|
||||
obj.transitions.remove(index);
|
||||
if (index == 0) {
|
||||
if (obj.transitions.isEmpty()) {
|
||||
transitionLookup.remove(obj.originalObj);
|
||||
overlay.remove(obj);
|
||||
obj.originalObj.setVisible(true);
|
||||
if (transitionLookup.isEmpty()) {
|
||||
overlay.setVisible(false);
|
||||
}
|
||||
}
|
||||
else {
|
||||
obj.transitions.getFirst().start(); //start next transition if needed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class TransitionObject extends FDisplayObject {
|
||||
private final FDisplayObject originalObj;
|
||||
private final LinkedList<ForgeTransition> transitions = new LinkedList<>();
|
||||
|
||||
private TransitionObject(final FDisplayObject originalObj0) {
|
||||
originalObj = originalObj0;
|
||||
setBounds(originalObj.screenPos.x, originalObj.screenPos.y, originalObj.getWidth(), originalObj.getHeight());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(final Graphics g) {
|
||||
originalObj.draw(g);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package forge.adventure.libgdxgui.animation;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.graphics.g2d.Animation;
|
||||
import com.badlogic.gdx.graphics.g2d.Animation.PlayMode;
|
||||
import com.badlogic.gdx.graphics.g2d.TextureRegion;
|
||||
import forge.adventure.libgdxgui.Graphics;
|
||||
|
||||
public class GifAnimation extends ForgeAnimation {
|
||||
private final Animation<TextureRegion> animation;
|
||||
private TextureRegion currentFrame;
|
||||
private float stateTime;
|
||||
|
||||
public GifAnimation(String filename) {
|
||||
animation = GifDecoder.loadGIFAnimation(PlayMode.NORMAL, Gdx.files.absolute(filename).read());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
currentFrame = animation.getKeyFrame(0);
|
||||
super.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean advance(float dt) {
|
||||
stateTime += dt;
|
||||
currentFrame = animation.getKeyFrame(stateTime);
|
||||
return currentFrame != null;
|
||||
}
|
||||
|
||||
public void draw(Graphics g, float x, float y, float w, float h) {
|
||||
if (currentFrame != null) {
|
||||
g.drawImage(currentFrame, x, y, w, h);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onEnd(boolean endingAll) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,737 @@
|
||||
package forge.adventure.libgdxgui.animation;
|
||||
|
||||
/* Copyright by Johannes Borchardt */
|
||||
/* LibGdx conversion 2014 by Anton Persson */
|
||||
/* Released under Apache 2.0 */
|
||||
/* https://code.google.com/p/animated-gifs-in-android/ */
|
||||
|
||||
import com.badlogic.gdx.graphics.Pixmap;
|
||||
import com.badlogic.gdx.graphics.Texture;
|
||||
import com.badlogic.gdx.graphics.g2d.Animation;
|
||||
import com.badlogic.gdx.graphics.g2d.Animation.PlayMode;
|
||||
import com.badlogic.gdx.graphics.g2d.TextureRegion;
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.Vector;
|
||||
|
||||
public class GifDecoder {
|
||||
/**
|
||||
* File read status: No errors.
|
||||
*/
|
||||
public static final int STATUS_OK = 0;
|
||||
/**
|
||||
* File read status: Error decoding file (may be partially decoded)
|
||||
*/
|
||||
public static final int STATUS_FORMAT_ERROR = 1;
|
||||
/**
|
||||
* File read status: Unable to open source.
|
||||
*/
|
||||
public static final int STATUS_OPEN_ERROR = 2;
|
||||
/** max decoder pixel stack size */
|
||||
protected static final int MAX_STACK_SIZE = 4096;
|
||||
protected InputStream in;
|
||||
protected int status;
|
||||
protected int width; // full image width
|
||||
protected int height; // full image height
|
||||
protected boolean gctFlag; // global color table used
|
||||
protected int gctSize; // size of global color table
|
||||
protected int loopCount = 1; // iterations; 0 = repeat forever
|
||||
protected int[] gct; // global color table
|
||||
protected int[] lct; // local color table
|
||||
protected int[] act; // active color table
|
||||
protected int bgIndex; // background color index
|
||||
protected int bgColor; // background color
|
||||
protected int lastBgColor; // previous bg color
|
||||
protected int pixelAspect; // pixel aspect ratio
|
||||
protected boolean lctFlag; // local color table flag
|
||||
protected boolean interlace; // interlace flag
|
||||
protected int lctSize; // local color table size
|
||||
protected int ix, iy, iw, ih; // current image rectangle
|
||||
protected int lrx, lry, lrw, lrh;
|
||||
protected DixieMap image; // current frame
|
||||
protected DixieMap lastPixmap; // previous frame
|
||||
protected byte[] block = new byte[256]; // current data block
|
||||
protected int blockSize = 0; // block size last graphic control extension info
|
||||
protected int dispose = 0; // 0=no action; 1=leave in place; 2=restore to bg; 3=restore to prev
|
||||
protected int lastDispose = 0;
|
||||
protected boolean transparency = false; // use transparent color
|
||||
protected int delay = 0; // delay in milliseconds
|
||||
protected int transIndex; // transparent color index
|
||||
// LZW decoder working arrays
|
||||
protected short[] prefix;
|
||||
protected byte[] suffix;
|
||||
protected byte[] pixelStack;
|
||||
protected byte[] pixels;
|
||||
protected Vector<GifFrame> frames; // frames read from current file
|
||||
protected int frameCount;
|
||||
|
||||
private static class DixieMap extends Pixmap {
|
||||
DixieMap(int w, int h, Format f) {
|
||||
super(w, h, f);
|
||||
}
|
||||
|
||||
DixieMap(int[] data, int w, int h, Format f) {
|
||||
super(w, h, f);
|
||||
|
||||
int x, y;
|
||||
|
||||
for(y = 0; y < h; y++) {
|
||||
for(x = 0; x < w; x++) {
|
||||
int pxl_ARGB8888 = data[x + y * w];
|
||||
int pxl_RGBA8888 =
|
||||
((pxl_ARGB8888 >> 24) & 0x000000ff) | ((pxl_ARGB8888 << 8) & 0xffffff00);
|
||||
// convert ARGB8888 > RGBA8888
|
||||
drawPixel(x, y, pxl_RGBA8888);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void getPixels(int[] pixels, int offset, int stride, int x, int y, int width, int height) {
|
||||
java.nio.ByteBuffer bb = getPixels();
|
||||
|
||||
int k, l;
|
||||
|
||||
for(k = y; k < y + height; k++) {
|
||||
int _offset = offset;
|
||||
for(l = x; l < x + width; l++) {
|
||||
int pxl = bb.getInt(4 * (l + k * width));
|
||||
|
||||
// convert RGBA8888 > ARGB8888
|
||||
pixels[_offset++] = ((pxl >> 8) & 0x00ffffff) | ((pxl << 24) & 0xff000000);
|
||||
}
|
||||
offset += stride;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class GifFrame {
|
||||
public GifFrame(DixieMap im, int del) {
|
||||
image = im;
|
||||
delay = del;
|
||||
}
|
||||
|
||||
public DixieMap image;
|
||||
public int delay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets display duration for specified frame.
|
||||
*
|
||||
* @param n
|
||||
* int index of frame
|
||||
* @return delay in milliseconds
|
||||
*/
|
||||
public int getDelay(int n) {
|
||||
delay = -1;
|
||||
if ((n >= 0) && (n < frameCount)) {
|
||||
delay = frames.elementAt(n).delay;
|
||||
}
|
||||
return delay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of frames read from file.
|
||||
*
|
||||
* @return frame count
|
||||
*/
|
||||
public int getFrameCount() {
|
||||
return frameCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the first (or only) image read.
|
||||
*
|
||||
* @return BufferedPixmap containing first frame, or null if none.
|
||||
*/
|
||||
public Pixmap getPixmap() {
|
||||
return getFrame(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the "Netscape" iteration count, if any. A count of 0 means repeat indefinitely.
|
||||
*
|
||||
* @return iteration count if one was specified, else 1.
|
||||
*/
|
||||
public int getLoopCount() {
|
||||
return loopCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new frame image from current data (and previous frames as specified by their disposition codes).
|
||||
*/
|
||||
protected void setPixels() {
|
||||
// expose destination image's pixels as int array
|
||||
int[] dest = new int[width * height];
|
||||
// fill in starting image contents based on last image's dispose code
|
||||
if (lastDispose > 0) {
|
||||
if (lastDispose == 3) {
|
||||
// use image before last
|
||||
int n = frameCount - 2;
|
||||
if (n > 0) {
|
||||
lastPixmap = getFrame(n - 1);
|
||||
} else {
|
||||
lastPixmap = null;
|
||||
}
|
||||
}
|
||||
if (lastPixmap != null) {
|
||||
lastPixmap.getPixels(dest, 0, width, 0, 0, width, height);
|
||||
// copy pixels
|
||||
if (lastDispose == 2) {
|
||||
// fill last image rect area with background color
|
||||
int c = 0;
|
||||
if (!transparency) {
|
||||
c = lastBgColor;
|
||||
}
|
||||
for (int i = 0; i < lrh; i++) {
|
||||
int n1 = (lry + i) * width + lrx;
|
||||
int n2 = n1 + lrw;
|
||||
for (int k = n1; k < n2; k++) {
|
||||
dest[k] = c;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// copy each source line to the appropriate place in the destination
|
||||
int pass = 1;
|
||||
int inc = 8;
|
||||
int iline = 0;
|
||||
for (int i = 0; i < ih; i++) {
|
||||
int line = i;
|
||||
if (interlace) {
|
||||
if (iline >= ih) {
|
||||
pass++;
|
||||
switch (pass) {
|
||||
case 2:
|
||||
iline = 4;
|
||||
break;
|
||||
case 3:
|
||||
iline = 2;
|
||||
inc = 4;
|
||||
break;
|
||||
case 4:
|
||||
iline = 1;
|
||||
inc = 2;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
line = iline;
|
||||
iline += inc;
|
||||
}
|
||||
line += iy;
|
||||
if (line < height) {
|
||||
int k = line * width;
|
||||
int dx = k + ix; // start of line in dest
|
||||
int dlim = dx + iw; // end of dest line
|
||||
if ((k + width) < dlim) {
|
||||
dlim = k + width; // past dest edge
|
||||
}
|
||||
int sx = i * iw; // start of line in source
|
||||
while (dx < dlim) {
|
||||
// map color and insert in destination
|
||||
int index = ((int) pixels[sx++]) & 0xff;
|
||||
int c = act[index];
|
||||
if (c != 0) {
|
||||
dest[dx] = c;
|
||||
}
|
||||
dx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
image = new DixieMap(dest, width, height, Pixmap.Format.RGBA8888);
|
||||
//Pixmap.createPixmap(dest, width, height, Config.ARGB_4444);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the image contents of frame n.
|
||||
*
|
||||
* @return BufferedPixmap representation of frame, or null if n is invalid.
|
||||
*/
|
||||
public DixieMap getFrame(int n) {
|
||||
if (frameCount <= 0)
|
||||
return null;
|
||||
n = n % frameCount;
|
||||
return frames.elementAt(n).image;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads GIF image from stream
|
||||
*
|
||||
* @param is
|
||||
* containing GIF file.
|
||||
* @return read status code (0 = no errors)
|
||||
*/
|
||||
public int read(InputStream is) {
|
||||
init();
|
||||
if (is != null) {
|
||||
in = is;
|
||||
readHeader();
|
||||
if (!err()) {
|
||||
readContents();
|
||||
if (frameCount < 0) {
|
||||
status = STATUS_FORMAT_ERROR;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
status = STATUS_OPEN_ERROR;
|
||||
}
|
||||
try {
|
||||
is.close();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes LZW image data into pixel array. Adapted from John Cristy's BitmapMagick.
|
||||
*/
|
||||
protected void decodeBitmapData() {
|
||||
int nullCode = -1;
|
||||
int npix = iw * ih;
|
||||
int available, clear, code_mask, code_size, end_of_information, in_code, old_code, bits, code, count, i, datum, data_size, first, top, bi, pi;
|
||||
if ((pixels == null) || (pixels.length < npix)) {
|
||||
pixels = new byte[npix]; // allocate new pixel array
|
||||
}
|
||||
if (prefix == null) {
|
||||
prefix = new short[MAX_STACK_SIZE];
|
||||
}
|
||||
if (suffix == null) {
|
||||
suffix = new byte[MAX_STACK_SIZE];
|
||||
}
|
||||
if (pixelStack == null) {
|
||||
pixelStack = new byte[MAX_STACK_SIZE + 1];
|
||||
}
|
||||
// Initialize GIF data stream decoder.
|
||||
data_size = read();
|
||||
clear = 1 << data_size;
|
||||
end_of_information = clear + 1;
|
||||
available = clear + 2;
|
||||
old_code = nullCode;
|
||||
code_size = data_size + 1;
|
||||
code_mask = (1 << code_size) - 1;
|
||||
for (code = 0; code < clear; code++) {
|
||||
prefix[code] = 0; // XXX ArrayIndexOutOfBoundsException
|
||||
suffix[code] = (byte) code;
|
||||
}
|
||||
// Decode GIF pixel stream.
|
||||
datum = bits = count = first = top = pi = bi = 0;
|
||||
for (i = 0; i < npix;) {
|
||||
if (top == 0) {
|
||||
if (bits < code_size) {
|
||||
// Load bytes until there are enough bits for a code.
|
||||
if (count == 0) {
|
||||
// Read a new data block.
|
||||
count = readBlock();
|
||||
if (count <= 0) {
|
||||
break;
|
||||
}
|
||||
bi = 0;
|
||||
}
|
||||
datum += (((int) block[bi]) & 0xff) << bits;
|
||||
bits += 8;
|
||||
bi++;
|
||||
count--;
|
||||
continue;
|
||||
}
|
||||
// Get the next code.
|
||||
code = datum & code_mask;
|
||||
datum >>= code_size;
|
||||
bits -= code_size;
|
||||
// Interpret the code
|
||||
if ((code > available) || (code == end_of_information)) {
|
||||
break;
|
||||
}
|
||||
if (code == clear) {
|
||||
// Reset decoder.
|
||||
code_size = data_size + 1;
|
||||
code_mask = (1 << code_size) - 1;
|
||||
available = clear + 2;
|
||||
old_code = nullCode;
|
||||
continue;
|
||||
}
|
||||
if (old_code == nullCode) {
|
||||
pixelStack[top++] = suffix[code];
|
||||
old_code = code;
|
||||
first = code;
|
||||
continue;
|
||||
}
|
||||
in_code = code;
|
||||
if (code == available) {
|
||||
pixelStack[top++] = (byte) first;
|
||||
code = old_code;
|
||||
}
|
||||
while (code > clear) {
|
||||
pixelStack[top++] = suffix[code];
|
||||
code = prefix[code];
|
||||
}
|
||||
first = ((int) suffix[code]) & 0xff;
|
||||
// Add a new string to the string table,
|
||||
if (available >= MAX_STACK_SIZE) {
|
||||
break;
|
||||
}
|
||||
pixelStack[top++] = (byte) first;
|
||||
prefix[available] = (short) old_code;
|
||||
suffix[available] = (byte) first;
|
||||
available++;
|
||||
if (((available & code_mask) == 0) && (available < MAX_STACK_SIZE)) {
|
||||
code_size++;
|
||||
code_mask += available;
|
||||
}
|
||||
old_code = in_code;
|
||||
}
|
||||
// Pop a pixel off the pixel stack.
|
||||
top--;
|
||||
pixels[pi++] = pixelStack[top];
|
||||
i++;
|
||||
}
|
||||
for (i = pi; i < npix; i++) {
|
||||
pixels[i] = 0; // clear missing pixels
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if an error was encountered during reading/decoding
|
||||
*/
|
||||
protected boolean err() {
|
||||
return status != STATUS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes or re-initializes reader
|
||||
*/
|
||||
protected void init() {
|
||||
status = STATUS_OK;
|
||||
frameCount = 0;
|
||||
frames = new Vector<>();
|
||||
gct = null;
|
||||
lct = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a single byte from the input stream.
|
||||
*/
|
||||
protected int read() {
|
||||
int curByte = 0;
|
||||
try {
|
||||
curByte = in.read();
|
||||
} catch (Exception e) {
|
||||
status = STATUS_FORMAT_ERROR;
|
||||
}
|
||||
return curByte;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads next variable length block from input.
|
||||
*
|
||||
* @return number of bytes stored in "buffer"
|
||||
*/
|
||||
protected int readBlock() {
|
||||
blockSize = read();
|
||||
int n = 0;
|
||||
if (blockSize > 0) {
|
||||
try {
|
||||
int count = 0;
|
||||
while (n < blockSize) {
|
||||
count = in.read(block, n, blockSize - n);
|
||||
if (count == -1) {
|
||||
break;
|
||||
}
|
||||
n += count;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (n < blockSize) {
|
||||
status = STATUS_FORMAT_ERROR;
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads color table as 256 RGB integer values
|
||||
*
|
||||
* @param ncolors
|
||||
* int number of colors to read
|
||||
* @return int array containing 256 colors (packed ARGB with full alpha)
|
||||
*/
|
||||
protected int[] readColorTable(int ncolors) {
|
||||
int nbytes = 3 * ncolors;
|
||||
int[] tab = null;
|
||||
byte[] c = new byte[nbytes];
|
||||
int n = 0;
|
||||
try {
|
||||
n = in.read(c);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (n < nbytes) {
|
||||
status = STATUS_FORMAT_ERROR;
|
||||
} else {
|
||||
tab = new int[256]; // max size to avoid bounds checks
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
while (i < ncolors) {
|
||||
int r = ((int) c[j++]) & 0xff;
|
||||
int g = ((int) c[j++]) & 0xff;
|
||||
int b = ((int) c[j++]) & 0xff;
|
||||
tab[i++] = 0xff000000 | (r << 16) | (g << 8) | b;
|
||||
}
|
||||
}
|
||||
return tab;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main file parser. Reads GIF content blocks.
|
||||
*/
|
||||
protected void readContents() {
|
||||
// read GIF file content blocks
|
||||
boolean done = false;
|
||||
while (!(done || err())) {
|
||||
int code = read();
|
||||
switch (code) {
|
||||
case 0x2C: // image separator
|
||||
readBitmap();
|
||||
break;
|
||||
case 0x21: // extension
|
||||
code = read();
|
||||
switch (code) {
|
||||
case 0xf9: // graphics control extension
|
||||
readGraphicControlExt();
|
||||
break;
|
||||
case 0xff: // application extension
|
||||
readBlock();
|
||||
StringBuilder app = new StringBuilder();
|
||||
for (int i = 0; i < 11; i++) {
|
||||
app.append((char) block[i]);
|
||||
}
|
||||
if (app.toString().equals("NETSCAPE2.0")) {
|
||||
readNetscapeExt();
|
||||
} else {
|
||||
skip(); // don't care
|
||||
}
|
||||
break;
|
||||
case 0xfe:// comment extension
|
||||
skip();
|
||||
break;
|
||||
case 0x01:// plain text extension
|
||||
skip();
|
||||
break;
|
||||
default: // uninteresting extension
|
||||
skip();
|
||||
}
|
||||
break;
|
||||
case 0x3b: // terminator
|
||||
done = true;
|
||||
break;
|
||||
case 0x00: // bad byte, but keep going and see what happens break;
|
||||
default:
|
||||
status = STATUS_FORMAT_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads Graphics Control Extension values
|
||||
*/
|
||||
protected void readGraphicControlExt() {
|
||||
read(); // block size
|
||||
int packed = read(); // packed fields
|
||||
dispose = (packed & 0x1c) >> 2; // disposal method
|
||||
if (dispose == 0) {
|
||||
dispose = 1; // elect to keep old image if discretionary
|
||||
}
|
||||
transparency = (packed & 1) != 0;
|
||||
delay = readShort() * 10; // delay in milliseconds
|
||||
transIndex = read(); // transparent color index
|
||||
read(); // block terminator
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads GIF file header information.
|
||||
*/
|
||||
protected void readHeader() {
|
||||
StringBuilder id = new StringBuilder();
|
||||
for (int i = 0; i < 6; i++) {
|
||||
id.append((char) read());
|
||||
}
|
||||
if (!id.toString().startsWith("GIF")) {
|
||||
status = STATUS_FORMAT_ERROR;
|
||||
return;
|
||||
}
|
||||
readLSD();
|
||||
if (gctFlag && !err()) {
|
||||
gct = readColorTable(gctSize);
|
||||
bgColor = gct[bgIndex];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads next frame image
|
||||
*/
|
||||
protected void readBitmap() {
|
||||
ix = readShort(); // (sub)image position & size
|
||||
iy = readShort();
|
||||
iw = readShort();
|
||||
ih = readShort();
|
||||
int packed = read();
|
||||
lctFlag = (packed & 0x80) != 0; // 1 - local color table flag interlace
|
||||
lctSize = (int) Math.pow(2, (packed & 0x07) + 1);
|
||||
// 3 - sort flag
|
||||
// 4-5 - reserved lctSize = 2 << (packed & 7); // 6-8 - local color
|
||||
// table size
|
||||
interlace = (packed & 0x40) != 0;
|
||||
if (lctFlag) {
|
||||
lct = readColorTable(lctSize); // read table
|
||||
act = lct; // make local table active
|
||||
} else {
|
||||
act = gct; // make global table active
|
||||
if (bgIndex == transIndex) {
|
||||
bgColor = 0;
|
||||
}
|
||||
}
|
||||
int save = 0;
|
||||
if (transparency) {
|
||||
save = act[transIndex];
|
||||
act[transIndex] = 0; // set transparent color if specified
|
||||
}
|
||||
if (act == null) {
|
||||
status = STATUS_FORMAT_ERROR; // no color table defined
|
||||
}
|
||||
if (err()) {
|
||||
return;
|
||||
}
|
||||
decodeBitmapData(); // decode pixel data
|
||||
skip();
|
||||
if (err()) {
|
||||
return;
|
||||
}
|
||||
frameCount++;
|
||||
// create new image to receive frame data
|
||||
image = new DixieMap(width, height, Pixmap.Format.RGBA8888);
|
||||
setPixels(); // transfer pixel data to image
|
||||
frames.addElement(new GifFrame(image, delay)); // add image to frame
|
||||
// list
|
||||
if (transparency) {
|
||||
act[transIndex] = save;
|
||||
}
|
||||
resetFrame();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads Logical Screen Descriptor
|
||||
*/
|
||||
protected void readLSD() {
|
||||
// logical screen size
|
||||
width = readShort();
|
||||
height = readShort();
|
||||
// packed fields
|
||||
int packed = read();
|
||||
gctFlag = (packed & 0x80) != 0; // 1 : global color table flag
|
||||
// 2-4 : color resolution
|
||||
// 5 : gct sort flag
|
||||
gctSize = 2 << (packed & 7); // 6-8 : gct size
|
||||
bgIndex = read(); // background color index
|
||||
pixelAspect = read(); // pixel aspect ratio
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads Netscape extenstion to obtain iteration count
|
||||
*/
|
||||
protected void readNetscapeExt() {
|
||||
do {
|
||||
readBlock();
|
||||
if (block[0] == 1) {
|
||||
// loop count sub-block
|
||||
int b1 = ((int) block[1]) & 0xff;
|
||||
int b2 = ((int) block[2]) & 0xff;
|
||||
loopCount = (b2 << 8) | b1;
|
||||
}
|
||||
} while ((blockSize > 0) && !err());
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads next 16-bit value, LSB first
|
||||
*/
|
||||
protected int readShort() {
|
||||
// read 16-bit value, LSB first
|
||||
return read() | (read() << 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets frame state for reading next image.
|
||||
*/
|
||||
protected void resetFrame() {
|
||||
lastDispose = dispose;
|
||||
lrx = ix;
|
||||
lry = iy;
|
||||
lrw = iw;
|
||||
lrh = ih;
|
||||
lastPixmap = image;
|
||||
lastBgColor = bgColor;
|
||||
dispose = 0;
|
||||
transparency = false;
|
||||
delay = 0;
|
||||
lct = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Skips variable length blocks up to and including next zero length block.
|
||||
*/
|
||||
protected void skip() {
|
||||
do {
|
||||
readBlock();
|
||||
} while ((blockSize > 0) && !err());
|
||||
}
|
||||
|
||||
private Animation<TextureRegion> getAnimation(PlayMode playType) {
|
||||
int nrFrames = getFrameCount();
|
||||
Pixmap frame = getFrame(0);
|
||||
int width = frame.getWidth();
|
||||
int height = frame.getHeight();
|
||||
int vzones = (int)Math.sqrt(nrFrames);
|
||||
int hzones = vzones;
|
||||
|
||||
while(vzones * hzones < nrFrames) vzones++;
|
||||
|
||||
int v, h;
|
||||
|
||||
Pixmap target = new Pixmap(width * hzones, height * vzones, Pixmap.Format.RGBA8888);
|
||||
|
||||
for(h = 0; h < hzones; h++) {
|
||||
for(v = 0; v < vzones; v++) {
|
||||
int frameID = v + h * vzones;
|
||||
if(frameID < nrFrames) {
|
||||
frame = getFrame(frameID);
|
||||
target.drawPixmap(frame, h * width, v * height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Texture texture = new Texture(target);
|
||||
Array<TextureRegion> texReg = new Array<>();
|
||||
|
||||
for(h = 0; h < hzones; h++) {
|
||||
for(v = 0; v < vzones; v++) {
|
||||
int frameID = v + h * vzones;
|
||||
if(frameID < nrFrames) {
|
||||
TextureRegion tr = new TextureRegion(texture, h * width, v * height, width, height);
|
||||
texReg.add(tr);
|
||||
}
|
||||
}
|
||||
}
|
||||
float frameDuration = (float)getDelay(0);
|
||||
frameDuration /= 1000; // convert milliseconds into seconds
|
||||
|
||||
return new Animation<>(frameDuration, texReg, playType);
|
||||
}
|
||||
|
||||
public static Animation<TextureRegion> loadGIFAnimation(PlayMode playType, InputStream is) {
|
||||
GifDecoder gdec = new GifDecoder();
|
||||
gdec.read(is);
|
||||
return gdec.getAnimation(playType);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
package forge.adventure.libgdxgui.assets;
|
||||
|
||||
import com.badlogic.gdx.Application.ApplicationType;
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import forge.adventure.libgdxgui.Forge;
|
||||
import forge.gui.FThreads;
|
||||
import forge.gui.GuiBase;
|
||||
import forge.gui.download.GuiDownloadZipService;
|
||||
import forge.gui.util.SOptionPane;
|
||||
import forge.localinstance.properties.ForgeConstants;
|
||||
import forge.adventure.libgdxgui.screens.SplashScreen;
|
||||
import forge.util.FileUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.List;
|
||||
|
||||
public class AssetsDownloader {
|
||||
public static final boolean SHARE_DESKTOP_ASSETS = true; //change to false to test downloading separate assets for desktop version
|
||||
|
||||
private final static ImmutableList<String> downloadIgnoreExit = ImmutableList.of("Download", "Ignore", "Exit");
|
||||
private final static ImmutableList<String> downloadExit = ImmutableList.of("Download", "Exit");
|
||||
|
||||
//if not sharing desktop assets, check whether assets are up to date
|
||||
public static void checkForUpdates(final SplashScreen splashScreen) {
|
||||
if (Gdx.app.getType() == ApplicationType.Desktop && SHARE_DESKTOP_ASSETS) { return; }
|
||||
|
||||
final boolean isSnapshots = Forge.CURRENT_VERSION.contains("SNAPSHOT");
|
||||
final String snapsURL = "https://downloads.cardforge.org/dailysnapshots/";
|
||||
final String releaseURL = "https://releases.cardforge.org/forge/forge-gui-android/";
|
||||
final String versionText = isSnapshots ? snapsURL + "version.txt" : releaseURL + "version.txt";
|
||||
|
||||
splashScreen.getProgressBar().setDescription("Checking for updates...");
|
||||
|
||||
String message;
|
||||
boolean connectedToInternet = Forge.getDeviceAdapter().isConnectedToInternet();
|
||||
if (connectedToInternet) {
|
||||
try {
|
||||
URL versionUrl = new URL(versionText);
|
||||
String version = FileUtil.readFileToString(versionUrl);
|
||||
String filename = "forge-android-" + version + "-signed-aligned.apk";
|
||||
String apkURL = isSnapshots ? snapsURL + filename : releaseURL + version + "/" + filename;
|
||||
if (!StringUtils.isEmpty(version) && !Forge.CURRENT_VERSION.equals(version)) {
|
||||
splashScreen.prepareForDialogs();
|
||||
|
||||
message = "A new version of Forge is available (" + version + ").\n" +
|
||||
"You are currently on an older version (" + Forge.CURRENT_VERSION + ").\n\n" +
|
||||
"Would you like to update to the new version now?";
|
||||
if (!Forge.getDeviceAdapter().isConnectedToWifi()) {
|
||||
message += " If so, you may want to connect to wifi first. The download is around 6.5MB.";
|
||||
}
|
||||
if (SOptionPane.showConfirmDialog(message, "New Version Available", "Update Now", "Update Later", true, true)) {
|
||||
String apkFile = new GuiDownloadZipService("", "update", apkURL,
|
||||
Forge.getDeviceAdapter().getDownloadsDir(), null, splashScreen.getProgressBar()).download(filename);
|
||||
if (apkFile != null) {
|
||||
/* FileUriExposedException was added on API 24, Forge now targets API 26 so Android 10 and above runs,
|
||||
most user thinks Forge crashes but in reality, the method below just can't open the apk when Forge
|
||||
exits silently to run the downloaded apk. Some devices allow the apk to run but most users are annoyed when
|
||||
Forge didn't open the apk so I downgrade the check so it will run only on target devices without FileUriExposedException */
|
||||
if (Forge.androidVersion < 24) {
|
||||
Forge.getDeviceAdapter().openFile(apkFile);
|
||||
Forge.exit(true);
|
||||
return;
|
||||
}
|
||||
// API 24 and above needs manual apk installation unless we provide a FileProvider for FileUriExposedException
|
||||
switch (SOptionPane.showOptionDialog("Download Successful. Go to your downloads folder and install " + filename +" to update Forge. Forge will now exit.", "", null, ImmutableList.of("Ok"))) {
|
||||
default:
|
||||
Forge.exit(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
SOptionPane.showMessageDialog("Could not download update. " +
|
||||
"Press OK to proceed without update.", "Update Failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
//see if assets need updating
|
||||
File versionFile = new File(ForgeConstants.ASSETS_DIR + "version.txt");
|
||||
if (!versionFile.exists()) {
|
||||
try {
|
||||
versionFile.createNewFile();
|
||||
}
|
||||
catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
Forge.exit(true); //can't continue if this fails
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (Forge.CURRENT_VERSION.equals(FileUtil.readFileToString(versionFile)) && FSkin.getSkinDir() != null) {
|
||||
return; //if version matches what had been previously saved and FSkin isn't requesting assets download, no need to download assets
|
||||
}
|
||||
|
||||
splashScreen.prepareForDialogs(); //ensure colors set up for showing message dialogs
|
||||
|
||||
boolean canIgnoreDownload = FSkin.getAllSkins() != null; //don't allow ignoring download if resource files haven't been previously loaded
|
||||
|
||||
if (!connectedToInternet) {
|
||||
message = "Updated resource files cannot be downloaded due to lack of internet connection.\n\n";
|
||||
if (canIgnoreDownload) {
|
||||
message += "You can continue without this download, but you may miss out on card fixes or experience other problems.";
|
||||
}
|
||||
else {
|
||||
message += "You cannot start the app since you haven't previously downloaded these files.";
|
||||
}
|
||||
SOptionPane.showMessageDialog(message, "No Internet Connection");
|
||||
if (!canIgnoreDownload) {
|
||||
Forge.exit(true); //exit if can't ignore download
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
//prompt user whether they wish to download the updated resource files
|
||||
message = "There are updated resource files to download. " +
|
||||
"This download is around 50MB, ";
|
||||
if (Forge.getDeviceAdapter().isConnectedToWifi()) {
|
||||
message += "which shouldn't take long if your wifi connection is good.";
|
||||
}
|
||||
else {
|
||||
message += "so it's highly recommended that you connect to wifi first.";
|
||||
}
|
||||
final List<String> options;
|
||||
message += "\n\n";
|
||||
if (canIgnoreDownload) {
|
||||
message += "If you choose to ignore this download, you may miss out on card fixes or experience other problems.";
|
||||
options = downloadIgnoreExit;
|
||||
} else {
|
||||
message += "This download is mandatory to start the app since you haven't previously downloaded these files.";
|
||||
options = downloadExit;
|
||||
}
|
||||
|
||||
switch (SOptionPane.showOptionDialog(message, "", null, options)) {
|
||||
case 1:
|
||||
if (!canIgnoreDownload) {
|
||||
Forge.exit(true); //exit if can't ignore download
|
||||
}
|
||||
return;
|
||||
case 2:
|
||||
Forge.exit(true);
|
||||
return;
|
||||
}
|
||||
|
||||
//allow deletion on Android 10 or if using app-specific directory
|
||||
boolean allowDeletion = Forge.androidVersion < 30 || GuiBase.isUsingAppDirectory();
|
||||
String assetURL = isSnapshots ? snapsURL + "assets.zip" : releaseURL + Forge.CURRENT_VERSION + "/" + "assets.zip";
|
||||
new GuiDownloadZipService("", "resource files", assetURL,
|
||||
ForgeConstants.ASSETS_DIR, ForgeConstants.RES_DIR, splashScreen.getProgressBar(), allowDeletion).downloadAndUnzip();
|
||||
|
||||
if (allowDeletion)
|
||||
FSkinFont.deleteCachedFiles(); //delete cached font files in case any skin's .ttf file changed
|
||||
|
||||
//reload light version of skin after assets updated
|
||||
FThreads.invokeInEdtAndWait(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
FSkinFont.updateAll(); //update all fonts used by splash screen
|
||||
FSkin.loadLight(FSkin.getName(), splashScreen);
|
||||
}
|
||||
});
|
||||
|
||||
//save version string to file once assets finish downloading
|
||||
//so they don't need to be re-downloaded until you upgrade again
|
||||
FileUtil.writeFile(versionFile, Forge.CURRENT_VERSION);
|
||||
|
||||
//add restart after assets update
|
||||
String msg = allowDeletion ? "Resource update finished..." : "Forge misses some files for deletion.\nIf you encounter issues, try deleting the Forge/res folder and/or deleting Forge/cache/fonts folder and try to download and update the assets.";
|
||||
switch (SOptionPane.showOptionDialog(msg, "", null, ImmutableList.of("Restart"))) {
|
||||
default:
|
||||
Forge.restart(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,391 @@
|
||||
/*******************************************************************************
|
||||
* Copyright 2011 See AUTHORS file.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
package forge.adventure.libgdxgui.assets;
|
||||
|
||||
import com.badlogic.gdx.files.FileHandle;
|
||||
import com.badlogic.gdx.graphics.Pixmap;
|
||||
import com.badlogic.gdx.graphics.PixmapIO;
|
||||
import com.badlogic.gdx.graphics.g2d.BitmapFont.BitmapFontData;
|
||||
import com.badlogic.gdx.graphics.g2d.BitmapFont.Glyph;
|
||||
import com.badlogic.gdx.graphics.g2d.PixmapPacker.Page;
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import forge.util.TextUtil;
|
||||
|
||||
/**
|
||||
* This file is 'borrowed' from gdx-tools in the libgdx source
|
||||
*/
|
||||
|
||||
/** A utility to output BitmapFontData to a FNT file. This can be useful for caching the result from TrueTypeFont, for faster load
|
||||
* times.
|
||||
* <p>
|
||||
* The font file format is from the AngelCodeFont BMFont tool.
|
||||
* <p>
|
||||
* Output is nearly identical to the FreeType settting in the Hiero tool {@Link com.badlogic.gdx.tools.hiero.Hiero}. BitmapFontWriter gives more flexibility, eg
|
||||
* borders and shadows can be used. Hiero is able to avoid outputting the same glyph image more than once if multiple character
|
||||
* codes have the exact same glyph.
|
||||
* @author mattdesl AKA davedes */
|
||||
public class BitmapFontWriter {
|
||||
|
||||
/** The output format. */
|
||||
public enum OutputFormat {
|
||||
|
||||
/** AngelCodeFont text format */
|
||||
Text,
|
||||
/** AngelCodeFont XML format */
|
||||
XML
|
||||
}
|
||||
|
||||
/** The output format */
|
||||
private static OutputFormat format = OutputFormat.Text;
|
||||
|
||||
/** Sets the AngelCodeFont output format for subsequent writes; can be text (for LibGDX) or XML (for other engines, like
|
||||
* Pixi.js).
|
||||
*
|
||||
* @param fmt the output format to use */
|
||||
public static void setOutputFormat (OutputFormat fmt) {
|
||||
if (fmt == null) throw new NullPointerException("format cannot be null");
|
||||
format = fmt;
|
||||
}
|
||||
|
||||
/** Returns the currently used output format.
|
||||
* @return the output format */
|
||||
public static OutputFormat getOutputFormat () {
|
||||
return format;
|
||||
}
|
||||
|
||||
/** The Padding parameter for FontInfo. */
|
||||
public static class Padding {
|
||||
public int up, down, left, right;
|
||||
|
||||
public Padding () {
|
||||
}
|
||||
|
||||
public Padding (int up, int down, int left, int right) {
|
||||
this.up = up;
|
||||
this.down = down;
|
||||
this.left = left;
|
||||
this.right = right;
|
||||
}
|
||||
}
|
||||
|
||||
/** The spacing parameter for FontInfo. */
|
||||
public static class Spacing {
|
||||
public int horizontal, vertical;
|
||||
}
|
||||
|
||||
/** The font "info" line; everything except padding and override metrics are ignored by LibGDX's BitmapFont reader, it is otherwise just useful for
|
||||
* clean and organized output. */
|
||||
public static class FontInfo {
|
||||
/** Face name */
|
||||
public String face;
|
||||
/** Font size (pt) */
|
||||
public int size = 12;
|
||||
/** Whether the font is bold */
|
||||
public boolean bold;
|
||||
/** Whether the font is italic */
|
||||
public boolean italic;
|
||||
/** The charset; or null/empty for default */
|
||||
public String charset;
|
||||
/** Whether the font uses unicode glyphs */
|
||||
public boolean unicode = true;
|
||||
/** Stretch for height; default to 100% */
|
||||
public int stretchH = 100;
|
||||
/** Whether smoothing is applied */
|
||||
public boolean smooth = true;
|
||||
/** Amount of anti-aliasing that was applied to the font */
|
||||
public int aa = 2;
|
||||
/** Padding that was applied to the font */
|
||||
public Padding padding = new Padding();
|
||||
/** Horizontal/vertical spacing that was applied to font */
|
||||
public Spacing spacing = new Spacing();
|
||||
public int outline = 0;
|
||||
|
||||
/** Override metrics */
|
||||
public boolean hasOverrideMetrics;
|
||||
public float ascent;
|
||||
public float descent;
|
||||
public float down;
|
||||
public float capHeight;
|
||||
public float lineHeight;
|
||||
public float spaceXAdvance;
|
||||
public float xHeight;
|
||||
|
||||
public FontInfo () {
|
||||
}
|
||||
|
||||
public FontInfo (String face, int size) {
|
||||
this.face = face;
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
public void overrideMetrics (BitmapFontData data) {
|
||||
hasOverrideMetrics = true;
|
||||
ascent = data.ascent;
|
||||
descent = data.descent;
|
||||
down = data.down;
|
||||
capHeight = data.capHeight;
|
||||
lineHeight = data.lineHeight;
|
||||
spaceXAdvance = data.spaceXadvance;
|
||||
xHeight = data.xHeight;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static String quote (Object params) {
|
||||
return quote(params, false);
|
||||
}
|
||||
|
||||
private static String quote (Object params, boolean spaceAfter) {
|
||||
if (BitmapFontWriter.getOutputFormat() == OutputFormat.XML)
|
||||
return "\"" + params.toString().trim() + "\"" + (spaceAfter ? " " : "");
|
||||
else
|
||||
return params.toString();
|
||||
}
|
||||
|
||||
/** Writes the given BitmapFontData to a file, using the specified <tt>pageRefs</tt> strings as the image paths for each
|
||||
* texture page. The glyphs in BitmapFontData have a "page" id, which references the index of the pageRef you specify here.
|
||||
*
|
||||
* The FontInfo parameter is useful for cleaner output; such as including a size and font face name hint. However, it can be
|
||||
* null to use default values. LibGDX ignores most of the "info" line when reading back fonts, only padding is used. Padding
|
||||
* also affects the size, location, and offset of the glyphs that are output.
|
||||
*
|
||||
* Likewise, the scaleW and scaleH are only for cleaner output. They are currently ignored by LibGDX's reader. For maximum
|
||||
* compatibility with other BMFont tools, you should use the width and height of your texture pages (each page should be the
|
||||
* same size).
|
||||
*
|
||||
* @param fontData the bitmap font
|
||||
* @param pageRefs the references to each texture page image file, generally in the same folder as outFntFile
|
||||
* @param outFntFile the font file to save to (typically ends with '.fnt')
|
||||
* @param info the optional info for the file header; can be null
|
||||
* @param scaleW the width of your texture pages
|
||||
* @param scaleH the height of your texture pages */
|
||||
public static void writeFont (BitmapFontData fontData, String[] pageRefs, FileHandle outFntFile, FontInfo info, int scaleW,
|
||||
int scaleH) {
|
||||
if (info == null) {
|
||||
info = new FontInfo();
|
||||
info.face = outFntFile.nameWithoutExtension();
|
||||
}
|
||||
|
||||
int lineHeight = (int)fontData.lineHeight;
|
||||
int pages = pageRefs.length;
|
||||
int packed = 0;
|
||||
int base = (int)((fontData.capHeight) + (fontData.flipped ? -fontData.ascent : fontData.ascent));
|
||||
OutputFormat fmt = BitmapFontWriter.getOutputFormat();
|
||||
boolean xml = fmt == OutputFormat.XML;
|
||||
|
||||
StringBuilder buf = new StringBuilder();
|
||||
|
||||
if (xml) {
|
||||
buf.append("<font>\n");
|
||||
}
|
||||
String xmlOpen = xml ? "\t<" : "";
|
||||
String xmlCloseSelf = xml ? "/>" : "";
|
||||
String xmlTab = xml ? "\t" : "";
|
||||
String xmlClose = xml ? ">" : "";
|
||||
|
||||
String xmlQuote = xml ? "\"" : "";
|
||||
String alphaChnlParams = xml ? " alphaChnl=\"0\" redChnl=\"0\" greenChnl=\"0\" blueChnl=\"0\""
|
||||
: " alphaChnl=0 redChnl=0 greenChnl=0 blueChnl=0";
|
||||
|
||||
// INFO LINE
|
||||
buf.append(xmlOpen).append("info face=\"").append(info.face == null ? "" : TextUtil.fastReplace(info.face,"\"", "'"))
|
||||
.append("\" size=").append(quote(info.size)).append(" bold=").append(quote(info.bold ? 1 : 0)).append(" italic=")
|
||||
.append(quote(info.italic ? 1 : 0)).append(" charset=\"").append(info.charset == null ? "" : info.charset)
|
||||
.append("\" unicode=").append(quote(info.unicode ? 1 : 0)).append(" stretchH=").append(quote(info.stretchH))
|
||||
.append(" smooth=").append(quote(info.smooth ? 1 : 0)).append(" aa=").append(quote(info.aa)).append(" padding=")
|
||||
.append(xmlQuote).append(info.padding.up).append(",").append(info.padding.right).append(",").append(info.padding.down)
|
||||
.append(",").append(info.padding.left).append(xmlQuote).append(" spacing=").append(xmlQuote)
|
||||
.append(info.spacing.horizontal).append(",").append(info.spacing.vertical).append(xmlQuote).append(xmlCloseSelf)
|
||||
.append("\n");
|
||||
|
||||
// COMMON line
|
||||
buf.append(xmlOpen).append("common lineHeight=").append(quote(lineHeight)).append(" base=").append(quote(base))
|
||||
.append(" scaleW=").append(quote(scaleW)).append(" scaleH=").append(quote(scaleH)).append(" pages=").append(quote(pages))
|
||||
.append(" packed=").append(quote(packed)).append(alphaChnlParams).append(xmlCloseSelf).append("\n");
|
||||
|
||||
if (xml) buf.append("\t<pages>\n");
|
||||
|
||||
// PAGES
|
||||
for (int i = 0; i < pageRefs.length; i++) {
|
||||
buf.append(xmlTab).append(xmlOpen).append("page id=").append(quote(i)).append(" file=\"").append(pageRefs[i])
|
||||
.append("\"").append(xmlCloseSelf).append("\n");
|
||||
}
|
||||
|
||||
if (xml) buf.append("\t</pages>\n");
|
||||
|
||||
// CHARS
|
||||
Array<Glyph> glyphs = new Array<Glyph>(256);
|
||||
for (int i = 0; i < fontData.glyphs.length; i++) {
|
||||
if (fontData.glyphs[i] == null) continue;
|
||||
|
||||
for (int j = 0; j < fontData.glyphs[i].length; j++) {
|
||||
if (fontData.glyphs[i][j] != null) {
|
||||
glyphs.add(fontData.glyphs[i][j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buf.append(xmlOpen).append("chars count=").append(quote(glyphs.size)).append(xmlClose).append("\n");
|
||||
|
||||
int padLeft = 0, padRight = 0, padTop = 0, padX = 0, padY = 0;
|
||||
if (info != null) {
|
||||
padTop = info.padding.up;
|
||||
padLeft = info.padding.left;
|
||||
padRight = info.padding.right;
|
||||
padX = padLeft + padRight;
|
||||
padY = info.padding.up + info.padding.down;
|
||||
}
|
||||
|
||||
// CHAR definitions
|
||||
for (int i = 0; i < glyphs.size; i++) {
|
||||
Glyph g = glyphs.get(i);
|
||||
boolean empty = g.width == 0 || g.height == 0;
|
||||
buf.append(xmlTab).append(xmlOpen).append("char id=").append(quote(String.format("%-6s", g.id), true)).append("x=")
|
||||
.append(quote(String.format("%-5s", empty ? 0 : g.srcX), true)).append("y=")
|
||||
.append(quote(String.format("%-5s", empty ? 0 : g.srcY), true)).append("width=")
|
||||
.append(quote(String.format("%-5s", empty ? 0 : g.width), true)).append("height=")
|
||||
.append(quote(String.format("%-5s", empty ? 0 : g.height), true)).append("xoffset=")
|
||||
.append(quote(String.format("%-5s", g.xoffset - padLeft), true)).append("yoffset=")
|
||||
.append(quote(String.format("%-5s", fontData.flipped ? g.yoffset + padTop : -(g.height + (g.yoffset + padTop))), true))
|
||||
.append("xadvance=").append(quote(String.format("%-5s", g.xadvance), true)).append("page=")
|
||||
.append(quote(String.format("%-5s", g.page), true)).append("chnl=").append(quote(0, true)).append(xmlCloseSelf)
|
||||
.append("\n");
|
||||
}
|
||||
|
||||
if (xml) buf.append("\t</chars>\n");
|
||||
|
||||
// KERNINGS
|
||||
int kernCount = 0;
|
||||
StringBuilder kernBuf = new StringBuilder();
|
||||
for (int i = 0; i < glyphs.size; i++) {
|
||||
for (int j = 0; j < glyphs.size; j++) {
|
||||
Glyph first = glyphs.get(i);
|
||||
Glyph second = glyphs.get(j);
|
||||
int kern = first.getKerning((char)second.id);
|
||||
if (kern != 0) {
|
||||
kernCount++;
|
||||
kernBuf.append(xmlTab).append(xmlOpen).append("kerning first=").append(quote(first.id)).append(" second=")
|
||||
.append(quote(second.id)).append(" amount=").append(quote(kern, true)).append(xmlCloseSelf).append("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// KERN info
|
||||
buf.append(xmlOpen).append("kernings count=").append(quote(kernCount)).append(xmlClose).append("\n");
|
||||
buf.append(kernBuf);
|
||||
|
||||
if (xml) {
|
||||
buf.append("\t</kernings>\n");
|
||||
}
|
||||
|
||||
// Override metrics
|
||||
if (info.hasOverrideMetrics) {
|
||||
if (xml) buf.append("\t<metrics>\n");
|
||||
|
||||
buf.append(xmlTab).append(xmlOpen)
|
||||
.append("metrics ascent=").append(quote(info.ascent, true))
|
||||
.append(" descent=").append(quote(info.descent, true))
|
||||
.append(" down=").append(quote(info.down, true))
|
||||
.append(" capHeight=").append(quote(info.capHeight, true))
|
||||
.append(" lineHeight=").append(quote(info.lineHeight, true))
|
||||
.append(" spaceXAdvance=").append(quote(info.spaceXAdvance, true))
|
||||
.append(" xHeight=").append(quote(info.xHeight, true))
|
||||
.append(xmlCloseSelf).append("\n");
|
||||
|
||||
if (xml) buf.append("\t</metrics>\n");
|
||||
}
|
||||
|
||||
if (xml) {
|
||||
buf.append("</font>");
|
||||
}
|
||||
|
||||
String charset = info.charset;
|
||||
if (charset != null && charset.length() == 0) charset = null;
|
||||
|
||||
outFntFile.writeString(buf.toString(), false, charset);
|
||||
}
|
||||
|
||||
/** A utility method which writes the given font data to a file.
|
||||
*
|
||||
* The specified pixmaps are written to the parent directory of <tt>outFntFile</tt>, using that file's name without an
|
||||
* extension for the PNG file name(s).
|
||||
*
|
||||
* The specified FontInfo is optional, and can be null.
|
||||
*
|
||||
* Typical usage looks like this:
|
||||
*
|
||||
* <pre>
|
||||
* BitmapFontWriter.writeFont(myFontData, myFontPixmaps, Gdx.files.external("fonts/output.fnt"), new FontInfo("Arial", 16));
|
||||
* </pre>
|
||||
*
|
||||
* @param fontData the font data
|
||||
* @param pages the pixmaps to write as PNGs
|
||||
* @param outFntFile the output file for the font definition
|
||||
* @param info the optional font info for the header file, can be null */
|
||||
public static void writeFont (BitmapFontData fontData, Pixmap[] pages, FileHandle outFntFile, FontInfo info) {
|
||||
String[] pageRefs = writePixmaps(pages, outFntFile.parent(), outFntFile.nameWithoutExtension());
|
||||
|
||||
// write the font data
|
||||
writeFont(fontData, pageRefs, outFntFile, info, pages[0].getWidth(), pages[0].getHeight());
|
||||
}
|
||||
|
||||
/** A utility method to write the given array of pixmaps to the given output directory, with the specified file name. If the
|
||||
* pages array is of length 1, then the resulting file ref will look like: "fileName.png".
|
||||
*
|
||||
* If the pages array is greater than length 1, the resulting file refs will be appended with "_N", such as "fileName_0.png",
|
||||
* "fileName_1.png", "fileName_2.png" etc.
|
||||
*
|
||||
* The returned string array can then be passed to the <tt>writeFont</tt> method.
|
||||
*
|
||||
* Note: None of the pixmaps will be disposed.
|
||||
*
|
||||
* @param pages the pages of pixmap data to write
|
||||
* @param outputDir the output directory
|
||||
* @param fileName the file names for the output images
|
||||
* @return the array of string references to be used with <tt>writeFont</tt> */
|
||||
public static String[] writePixmaps (Pixmap[] pages, FileHandle outputDir, String fileName) {
|
||||
if (pages == null || pages.length == 0) throw new IllegalArgumentException("no pixmaps supplied to BitmapFontWriter.write");
|
||||
|
||||
String[] pageRefs = new String[pages.length];
|
||||
|
||||
for (int i = 0; i < pages.length; i++) {
|
||||
String ref = pages.length == 1 ? (fileName + ".png") : (fileName + "_" + i + ".png");
|
||||
|
||||
// the ref for this image
|
||||
pageRefs[i] = ref;
|
||||
|
||||
// write the PNG in that directory
|
||||
PixmapIO.writePNG(outputDir.child(ref), pages[i]);
|
||||
}
|
||||
return pageRefs;
|
||||
}
|
||||
|
||||
/** A convenience method to write pixmaps by page; typically returned from a PixmapPacker when used alongside
|
||||
* FreeTypeFontGenerator.
|
||||
*
|
||||
* @param pages the pages containing the Pixmaps
|
||||
* @param outputDir the output directory
|
||||
* @param fileName the file name
|
||||
* @return the file refs */
|
||||
public static String[] writePixmaps (Array<Page> pages, FileHandle outputDir, String fileName) {
|
||||
Pixmap[] pix = new Pixmap[pages.size];
|
||||
for (int i = 0; i < pages.size; i++) {
|
||||
pix[i] = pages.get(i).getPixmap();
|
||||
}
|
||||
return writePixmaps(pix, outputDir, fileName);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package forge.adventure.libgdxgui.assets;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
import com.badlogic.gdx.graphics.Pixmap.Format;
|
||||
import com.badlogic.gdx.graphics.Texture;
|
||||
import com.badlogic.gdx.graphics.glutils.FrameBuffer;
|
||||
import com.badlogic.gdx.math.Matrix4;
|
||||
import forge.adventure.libgdxgui.Graphics;
|
||||
import forge.gui.FThreads;
|
||||
|
||||
//Special graphics object for rendering to a texture
|
||||
public abstract class FBufferedImage extends FImageComplex {
|
||||
private final float width, height, opacity;
|
||||
private FrameBuffer frameBuffer;
|
||||
|
||||
public FBufferedImage(float width0, float height0) {
|
||||
this(width0, height0, 1);
|
||||
}
|
||||
public FBufferedImage(float width0, float height0, float opacity0) {
|
||||
width = width0;
|
||||
height = height0;
|
||||
opacity = opacity0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRegionX() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRegionY() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Texture getTexture() {
|
||||
if (frameBuffer == null) {
|
||||
Gdx.gl.glDisable(GL20.GL_SCISSOR_TEST); //prevent buffered image being clipped
|
||||
|
||||
//render texture to frame buffer if needed
|
||||
frameBuffer = new FrameBuffer(Format.RGBA8888, (int)width, (int)height, false);
|
||||
frameBuffer.begin();
|
||||
|
||||
//frame graphics must be given a projection matrix
|
||||
//so stuff is rendered properly to custom sized frame buffer
|
||||
Graphics frameGraphics = new Graphics();
|
||||
Matrix4 matrix = new Matrix4();
|
||||
matrix.setToOrtho2D(0, 0, width, height);
|
||||
frameGraphics.setProjectionMatrix(matrix);
|
||||
|
||||
frameGraphics.begin(width, height);
|
||||
draw(frameGraphics, width, height);
|
||||
frameGraphics.end();
|
||||
|
||||
frameBuffer.end();
|
||||
frameGraphics.dispose();
|
||||
|
||||
Gdx.gl.glEnable(GL20.GL_SCISSOR_TEST);
|
||||
}
|
||||
return frameBuffer.getColorBufferTexture();
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
final FrameBuffer fb = frameBuffer;
|
||||
if (fb != null) {
|
||||
frameBuffer = null;
|
||||
FThreads.invokeInEdtNowOrLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
fb.dispose(); //must be disposed on EDT thread
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void draw(Graphics g, float w, float h);
|
||||
|
||||
@Override
|
||||
public void draw(Graphics g, float x, float y, float w, float h) {
|
||||
if (opacity < 1) {
|
||||
g.setAlphaComposite(opacity);
|
||||
}
|
||||
g.drawFlippedImage(getTexture(), x, y, w, h); //need to draw image flipped because of how FrameBuffer works
|
||||
if (opacity < 1) {
|
||||
g.resetAlphaComposite();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package forge.adventure.libgdxgui.assets;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.graphics.Texture;
|
||||
import forge.adventure.libgdxgui.Graphics;
|
||||
|
||||
//Special wrapper for a texture to be loaded later when it's needed
|
||||
public class FDelayLoadImage extends FImageComplex {
|
||||
private final String filename;
|
||||
private Texture texture;
|
||||
|
||||
public FDelayLoadImage(String filename0) {
|
||||
filename = filename0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getWidth() {
|
||||
return getTexture().getWidth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getHeight() {
|
||||
return getTexture().getHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Texture getTexture() {
|
||||
if (texture == null) {
|
||||
texture = new Texture(Gdx.files.absolute(filename));
|
||||
}
|
||||
return texture;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRegionX() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRegionY() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Graphics g, float x, float y, float w, float h) {
|
||||
g.drawImage(getTexture(), x, y, w, h);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package forge.adventure.libgdxgui.assets;
|
||||
|
||||
import forge.adventure.libgdxgui.Graphics;
|
||||
import forge.localinstance.skin.ISkinImage;
|
||||
|
||||
public interface FImage extends ISkinImage {
|
||||
float getWidth();
|
||||
float getHeight();
|
||||
void draw(Graphics g, float x, float y, float w, float h);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package forge.adventure.libgdxgui.assets;
|
||||
|
||||
import com.badlogic.gdx.graphics.Texture;
|
||||
|
||||
public abstract class FImageComplex implements FImage {
|
||||
public abstract Texture getTexture();
|
||||
public abstract int getRegionX();
|
||||
public abstract int getRegionY();
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package forge.adventure.libgdxgui.assets;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.files.FileHandle;
|
||||
import forge.localinstance.properties.ForgeConstants;
|
||||
import forge.localinstance.properties.ForgePreferences;
|
||||
import forge.localinstance.properties.ForgePreferences.FPref;
|
||||
import forge.model.FModel;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class FLanguage {
|
||||
|
||||
public static void changeLanguage(final String languageName) {
|
||||
final ForgePreferences prefs = FModel.getPreferences();
|
||||
if (languageName.equals(prefs.getPref(FPref.UI_LANGUAGE))) { return; }
|
||||
|
||||
//save language preference
|
||||
prefs.setPref(FPref.UI_LANGUAGE, languageName);
|
||||
prefs.save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the languages.
|
||||
*
|
||||
* @return the languages
|
||||
*/
|
||||
public static Iterable<String> getAllLanguages() {
|
||||
final List<String> allLanguages = new ArrayList<>();
|
||||
|
||||
final FileHandle dir = Gdx.files.absolute(ForgeConstants.LANG_DIR);
|
||||
for (FileHandle languageFile : dir.list()) {
|
||||
String languageName = languageFile.name();
|
||||
if (!languageName.endsWith(".properties")) { continue; }
|
||||
allLanguages.add(languageName.replace(".properties", ""));
|
||||
}
|
||||
|
||||
return allLanguages;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package forge.adventure.libgdxgui.assets;
|
||||
|
||||
import com.badlogic.gdx.graphics.Texture;
|
||||
import forge.adventure.libgdxgui.Graphics;
|
||||
|
||||
public class FRotatedImage extends FImageComplex {
|
||||
private final Texture texture;
|
||||
private final int srcX, srcY, srcWidth, srcHeight;
|
||||
private final boolean clockwise;
|
||||
|
||||
public FRotatedImage(Texture texture0, int srcX0, int srcY0, int srcWidth0, int srcHeight0, boolean clockwise0) {
|
||||
texture = texture0;
|
||||
srcX = srcX0;
|
||||
srcY = srcY0;
|
||||
srcWidth = srcWidth0;
|
||||
srcHeight = srcHeight0;
|
||||
clockwise = clockwise0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getWidth() {
|
||||
return srcHeight; //width and height are swapped since image rotated
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getHeight() {
|
||||
return srcWidth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Texture getTexture() {
|
||||
return texture;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRegionX() {
|
||||
return srcX;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRegionY() {
|
||||
return srcY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Graphics g, float x, float y, float w, float h) {
|
||||
float originX, originY, rotation;
|
||||
if (clockwise) {
|
||||
originX = x + w / 2;
|
||||
originY = y + w / 2;
|
||||
rotation = -90;
|
||||
}
|
||||
else {
|
||||
originX = x + h / 2;
|
||||
originY = y + h / 2;
|
||||
rotation = 90;
|
||||
}
|
||||
g.drawRotatedImage(texture, x, y, h, w, originX, originY, srcX, srcY, srcWidth, srcHeight, rotation);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,475 @@
|
||||
package forge.adventure.libgdxgui.assets;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.files.FileHandle;
|
||||
import com.badlogic.gdx.graphics.Color;
|
||||
import com.badlogic.gdx.graphics.Pixmap;
|
||||
import com.badlogic.gdx.graphics.Texture;
|
||||
import com.badlogic.gdx.graphics.g2d.TextureRegion;
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import forge.adventure.libgdxgui.Forge;
|
||||
import forge.adventure.libgdxgui.assets.FSkinImage.SourceFile;
|
||||
import forge.adventure.libgdxgui.card.CardFaceSymbols;
|
||||
import forge.adventure.util.Config;
|
||||
import forge.gui.FThreads;
|
||||
import forge.gui.GuiBase;
|
||||
import forge.localinstance.properties.ForgeConstants;
|
||||
import forge.localinstance.properties.ForgePreferences;
|
||||
import forge.localinstance.properties.ForgePreferences.FPref;
|
||||
import forge.localinstance.skin.FSkinProp;
|
||||
import forge.model.FModel;
|
||||
import forge.adventure.libgdxgui.screens.LoadingOverlay;
|
||||
import forge.adventure.libgdxgui.screens.SplashScreen;
|
||||
import forge.adventure.libgdxgui.toolbox.FProgressBar;
|
||||
import forge.util.WordUtil;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class FSkin {
|
||||
private static final Map<FSkinProp, FSkinImage> images = new HashMap<>(512);
|
||||
private static final Map<Integer, TextureRegion> avatars = new HashMap<>(150);
|
||||
private static final Map<Integer, TextureRegion> sleeves = new HashMap<>(64);
|
||||
private static final Map<Integer, TextureRegion> borders = new HashMap<>();
|
||||
private static final Map<Integer, TextureRegion> deckbox = new HashMap<>();
|
||||
|
||||
private static Array<String> allSkins;
|
||||
private static FileHandle preferredDir;
|
||||
private static String preferredName;
|
||||
private static boolean loaded = false;
|
||||
public static Texture hdLogo = null;
|
||||
|
||||
public static void changeSkin(final String skinName) {
|
||||
final ForgePreferences prefs = FModel.getPreferences();
|
||||
if (skinName.equals(prefs.getPref(FPref.UI_SKIN))) { return; }
|
||||
|
||||
//save skin preference
|
||||
prefs.setPref(FPref.UI_SKIN, skinName);
|
||||
prefs.save();
|
||||
|
||||
//load skin
|
||||
loaded = false; //reset this temporarily until end of loadFull()
|
||||
|
||||
final LoadingOverlay loader = new LoadingOverlay("Loading new theme...");
|
||||
loader.show(); //show loading overlay then delay running remaining logic so UI can respond
|
||||
FThreads.invokeInBackgroundThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
FThreads.invokeInEdtLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
loadLight(skinName, null);
|
||||
loadFull(null);
|
||||
loader.setCaption("Loading fonts...");
|
||||
FThreads.invokeInBackgroundThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
FSkinFont.deleteCachedFiles(); //delete cached font files so font can be update for new skin
|
||||
FSkinFont.updateAll();
|
||||
//CardImageRenderer.forceStaticFieldUpdate();
|
||||
FThreads.invokeInEdtLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
loader.hide();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* Loads a "light" version of FSkin, just enough for the splash screen:
|
||||
* skin name. Generates custom skin settings, fonts, and backgrounds.
|
||||
*
|
||||
*
|
||||
* @param skinName
|
||||
* the skin name
|
||||
*/
|
||||
public static void loadLight(String skinName, final SplashScreen splashScreen) {
|
||||
preferredName = skinName.toLowerCase().replace(' ', '_');
|
||||
|
||||
//reset hd buttons/icons
|
||||
Forge.hdbuttons = false;
|
||||
Forge.hdstart = false;
|
||||
preferredDir = Config.instance().getFile("skin");
|
||||
|
||||
FSkinTexture.BG_TEXTURE.load(); //load background texture early for splash screen
|
||||
|
||||
if (splashScreen != null) {
|
||||
final FileHandle f = getSkinFile("bg_splash.png");
|
||||
final FileHandle f2 = getSkinFile("bg_splash_hd.png"); //HD Splashscreen
|
||||
final FileHandle f3 = getSkinFile("hd_logo.png");
|
||||
|
||||
if (!f.exists()) {
|
||||
if (!skinName.equals("default")) {
|
||||
FSkin.loadLight("default", splashScreen);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Texture txSplash = new Texture(f);
|
||||
final int w = txSplash.getWidth();
|
||||
final int h = txSplash.getHeight();
|
||||
|
||||
if (f2.exists()) {
|
||||
Texture txSplashHD = new Texture(f2, true);
|
||||
txSplashHD.setFilter(Texture.TextureFilter.MipMapLinearLinear, Texture.TextureFilter.Linear);
|
||||
splashScreen.setBackground(new TextureRegion(txSplashHD));
|
||||
} else {
|
||||
splashScreen.setBackground(new TextureRegion(txSplash, 0, 0, w, h - 100));
|
||||
}
|
||||
|
||||
if (f3.exists()) {
|
||||
Texture txOverlay = new Texture(f3, true);
|
||||
txOverlay.setFilter(Texture.TextureFilter.MipMapLinearLinear, Texture.TextureFilter.Linear);
|
||||
hdLogo = txOverlay;
|
||||
} else {
|
||||
hdLogo = null;
|
||||
}
|
||||
|
||||
Pixmap pxSplash = new Pixmap(f);
|
||||
FProgressBar.BACK_COLOR = new Color(pxSplash.getPixel(25, h - 75));
|
||||
FProgressBar.FORE_COLOR = new Color(pxSplash.getPixel(75, h - 75));
|
||||
FProgressBar.SEL_BACK_COLOR = new Color(pxSplash.getPixel(25, h - 25));
|
||||
FProgressBar.SEL_FORE_COLOR = new Color(pxSplash.getPixel(75, h - 25));
|
||||
}
|
||||
catch (final Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
loaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads two sprites: the default (which should be a complete
|
||||
* collection of all symbols) and the preferred (which may be
|
||||
* incomplete).
|
||||
*
|
||||
* Font must be present in the skin folder, and will not
|
||||
* be replaced by default. The fonts are pre-derived
|
||||
* in this method and saved in a HashMap for future access.
|
||||
*
|
||||
* Color swatches must be present in the preferred
|
||||
* sprite, and will not be replaced by default.
|
||||
*
|
||||
* Background images must be present in skin folder,
|
||||
* and will not be replaced by default.
|
||||
*
|
||||
* Icons, however, will be pulled from the two sprites. Obviously,
|
||||
* preferred takes precedence over default, but if something is
|
||||
* missing, the default picture is retrieved.
|
||||
*/
|
||||
public static void loadFull(final SplashScreen splashScreen) {
|
||||
if (splashScreen != null) {
|
||||
// Preferred skin name must be called via loadLight() method,
|
||||
// which does some cleanup and init work.
|
||||
if (FSkin.preferredName.isEmpty()) { FSkin.loadLight("default", splashScreen); }
|
||||
}
|
||||
|
||||
avatars.clear();
|
||||
sleeves.clear();
|
||||
|
||||
boolean textureFilter = Forge.isTextureFilteringEnabled();
|
||||
|
||||
final Map<String, Texture> textures = new HashMap<>();
|
||||
|
||||
// Grab and test various sprite files.
|
||||
final FileHandle f1 = getDefaultSkinFile(SourceFile.ICONS.getFilename());
|
||||
final FileHandle f2 = getSkinFile(SourceFile.ICONS.getFilename());
|
||||
final FileHandle f3 = getDefaultSkinFile(SourceFile.FOILS.getFilename());
|
||||
final FileHandle f4 = getDefaultSkinFile(ForgeConstants.SPRITE_AVATARS_FILE);
|
||||
final FileHandle f5 = getSkinFile(ForgeConstants.SPRITE_AVATARS_FILE);
|
||||
final FileHandle f6 = getDefaultSkinFile(SourceFile.OLD_FOILS.getFilename());
|
||||
final FileHandle f7 = getSkinFile(ForgeConstants.SPRITE_MANAICONS_FILE);
|
||||
final FileHandle f8 = getDefaultSkinFile(ForgeConstants.SPRITE_SLEEVES_FILE);
|
||||
final FileHandle f9 = getDefaultSkinFile(ForgeConstants.SPRITE_SLEEVES2_FILE);
|
||||
final FileHandle f10 = getDefaultSkinFile(ForgeConstants.SPRITE_BORDER_FILE);
|
||||
final FileHandle f11 = getSkinFile(ForgeConstants.SPRITE_BUTTONS_FILE);
|
||||
final FileHandle f12 = getSkinFile(ForgeConstants.SPRITE_START_FILE);
|
||||
final FileHandle f13 = getDefaultSkinFile(ForgeConstants.SPRITE_DECKBOX_FILE);
|
||||
|
||||
try {
|
||||
textures.put(f1.path(), new Texture(f1));
|
||||
Pixmap preferredIcons = new Pixmap(f1);
|
||||
if (f2.exists()) {
|
||||
textures.put(f2.path(), new Texture(f2));
|
||||
preferredIcons = new Pixmap(f2);
|
||||
}
|
||||
|
||||
textures.put(f3.path(), new Texture(f3));
|
||||
if (f6.exists()) {
|
||||
textures.put(f6.path(), new Texture(f6));
|
||||
}
|
||||
else {
|
||||
textures.put(f6.path(), textures.get(f3.path()));
|
||||
}
|
||||
if (f7.exists()){
|
||||
Texture t = new Texture(f7, true);
|
||||
//t.setFilter(Texture.TextureFilter.MipMapLinearLinear, Texture.TextureFilter.Linear);
|
||||
textures.put(f7.path(), t);
|
||||
}
|
||||
|
||||
//hdbuttons
|
||||
if (f11.exists()) {
|
||||
if (GuiBase.isAndroid() && Forge.totalDeviceRAM <5000) {
|
||||
Forge.hdbuttons = false;
|
||||
} else {
|
||||
Texture t = new Texture(f11, true);
|
||||
t.setFilter(Texture.TextureFilter.MipMapLinearLinear, Texture.TextureFilter.Linear);
|
||||
textures.put(f11.path(), t);
|
||||
Forge.hdbuttons = true;
|
||||
}
|
||||
} else { Forge.hdbuttons = false; } //how to refresh buttons when a theme don't have hd buttons?
|
||||
if (f12.exists()) {
|
||||
if (GuiBase.isAndroid() && Forge.totalDeviceRAM <5000) {
|
||||
Forge.hdstart = false;
|
||||
} else {
|
||||
Texture t = new Texture(f12, true);
|
||||
t.setFilter(Texture.TextureFilter.MipMapLinearLinear, Texture.TextureFilter.Linear);
|
||||
textures.put(f12.path(), t);
|
||||
Forge.hdstart = true;
|
||||
}
|
||||
} else { Forge.hdstart = false; }
|
||||
//update colors
|
||||
for (final FSkinColor.Colors c : FSkinColor.Colors.values()) {
|
||||
c.setColor(new Color(preferredIcons.getPixel(c.getX(), c.getY())));
|
||||
}
|
||||
|
||||
//load images
|
||||
for (FSkinImage image : FSkinImage.values()) {
|
||||
if (GuiBase.isAndroid()) {
|
||||
if (Forge.totalDeviceRAM>5000)
|
||||
image.load(textures, preferredIcons);
|
||||
else if (image.toString().equals("HDMULTI"))
|
||||
image.load(textures, preferredIcons);
|
||||
else if (!image.toString().startsWith("HD"))
|
||||
image.load(textures, preferredIcons);
|
||||
} else {
|
||||
image.load(textures, preferredIcons);
|
||||
}
|
||||
}
|
||||
for (FSkinTexture texture : FSkinTexture.values()) {
|
||||
if (texture != FSkinTexture.BG_TEXTURE) {
|
||||
texture.load();
|
||||
}
|
||||
}
|
||||
|
||||
//assemble avatar textures
|
||||
int counter = 0;
|
||||
int scount = 0;
|
||||
Color pxTest;
|
||||
Pixmap pxDefaultAvatars, pxPreferredAvatars, pxDefaultSleeves;
|
||||
Texture txDefaultAvatars, txPreferredAvatars, txDefaultSleeves;
|
||||
|
||||
pxDefaultAvatars = new Pixmap(f4);
|
||||
pxDefaultSleeves = new Pixmap(f8);
|
||||
txDefaultAvatars = new Texture(f4, textureFilter);
|
||||
if (textureFilter)
|
||||
txDefaultAvatars.setFilter(Texture.TextureFilter.MipMapLinearLinear, Texture.TextureFilter.Linear);
|
||||
txDefaultSleeves = new Texture(f8, textureFilter);
|
||||
if (textureFilter)
|
||||
txDefaultSleeves.setFilter(Texture.TextureFilter.MipMapLinearLinear, Texture.TextureFilter.Linear);
|
||||
|
||||
if (f5.exists()) {
|
||||
pxPreferredAvatars = new Pixmap(f5);
|
||||
txPreferredAvatars = new Texture(f5, textureFilter);
|
||||
if (textureFilter)
|
||||
txPreferredAvatars.setFilter(Texture.TextureFilter.MipMapLinearLinear, Texture.TextureFilter.Linear);
|
||||
|
||||
final int pw = pxPreferredAvatars.getWidth();
|
||||
final int ph = pxPreferredAvatars.getHeight();
|
||||
|
||||
for (int j = 0; j < ph; j += 100) {
|
||||
for (int i = 0; i < pw; i += 100) {
|
||||
if (i == 0 && j == 0) { continue; }
|
||||
pxTest = new Color(pxPreferredAvatars.getPixel(i + 50, j + 50));
|
||||
if (pxTest.a == 0) { continue; }
|
||||
FSkin.avatars.put(counter++, new TextureRegion(txPreferredAvatars, i, j, 100, 100));
|
||||
}
|
||||
}
|
||||
pxPreferredAvatars.dispose();
|
||||
} else if (!FSkin.preferredName.isEmpty()){
|
||||
//workaround bug crash fix if missing sprite avatar on preferred theme for quest tournament...
|
||||
//i really don't know why it needs to populate the avatars twice.... needs investigation
|
||||
final int pw = pxDefaultAvatars.getWidth();
|
||||
final int ph = pxDefaultAvatars.getHeight();
|
||||
|
||||
for (int j = 0; j < ph; j += 100) {
|
||||
for (int i = 0; i < pw; i += 100) {
|
||||
if (i == 0 && j == 0) { continue; }
|
||||
pxTest = new Color(pxDefaultAvatars.getPixel(i + 50, j + 50));
|
||||
if (pxTest.a == 0) { continue; }
|
||||
FSkin.avatars.put(counter++, new TextureRegion(txDefaultAvatars, i, j, 100, 100));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final int aw = pxDefaultAvatars.getWidth();
|
||||
final int ah = pxDefaultAvatars.getHeight();
|
||||
|
||||
for (int j = 0; j < ah; j += 100) {
|
||||
for (int i = 0; i < aw; i += 100) {
|
||||
if (i == 0 && j == 0) { continue; }
|
||||
pxTest = new Color(pxDefaultAvatars.getPixel(i + 50, j + 50));
|
||||
if (pxTest.a == 0) { continue; }
|
||||
FSkin.avatars.put(counter++, new TextureRegion(txDefaultAvatars, i, j, 100, 100));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
final int sw = pxDefaultSleeves.getWidth();
|
||||
final int sh = pxDefaultSleeves.getHeight();
|
||||
|
||||
for (int j = 0; j < sh; j += 500) {
|
||||
for (int i = 0; i < sw; i += 360) {
|
||||
pxTest = new Color(pxDefaultSleeves.getPixel(i + 180, j + 250));
|
||||
if (pxTest.a == 0) { continue; }
|
||||
FSkin.sleeves.put(scount++, new TextureRegion(txDefaultSleeves, i, j, 360, 500));
|
||||
}
|
||||
}
|
||||
|
||||
//re init second set of sleeves
|
||||
pxDefaultSleeves = new Pixmap(f9);
|
||||
txDefaultSleeves = new Texture(f9, textureFilter);
|
||||
if (textureFilter)
|
||||
txDefaultSleeves.setFilter(Texture.TextureFilter.MipMapLinearLinear, Texture.TextureFilter.Linear);
|
||||
|
||||
final int sw2 = pxDefaultSleeves.getWidth();
|
||||
final int sh2 = pxDefaultSleeves.getHeight();
|
||||
|
||||
for (int j = 0; j < sh2; j += 500) {
|
||||
for (int i = 0; i < sw2; i += 360) {
|
||||
pxTest = new Color(pxDefaultSleeves.getPixel(i + 180, j + 250));
|
||||
if (pxTest.a == 0) { continue; }
|
||||
FSkin.sleeves.put(scount++, new TextureRegion(txDefaultSleeves, i, j, 360, 500));
|
||||
}
|
||||
}
|
||||
//borders
|
||||
Texture bordersBW = new Texture(f10);
|
||||
FSkin.borders.put(0, new TextureRegion(bordersBW, 2, 2, 672, 936));
|
||||
FSkin.borders.put(1, new TextureRegion(bordersBW, 676, 2, 672, 936));
|
||||
//deckboxes
|
||||
Texture deckboxes = new Texture(f13, textureFilter);
|
||||
if (textureFilter)
|
||||
deckboxes.setFilter(Texture.TextureFilter.MipMapLinearLinear, Texture.TextureFilter.Linear);
|
||||
//gold bg
|
||||
FSkin.deckbox.put(0, new TextureRegion(deckboxes, 2, 2, 488, 680));
|
||||
//deck box for card art
|
||||
FSkin.deckbox.put(1, new TextureRegion(deckboxes, 492, 2, 488, 680));
|
||||
//generic deck box
|
||||
FSkin.deckbox.put(2, new TextureRegion(deckboxes, 982, 2, 488, 680));
|
||||
|
||||
preferredIcons.dispose();
|
||||
pxDefaultAvatars.dispose();
|
||||
pxDefaultSleeves.dispose();
|
||||
}
|
||||
catch (final Exception e) {
|
||||
System.err.println("FSkin$loadFull: Missing a sprite (default icons, "
|
||||
+ "preferred icons, or foils.");
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
// Run through enums and load their coords.
|
||||
FSkinColor.updateAll();
|
||||
|
||||
// Images loaded; can start UI init.
|
||||
loaded = true;
|
||||
|
||||
if (splashScreen != null) {
|
||||
CardFaceSymbols.loadImages();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name.
|
||||
*
|
||||
* @return Name of the current skin.
|
||||
*/
|
||||
public static String getName() {
|
||||
return FSkin.preferredName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a FileHandle for a file within the directory where skin files should be stored
|
||||
*/
|
||||
public static FileHandle getSkinFile(String filename) {
|
||||
return preferredDir.child(filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a FileHandle for a file within the directory where the default skin files should be stored
|
||||
*/
|
||||
public static FileHandle getDefaultSkinFile(String filename) {
|
||||
return Gdx.files.absolute(ForgeConstants.DEFAULT_SKINS_DIR + filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a FileHandle for a file within the planechase cache directory
|
||||
*/
|
||||
public static FileHandle getCachePlanechaseFile(String filename) {
|
||||
return Gdx.files.absolute(ForgeConstants.CACHE_PLANECHASE_PICS_DIR + filename);
|
||||
}
|
||||
|
||||
public static FileHandle getSkinDir() {
|
||||
return preferredDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the skins.
|
||||
*
|
||||
* @return the skins
|
||||
*/
|
||||
public static Array<String> getSkinDirectoryNames() {
|
||||
final Array<String> mySkins = new Array<>();
|
||||
|
||||
final FileHandle dir = Gdx.files.absolute(ForgeConstants.CACHE_SKINS_DIR);
|
||||
for (FileHandle skinFile : dir.list()) {
|
||||
String skinName = skinFile.name();
|
||||
if (skinName.equalsIgnoreCase(".svn")) { continue; }
|
||||
if (skinName.equalsIgnoreCase(".DS_Store")) { continue; }
|
||||
mySkins.add(skinName);
|
||||
}
|
||||
|
||||
return mySkins;
|
||||
}
|
||||
|
||||
public static Iterable<String> getAllSkins() {
|
||||
if (allSkins != null) {
|
||||
allSkins.clear();
|
||||
allSkins.add("Default"); //init default
|
||||
final Array<String> skinDirectoryNames = getSkinDirectoryNames();
|
||||
for (final String skinDirectoryName : skinDirectoryNames) {
|
||||
allSkins.add(WordUtil.capitalize(skinDirectoryName.replace('_', ' ')));
|
||||
}
|
||||
allSkins.sort();
|
||||
}
|
||||
return allSkins;
|
||||
}
|
||||
|
||||
public static Map<FSkinProp, FSkinImage> getImages() {
|
||||
return images;
|
||||
}
|
||||
|
||||
public static Map<Integer, TextureRegion> getAvatars() {
|
||||
return avatars;
|
||||
}
|
||||
|
||||
public static Map<Integer, TextureRegion> getSleeves() {
|
||||
return sleeves;
|
||||
}
|
||||
|
||||
public static Map<Integer, TextureRegion> getBorders() {
|
||||
return borders;
|
||||
}
|
||||
|
||||
public static Map<Integer, TextureRegion> getDeckbox() {
|
||||
return deckbox;
|
||||
}
|
||||
|
||||
public static boolean isLoaded() { return loaded; }
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package forge.adventure.libgdxgui.assets;
|
||||
|
||||
import forge.adventure.libgdxgui.Graphics;
|
||||
|
||||
public class FSkinBorder {
|
||||
private final FSkinColor color;
|
||||
private final float thickness;
|
||||
|
||||
public FSkinBorder(FSkinColor color0, float thickness0) {
|
||||
color = color0;
|
||||
thickness = thickness0;
|
||||
}
|
||||
|
||||
public FSkinColor getColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
public float getThickness() {
|
||||
return thickness;
|
||||
}
|
||||
|
||||
public void draw(Graphics g, float x, float y, float w, float h) {
|
||||
x -= thickness;
|
||||
y -= thickness;
|
||||
w += 2 * thickness;
|
||||
h += 2 * thickness;
|
||||
g.fillRect(color, x, y, w, h); //draw filled rectangle behind object
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,268 @@
|
||||
package forge.adventure.libgdxgui.assets;
|
||||
|
||||
import com.badlogic.gdx.graphics.Color;
|
||||
import forge.localinstance.skin.FSkinProp;
|
||||
import forge.adventure.libgdxgui.screens.match.TargetingOverlay;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
public class FSkinColor {
|
||||
public enum Colors {
|
||||
CLR_THEME (FSkinProp.CLR_THEME),
|
||||
CLR_BORDERS (FSkinProp.CLR_BORDERS),
|
||||
CLR_ZEBRA (FSkinProp.CLR_ZEBRA),
|
||||
CLR_HOVER (FSkinProp.CLR_HOVER),
|
||||
CLR_ACTIVE (FSkinProp.CLR_ACTIVE),
|
||||
CLR_INACTIVE (FSkinProp.CLR_INACTIVE),
|
||||
CLR_TEXT (FSkinProp.CLR_TEXT),
|
||||
CLR_PHASE_INACTIVE_ENABLED (FSkinProp.CLR_PHASE_INACTIVE_ENABLED),
|
||||
CLR_PHASE_INACTIVE_DISABLED (FSkinProp.CLR_PHASE_INACTIVE_DISABLED),
|
||||
CLR_PHASE_ACTIVE_ENABLED (FSkinProp.CLR_PHASE_ACTIVE_ENABLED),
|
||||
CLR_PHASE_ACTIVE_DISABLED (FSkinProp.CLR_PHASE_ACTIVE_DISABLED),
|
||||
CLR_THEME2 (FSkinProp.CLR_THEME2),
|
||||
CLR_OVERLAY (FSkinProp.CLR_OVERLAY),
|
||||
CLR_COMBAT_TARGETING_ARROW (FSkinProp.CLR_COMBAT_TARGETING_ARROW),
|
||||
CLR_NORMAL_TARGETING_ARROW (FSkinProp.CLR_NORMAL_TARGETING_ARROW),
|
||||
CLR_PWATTK_TARGETING_ARROW (FSkinProp.CLR_PWATTK_TARGETING_ARROW);
|
||||
|
||||
private Color color;
|
||||
private final int x, y;
|
||||
private final FSkinProp skinProp;
|
||||
|
||||
Colors(final FSkinProp skinProp0) {
|
||||
skinProp = skinProp0;
|
||||
int[] coords = skinProp.getCoords();
|
||||
x = coords[0];
|
||||
y = coords[1];
|
||||
}
|
||||
|
||||
public int getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public int getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
public void setColor(Color color0) {
|
||||
color = color0;
|
||||
}
|
||||
|
||||
public static Colors fromSkinProp(FSkinProp skinProp) {
|
||||
for (final Colors c : Colors.values()) {
|
||||
if (c.skinProp == skinProp) {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static FSkinColor get(final Colors c0) {
|
||||
return baseColors.get(c0);
|
||||
}
|
||||
|
||||
public static FSkinColor getStandardColor(int r, int g, int b) {
|
||||
return getStandardColor(fromRGB(r, g, b));
|
||||
}
|
||||
public static FSkinColor getStandardColor(final Color c0) {
|
||||
return new FSkinColor(c0, NO_BRIGHTNESS_DELTA, NO_STEP, NO_STEP, NO_ALPHA);
|
||||
}
|
||||
|
||||
private static final HashMap<Colors, FSkinColor> baseColors = new HashMap<>();
|
||||
private static final HashMap<String, FSkinColor> derivedColors = new HashMap<>();
|
||||
private static final int NO_BRIGHTNESS_DELTA = 0;
|
||||
private static final int NO_STEP = -999; //needs to be large negative since small negative values are valid
|
||||
private static final int NO_ALPHA = -1;
|
||||
|
||||
private final Colors baseColor;
|
||||
private final int brightnessDelta;
|
||||
private final int step;
|
||||
private final int contrastStep;
|
||||
private final float alpha;
|
||||
protected Color color;
|
||||
|
||||
public Color getColor() { return color; }
|
||||
|
||||
private FSkinColor(Colors baseColor0) {
|
||||
this(baseColor0, NO_BRIGHTNESS_DELTA, NO_STEP, NO_STEP, NO_ALPHA);
|
||||
}
|
||||
private FSkinColor(Colors baseColor0, int brightnessDelta0, int step0, int contrastStep0, float alpha0) {
|
||||
baseColor = baseColor0;
|
||||
brightnessDelta = brightnessDelta0;
|
||||
step = step0;
|
||||
contrastStep = contrastStep0;
|
||||
alpha = alpha0;
|
||||
updateColor();
|
||||
}
|
||||
private FSkinColor(Color color0, int brightnessDelta0, int step0, int contrastStep0, float alpha0) {
|
||||
color = color0;
|
||||
baseColor = null;
|
||||
brightnessDelta = brightnessDelta0;
|
||||
step = step0;
|
||||
contrastStep = contrastStep0;
|
||||
alpha = alpha0;
|
||||
updateColor();
|
||||
}
|
||||
|
||||
private FSkinColor getDerivedColor(int brightnessDelta0, int step0, int contrastStep0, float alpha0) {
|
||||
if (baseColor == null) { //handle deriving from standard color
|
||||
return new FSkinColor(color, brightnessDelta0, step0, contrastStep0, alpha0);
|
||||
}
|
||||
String key = baseColor.name() + "|" + brightnessDelta0 + "|" + step0 + "|" + contrastStep0 + "|" + alpha0;
|
||||
FSkinColor derivedColor = derivedColors.get(key);
|
||||
if (derivedColor == null) {
|
||||
derivedColor = new FSkinColor(baseColor, brightnessDelta0, step0, contrastStep0, alpha0);
|
||||
derivedColors.put(key, derivedColor);
|
||||
}
|
||||
return derivedColor;
|
||||
}
|
||||
|
||||
public FSkinColor brighter() {
|
||||
return getDerivedColor(brightnessDelta + 1, step, contrastStep, alpha);
|
||||
}
|
||||
|
||||
public FSkinColor darker() {
|
||||
return getDerivedColor(brightnessDelta - 1, step, contrastStep, alpha);
|
||||
}
|
||||
|
||||
public FSkinColor stepColor(int step0) {
|
||||
if (step != NO_STEP) {
|
||||
step0 += step;
|
||||
}
|
||||
return getDerivedColor(brightnessDelta, step0, contrastStep, alpha);
|
||||
}
|
||||
|
||||
public FSkinColor getContrastColor(int contrastStep0) {
|
||||
if (contrastStep != NO_STEP) {
|
||||
contrastStep0 += contrastStep;
|
||||
}
|
||||
return getDerivedColor(brightnessDelta, step, contrastStep0, alpha);
|
||||
}
|
||||
|
||||
public FSkinColor getHighContrastColor() {
|
||||
return getContrastColor(255);
|
||||
}
|
||||
|
||||
public FSkinColor alphaColor(float alpha0) {
|
||||
return getDerivedColor(brightnessDelta, step, contrastStep, alpha0);
|
||||
}
|
||||
|
||||
protected void updateColor() {
|
||||
if (baseColor != null) {
|
||||
color = baseColor.color;
|
||||
}
|
||||
if (brightnessDelta != NO_BRIGHTNESS_DELTA) {
|
||||
if (brightnessDelta < 0) {
|
||||
for (int i = 0; i > brightnessDelta; i--) {
|
||||
color = FSkinColor.stepColor(color, -20);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < brightnessDelta; i++) {
|
||||
color = FSkinColor.stepColor(color, 20);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (step != NO_STEP) {
|
||||
color = FSkinColor.stepColor(color, step);
|
||||
}
|
||||
if (contrastStep != NO_STEP) {
|
||||
color = FSkinColor.stepColor(color, FSkinColor.isColorBright(color) ? -contrastStep : contrastStep);
|
||||
}
|
||||
if (alpha != NO_ALPHA) {
|
||||
color = FSkinColor.alphaColor(color, alpha);
|
||||
}
|
||||
}
|
||||
|
||||
/** Steps RGB components of a color up or down.
|
||||
* Returns opaque (non-alpha) stepped color.
|
||||
* Plus for lighter, minus for darker.
|
||||
*
|
||||
* @param clr0 {Color}
|
||||
* @param step int
|
||||
* @return {@link Color}
|
||||
*/
|
||||
public static Color stepColor(Color clr0, int step) {
|
||||
float r = clr0.r * 255;
|
||||
float g = clr0.g * 255;
|
||||
float b = clr0.b * 255;
|
||||
|
||||
// Darker
|
||||
if (step < 0) {
|
||||
r = ((r + step > 0) ? r + step : 0);
|
||||
g = ((g + step > 0) ? g + step : 0);
|
||||
b = ((b + step > 0) ? b + step : 0);
|
||||
}
|
||||
else {
|
||||
r = ((r + step < 255) ? r + step : 255);
|
||||
g = ((g + step < 255) ? g + step : 255);
|
||||
b = ((b + step < 255) ? b + step : 255);
|
||||
}
|
||||
|
||||
return new Color(r / 255, g / 255, b / 255, clr0.a);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns RGB components of a color, with a new
|
||||
* value for alpha. 0f = transparent, 1f = opaque.
|
||||
*/
|
||||
public static Color alphaColor(Color clr0, float alpha) {
|
||||
return new Color(clr0.r, clr0.g, clr0.b, alpha);
|
||||
}
|
||||
|
||||
/**
|
||||
* see http://www.nbdtech.com/Blog/archive/2008/04/27/Calculating-the-Perceived-Brightness-of-a-Color.aspx
|
||||
*/
|
||||
public static boolean isColorBright(Color c) {
|
||||
double v = Math.sqrt(
|
||||
c.r * c.r * 0.241 +
|
||||
c.g * c.g * 0.691 +
|
||||
c.b * c.b * 0.068);
|
||||
return v > 0.5;
|
||||
}
|
||||
|
||||
public static Color getHighContrastColor(Color c) {
|
||||
return isColorBright(c) ? Color.BLACK : Color.WHITE;
|
||||
}
|
||||
|
||||
public static Color tintColor(Color source, Color tint, float alpha) {
|
||||
float r = (tint.r - source.r) * alpha + source.r;
|
||||
float g = (tint.g - source.g) * alpha + source.g;
|
||||
float b = (tint.b - source.b) * alpha + source.b;
|
||||
return new Color(r, g, b, 1f);
|
||||
}
|
||||
|
||||
public static Color[] tintColors(Color source, Color[] tints, float alpha) {
|
||||
Color[] tintedColors = new Color[tints.length];
|
||||
for (int i = 0; i < tints.length; i++) {
|
||||
tintedColors[i] = tintColor(source, tints[i], alpha);
|
||||
}
|
||||
return tintedColors;
|
||||
}
|
||||
|
||||
public static Color fromRGB(int r, int g, int b) {
|
||||
return new Color((float)r / 255f, (float)g / 255f, (float)b / 255f, 1f);
|
||||
}
|
||||
|
||||
public static void updateAll() {
|
||||
if (FSkinColor.baseColors.size() == 0) { //initialize base skin colors if needed
|
||||
for (final Colors c : Colors.values()) {
|
||||
FSkinColor.baseColors.put(c, new FSkinColor(c));
|
||||
}
|
||||
}
|
||||
else { //update existing FSkinColors if baseColors already initialized
|
||||
for (final FSkinColor c : FSkinColor.baseColors.values()) {
|
||||
c.updateColor();
|
||||
}
|
||||
for (final FSkinColor c : FSkinColor.derivedColors.values()) {
|
||||
c.updateColor();
|
||||
}
|
||||
}
|
||||
TargetingOverlay.updateColors();
|
||||
}
|
||||
|
||||
public float getAlpha() {
|
||||
return color.a;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,476 @@
|
||||
package forge.adventure.libgdxgui.assets;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.files.FileHandle;
|
||||
import com.badlogic.gdx.graphics.Color;
|
||||
import com.badlogic.gdx.graphics.Pixmap;
|
||||
import com.badlogic.gdx.graphics.Texture;
|
||||
import com.badlogic.gdx.graphics.g2d.Batch;
|
||||
import com.badlogic.gdx.graphics.g2d.BitmapFont;
|
||||
import com.badlogic.gdx.graphics.g2d.BitmapFont.BitmapFontData;
|
||||
import com.badlogic.gdx.graphics.g2d.BitmapFont.Glyph;
|
||||
import com.badlogic.gdx.graphics.g2d.PixmapPacker;
|
||||
import com.badlogic.gdx.graphics.g2d.TextureRegion;
|
||||
import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator;
|
||||
import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator.FreeTypeFontParameter;
|
||||
import com.badlogic.gdx.graphics.glutils.PixmapTextureData;
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import forge.adventure.libgdxgui.Forge;
|
||||
import forge.adventure.libgdxgui.util.TextBounds;
|
||||
import forge.adventure.libgdxgui.util.Utils;
|
||||
import forge.gui.FThreads;
|
||||
import forge.localinstance.properties.ForgeConstants;
|
||||
import forge.util.FileUtil;
|
||||
import forge.util.LineReader;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
|
||||
public class FSkinFont {
|
||||
private static final int MIN_FONT_SIZE = 8;
|
||||
private static int MAX_FONT_SIZE = 72;
|
||||
|
||||
private static final int MAX_FONT_SIZE_LESS_GLYPHS = 72;
|
||||
private static final int MAX_FONT_SIZE_MANY_GLYPHS = 36;
|
||||
|
||||
private static final String TTF_FILE = "font1.ttf";
|
||||
private static final Map<Integer, FSkinFont> fonts = new HashMap<>();
|
||||
|
||||
private static final String commonCharacterSet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklm"
|
||||
+ "nopqrstuvwxyz1234567890\"!?'.,;:()[]{}<>|/@\\^$-%+=#_&*\u2014"
|
||||
+ "\u2022ÁÉÍÓÚáéíóúÀÈÌÒÙàèìòùÑñÄËÏÖÜäëïöüẞß¿¡";
|
||||
private static final Map<String, String> langUniqueCharacterSet = new HashMap<>();
|
||||
|
||||
static {
|
||||
FileUtil.ensureDirectoryExists(ForgeConstants.FONTS_DIR);
|
||||
}
|
||||
|
||||
public static FSkinFont get(final int unscaledSize) {
|
||||
return _get((int) Utils.scale(unscaledSize));
|
||||
}
|
||||
public static FSkinFont _get(final int scaledSize) {
|
||||
FSkinFont skinFont = fonts.get(scaledSize);
|
||||
if (skinFont == null) {
|
||||
skinFont = new FSkinFont(scaledSize);
|
||||
fonts.put(scaledSize, skinFont);
|
||||
}
|
||||
return skinFont;
|
||||
}
|
||||
|
||||
public static FSkinFont forHeight(final float height) {
|
||||
int size = MIN_FONT_SIZE + 1;
|
||||
while (true) {
|
||||
if (_get(size).getLineHeight() > height) {
|
||||
return _get(size - 1);
|
||||
}
|
||||
size++;
|
||||
}
|
||||
}
|
||||
|
||||
//pre-load all supported font sizes
|
||||
public static void preloadAll(String language) {
|
||||
//todo:really check the language glyph is a lot
|
||||
MAX_FONT_SIZE = (language.equals("zh-CN") || language.equals("ja-JP")) ? MAX_FONT_SIZE_MANY_GLYPHS : MAX_FONT_SIZE_LESS_GLYPHS;
|
||||
for (int size = MIN_FONT_SIZE; size <= MAX_FONT_SIZE; size++) {
|
||||
_get(size);
|
||||
}
|
||||
}
|
||||
|
||||
//delete all cached font files
|
||||
public static void deleteCachedFiles() {
|
||||
final FileHandle dir = Gdx.files.absolute(ForgeConstants.FONTS_DIR);
|
||||
for (FileHandle fontFile : dir.list()) {
|
||||
String name = fontFile.name();
|
||||
if (name.endsWith(".fnt") || name.endsWith(".png")) {
|
||||
fontFile.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void updateAll() {
|
||||
for (FSkinFont skinFont : fonts.values()) {
|
||||
skinFont.updateFont();
|
||||
}
|
||||
}
|
||||
|
||||
private final int fontSize;
|
||||
private final float scale;
|
||||
private BitmapFont font;
|
||||
|
||||
private FSkinFont(int fontSize0) {
|
||||
if (fontSize0 > MAX_FONT_SIZE) {
|
||||
scale = (float)fontSize0 / MAX_FONT_SIZE;
|
||||
}
|
||||
else if (fontSize0 < MIN_FONT_SIZE) {
|
||||
scale = (float)fontSize0 / MIN_FONT_SIZE;
|
||||
}
|
||||
else {
|
||||
scale = 1;
|
||||
}
|
||||
fontSize = fontSize0;
|
||||
updateFont();
|
||||
}
|
||||
static int indexOf (CharSequence text, char ch, int start) {
|
||||
final int n = text.length();
|
||||
for (; start < n; start++)
|
||||
if (text.charAt(start) == ch) return start;
|
||||
return n;
|
||||
|
||||
}
|
||||
public int computeVisibleGlyphs (CharSequence str, int start, int end, float availableWidth) {
|
||||
BitmapFontData data = font.getData();
|
||||
int index = start;
|
||||
float width = 0;
|
||||
Glyph lastGlyph = null;
|
||||
availableWidth /= data.scaleX;
|
||||
|
||||
for (; index < end; index++) {
|
||||
char ch = str.charAt(index);
|
||||
if (ch == '[' && data.markupEnabled) {
|
||||
index++;
|
||||
if (!(index < end && str.charAt(index) == '[')) { // non escaped '['
|
||||
while (index < end && str.charAt(index) != ']')
|
||||
index++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
Glyph g = data.getGlyph(ch);
|
||||
|
||||
if (g != null) {
|
||||
if (lastGlyph != null) width += lastGlyph.getKerning(ch);
|
||||
if ((width + g.xadvance) - availableWidth > 0.001f) break;
|
||||
width += g.xadvance;
|
||||
lastGlyph = g;
|
||||
}
|
||||
}
|
||||
|
||||
return index - start;
|
||||
}
|
||||
public boolean isBreakChar (char c) {
|
||||
BitmapFontData data = font.getData();
|
||||
if (data.breakChars == null) return false;
|
||||
for (char br : data.breakChars)
|
||||
if (c == br) return true;
|
||||
return false;
|
||||
}
|
||||
static boolean isWhitespace (char c) {
|
||||
switch (c) {
|
||||
case '\n':
|
||||
case '\r':
|
||||
case '\t':
|
||||
case ' ':
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Expose methods from font that updates scale as needed
|
||||
public TextBounds getBounds(CharSequence str) {
|
||||
updateScale(); //must update scale before measuring text
|
||||
return getBounds(str, 0, str.length());
|
||||
}
|
||||
public TextBounds getBounds(CharSequence str, int start, int end) {
|
||||
BitmapFontData data = font.getData();
|
||||
//int start = 0;
|
||||
//int end = str.length();
|
||||
int width = 0;
|
||||
Glyph lastGlyph = null;
|
||||
|
||||
while (start < end) {
|
||||
char ch = str.charAt(start++);
|
||||
if (ch == '[' && data.markupEnabled) {
|
||||
if (!(start < end && str.charAt(start) == '[')) { // non escaped '['
|
||||
while (start < end && str.charAt(start) != ']')
|
||||
start++;
|
||||
start++;
|
||||
continue;
|
||||
}
|
||||
start++;
|
||||
}
|
||||
lastGlyph = data.getGlyph(ch);
|
||||
if (lastGlyph != null) {
|
||||
width = lastGlyph.xadvance;
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (start < end) {
|
||||
char ch = str.charAt(start++);
|
||||
if (ch == '[' && data.markupEnabled) {
|
||||
if (!(start < end && str.charAt(start) == '[')) { // non escaped '['
|
||||
while (start < end && str.charAt(start) != ']')
|
||||
start++;
|
||||
start++;
|
||||
continue;
|
||||
}
|
||||
start++;
|
||||
}
|
||||
|
||||
Glyph g = data.getGlyph(ch);
|
||||
if (g != null) {
|
||||
width += lastGlyph.getKerning(ch);
|
||||
lastGlyph = g;
|
||||
width += g.xadvance;
|
||||
}
|
||||
}
|
||||
|
||||
return new TextBounds(width * data.scaleX, data.capHeight);
|
||||
|
||||
}
|
||||
public TextBounds getMultiLineBounds(CharSequence str) {
|
||||
updateScale();
|
||||
BitmapFontData data = font.getData();
|
||||
int start = 0;
|
||||
float maxWidth = 0;
|
||||
int numLines = 0;
|
||||
int length = str.length();
|
||||
|
||||
while (start < length) {
|
||||
int lineEnd = indexOf(str, '\n', start);
|
||||
float lineWidth = getBounds(str, start, lineEnd).width;
|
||||
maxWidth = Math.max(maxWidth, lineWidth);
|
||||
start = lineEnd + 1;
|
||||
numLines++;
|
||||
}
|
||||
|
||||
return new TextBounds(maxWidth, data.capHeight + (numLines - 1) * data.lineHeight);
|
||||
|
||||
}
|
||||
public TextBounds getWrappedBounds(CharSequence str, float wrapWidth) {
|
||||
updateScale();
|
||||
BitmapFontData data = font.getData();
|
||||
if (wrapWidth <= 0) wrapWidth = Integer.MAX_VALUE;
|
||||
int start = 0;
|
||||
int numLines = 0;
|
||||
int length = str.length();
|
||||
float maxWidth = 0;
|
||||
while (start < length) {
|
||||
int newLine = indexOf(str, '\n', start);
|
||||
int lineEnd = start + computeVisibleGlyphs(str, start, newLine, wrapWidth);
|
||||
int nextStart = lineEnd + 1;
|
||||
if (lineEnd < newLine) {
|
||||
// Find char to break on.
|
||||
while (lineEnd > start) {
|
||||
if (isWhitespace(str.charAt(lineEnd))) break;
|
||||
if (isBreakChar(str.charAt(lineEnd - 1))) break;
|
||||
lineEnd--;
|
||||
}
|
||||
|
||||
if (lineEnd == start) {
|
||||
|
||||
if (nextStart > start + 1) nextStart--;
|
||||
|
||||
lineEnd = nextStart; // If no characters to break, show all.
|
||||
|
||||
} else {
|
||||
nextStart = lineEnd;
|
||||
|
||||
// Eat whitespace at start of wrapped line.
|
||||
|
||||
while (nextStart < length) {
|
||||
char c = str.charAt(nextStart);
|
||||
if (!isWhitespace(c)) break;
|
||||
nextStart++;
|
||||
if (c == '\n') break; // Eat only the first wrapped newline.
|
||||
}
|
||||
|
||||
// Eat whitespace at end of line.
|
||||
while (lineEnd > start) {
|
||||
|
||||
if (!isWhitespace(str.charAt(lineEnd - 1))) break;
|
||||
lineEnd--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (lineEnd > start) {
|
||||
float lineWidth = getBounds(str, start, lineEnd).width;
|
||||
maxWidth = Math.max(maxWidth, lineWidth);
|
||||
}
|
||||
start = nextStart;
|
||||
numLines++;
|
||||
}
|
||||
|
||||
return new TextBounds(maxWidth, data.capHeight + (numLines - 1) * data.lineHeight);
|
||||
}
|
||||
public float getAscent() {
|
||||
updateScale();
|
||||
return font.getAscent();
|
||||
}
|
||||
public float getCapHeight() {
|
||||
updateScale();
|
||||
return font.getCapHeight();
|
||||
}
|
||||
public float getLineHeight() {
|
||||
updateScale();
|
||||
return font.getLineHeight();
|
||||
}
|
||||
|
||||
public void draw(Batch batch, String text, Color color, float x, float y, float w, boolean wrap, int horzAlignment) {
|
||||
updateScale();
|
||||
font.setColor(color);
|
||||
font.draw(batch, text, x, y, w, horzAlignment, wrap);
|
||||
}
|
||||
|
||||
//update scale of font if needed
|
||||
private void updateScale() {
|
||||
if (font.getScaleX() != scale) {
|
||||
font.getData().setScale(scale);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean canShrink() {
|
||||
return fontSize > MIN_FONT_SIZE;
|
||||
}
|
||||
|
||||
public FSkinFont shrink() {
|
||||
return _get(fontSize - 1);
|
||||
}
|
||||
|
||||
public String getCharacterSet(String langCode) {
|
||||
if (langUniqueCharacterSet.containsKey(langCode)) {
|
||||
return langUniqueCharacterSet.get(langCode);
|
||||
}
|
||||
StringBuilder characters = new StringBuilder(commonCharacterSet);
|
||||
Set<Integer> characterSet = new HashSet<>();
|
||||
for (int offset = 0; offset < commonCharacterSet.length();) {
|
||||
final int codePoint = commonCharacterSet.codePointAt(offset);
|
||||
characterSet.add(codePoint);
|
||||
offset += Character.charCount(codePoint);
|
||||
}
|
||||
String[] translationFilePaths = { ForgeConstants.LANG_DIR + "cardnames-" + langCode + ".txt",
|
||||
ForgeConstants.LANG_DIR + langCode + ".properties" };
|
||||
for (int i = 0; i < translationFilePaths.length; i++) {
|
||||
try (LineReader translationFile = new LineReader(new FileInputStream(translationFilePaths[i]),
|
||||
StandardCharsets.UTF_8)) {
|
||||
for (String fileLine : translationFile.readLines()) {
|
||||
final int stringLength = fileLine.length();
|
||||
for (int offset = 0; offset < stringLength;) {
|
||||
final int codePoint = fileLine.codePointAt(offset);
|
||||
if (!characterSet.contains(codePoint)) {
|
||||
characterSet.add(codePoint);
|
||||
characters.append(Character.toChars(codePoint));
|
||||
}
|
||||
offset += Character.charCount(codePoint);
|
||||
}
|
||||
}
|
||||
translationFile.close();
|
||||
} catch (IOException e) {
|
||||
System.err.println("Error reading translation file: " + translationFilePaths[i]);
|
||||
}
|
||||
}
|
||||
langUniqueCharacterSet.put(langCode, characters.toString());
|
||||
|
||||
return characters.toString();
|
||||
}
|
||||
|
||||
private void updateFont() {
|
||||
if (scale != 1) { //re-use font inside range if possible
|
||||
if (fontSize > MAX_FONT_SIZE) {
|
||||
font = _get(MAX_FONT_SIZE).font;
|
||||
} else {
|
||||
font = _get(MIN_FONT_SIZE).font;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
String fontName = "f" + fontSize;
|
||||
if (Forge.locale.equals("zh-CN") || Forge.locale.equals("ja-JP")) {
|
||||
fontName += Forge.locale;
|
||||
}
|
||||
FileHandle fontFile = Gdx.files.absolute(ForgeConstants.FONTS_DIR + fontName + ".fnt");
|
||||
if (fontFile != null && fontFile.exists()) {
|
||||
final BitmapFontData data = new BitmapFontData(fontFile, false);
|
||||
FThreads.invokeInEdtNowOrLater(new Runnable() {
|
||||
@Override
|
||||
public void run() { //font must be initialized on UI thread
|
||||
font = new BitmapFont(data, (TextureRegion)null, true);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (Forge.locale.equals("zh-CN") || Forge.locale.equals("ja-JP")) {
|
||||
String ttfName = Forge.CJK_Font;
|
||||
FileHandle ttfFile = Gdx.files.absolute(ForgeConstants.FONTS_DIR + ttfName + ".ttf");
|
||||
if (ttfFile != null && ttfFile.exists()) {
|
||||
generateFont(ttfFile, fontName, fontSize);
|
||||
}
|
||||
} else {
|
||||
generateFont(FSkin.getSkinFile(TTF_FILE), fontName, fontSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void generateFont(final FileHandle ttfFile, final String fontName, final int fontSize) {
|
||||
if (!ttfFile.exists()) { return; }
|
||||
|
||||
final FreeTypeFontGenerator generator = new FreeTypeFontGenerator(ttfFile);
|
||||
|
||||
//approximate optimal page size
|
||||
int pageSize;
|
||||
if (fontSize >= 28) {
|
||||
pageSize = 256;
|
||||
}
|
||||
else {
|
||||
pageSize = 128;
|
||||
}
|
||||
|
||||
final PixmapPacker packer = new PixmapPacker(pageSize, pageSize, Pixmap.Format.RGBA8888, 2, false);
|
||||
final FreeTypeFontParameter parameter = new FreeTypeFontParameter();
|
||||
parameter.characters = getCharacterSet(Forge.locale);
|
||||
parameter.size = fontSize;
|
||||
parameter.packer = packer;
|
||||
final FreeTypeFontGenerator.FreeTypeBitmapFontData fontData = generator.generateData(parameter);
|
||||
final Array<PixmapPacker.Page> pages = packer.getPages();
|
||||
|
||||
//finish generating font on UI thread
|
||||
FThreads.invokeInEdtNowOrLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Array<TextureRegion> textureRegions = new Array<>();
|
||||
for (int i = 0; i < pages.size; i++) {
|
||||
PixmapPacker.Page p = pages.get(i);
|
||||
Texture texture = new Texture(new PixmapTextureData(p.getPixmap(), p.getPixmap().getFormat(), false, false)) {
|
||||
@Override
|
||||
public void dispose() {
|
||||
super.dispose();
|
||||
getTextureData().consumePixmap().dispose();
|
||||
}
|
||||
};
|
||||
texture.setFilter(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest);
|
||||
textureRegions.addAll(new TextureRegion(texture));
|
||||
}
|
||||
|
||||
font = new BitmapFont(fontData, textureRegions, true);
|
||||
|
||||
//create .fnt and .png files for font
|
||||
FileHandle pixmapDir = Gdx.files.absolute(ForgeConstants.FONTS_DIR);
|
||||
if (pixmapDir != null) {
|
||||
FileHandle fontFile = pixmapDir.child(fontName + ".fnt");
|
||||
BitmapFontWriter.setOutputFormat(BitmapFontWriter.OutputFormat.Text);
|
||||
|
||||
String[] pageRefs = BitmapFontWriter.writePixmaps(packer.getPages(), pixmapDir, fontName);
|
||||
BitmapFontWriter.writeFont(font.getData(), pageRefs, fontFile, new BitmapFontWriter.FontInfo(fontName, fontSize), 1, 1);
|
||||
}
|
||||
|
||||
generator.dispose();
|
||||
packer.dispose();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static Iterable<String> getAllCJKFonts() {
|
||||
final List<String> allCJKFonts = new ArrayList<>();
|
||||
|
||||
allCJKFonts.add("None");
|
||||
final FileHandle dir = Gdx.files.absolute(ForgeConstants.FONTS_DIR);
|
||||
for (FileHandle fontFile : dir.list()) {
|
||||
String fontName = fontFile.name();
|
||||
if (!fontName.endsWith(".ttf")) { continue; }
|
||||
allCJKFonts.add(fontName.replace(".ttf", ""));
|
||||
}
|
||||
|
||||
return allCJKFonts;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,575 @@
|
||||
package forge.adventure.libgdxgui.assets;
|
||||
|
||||
import com.badlogic.gdx.files.FileHandle;
|
||||
import com.badlogic.gdx.graphics.Color;
|
||||
import com.badlogic.gdx.graphics.Pixmap;
|
||||
import com.badlogic.gdx.graphics.Texture;
|
||||
import com.badlogic.gdx.graphics.g2d.TextureRegion;
|
||||
import forge.adventure.libgdxgui.Forge;
|
||||
import forge.adventure.libgdxgui.Graphics;
|
||||
import forge.localinstance.properties.ForgeConstants;
|
||||
import forge.localinstance.skin.FSkinProp;
|
||||
import forge.util.ImageUtil;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/** Properties of various components that make up the skin.
|
||||
* This interface allows all enums to be under the same roof.
|
||||
* It also enforces a getter for coordinate locations in sprites. */
|
||||
public enum FSkinImage implements FImage {
|
||||
//Zones
|
||||
HAND (FSkinProp.IMG_ZONE_HAND, SourceFile.ICONS),
|
||||
HDHAND (FSkinProp.IMG_HDZONE_HAND, SourceFile.BUTTONS),
|
||||
|
||||
LIBRARY (FSkinProp.IMG_ZONE_LIBRARY, SourceFile.ICONS),
|
||||
HDLIBRARY (FSkinProp.IMG_HDZONE_LIBRARY, SourceFile.BUTTONS),
|
||||
|
||||
EXILE (FSkinProp.IMG_ZONE_EXILE, SourceFile.ICONS),
|
||||
HDEXILE (FSkinProp.IMG_HDZONE_EXILE, SourceFile.BUTTONS),
|
||||
|
||||
FLASHBACK (FSkinProp.IMG_ZONE_FLASHBACK, SourceFile.ICONS),
|
||||
HDFLASHBACK (FSkinProp.IMG_HDZONE_FLASHBACK, SourceFile.BUTTONS),
|
||||
|
||||
GRAVEYARD (FSkinProp.IMG_ZONE_GRAVEYARD, SourceFile.ICONS),
|
||||
HDGRAVEYARD (FSkinProp.IMG_HDZONE_GRAVEYARD, SourceFile.BUTTONS),
|
||||
|
||||
HDMANAPOOL (FSkinProp.IMG_HDZONE_MANAPOOL, SourceFile.BUTTONS),
|
||||
|
||||
POISON (FSkinProp.IMG_ZONE_POISON, SourceFile.ICONS),
|
||||
|
||||
//Mana symbols
|
||||
MANA_COLORLESS (FSkinProp.IMG_MANA_COLORLESS, SourceFile.MANAICONS),
|
||||
MANA_B (FSkinProp.IMG_MANA_B, SourceFile.MANAICONS),
|
||||
MANA_R (FSkinProp.IMG_MANA_R, SourceFile.MANAICONS),
|
||||
MANA_U (FSkinProp.IMG_MANA_U, SourceFile.MANAICONS),
|
||||
MANA_G (FSkinProp.IMG_MANA_G, SourceFile.MANAICONS),
|
||||
MANA_W (FSkinProp.IMG_MANA_W, SourceFile.MANAICONS),
|
||||
MANA_2B (FSkinProp.IMG_MANA_2B, SourceFile.MANAICONS),
|
||||
MANA_2G (FSkinProp.IMG_MANA_2G, SourceFile.MANAICONS),
|
||||
MANA_2R (FSkinProp.IMG_MANA_2R, SourceFile.MANAICONS),
|
||||
MANA_2U (FSkinProp.IMG_MANA_2U, SourceFile.MANAICONS),
|
||||
MANA_2W (FSkinProp.IMG_MANA_2W, SourceFile.MANAICONS),
|
||||
MANA_HYBRID_BG (FSkinProp.IMG_MANA_HYBRID_BG, SourceFile.MANAICONS),
|
||||
MANA_HYBRID_BR (FSkinProp.IMG_MANA_HYBRID_BR, SourceFile.MANAICONS),
|
||||
MANA_HYBRID_GU (FSkinProp.IMG_MANA_HYBRID_GU, SourceFile.MANAICONS),
|
||||
MANA_HYBRID_GW (FSkinProp.IMG_MANA_HYBRID_GW, SourceFile.MANAICONS),
|
||||
MANA_HYBRID_RG (FSkinProp.IMG_MANA_HYBRID_RG, SourceFile.MANAICONS),
|
||||
MANA_HYBRID_RW (FSkinProp.IMG_MANA_HYBRID_RW, SourceFile.MANAICONS),
|
||||
MANA_HYBRID_UB (FSkinProp.IMG_MANA_HYBRID_UB, SourceFile.MANAICONS),
|
||||
MANA_HYBRID_UR (FSkinProp.IMG_MANA_HYBRID_UR, SourceFile.MANAICONS),
|
||||
MANA_HYBRID_WB (FSkinProp.IMG_MANA_HYBRID_WB, SourceFile.MANAICONS),
|
||||
MANA_HYBRID_WU (FSkinProp.IMG_MANA_HYBRID_WU, SourceFile.MANAICONS),
|
||||
MANA_PHRYX_U (FSkinProp.IMG_MANA_PHRYX_U, SourceFile.MANAICONS),
|
||||
MANA_PHRYX_W (FSkinProp.IMG_MANA_PHRYX_W, SourceFile.MANAICONS),
|
||||
MANA_PHRYX_R (FSkinProp.IMG_MANA_PHRYX_R, SourceFile.MANAICONS),
|
||||
MANA_PHRYX_G (FSkinProp.IMG_MANA_PHRYX_G, SourceFile.MANAICONS),
|
||||
MANA_PHRYX_B (FSkinProp.IMG_MANA_PHRYX_B, SourceFile.MANAICONS),
|
||||
MANA_SNOW (FSkinProp.IMG_MANA_SNOW, SourceFile.MANAICONS),
|
||||
MANA_0 (FSkinProp.IMG_MANA_0, SourceFile.MANAICONS),
|
||||
MANA_1 (FSkinProp.IMG_MANA_1, SourceFile.MANAICONS),
|
||||
MANA_2 (FSkinProp.IMG_MANA_2, SourceFile.MANAICONS),
|
||||
MANA_3 (FSkinProp.IMG_MANA_3, SourceFile.MANAICONS),
|
||||
MANA_4 (FSkinProp.IMG_MANA_4, SourceFile.MANAICONS),
|
||||
MANA_5 (FSkinProp.IMG_MANA_5, SourceFile.MANAICONS),
|
||||
MANA_6 (FSkinProp.IMG_MANA_6, SourceFile.MANAICONS),
|
||||
MANA_7 (FSkinProp.IMG_MANA_7, SourceFile.MANAICONS),
|
||||
MANA_8 (FSkinProp.IMG_MANA_8, SourceFile.MANAICONS),
|
||||
MANA_9 (FSkinProp.IMG_MANA_9, SourceFile.MANAICONS),
|
||||
MANA_10 (FSkinProp.IMG_MANA_10, SourceFile.MANAICONS),
|
||||
MANA_11 (FSkinProp.IMG_MANA_11, SourceFile.MANAICONS),
|
||||
MANA_12 (FSkinProp.IMG_MANA_12, SourceFile.MANAICONS),
|
||||
MANA_13 (FSkinProp.IMG_MANA_13, SourceFile.MANAICONS),
|
||||
MANA_14 (FSkinProp.IMG_MANA_14, SourceFile.MANAICONS),
|
||||
MANA_15 (FSkinProp.IMG_MANA_15, SourceFile.MANAICONS),
|
||||
MANA_16 (FSkinProp.IMG_MANA_16, SourceFile.MANAICONS),
|
||||
MANA_17 (FSkinProp.IMG_MANA_17, SourceFile.MANAICONS),
|
||||
MANA_18 (FSkinProp.IMG_MANA_18, SourceFile.MANAICONS),
|
||||
MANA_19 (FSkinProp.IMG_MANA_19, SourceFile.MANAICONS),
|
||||
MANA_20 (FSkinProp.IMG_MANA_20, SourceFile.MANAICONS),
|
||||
MANA_X (FSkinProp.IMG_MANA_X, SourceFile.MANAICONS),
|
||||
MANA_Y (FSkinProp.IMG_MANA_Y, SourceFile.MANAICONS),
|
||||
MANA_Z (FSkinProp.IMG_MANA_Z, SourceFile.MANAICONS),
|
||||
|
||||
//CMC ranges
|
||||
CMC_LOW (FSkinProp.IMG_CMC_LOW, SourceFile.MANAICONS),
|
||||
CMC_LOW_MID (FSkinProp.IMG_CMC_LOW_MID, SourceFile.MANAICONS),
|
||||
CMC_MID_HIGH (FSkinProp.IMG_CMC_MID_HIGH, SourceFile.MANAICONS),
|
||||
CMC_HIGH (FSkinProp.IMG_CMC_HIGH, SourceFile.MANAICONS),
|
||||
|
||||
//Gameplay
|
||||
TAP (FSkinProp.IMG_TAP, SourceFile.MANAICONS),
|
||||
UNTAP (FSkinProp.IMG_UNTAP, SourceFile.MANAICONS),
|
||||
CHAOS (FSkinProp.IMG_CHAOS, SourceFile.ICONS),
|
||||
SLASH (FSkinProp.IMG_SLASH, SourceFile.ICONS),
|
||||
ATTACK (FSkinProp.IMG_ATTACK, SourceFile.ICONS),
|
||||
DEFEND (FSkinProp.IMG_DEFEND, SourceFile.ICONS),
|
||||
SUMMONSICK (FSkinProp.IMG_SUMMONSICK, SourceFile.ICONS),
|
||||
PHASING (FSkinProp.IMG_PHASING, SourceFile.ICONS),
|
||||
COSTRESERVED (FSkinProp.IMG_COSTRESERVED, SourceFile.ICONS),
|
||||
COUNTERS1 (FSkinProp.IMG_COUNTERS1, SourceFile.ICONS),
|
||||
COUNTERS2 (FSkinProp.IMG_COUNTERS2, SourceFile.ICONS),
|
||||
COUNTERS3 (FSkinProp.IMG_COUNTERS3, SourceFile.ICONS),
|
||||
COUNTERS_MULTI (FSkinProp.IMG_COUNTERS_MULTI, SourceFile.ICONS),
|
||||
ENERGY (FSkinProp.IMG_ENERGY, SourceFile.ICONS),
|
||||
|
||||
//Dock Icons
|
||||
SHORTCUTS (FSkinProp.ICO_SHORTCUTS, SourceFile.ICONS),
|
||||
SETTINGS (FSkinProp.ICO_SETTINGS, SourceFile.ICONS),
|
||||
ENDTURN (FSkinProp.ICO_ENDTURN, SourceFile.ICONS),
|
||||
CONCEDE (FSkinProp.ICO_CONCEDE, SourceFile.ICONS),
|
||||
REVERTLAYOUT (FSkinProp.ICO_REVERTLAYOUT, SourceFile.ICONS),
|
||||
OPENLAYOUT (FSkinProp.ICO_OPENLAYOUT, SourceFile.ICONS),
|
||||
SAVELAYOUT (FSkinProp.ICO_SAVELAYOUT, SourceFile.ICONS),
|
||||
DECKLIST (FSkinProp.ICO_DECKLIST, SourceFile.ICONS),
|
||||
ALPHASTRIKE (FSkinProp.ICO_ALPHASTRIKE, SourceFile.ICONS),
|
||||
ARCSOFF (FSkinProp.ICO_ARCSOFF, SourceFile.ICONS),
|
||||
ARCSON (FSkinProp.ICO_ARCSON, SourceFile.ICONS),
|
||||
ARCSHOVER (FSkinProp.ICO_ARCSHOVER, SourceFile.ICONS),
|
||||
|
||||
//choice-search-misc
|
||||
HDCHOICE (FSkinProp.ICO_HDCHOICE, SourceFile.BUTTONS),
|
||||
HDSIDEBOARD (FSkinProp.ICO_HDSIDEBOARD, SourceFile.BUTTONS),
|
||||
HDPREFERENCE (FSkinProp.ICO_HDPREFERENCE, SourceFile.BUTTONS),
|
||||
HDIMPORT (FSkinProp.ICO_HDIMPORT, SourceFile.BUTTONS),
|
||||
HDEXPORT (FSkinProp.ICO_HDEXPORT, SourceFile.BUTTONS),
|
||||
HDYIELD (FSkinProp.ICO_HDYIELD, SourceFile.BUTTONS),
|
||||
BLANK (FSkinProp.ICO_BLANK, SourceFile.ICONS),
|
||||
|
||||
//Achievement Trophies
|
||||
COMMON_TROPHY (FSkinProp.IMG_COMMON_TROPHY, SourceFile.TROPHIES),
|
||||
UNCOMMON_TROPHY (FSkinProp.IMG_UNCOMMON_TROPHY, SourceFile.TROPHIES),
|
||||
RARE_TROPHY (FSkinProp.IMG_RARE_TROPHY, SourceFile.TROPHIES),
|
||||
MYTHIC_TROPHY (FSkinProp.IMG_MYTHIC_TROPHY, SourceFile.TROPHIES),
|
||||
SPECIAL_TROPHY (FSkinProp.IMG_SPECIAL_TROPHY, SourceFile.TROPHIES),
|
||||
TROPHY_PLATE (FSkinProp.IMG_TROPHY_PLATE, SourceFile.TROPHIES),
|
||||
TROPHY_CASE_TOP (FSkinProp.IMG_TROPHY_CASE_TOP, SourceFile.TROPHIES),
|
||||
TROPHY_SHELF (FSkinProp.IMG_TROPHY_SHELF, SourceFile.TROPHIES),
|
||||
|
||||
//Planar Conquest Images
|
||||
PLANE_MONITOR (FSkinProp.IMG_PLANE_MONITOR, SourceFile.PLANAR_CONQUEST),
|
||||
AETHER_SHARD (FSkinProp.IMG_AETHER_SHARD, SourceFile.PLANAR_CONQUEST),
|
||||
MULTIVERSE (FSkinProp.IMG_MULTIVERSE, SourceFile.PLANAR_CONQUEST),
|
||||
SPELLBOOK (FSkinProp.IMG_SPELLBOOK, SourceFile.PLANAR_CONQUEST),
|
||||
PW_BADGE_COMMON (FSkinProp.IMG_PW_BADGE_COMMON, SourceFile.PLANAR_CONQUEST),
|
||||
PW_BADGE_UNCOMMON (FSkinProp.IMG_PW_BADGE_UNCOMMON, SourceFile.PLANAR_CONQUEST),
|
||||
PW_BADGE_RARE (FSkinProp.IMG_PW_BADGE_RARE, SourceFile.PLANAR_CONQUEST),
|
||||
PW_BADGE_MYTHIC (FSkinProp.IMG_PW_BADGE_MYTHIC, SourceFile.PLANAR_CONQUEST),
|
||||
|
||||
//Quest Icons
|
||||
QUEST_ZEP (FSkinProp.ICO_QUEST_ZEP, SourceFile.ICONS),
|
||||
QUEST_GEAR (FSkinProp.ICO_QUEST_GEAR, SourceFile.ICONS),
|
||||
QUEST_GOLD (FSkinProp.ICO_QUEST_GOLD, SourceFile.ICONS),
|
||||
QUEST_ELIXIR (FSkinProp.ICO_QUEST_ELIXIR, SourceFile.ICONS),
|
||||
QUEST_BOOK (FSkinProp.ICO_QUEST_BOOK, SourceFile.ICONS),
|
||||
QUEST_BOTTLES (FSkinProp.ICO_QUEST_BOTTLES, SourceFile.ICONS),
|
||||
QUEST_BOX (FSkinProp.ICO_QUEST_BOX, SourceFile.ICONS),
|
||||
QUEST_COIN (FSkinProp.ICO_QUEST_COIN, SourceFile.ICONS),
|
||||
QUEST_CHARM (FSkinProp.ICO_QUEST_CHARM, SourceFile.ICONS),
|
||||
QUEST_FOX (FSkinProp.ICO_QUEST_FOX, SourceFile.ICONS),
|
||||
QUEST_LEAF (FSkinProp.ICO_QUEST_LEAF, SourceFile.ICONS),
|
||||
QUEST_LIFE (FSkinProp.ICO_QUEST_LIFE, SourceFile.ICONS),
|
||||
QUEST_COINSTACK (FSkinProp.ICO_QUEST_COINSTACK, SourceFile.ICONS),
|
||||
QUEST_MAP (FSkinProp.ICO_QUEST_MAP, SourceFile.ICONS),
|
||||
QUEST_NOTES (FSkinProp.ICO_QUEST_NOTES, SourceFile.ICONS),
|
||||
QUEST_HEART (FSkinProp.ICO_QUEST_HEART, SourceFile.ICONS),
|
||||
QUEST_BREW (FSkinProp.ICO_QUEST_BREW, SourceFile.ICONS),
|
||||
QUEST_STAKES (FSkinProp.ICO_QUEST_STAKES, SourceFile.ICONS),
|
||||
QUEST_MINUS (FSkinProp.ICO_QUEST_MINUS, SourceFile.ICONS),
|
||||
QUEST_PLUS (FSkinProp.ICO_QUEST_PLUS, SourceFile.ICONS),
|
||||
QUEST_PLUSPLUS (FSkinProp.ICO_QUEST_PLUSPLUS, SourceFile.ICONS),
|
||||
QUEST_BIG_ELIXIR (FSkinProp.ICO_QUEST_BIG_ELIXIR, SourceFile.ICONS),
|
||||
QUEST_BIG_BREW (FSkinProp.ICO_QUEST_BIG_BREW, SourceFile.ICONS),
|
||||
QUEST_BIG_BM (FSkinProp.ICO_QUEST_BIG_BM, SourceFile.ICONS),
|
||||
QUEST_BIG_STAKES (FSkinProp.ICO_QUEST_BIG_STAKES, SourceFile.ICONS),
|
||||
QUEST_BIG_HOUSE (FSkinProp.ICO_QUEST_BIG_HOUSE, SourceFile.ICONS),
|
||||
QUEST_BIG_COIN (FSkinProp.ICO_QUEST_BIG_COIN, SourceFile.ICONS),
|
||||
QUEST_BIG_BOOK (FSkinProp.ICO_QUEST_BIG_BOOK, SourceFile.ICONS),
|
||||
QUEST_BIG_MAP (FSkinProp.ICO_QUEST_BIG_MAP, SourceFile.ICONS),
|
||||
QUEST_BIG_ZEP (FSkinProp.ICO_QUEST_BIG_ZEP, SourceFile.ICONS),
|
||||
QUEST_BIG_CHARM (FSkinProp.ICO_QUEST_BIG_CHARM, SourceFile.ICONS),
|
||||
QUEST_BIG_BOOTS (FSkinProp.ICO_QUEST_BIG_BOOTS, SourceFile.ICONS),
|
||||
QUEST_BIG_SHIELD (FSkinProp.ICO_QUEST_BIG_SHIELD, SourceFile.ICONS),
|
||||
QUEST_BIG_ARMOR (FSkinProp.ICO_QUEST_BIG_ARMOR, SourceFile.ICONS),
|
||||
QUEST_BIG_AXE (FSkinProp.ICO_QUEST_BIG_AXE, SourceFile.ICONS),
|
||||
QUEST_BIG_SWORD (FSkinProp.ICO_QUEST_BIG_SWORD, SourceFile.ICONS),
|
||||
QUEST_BIG_BAG (FSkinProp.ICO_QUEST_BIG_BAG, SourceFile.ICONS),
|
||||
|
||||
//menu icon
|
||||
MENU_GALAXY (FSkinProp.ICO_MENU_GALAXY, SourceFile.ICONS),
|
||||
MENU_STATS (FSkinProp.ICO_MENU_STATS, SourceFile.ICONS),
|
||||
MENU_PUZZLE (FSkinProp.ICO_MENU_PUZZLE, SourceFile.ICONS),
|
||||
MENU_GAUNTLET (FSkinProp.ICO_MENU_GAUNTLET, SourceFile.ICONS),
|
||||
MENU_SEALED (FSkinProp.ICO_MENU_SEALED, SourceFile.ICONS),
|
||||
MENU_DRAFT (FSkinProp.ICO_MENU_DRAFT, SourceFile.ICONS),
|
||||
MENU_CONSTRUCTED (FSkinProp.ICO_MENU_CONSTRUCTED, SourceFile.ICONS),
|
||||
|
||||
//Interface icons
|
||||
QUESTION (FSkinProp.ICO_QUESTION, SourceFile.ICONS),
|
||||
INFORMATION (FSkinProp.ICO_INFORMATION, SourceFile.ICONS),
|
||||
WARNING (FSkinProp.ICO_WARNING, SourceFile.ICONS),
|
||||
ERROR (FSkinProp.ICO_ERROR, SourceFile.ICONS),
|
||||
|
||||
DELETE (FSkinProp.ICO_DELETE, SourceFile.ICONS),
|
||||
HDDELETE (FSkinProp.ICO_HDDELETE, SourceFile.BUTTONS),
|
||||
|
||||
DELETE_OVER (FSkinProp.ICO_DELETE_OVER, SourceFile.ICONS),
|
||||
|
||||
EDIT (FSkinProp.ICO_EDIT, SourceFile.ICONS),
|
||||
HDEDIT (FSkinProp.ICO_HDEDIT, SourceFile.BUTTONS),
|
||||
|
||||
EDIT_OVER (FSkinProp.ICO_EDIT_OVER, SourceFile.ICONS),
|
||||
|
||||
OPEN (FSkinProp.ICO_OPEN, SourceFile.ICONS),
|
||||
HDOPEN (FSkinProp.ICO_HDOPEN, SourceFile.BUTTONS),
|
||||
|
||||
MINUS (FSkinProp.ICO_MINUS, SourceFile.ICONS),
|
||||
HDMINUS (FSkinProp.ICO_HDMINUS, SourceFile.BUTTONS),
|
||||
|
||||
NEW (FSkinProp.ICO_NEW, SourceFile.ICONS),
|
||||
|
||||
PLUS (FSkinProp.ICO_PLUS, SourceFile.ICONS),
|
||||
HDPLUS (FSkinProp.ICO_HDPLUS, SourceFile.BUTTONS),
|
||||
|
||||
PRINT (FSkinProp.ICO_PRINT, SourceFile.ICONS),
|
||||
|
||||
SAVE (FSkinProp.ICO_SAVE, SourceFile.ICONS),
|
||||
HDSAVE (FSkinProp.ICO_HDSAVE, SourceFile.BUTTONS),
|
||||
SAVEAS (FSkinProp.ICO_SAVEAS, SourceFile.ICONS),
|
||||
HDSAVEAS (FSkinProp.ICO_HDSAVEAS, SourceFile.BUTTONS),
|
||||
|
||||
CLOSE (FSkinProp.ICO_CLOSE, SourceFile.ICONS),
|
||||
LIST (FSkinProp.ICO_LIST, SourceFile.ICONS),
|
||||
CARD_IMAGE (FSkinProp.ICO_CARD_IMAGE, SourceFile.ICONS),
|
||||
|
||||
FOLDER (FSkinProp.ICO_FOLDER, SourceFile.ICONS),
|
||||
HDFOLDER (FSkinProp.ICO_HDFOLDER, SourceFile.BUTTONS),
|
||||
|
||||
SEARCH (FSkinProp.ICO_SEARCH, SourceFile.ICONS),
|
||||
HDSEARCH (FSkinProp.ICO_HDSEARCH, SourceFile.BUTTONS),
|
||||
|
||||
UNKNOWN (FSkinProp.ICO_UNKNOWN, SourceFile.ICONS),
|
||||
LOGO (FSkinProp.ICO_LOGO, SourceFile.ICONS),
|
||||
|
||||
FLIPCARD (FSkinProp.ICO_FLIPCARD, SourceFile.ICONS),
|
||||
HDFLIPCARD (FSkinProp.ICO_HDFLIPCARD, SourceFile.BUTTONS),
|
||||
|
||||
FAVICON (FSkinProp.ICO_FAVICON, SourceFile.ICONS),
|
||||
LOCK (FSkinProp.ICO_LOCK, SourceFile.ICONS),
|
||||
|
||||
//Layout images
|
||||
HANDLE (FSkinProp.IMG_HANDLE, SourceFile.ICONS),
|
||||
CUR_L (FSkinProp.IMG_CUR_L, SourceFile.ICONS),
|
||||
CUR_R (FSkinProp.IMG_CUR_R, SourceFile.ICONS),
|
||||
CUR_T (FSkinProp.IMG_CUR_T, SourceFile.ICONS),
|
||||
CUR_B (FSkinProp.IMG_CUR_B, SourceFile.ICONS),
|
||||
CUR_TAB (FSkinProp.IMG_CUR_TAB, SourceFile.ICONS),
|
||||
|
||||
//Editor images
|
||||
STAR_OUTLINE (FSkinProp.IMG_STAR_OUTLINE, SourceFile.ICONS),
|
||||
HDSTAR_OUTLINE (FSkinProp.IMG_HDSTAR_OUTLINE, SourceFile.BUTTONS),
|
||||
STAR_FILLED (FSkinProp.IMG_STAR_FILLED, SourceFile.ICONS),
|
||||
HDSTAR_FILLED (FSkinProp.IMG_HDSTAR_FILLED, SourceFile.BUTTONS),
|
||||
|
||||
ARTIFACT (FSkinProp.IMG_ARTIFACT, SourceFile.MANAICONS),
|
||||
CREATURE (FSkinProp.IMG_CREATURE, SourceFile.MANAICONS),
|
||||
ENCHANTMENT (FSkinProp.IMG_ENCHANTMENT, SourceFile.MANAICONS),
|
||||
INSTANT (FSkinProp.IMG_INSTANT, SourceFile.MANAICONS),
|
||||
LAND (FSkinProp.IMG_LAND, SourceFile.MANAICONS),
|
||||
LANDLOGO (FSkinProp.IMG_LANDLOGO, SourceFile.MANAICONS),
|
||||
MULTI (FSkinProp.IMG_MULTI, SourceFile.ICONS),
|
||||
HDMULTI (FSkinProp.IMG_HDMULTI, SourceFile.MANAICONS),
|
||||
PLANESWALKER (FSkinProp.IMG_PLANESWALKER, SourceFile.MANAICONS),
|
||||
PACK (FSkinProp.IMG_PACK, SourceFile.ICONS),
|
||||
SORCERY (FSkinProp.IMG_SORCERY, SourceFile.MANAICONS),
|
||||
COMMANDER (FSkinProp.IMG_COMMANDER, SourceFile.ICONS),
|
||||
|
||||
//Buttons
|
||||
BTN_START_UP (FSkinProp.IMG_BTN_START_UP, SourceFile.ICONS),
|
||||
BTN_START_OVER (FSkinProp.IMG_BTN_START_OVER, SourceFile.ICONS),
|
||||
BTN_START_DOWN (FSkinProp.IMG_BTN_START_DOWN, SourceFile.ICONS),
|
||||
BTN_UP_LEFT (FSkinProp.IMG_BTN_UP_LEFT, SourceFile.ICONS),
|
||||
BTN_UP_CENTER (FSkinProp.IMG_BTN_UP_CENTER, SourceFile.ICONS),
|
||||
BTN_UP_RIGHT (FSkinProp.IMG_BTN_UP_RIGHT, SourceFile.ICONS),
|
||||
BTN_OVER_LEFT (FSkinProp.IMG_BTN_OVER_LEFT, SourceFile.ICONS),
|
||||
BTN_OVER_CENTER (FSkinProp.IMG_BTN_OVER_CENTER, SourceFile.ICONS),
|
||||
BTN_OVER_RIGHT (FSkinProp.IMG_BTN_OVER_RIGHT, SourceFile.ICONS),
|
||||
BTN_DOWN_LEFT (FSkinProp.IMG_BTN_DOWN_LEFT, SourceFile.ICONS),
|
||||
BTN_DOWN_CENTER (FSkinProp.IMG_BTN_DOWN_CENTER, SourceFile.ICONS),
|
||||
BTN_DOWN_RIGHT (FSkinProp.IMG_BTN_DOWN_RIGHT, SourceFile.ICONS),
|
||||
BTN_FOCUS_LEFT (FSkinProp.IMG_BTN_FOCUS_LEFT, SourceFile.ICONS),
|
||||
BTN_FOCUS_CENTER (FSkinProp.IMG_BTN_FOCUS_CENTER, SourceFile.ICONS),
|
||||
BTN_FOCUS_RIGHT (FSkinProp.IMG_BTN_FOCUS_RIGHT, SourceFile.ICONS),
|
||||
BTN_TOGGLE_LEFT (FSkinProp.IMG_BTN_TOGGLE_LEFT, SourceFile.ICONS),
|
||||
BTN_TOGGLE_CENTER (FSkinProp.IMG_BTN_TOGGLE_CENTER, SourceFile.ICONS),
|
||||
BTN_TOGGLE_RIGHT (FSkinProp.IMG_BTN_TOGGLE_RIGHT, SourceFile.ICONS),
|
||||
BTN_DISABLED_LEFT (FSkinProp.IMG_BTN_DISABLED_LEFT, SourceFile.ICONS),
|
||||
BTN_DISABLED_CENTER (FSkinProp.IMG_BTN_DISABLED_CENTER, SourceFile.ICONS),
|
||||
BTN_DISABLED_RIGHT (FSkinProp.IMG_BTN_DISABLED_RIGHT, SourceFile.ICONS),
|
||||
//Hdbuttons
|
||||
HDBTN_START_UP (FSkinProp.IMG_HDBTN_START_UP, SourceFile.BTNSTART),
|
||||
HDBTN_START_OVER (FSkinProp.IMG_HDBTN_START_OVER, SourceFile.BTNSTART),
|
||||
HDBTN_START_DOWN (FSkinProp.IMG_HDBTN_START_DOWN, SourceFile.BTNSTART),
|
||||
HDBTN_UP_LEFT (FSkinProp.IMG_HDBTN_UP_LEFT, SourceFile.BUTTONS),
|
||||
HDBTN_UP_CENTER (FSkinProp.IMG_HDBTN_UP_CENTER, SourceFile.BUTTONS),
|
||||
HDBTN_UP_RIGHT (FSkinProp.IMG_HDBTN_UP_RIGHT, SourceFile.BUTTONS),
|
||||
HDBTN_OVER_LEFT (FSkinProp.IMG_HDBTN_OVER_LEFT, SourceFile.BUTTONS),
|
||||
HDBTN_OVER_CENTER (FSkinProp.IMG_HDBTN_OVER_CENTER, SourceFile.BUTTONS),
|
||||
HDBTN_OVER_RIGHT (FSkinProp.IMG_HDBTN_OVER_RIGHT, SourceFile.BUTTONS),
|
||||
HDBTN_DOWN_LEFT (FSkinProp.IMG_HDBTN_DOWN_LEFT, SourceFile.BUTTONS),
|
||||
HDBTN_DOWN_CENTER (FSkinProp.IMG_HDBTN_DOWN_CENTER, SourceFile.BUTTONS),
|
||||
HDBTN_DOWN_RIGHT (FSkinProp.IMG_HDBTN_DOWN_RIGHT, SourceFile.BUTTONS),
|
||||
HDBTN_FOCUS_LEFT (FSkinProp.IMG_HDBTN_FOCUS_LEFT, SourceFile.BUTTONS),
|
||||
HDBTN_FOCUS_CENTER (FSkinProp.IMG_HDBTN_FOCUS_CENTER, SourceFile.BUTTONS),
|
||||
HDBTN_FOCUS_RIGHT (FSkinProp.IMG_HDBTN_FOCUS_RIGHT, SourceFile.BUTTONS),
|
||||
HDBTN_TOGGLE_LEFT (FSkinProp.IMG_HDBTN_TOGGLE_LEFT, SourceFile.BUTTONS),
|
||||
HDBTN_TOGGLE_CENTER (FSkinProp.IMG_HDBTN_TOGGLE_CENTER, SourceFile.BUTTONS),
|
||||
HDBTN_TOGGLE_RIGHT (FSkinProp.IMG_HDBTN_TOGGLE_RIGHT, SourceFile.BUTTONS),
|
||||
HDBTN_DISABLED_LEFT (FSkinProp.IMG_HDBTN_DISABLED_LEFT, SourceFile.BUTTONS),
|
||||
HDBTN_DISABLED_CENTER (FSkinProp.IMG_HDBTN_DISABLED_CENTER, SourceFile.BUTTONS),
|
||||
HDBTN_DISABLED_RIGHT (FSkinProp.IMG_HDBTN_DISABLED_RIGHT, SourceFile.BUTTONS),
|
||||
|
||||
//Foils
|
||||
FOIL_01 (FSkinProp.FOIL_01, SourceFile.FOILS),
|
||||
FOIL_02 (FSkinProp.FOIL_02, SourceFile.FOILS),
|
||||
FOIL_03 (FSkinProp.FOIL_03, SourceFile.FOILS),
|
||||
FOIL_04 (FSkinProp.FOIL_04, SourceFile.FOILS),
|
||||
FOIL_05 (FSkinProp.FOIL_05, SourceFile.FOILS),
|
||||
FOIL_06 (FSkinProp.FOIL_06, SourceFile.FOILS),
|
||||
FOIL_07 (FSkinProp.FOIL_07, SourceFile.FOILS),
|
||||
FOIL_08 (FSkinProp.FOIL_08, SourceFile.FOILS),
|
||||
FOIL_09 (FSkinProp.FOIL_09, SourceFile.FOILS),
|
||||
FOIL_10 (FSkinProp.FOIL_10, SourceFile.FOILS),
|
||||
|
||||
//Old Foils
|
||||
FOIL_11 (FSkinProp.FOIL_11, SourceFile.OLD_FOILS),
|
||||
FOIL_12 (FSkinProp.FOIL_12, SourceFile.OLD_FOILS),
|
||||
FOIL_13 (FSkinProp.FOIL_13, SourceFile.OLD_FOILS),
|
||||
FOIL_14 (FSkinProp.FOIL_14, SourceFile.OLD_FOILS),
|
||||
FOIL_15 (FSkinProp.FOIL_15, SourceFile.OLD_FOILS),
|
||||
FOIL_16 (FSkinProp.FOIL_16, SourceFile.OLD_FOILS),
|
||||
FOIL_17 (FSkinProp.FOIL_17, SourceFile.OLD_FOILS),
|
||||
FOIL_18 (FSkinProp.FOIL_18, SourceFile.OLD_FOILS),
|
||||
FOIL_19 (FSkinProp.FOIL_19, SourceFile.OLD_FOILS),
|
||||
FOIL_20 (FSkinProp.FOIL_20, SourceFile.OLD_FOILS),
|
||||
|
||||
//COMMANDER
|
||||
IMG_ABILITY_COMMANDER (FSkinProp.IMG_ABILITY_COMMANDER, SourceFile.ABILITIES),
|
||||
//ABILITY ICONS
|
||||
IMG_ABILITY_DEATHTOUCH (FSkinProp.IMG_ABILITY_DEATHTOUCH, SourceFile.ABILITIES),
|
||||
IMG_ABILITY_DEFENDER (FSkinProp.IMG_ABILITY_DEFENDER, SourceFile.ABILITIES),
|
||||
IMG_ABILITY_DOUBLE_STRIKE (FSkinProp.IMG_ABILITY_DOUBLE_STRIKE, SourceFile.ABILITIES),
|
||||
IMG_ABILITY_FIRST_STRIKE (FSkinProp.IMG_ABILITY_FIRST_STRIKE, SourceFile.ABILITIES),
|
||||
IMG_ABILITY_FEAR (FSkinProp.IMG_ABILITY_FEAR, SourceFile.ABILITIES),
|
||||
IMG_ABILITY_FLASH (FSkinProp.IMG_ABILITY_FLASH, SourceFile.ABILITIES),
|
||||
IMG_ABILITY_FLYING (FSkinProp.IMG_ABILITY_FLYING, SourceFile.ABILITIES),
|
||||
IMG_ABILITY_HASTE (FSkinProp.IMG_ABILITY_HASTE, SourceFile.ABILITIES),
|
||||
IMG_ABILITY_HEXPROOF (FSkinProp.IMG_ABILITY_HEXPROOF, SourceFile.ABILITIES),
|
||||
IMG_ABILITY_HORSEMANSHIP (FSkinProp.IMG_ABILITY_HORSEMANSHIP, SourceFile.ABILITIES),
|
||||
IMG_ABILITY_INDESTRUCTIBLE (FSkinProp.IMG_ABILITY_INDESTRUCTIBLE, SourceFile.ABILITIES),
|
||||
IMG_ABILITY_INTIMIDATE (FSkinProp.IMG_ABILITY_INTIMIDATE, SourceFile.ABILITIES),
|
||||
IMG_ABILITY_LANDWALK (FSkinProp.IMG_ABILITY_LANDWALK, SourceFile.ABILITIES),
|
||||
IMG_ABILITY_LIFELINK (FSkinProp.IMG_ABILITY_LIFELINK, SourceFile.ABILITIES),
|
||||
IMG_ABILITY_MENACE (FSkinProp.IMG_ABILITY_MENACE, SourceFile.ABILITIES),
|
||||
IMG_ABILITY_REACH (FSkinProp.IMG_ABILITY_REACH, SourceFile.ABILITIES),
|
||||
IMG_ABILITY_SHADOW (FSkinProp.IMG_ABILITY_SHADOW, SourceFile.ABILITIES),
|
||||
IMG_ABILITY_SHROUD (FSkinProp.IMG_ABILITY_SHROUD, SourceFile.ABILITIES),
|
||||
IMG_ABILITY_TRAMPLE (FSkinProp.IMG_ABILITY_TRAMPLE, SourceFile.ABILITIES),
|
||||
IMG_ABILITY_VIGILANCE (FSkinProp.IMG_ABILITY_VIGILANCE, SourceFile.ABILITIES),
|
||||
//HEXPROOF FROM
|
||||
IMG_ABILITY_HEXPROOF_R (FSkinProp.IMG_ABILITY_HEXPROOF_R, SourceFile.ABILITIES),
|
||||
IMG_ABILITY_HEXPROOF_G (FSkinProp.IMG_ABILITY_HEXPROOF_G, SourceFile.ABILITIES),
|
||||
IMG_ABILITY_HEXPROOF_B (FSkinProp.IMG_ABILITY_HEXPROOF_B, SourceFile.ABILITIES),
|
||||
IMG_ABILITY_HEXPROOF_U (FSkinProp.IMG_ABILITY_HEXPROOF_U, SourceFile.ABILITIES),
|
||||
IMG_ABILITY_HEXPROOF_W (FSkinProp.IMG_ABILITY_HEXPROOF_W, SourceFile.ABILITIES),
|
||||
IMG_ABILITY_HEXPROOF_C (FSkinProp.IMG_ABILITY_HEXPROOF_C, SourceFile.ABILITIES),
|
||||
IMG_ABILITY_HEXPROOF_UB (FSkinProp.IMG_ABILITY_HEXPROOF_UB, SourceFile.ABILITIES),
|
||||
//token icon
|
||||
IMG_ABILITY_TOKEN (FSkinProp.IMG_ABILITY_TOKEN, SourceFile.ABILITIES),
|
||||
//border
|
||||
IMG_BORDER_BLACK (FSkinProp.IMG_BORDER_BLACK, SourceFile.BORDERS),
|
||||
IMG_BORDER_WHITE (FSkinProp.IMG_BORDER_WHITE, SourceFile.BORDERS),
|
||||
//PROTECT ICONS
|
||||
IMG_ABILITY_PROTECT_ALL (FSkinProp.IMG_ABILITY_PROTECT_ALL, SourceFile.ABILITIES),
|
||||
IMG_ABILITY_PROTECT_B (FSkinProp.IMG_ABILITY_PROTECT_B, SourceFile.ABILITIES),
|
||||
IMG_ABILITY_PROTECT_BU (FSkinProp.IMG_ABILITY_PROTECT_BU, SourceFile.ABILITIES),
|
||||
IMG_ABILITY_PROTECT_BW (FSkinProp.IMG_ABILITY_PROTECT_BW, SourceFile.ABILITIES),
|
||||
IMG_ABILITY_PROTECT_COLOREDSPELLS (FSkinProp.IMG_ABILITY_PROTECT_COLOREDSPELLS, SourceFile.ABILITIES),
|
||||
IMG_ABILITY_PROTECT_G (FSkinProp.IMG_ABILITY_PROTECT_G, SourceFile.ABILITIES),
|
||||
IMG_ABILITY_PROTECT_GB (FSkinProp.IMG_ABILITY_PROTECT_GB, SourceFile.ABILITIES),
|
||||
IMG_ABILITY_PROTECT_GU (FSkinProp.IMG_ABILITY_PROTECT_GU, SourceFile.ABILITIES),
|
||||
IMG_ABILITY_PROTECT_GW (FSkinProp.IMG_ABILITY_PROTECT_GW, SourceFile.ABILITIES),
|
||||
IMG_ABILITY_PROTECT_GENERIC (FSkinProp.IMG_ABILITY_PROTECT_GENERIC, SourceFile.ABILITIES),
|
||||
IMG_ABILITY_PROTECT_R (FSkinProp.IMG_ABILITY_PROTECT_R, SourceFile.ABILITIES),
|
||||
IMG_ABILITY_PROTECT_RB (FSkinProp.IMG_ABILITY_PROTECT_RB, SourceFile.ABILITIES),
|
||||
IMG_ABILITY_PROTECT_RG (FSkinProp.IMG_ABILITY_PROTECT_RG, SourceFile.ABILITIES),
|
||||
IMG_ABILITY_PROTECT_RU (FSkinProp.IMG_ABILITY_PROTECT_RU, SourceFile.ABILITIES),
|
||||
IMG_ABILITY_PROTECT_RW (FSkinProp.IMG_ABILITY_PROTECT_RW, SourceFile.ABILITIES),
|
||||
IMG_ABILITY_PROTECT_U (FSkinProp.IMG_ABILITY_PROTECT_U, SourceFile.ABILITIES),
|
||||
IMG_ABILITY_PROTECT_UW (FSkinProp.IMG_ABILITY_PROTECT_UW, SourceFile.ABILITIES),
|
||||
IMG_ABILITY_PROTECT_W (FSkinProp.IMG_ABILITY_PROTECT_W, SourceFile.ABILITIES);
|
||||
|
||||
public enum SourceFile {
|
||||
ICONS(ForgeConstants.SPRITE_ICONS_FILE),
|
||||
FOILS(ForgeConstants.SPRITE_FOILS_FILE),
|
||||
OLD_FOILS(ForgeConstants.SPRITE_OLD_FOILS_FILE),
|
||||
TROPHIES(ForgeConstants.SPRITE_TROPHIES_FILE),
|
||||
ABILITIES(ForgeConstants.SPRITE_ABILITY_FILE),
|
||||
BORDERS(ForgeConstants.SPRITE_BORDER_FILE),
|
||||
BUTTONS(ForgeConstants.SPRITE_BUTTONS_FILE),
|
||||
BTNSTART(ForgeConstants.SPRITE_START_FILE),
|
||||
MANAICONS(ForgeConstants.SPRITE_MANAICONS_FILE),
|
||||
PLANAR_CONQUEST(ForgeConstants.SPRITE_PLANAR_CONQUEST_FILE);
|
||||
|
||||
private final String filename;
|
||||
|
||||
SourceFile(String filename0) {
|
||||
filename = filename0;
|
||||
}
|
||||
|
||||
public String getFilename() {
|
||||
return filename;
|
||||
}
|
||||
}
|
||||
|
||||
private final int x, y, w, h;
|
||||
private final SourceFile sourceFile;
|
||||
private TextureRegion textureRegion;
|
||||
|
||||
FSkinImage(FSkinProp skinProp, SourceFile sourceFile0) {
|
||||
int[] coords = skinProp.getCoords();
|
||||
x = coords[0];
|
||||
y = coords[1];
|
||||
w = coords[2];
|
||||
h = coords[3];
|
||||
sourceFile = sourceFile0;
|
||||
FSkin.getImages().put(skinProp, this);
|
||||
}
|
||||
|
||||
public void load(Map<String, Texture> textures, Pixmap preferredIcons) {
|
||||
String filename = sourceFile.getFilename();
|
||||
FileHandle preferredFile = FSkin.getSkinFile(filename);
|
||||
Texture texture = textures.get(preferredFile.path());
|
||||
if (texture == null) {
|
||||
if (preferredFile.exists()) {
|
||||
try {
|
||||
if (Forge.isTextureFilteringEnabled()){
|
||||
texture = new Texture(preferredFile, true);
|
||||
texture.setFilter(Texture.TextureFilter.MipMapLinearLinear, Texture.TextureFilter.Linear);
|
||||
} else {
|
||||
texture = new Texture(preferredFile);
|
||||
}
|
||||
}
|
||||
catch (final Exception e) {
|
||||
System.err.println("Failed to load skin file: " + preferredFile);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (texture != null) {
|
||||
if (!(sourceFile == SourceFile.ICONS || sourceFile == SourceFile.MANAICONS)) { //just return region for preferred file if not icons file
|
||||
textureRegion = new TextureRegion(texture, x, y, w, h);
|
||||
return;
|
||||
}
|
||||
|
||||
int fullWidth = texture.getWidth();
|
||||
int fullHeight = texture.getHeight();
|
||||
|
||||
// Test if requested sub-image in inside bounds of preferred sprite.
|
||||
// (Height and width of preferred sprite were set in loadFontAndImages.)
|
||||
if (x + w <= fullWidth && y + h <= fullHeight) {
|
||||
// Test if various points of requested sub-image are transparent.
|
||||
// If any return true, image exists.
|
||||
int x0 = 0, y0 = 0;
|
||||
Color c;
|
||||
|
||||
// Center
|
||||
x0 = (x + w / 2);
|
||||
y0 = (y + h / 2);
|
||||
c = new Color(preferredIcons.getPixel(x0, y0));
|
||||
if (c.a != 0) {
|
||||
textureRegion = new TextureRegion(texture, x, y, w, h);
|
||||
return;
|
||||
}
|
||||
|
||||
x0 += 2;
|
||||
y0 += 2;
|
||||
c = new Color(preferredIcons.getPixel(x0, y0));
|
||||
if (c.a != 0) {
|
||||
textureRegion = new TextureRegion(texture, x, y, w, h);
|
||||
return;
|
||||
}
|
||||
|
||||
x0 -= 4;
|
||||
c = new Color(preferredIcons.getPixel(x0, y0));
|
||||
if (c.a != 0) {
|
||||
textureRegion = new TextureRegion(texture, x, y, w, h);
|
||||
return;
|
||||
}
|
||||
|
||||
y0 -= 4;
|
||||
c = new Color(preferredIcons.getPixel(x0, y0));
|
||||
if (c.a != 0) {
|
||||
textureRegion = new TextureRegion(texture, x, y, w, h);
|
||||
return;
|
||||
}
|
||||
|
||||
x0 += 4;
|
||||
c = new Color(preferredIcons.getPixel(x0, y0));
|
||||
if (c.a != 0) {
|
||||
textureRegion = new TextureRegion(texture, x, y, w, h);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//use default file if can't use preferred file
|
||||
FileHandle defaultFile = FSkin.getDefaultSkinFile(filename);
|
||||
texture = textures.get(defaultFile.path());
|
||||
if (texture == null) {
|
||||
if (defaultFile.exists()) {
|
||||
try {
|
||||
if (Forge.isTextureFilteringEnabled()){
|
||||
texture = new Texture(defaultFile, true);
|
||||
texture.setFilter(Texture.TextureFilter.MipMapLinearLinear, Texture.TextureFilter.Linear);
|
||||
} else {
|
||||
texture = new Texture(defaultFile);
|
||||
}
|
||||
}
|
||||
catch (final Exception e) {
|
||||
System.err.println("Failed to load skin file: " + defaultFile);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (texture != null) {
|
||||
textureRegion = new TextureRegion(texture, x, y, w, h);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getWidth() {
|
||||
return w;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getHeight() {
|
||||
return h;
|
||||
}
|
||||
|
||||
public TextureRegion getTextureRegion() {
|
||||
return textureRegion;
|
||||
}
|
||||
|
||||
public float getNearestHQWidth(float baseWidth) {
|
||||
return ImageUtil.getNearestHQSize(baseWidth, w);
|
||||
}
|
||||
|
||||
public float getNearestHQHeight(float baseHeight) {
|
||||
return ImageUtil.getNearestHQSize(baseHeight, h);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Graphics g, float x, float y, float w, float h) {
|
||||
g.drawImage(textureRegion, x, y, w, h);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
package forge.adventure.libgdxgui.assets;
|
||||
|
||||
import com.badlogic.gdx.files.FileHandle;
|
||||
import com.badlogic.gdx.graphics.Texture;
|
||||
import com.badlogic.gdx.graphics.Texture.TextureWrap;
|
||||
import forge.adventure.libgdxgui.Graphics;
|
||||
import forge.localinstance.properties.ForgeConstants;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public enum FSkinTexture implements FImage {
|
||||
BG_TEXTURE(ForgeConstants.TEXTURE_BG_FILE, true, false),
|
||||
BG_MATCH(ForgeConstants.MATCH_BG_FILE, false, false),
|
||||
BG_SPACE(ForgeConstants.SPACE_BG_FILE, false, false),
|
||||
BG_CHAOS_WHEEL(ForgeConstants.CHAOS_WHEEL_IMG_FILE, false, false),
|
||||
Academy_at_Tolaria_West(ForgeConstants.BG_1, false, true),
|
||||
Agyrem(ForgeConstants.BG_2, false, true),
|
||||
Akoum(ForgeConstants.BG_3, false, true),
|
||||
Aretopolis(ForgeConstants.BG_4, false, true),
|
||||
Astral_Arena(ForgeConstants.BG_5, false, true),
|
||||
Bant(ForgeConstants.BG_6, false, true),
|
||||
Bloodhill_Bastion(ForgeConstants.BG_7, false, true),
|
||||
Cliffside_Market(ForgeConstants.BG_8, false, true),
|
||||
Edge_of_Malacol(ForgeConstants.BG_9, false, true),
|
||||
Eloren_Wilds(ForgeConstants.BG_10, false, true),
|
||||
Feeding_Grounds(ForgeConstants.BG_11, false, true),
|
||||
Fields_of_Summer(ForgeConstants.BG_12, false, true),
|
||||
Furnace_Layer(ForgeConstants.BG_13, false, true),
|
||||
Gavony(ForgeConstants.BG_14, false, true),
|
||||
Glen_Elendra(ForgeConstants.BG_15, false, true),
|
||||
Glimmervoid_Basin(ForgeConstants.BG_16, false, true),
|
||||
Goldmeadow(ForgeConstants.BG_17, false, true),
|
||||
Grand_Ossuary(ForgeConstants.BG_18, false, true),
|
||||
Grixis(ForgeConstants.BG_19, false, true),
|
||||
Grove_of_the_Dreampods(ForgeConstants.BG_20, false, true),
|
||||
Hedron_Fields_of_Agadeem(ForgeConstants.BG_21, false, true),
|
||||
Immersturm(ForgeConstants.BG_22, false, true),
|
||||
Isle_of_Vesuva(ForgeConstants.BG_23, false, true),
|
||||
Izzet_Steam_Maze(ForgeConstants.BG_24, false, true),
|
||||
Jund(ForgeConstants.BG_25, false, true),
|
||||
Kessig(ForgeConstants.BG_26, false, true),
|
||||
Kharasha_Foothills(ForgeConstants.BG_27, false, true),
|
||||
Kilnspire_District(ForgeConstants.BG_28, false, true),
|
||||
Krosa(ForgeConstants.BG_29, false, true),
|
||||
Lair_of_the_Ashen_Idol(ForgeConstants.BG_30, false, true),
|
||||
Lethe_Lake(ForgeConstants.BG_31, false, true),
|
||||
Llanowar(ForgeConstants.BG_32, false, true),
|
||||
Minamo(ForgeConstants.BG_33, false, true),
|
||||
Mount_Keralia(ForgeConstants.BG_34, false, true),
|
||||
Murasa(ForgeConstants.BG_35, false, true),
|
||||
Naar_Isle(ForgeConstants.BG_36, false, true),
|
||||
Naya(ForgeConstants.BG_37, false, true),
|
||||
Nephalia(ForgeConstants.BG_38, false, true),
|
||||
Norns_Dominion(ForgeConstants.BG_39, false, true),
|
||||
Onakke_Catacomb(ForgeConstants.BG_40, false, true),
|
||||
Orochi_Colony(ForgeConstants.BG_41, false, true),
|
||||
Orzhova(ForgeConstants.BG_42, false, true),
|
||||
Otaria(ForgeConstants.BG_43, false, true),
|
||||
Panopticon(ForgeConstants.BG_44, false, true),
|
||||
Pools_of_Becoming(ForgeConstants.BG_45, false, true),
|
||||
Prahv(ForgeConstants.BG_46, false, true),
|
||||
Quicksilver_Sea(ForgeConstants.BG_47, false, true),
|
||||
Ravens_Run(ForgeConstants.BG_48, false, true),
|
||||
Sanctum_of_Serra(ForgeConstants.BG_49, false, true),
|
||||
Sea_of_Sand(ForgeConstants.BG_50, false, true),
|
||||
Selesnya_Loft_Gardens(ForgeConstants.BG_51, false, true),
|
||||
Shiv(ForgeConstants.BG_52, false, true),
|
||||
Skybreen(ForgeConstants.BG_53, false, true),
|
||||
Sokenzan(ForgeConstants.BG_54, false, true),
|
||||
Stairs_to_Infinity(ForgeConstants.BG_55, false, true),
|
||||
Stensia(ForgeConstants.BG_56, false, true),
|
||||
Stronghold_Furnace(ForgeConstants.BG_57, false, true),
|
||||
Takenuma(ForgeConstants.BG_58, false, true),
|
||||
Tazeem(ForgeConstants.BG_59, false, true),
|
||||
The_Aether_Flues(ForgeConstants.BG_60, false, true),
|
||||
The_Dark_Barony(ForgeConstants.BG_61, false, true),
|
||||
The_Eon_Fog(ForgeConstants.BG_62, false, true),
|
||||
The_Fourth_Sphere(ForgeConstants.BG_63, false, true),
|
||||
The_Great_Forest(ForgeConstants.BG_64, false, true),
|
||||
The_Hippodrome(ForgeConstants.BG_65, false, true),
|
||||
The_Maelstrom(ForgeConstants.BG_66, false, true),
|
||||
The_Zephyr_Maze(ForgeConstants.BG_67, false, true),
|
||||
Trail_of_the_MageRings(ForgeConstants.BG_68, false, true),
|
||||
Truga_Jungle(ForgeConstants.BG_69, false, true),
|
||||
Turri_Island(ForgeConstants.BG_70, false, true),
|
||||
Undercity_Reaches(ForgeConstants.BG_71, false, true),
|
||||
Velis_Vel(ForgeConstants.BG_72, false, true),
|
||||
Windriddle_Palaces(ForgeConstants.BG_73, false, true),
|
||||
Tember_City(ForgeConstants.BG_74, false, true),
|
||||
Celestine_Reef(ForgeConstants.BG_75, false, true),
|
||||
Horizon_Boughs(ForgeConstants.BG_76, false, true),
|
||||
Mirrored_Depths(ForgeConstants.BG_77, false, true),
|
||||
Talon_Gates(ForgeConstants.BG_78, false, true);
|
||||
|
||||
private final String filename;
|
||||
private final boolean repeat;
|
||||
private Texture texture;
|
||||
private final boolean isPlane;
|
||||
private static final List<String> PlanesValue;
|
||||
|
||||
FSkinTexture(String filename0, boolean repeat0, boolean isPlane0) {
|
||||
filename = filename0;
|
||||
repeat = repeat0;
|
||||
isPlane = isPlane0;
|
||||
}
|
||||
|
||||
static {
|
||||
PlanesValue = new ArrayList<>();
|
||||
for (FSkinTexture PlanesEnum : FSkinTexture.values()) {
|
||||
PlanesValue.add(PlanesEnum.filename
|
||||
.replace(".jpg", "")
|
||||
.replace("'", "")
|
||||
.replace("-", ""));
|
||||
}
|
||||
}
|
||||
|
||||
public static List<String> getValues() {
|
||||
return Collections.unmodifiableList(PlanesValue);
|
||||
}
|
||||
|
||||
public void load() {
|
||||
FileHandle preferredFile = isPlane ? FSkin.getCachePlanechaseFile(filename) : FSkin.getSkinFile(filename);
|
||||
if (preferredFile.exists()) {
|
||||
try {
|
||||
texture = new Texture(preferredFile);
|
||||
}
|
||||
catch (final Exception e) {
|
||||
System.err.println("Failed to load skin file: " + preferredFile);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
if (texture == null) {
|
||||
//use default file if can't use preferred file
|
||||
FileHandle defaultFile = FSkin.getDefaultSkinFile(filename);
|
||||
if(isPlane) {
|
||||
defaultFile = FSkin.getSkinFile(ForgeConstants.MATCH_BG_FILE);
|
||||
if(!defaultFile.exists())
|
||||
defaultFile = FSkin.getDefaultSkinFile(ForgeConstants.MATCH_BG_FILE);
|
||||
}
|
||||
|
||||
if (defaultFile.exists()) {
|
||||
try {
|
||||
texture = new Texture(defaultFile);
|
||||
}
|
||||
catch (final Exception e) {
|
||||
System.err.println("Failed to load skin file: " + defaultFile);
|
||||
e.printStackTrace();
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
System.err.println("Failed to load skin file: " + defaultFile);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (repeat) {
|
||||
texture.setWrap(TextureWrap.Repeat, TextureWrap.Repeat);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getWidth() {
|
||||
return texture.getWidth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getHeight() {
|
||||
return texture.getHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Graphics g, float x, float y, float w, float h) {
|
||||
if (repeat) {
|
||||
g.drawRepeatingImage(texture, x, y, w, h);
|
||||
}
|
||||
else {
|
||||
g.drawImage(texture, x, y, w, h);
|
||||
}
|
||||
}
|
||||
|
||||
public void drawRotated(Graphics g, float x, float y, float w, float h, float rotation) {
|
||||
g.drawRotatedImage(texture, x, y, w, h, x + w / 2, y + h / 2, rotation);
|
||||
}
|
||||
|
||||
public void drawFlipped(Graphics g, float x, float y, float w, float h) {
|
||||
g.drawFlippedImage(texture, x, y, w, h);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package forge.adventure.libgdxgui.assets;
|
||||
|
||||
import com.badlogic.gdx.graphics.Texture;
|
||||
import forge.adventure.libgdxgui.Graphics;
|
||||
|
||||
public class FTextureImage extends FImageComplex {
|
||||
private final Texture texture;
|
||||
|
||||
public FTextureImage(Texture texture0) {
|
||||
texture = texture0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getWidth() {
|
||||
return texture.getWidth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getHeight() {
|
||||
return texture.getHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Texture getTexture() {
|
||||
return texture;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRegionX() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRegionY() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Graphics g, float x, float y, float w, float h) {
|
||||
g.drawImage(texture, x, y, w, h);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package forge.adventure.libgdxgui.assets;
|
||||
|
||||
import com.badlogic.gdx.graphics.Texture;
|
||||
import com.badlogic.gdx.graphics.g2d.TextureRegion;
|
||||
import forge.adventure.libgdxgui.Graphics;
|
||||
|
||||
public class FTextureRegionImage extends FImageComplex {
|
||||
private final TextureRegion textureRegion;
|
||||
|
||||
public FTextureRegionImage(TextureRegion textureRegion0) {
|
||||
textureRegion = textureRegion0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getWidth() {
|
||||
return textureRegion.getRegionWidth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getHeight() {
|
||||
return textureRegion.getRegionHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Texture getTexture() {
|
||||
return textureRegion.getTexture();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRegionX() {
|
||||
return textureRegion.getRegionX();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRegionY() {
|
||||
return textureRegion.getRegionY();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Graphics g, float x, float y, float w, float h) {
|
||||
g.drawImage(textureRegion, x, y, w, h);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,337 @@
|
||||
/*
|
||||
* Forge: Play Magic: the Gathering.
|
||||
* Copyright (C) 2011 Forge Team
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package forge.adventure.libgdxgui.assets;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.graphics.Color;
|
||||
import com.badlogic.gdx.graphics.Pixmap.Format;
|
||||
import com.badlogic.gdx.graphics.Texture;
|
||||
import com.badlogic.gdx.graphics.g2d.TextureRegion;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.cache.RemovalListener;
|
||||
import com.google.common.cache.RemovalNotification;
|
||||
import forge.adventure.libgdxgui.Forge;
|
||||
import forge.ImageKeys;
|
||||
import forge.adventure.libgdxgui.card.CardRenderer;
|
||||
import forge.card.CardEdition;
|
||||
import forge.deck.Deck;
|
||||
import forge.game.card.CardView;
|
||||
import forge.game.player.IHasIcon;
|
||||
import forge.item.InventoryItem;
|
||||
import forge.item.PaperCard;
|
||||
import forge.localinstance.properties.ForgeConstants;
|
||||
import forge.localinstance.properties.ForgePreferences;
|
||||
import forge.model.FModel;
|
||||
import forge.util.ImageUtil;
|
||||
import forge.util.TextUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* This class stores ALL card images in a cache with soft values. this means
|
||||
* that the images may be collected when they are not needed any more, but will
|
||||
* be kept as long as possible.
|
||||
* <p/>
|
||||
* The keys are the following:
|
||||
* <ul>
|
||||
* <li>Keys start with the file name, extension is skipped</li>
|
||||
* <li>The key without suffix belongs to the unmodified image from the file</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Forge
|
||||
* @version $Id: ImageCache.java 24769 2014-02-09 13:56:04Z Hellfish $
|
||||
*/
|
||||
public class ImageCache {
|
||||
// short prefixes to save memory
|
||||
|
||||
private static final Set<String> missingIconKeys = new HashSet<>();
|
||||
private static LoadingCache<String, Texture> cache;
|
||||
public static void initCache(int capacity) {
|
||||
cache = CacheBuilder.newBuilder()
|
||||
.maximumSize(capacity)
|
||||
.expireAfterAccess(15, TimeUnit.MINUTES)
|
||||
.removalListener(new RemovalListener<String, Texture>() {
|
||||
@Override
|
||||
public void onRemoval(RemovalNotification<String, Texture> removalNotification) {
|
||||
if (removalNotification.wasEvicted()) {
|
||||
if (removalNotification.getValue() != ImageCache.defaultImage)
|
||||
removalNotification.getValue().dispose();
|
||||
|
||||
CardRenderer.clearcardArtCache();
|
||||
}
|
||||
}
|
||||
})
|
||||
.build(new ImageLoader());
|
||||
System.out.println("Card Texture Cache Size: "+capacity);
|
||||
}
|
||||
private static final LoadingCache<String, Texture> otherCache = CacheBuilder.newBuilder().build(new OtherImageLoader());
|
||||
public static final Texture defaultImage;
|
||||
public static FImage BlackBorder = FSkinImage.IMG_BORDER_BLACK;
|
||||
public static FImage WhiteBorder = FSkinImage.IMG_BORDER_WHITE;
|
||||
private static final Map<String, Pair<String, Boolean>> imageBorder = new HashMap<>(1024);
|
||||
|
||||
private static boolean imageLoaded, delayLoadRequested;
|
||||
public static void allowSingleLoad() {
|
||||
imageLoaded = false; //reset at the beginning of each render
|
||||
delayLoadRequested = false;
|
||||
}
|
||||
|
||||
static {
|
||||
Texture defImage = null;
|
||||
try {
|
||||
defImage = new Texture(Gdx.files.absolute(ForgeConstants.NO_CARD_FILE));
|
||||
} catch (Exception ex) {
|
||||
System.err.println("could not load default card image");
|
||||
} finally {
|
||||
defaultImage = (null == defImage) ? new Texture(10, 10, Format.RGBA8888) : defImage;
|
||||
}
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
cache.invalidateAll();
|
||||
cache.cleanUp();
|
||||
missingIconKeys.clear();
|
||||
}
|
||||
|
||||
public static void disposeTexture(){
|
||||
CardRenderer.clearcardArtCache();
|
||||
clear();
|
||||
}
|
||||
|
||||
public static Texture getImage(InventoryItem ii) {
|
||||
String imageKey = ii.getImageKey(false);
|
||||
if (imageKey != null) {
|
||||
if(imageKey.startsWith(ImageKeys.CARD_PREFIX) || imageKey.startsWith(ImageKeys.TOKEN_PREFIX))
|
||||
return getImage(ii.getImageKey(false), true, false);
|
||||
}
|
||||
return getImage(ii.getImageKey(false), true, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* retrieve an icon from the cache. returns the current skin's ICO_UNKNOWN if the icon image is not found
|
||||
* in the cache and cannot be loaded from disk.
|
||||
*/
|
||||
public static FImage getIcon(IHasIcon ihi) {
|
||||
String imageKey = ihi.getIconImageKey();
|
||||
final Texture icon;
|
||||
if (missingIconKeys.contains(imageKey) || (icon = getImage(ihi.getIconImageKey(), false, true)) == null) {
|
||||
missingIconKeys.add(imageKey);
|
||||
return FSkinImage.UNKNOWN;
|
||||
}
|
||||
return new FTextureImage(icon);
|
||||
}
|
||||
|
||||
/**
|
||||
* checks the card image exists from the disk.
|
||||
*/
|
||||
public static boolean imageKeyFileExists(String imageKey) {
|
||||
if (StringUtils.isEmpty(imageKey))
|
||||
return false;
|
||||
|
||||
if (imageKey.length() < 2)
|
||||
return false;
|
||||
|
||||
final String prefix = imageKey.substring(0, 2);
|
||||
|
||||
if (prefix.equals(ImageKeys.CARD_PREFIX)) {
|
||||
PaperCard paperCard = ImageUtil.getPaperCardFromImageKey(imageKey);
|
||||
if (paperCard == null)
|
||||
return false;
|
||||
|
||||
final boolean backFace = imageKey.endsWith(ImageKeys.BACKFACE_POSTFIX);
|
||||
final String cardfilename = ImageUtil.getImageKey(paperCard, backFace, true);
|
||||
if (!new File(ForgeConstants.CACHE_CARD_PICS_DIR + "/" + cardfilename + ".jpg").exists())
|
||||
if (!new File(ForgeConstants.CACHE_CARD_PICS_DIR + "/" + cardfilename + ".png").exists())
|
||||
return new File(ForgeConstants.CACHE_CARD_PICS_DIR + "/" + TextUtil.fastReplace(cardfilename, ".full", ".fullborder") + ".jpg").exists();
|
||||
} else if (prefix.equals(ImageKeys.TOKEN_PREFIX)) {
|
||||
final String tokenfilename = imageKey.substring(2) + ".jpg";
|
||||
|
||||
return new File(ForgeConstants.CACHE_TOKEN_PICS_DIR, tokenfilename).exists();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This requests the original unscaled image from the cache for the given key.
|
||||
* If the image does not exist then it can return a default image if desired.
|
||||
* <p>
|
||||
* If the requested image is not present in the cache then it attempts to load
|
||||
* the image from file (slower) and then add it to the cache for fast future access.
|
||||
* </p>
|
||||
*/
|
||||
public static Texture getImage(String imageKey, boolean useDefaultIfNotFound) {
|
||||
return getImage(imageKey, useDefaultIfNotFound, false);
|
||||
}
|
||||
public static Texture getImage(String imageKey, boolean useDefaultIfNotFound, boolean useOtherCache) {
|
||||
if (StringUtils.isEmpty(imageKey)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
boolean altState = imageKey.endsWith(ImageKeys.BACKFACE_POSTFIX);
|
||||
if (altState) {
|
||||
imageKey = imageKey.substring(0, imageKey.length() - ImageKeys.BACKFACE_POSTFIX.length());
|
||||
}
|
||||
if (imageKey.startsWith(ImageKeys.CARD_PREFIX)) {
|
||||
imageKey = ImageUtil.getImageKey(ImageUtil.getPaperCardFromImageKey(imageKey), altState, true);
|
||||
if (StringUtils.isBlank(imageKey)) {
|
||||
return defaultImage;
|
||||
}
|
||||
}
|
||||
|
||||
Texture image;
|
||||
if (useDefaultIfNotFound) {
|
||||
// Load from file and add to cache if not found in cache initially.
|
||||
image = useOtherCache ? otherCache.getIfPresent(imageKey) : cache.getIfPresent(imageKey);
|
||||
|
||||
if (image != null) { return image; }
|
||||
|
||||
if (imageLoaded) { //prevent loading more than one image each render for performance
|
||||
if (!delayLoadRequested) {
|
||||
//ensure images continue to load even if no input is being received
|
||||
delayLoadRequested = true;
|
||||
Gdx.graphics.requestRendering();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
imageLoaded = true;
|
||||
}
|
||||
|
||||
try { image = useOtherCache ? otherCache.get(imageKey) : cache.get(imageKey); }
|
||||
catch (final Exception ex) {
|
||||
image = null;
|
||||
}
|
||||
|
||||
// No image file exists for the given key so optionally associate with
|
||||
// a default "not available" image and add to cache for given key.
|
||||
if (image == null) {
|
||||
if (useDefaultIfNotFound) {
|
||||
image = defaultImage;
|
||||
if (useOtherCache)
|
||||
otherCache.put(imageKey, defaultImage);
|
||||
else
|
||||
cache.put(imageKey, defaultImage);
|
||||
if (imageBorder.get(image.toString()) == null)
|
||||
imageBorder.put(image.toString(), Pair.of(Color.valueOf("#171717").toString(), false)); //black border
|
||||
}
|
||||
}
|
||||
return image;
|
||||
}
|
||||
public static void preloadCache(Iterable<String> keys) {
|
||||
if (FModel.getPreferences().getPrefBoolean(ForgePreferences.FPref.UI_DISABLE_CARD_IMAGES))
|
||||
return;
|
||||
for (String imageKey : keys){
|
||||
if(getImage(imageKey, false) == null)
|
||||
System.err.println("could not load card image:"+imageKey);
|
||||
}
|
||||
}
|
||||
public static void preloadCache(Deck deck) {
|
||||
if (FModel.getPreferences().getPrefBoolean(ForgePreferences.FPref.UI_DISABLE_CARD_IMAGES))
|
||||
return;
|
||||
if(deck == null||!Forge.enablePreloadExtendedArt)
|
||||
return;
|
||||
if (deck.getAllCardsInASinglePool().toFlatList().size() <= 100) {
|
||||
for (PaperCard p : deck.getAllCardsInASinglePool().toFlatList()) {
|
||||
if (getImage(p.getImageKey(false),false) == null)
|
||||
System.err.println("could not load card image:"+ p);
|
||||
}
|
||||
}
|
||||
}
|
||||
public static TextureRegion croppedBorderImage(Texture image) {
|
||||
if (!image.toString().contains(".fullborder."))
|
||||
return new TextureRegion(image);
|
||||
float rscale = 0.96f;
|
||||
int rw = Math.round(image.getWidth()*rscale);
|
||||
int rh = Math.round(image.getHeight()*rscale);
|
||||
int rx = Math.round((image.getWidth() - rw)/2f);
|
||||
int ry = Math.round((image.getHeight() - rh)/2f)-2;
|
||||
return new TextureRegion(image, rx, ry, rw, rh);
|
||||
}
|
||||
public static Color borderColor(Texture t) {
|
||||
if (t == null)
|
||||
return Color.valueOf("#171717");
|
||||
return Color.valueOf(imageBorder.get(t.toString()).getLeft());
|
||||
}
|
||||
public static int getFSkinBorders(CardView c) {
|
||||
if (c == null)
|
||||
return 0;
|
||||
|
||||
CardView.CardStateView state = c.getCurrentState();
|
||||
CardEdition ed = FModel.getMagicDb().getEditions().get(state.getSetCode());
|
||||
// TODO: Treatment for silver here
|
||||
if (ed != null && ed.getBorderColor() == CardEdition.BorderColor.WHITE && state.getFoilIndex() == 0)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
public static boolean isBorderlessCardArt(Texture t) {
|
||||
return ImageLoader.isBorderless(t);
|
||||
}
|
||||
public static void updateBorders(String textureString, Pair<String, Boolean> colorPair){
|
||||
imageBorder.put(textureString, colorPair);
|
||||
}
|
||||
public static FImage getBorder(String textureString) {
|
||||
if (imageBorder.get(textureString) == null)
|
||||
return BlackBorder;
|
||||
return imageBorder.get(textureString).getRight() ? WhiteBorder : BlackBorder;
|
||||
}
|
||||
public static FImage getBorderImage(String textureString, boolean canshow) {
|
||||
if (!canshow)
|
||||
return BlackBorder;
|
||||
return getBorder(textureString);
|
||||
}
|
||||
public static FImage getBorderImage(String textureString) {
|
||||
return getBorder(textureString);
|
||||
}
|
||||
public static Color getTint(CardView c, Texture t) {
|
||||
if (c == null)
|
||||
return borderColor(t);
|
||||
if (c.isFaceDown())
|
||||
return Color.valueOf("#171717");
|
||||
|
||||
CardView.CardStateView state = c.getCurrentState();
|
||||
if (state.getColors().isColorless()) { //Moonlace -> target spell or permanent becomes colorless.
|
||||
if (state.hasDevoid()) //devoid is colorless at all zones so return its corresponding border color...
|
||||
return borderColor(t);
|
||||
return Color.valueOf("#A0A6A4");
|
||||
}
|
||||
else if (state.getColors().isMonoColor()) {
|
||||
if (state.getColors().hasBlack())
|
||||
return Color.valueOf("#48494a");
|
||||
else if (state.getColors().hasBlue())
|
||||
return Color.valueOf("#62b5f8");
|
||||
else if (state.getColors().hasRed())
|
||||
return Color.valueOf("#f6532d");
|
||||
else if (state.getColors().hasGreen())
|
||||
return Color.valueOf("#66cb35");
|
||||
else if (state.getColors().hasWhite())
|
||||
return Color.valueOf("#EEEBE1");
|
||||
}
|
||||
else if (state.getColors().isMulticolor())
|
||||
return Color.valueOf("#F9E084");
|
||||
|
||||
return borderColor(t);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
package forge.adventure.libgdxgui.assets;
|
||||
|
||||
import com.badlogic.gdx.files.FileHandle;
|
||||
import com.badlogic.gdx.graphics.Color;
|
||||
import com.badlogic.gdx.graphics.Pixmap;
|
||||
import com.badlogic.gdx.graphics.Texture;
|
||||
import com.badlogic.gdx.graphics.TextureData;
|
||||
import com.badlogic.gdx.graphics.glutils.PixmapTextureData;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import forge.adventure.libgdxgui.Forge;
|
||||
import forge.ImageKeys;
|
||||
import forge.gui.FThreads;
|
||||
import forge.localinstance.properties.ForgeConstants;
|
||||
import forge.localinstance.properties.ForgePreferences;
|
||||
import forge.model.FModel;
|
||||
import forge.util.FileUtil;
|
||||
import forge.util.TextUtil;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
import static forge.adventure.libgdxgui.assets.ImageCache.croppedBorderImage;
|
||||
|
||||
final class ImageLoader extends CacheLoader<String, Texture> {
|
||||
private static final List<String> borderlessCardlistKey = FileUtil.readFile(ForgeConstants.BORDERLESS_CARD_LIST_FILE);
|
||||
|
||||
Texture n;
|
||||
@Override
|
||||
public Texture load(String key) {
|
||||
if (FModel.getPreferences().getPrefBoolean(ForgePreferences.FPref.UI_DISABLE_CARD_IMAGES))
|
||||
return null;
|
||||
|
||||
boolean extendedArt = isBorderless(key) && Forge.enableUIMask.equals("Full");
|
||||
boolean textureFilter = Forge.isTextureFilteringEnabled();
|
||||
File file = ImageKeys.getImageFile(key);
|
||||
if (file != null) {
|
||||
FileHandle fh = new FileHandle(file);
|
||||
try {
|
||||
Texture t = new Texture(fh, textureFilter);
|
||||
//update
|
||||
ImageCache.updateBorders(t.toString(), extendedArt ? Pair.of(Color.valueOf("#171717").toString(), false): isCloserToWhite(getpixelColor(t)));
|
||||
if (textureFilter)
|
||||
t.setFilter(Texture.TextureFilter.MipMapLinearLinear, Texture.TextureFilter.Linear);
|
||||
if (extendedArt)
|
||||
return generateTexture(fh, t, textureFilter);
|
||||
return t;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
Forge.log("Could not read image file " + fh.path() + "\nException:\n" + ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Texture generateTexture(FileHandle fh, Texture t, boolean textureFilter) {
|
||||
if (t == null || fh == null)
|
||||
return t;
|
||||
FThreads.invokeInEdtNowOrLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Pixmap pImage = new Pixmap(fh);
|
||||
int w = pImage.getWidth();
|
||||
int h = pImage.getHeight();
|
||||
int radius = (h - w) / 8;
|
||||
Pixmap pMask = createRoundedRectangle(w, h, radius, Color.RED);
|
||||
drawPixelstoMask(pImage, pMask);
|
||||
TextureData textureData = new PixmapTextureData(
|
||||
pMask, //pixmap to use
|
||||
Pixmap.Format.RGBA8888,
|
||||
textureFilter, //use mipmaps
|
||||
false, true);
|
||||
n = new Texture(textureData);
|
||||
if (textureFilter)
|
||||
n.setFilter(Texture.TextureFilter.MipMapLinearLinear, Texture.TextureFilter.Linear);
|
||||
pImage.dispose();
|
||||
pMask.dispose();
|
||||
}
|
||||
});
|
||||
return n;
|
||||
}
|
||||
public Pixmap createRoundedRectangle(int width, int height, int cornerRadius, Color color) {
|
||||
Pixmap pixmap = new Pixmap(width, height, Pixmap.Format.RGBA8888);
|
||||
Pixmap ret = new Pixmap(width, height, Pixmap.Format.RGBA8888);
|
||||
pixmap.setColor(color);
|
||||
//round corners
|
||||
pixmap.fillCircle(cornerRadius, cornerRadius, cornerRadius);
|
||||
pixmap.fillCircle(width - cornerRadius - 1, cornerRadius, cornerRadius);
|
||||
pixmap.fillCircle(cornerRadius, height - cornerRadius - 1, cornerRadius);
|
||||
pixmap.fillCircle(width - cornerRadius - 1, height - cornerRadius - 1, cornerRadius);
|
||||
//two rectangle parts
|
||||
pixmap.fillRectangle(cornerRadius, 0, width - cornerRadius * 2, height);
|
||||
pixmap.fillRectangle(0, cornerRadius, width, height - cornerRadius * 2);
|
||||
//draw rounded rectangle
|
||||
ret.setColor(color);
|
||||
for (int x = 0; x < width; x++) {
|
||||
for (int y = 0; y < height; y++) {
|
||||
if (pixmap.getPixel(x, y) != 0) ret.drawPixel(x, y);
|
||||
}
|
||||
}
|
||||
pixmap.dispose();
|
||||
return ret;
|
||||
}
|
||||
public void drawPixelstoMask(Pixmap pixmap, Pixmap mask){
|
||||
int pixmapWidth = mask.getWidth();
|
||||
int pixmapHeight = mask.getHeight();
|
||||
Color pixelColor = new Color();
|
||||
for (int x=0; x<pixmapWidth; x++){
|
||||
for (int y=0; y<pixmapHeight; y++){
|
||||
if (mask.getPixel(x, y) != 0) {
|
||||
Color.rgba8888ToColor(pixelColor, pixmap.getPixel(x, y));
|
||||
mask.setColor(pixelColor);
|
||||
mask.drawPixel(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isBorderless(String imagekey) {
|
||||
if(borderlessCardlistKey.isEmpty())
|
||||
return false;
|
||||
if (imagekey.length() > 7) {
|
||||
if ((!imagekey.substring(0, 7).contains("MPS_KLD"))&&(imagekey.substring(0, 4).contains("MPS_"))) //MPS_ sets except MPD_KLD
|
||||
return true;
|
||||
}
|
||||
return borderlessCardlistKey.contains(TextUtil.fastReplace(imagekey,".full",".fullborder"));
|
||||
}
|
||||
|
||||
public static boolean isBorderless(Texture t) {
|
||||
if(borderlessCardlistKey.isEmpty())
|
||||
return false;
|
||||
//generated texture/pixmap?
|
||||
if (t.toString().contains("com.badlogic.gdx.graphics.Texture@"))
|
||||
return true;
|
||||
for (String key : borderlessCardlistKey) {
|
||||
if (t.toString().contains(key))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static String getpixelColor(Texture i) {
|
||||
if (!i.getTextureData().isPrepared()) {
|
||||
i.getTextureData().prepare(); //prepare texture
|
||||
}
|
||||
//get pixmap from texture data
|
||||
Pixmap pixmap = i.getTextureData().consumePixmap();
|
||||
//get pixel color from x,y texture coordinate based on the image fullborder or not
|
||||
Color color = new Color(pixmap.getPixel(croppedBorderImage(i).getRegionX()+1, croppedBorderImage(i).getRegionY()+1));
|
||||
pixmap.dispose();
|
||||
return color.toString();
|
||||
}
|
||||
public static Pair<String, Boolean> isCloserToWhite(String c){
|
||||
if (c == null || c == "")
|
||||
return Pair.of(Color.valueOf("#171717").toString(), false);
|
||||
int c_r = Integer.parseInt(c.substring(0,2),16);
|
||||
int c_g = Integer.parseInt(c.substring(2,4),16);
|
||||
int c_b = Integer.parseInt(c.substring(4,6),16);
|
||||
int brightness = ((c_r * 299) + (c_g * 587) + (c_b * 114)) / 1000;
|
||||
return Pair.of(c,brightness > 155);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package forge.adventure.libgdxgui.assets;
|
||||
|
||||
import com.badlogic.gdx.files.FileHandle;
|
||||
import com.badlogic.gdx.graphics.Texture;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import forge.adventure.libgdxgui.Forge;
|
||||
import forge.ImageKeys;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
final class OtherImageLoader extends CacheLoader<String, Texture> {
|
||||
@Override
|
||||
public Texture load(String key) {
|
||||
File file = ImageKeys.getImageFile(key);
|
||||
if (file != null) {
|
||||
FileHandle fh = new FileHandle(file);
|
||||
try {
|
||||
if (Forge.isTextureFilteringEnabled()) {
|
||||
Texture t = new Texture(fh, true);
|
||||
t.setFilter(Texture.TextureFilter.MipMapLinearLinear, Texture.TextureFilter.Linear);
|
||||
return t;
|
||||
} else {
|
||||
return new Texture(fh);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
Forge.log("Could not read image file " + fh.path() + "\n\nException:\n" + ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,649 @@
|
||||
package forge.adventure.libgdxgui.assets;
|
||||
|
||||
import com.badlogic.gdx.graphics.Color;
|
||||
import com.badlogic.gdx.utils.Align;
|
||||
import forge.adventure.libgdxgui.Forge;
|
||||
import forge.adventure.libgdxgui.Graphics;
|
||||
import forge.adventure.libgdxgui.card.CardFaceSymbols;
|
||||
import forge.localinstance.properties.ForgePreferences;
|
||||
import forge.localinstance.properties.ForgePreferences.FPref;
|
||||
import forge.model.FModel;
|
||||
import forge.adventure.libgdxgui.util.TextBounds;
|
||||
|
||||
import java.text.BreakIterator;
|
||||
import java.util.*;
|
||||
|
||||
//Encodes text for drawing with symbols and reminder text
|
||||
public class TextRenderer {
|
||||
private static final Map<String, FSkinImage> symbolLookup = new HashMap<>(64);
|
||||
static {
|
||||
symbolLookup.put("C", FSkinImage.MANA_COLORLESS);
|
||||
symbolLookup.put("W", FSkinImage.MANA_W);
|
||||
symbolLookup.put("U", FSkinImage.MANA_U);
|
||||
symbolLookup.put("B", FSkinImage.MANA_B);
|
||||
symbolLookup.put("R", FSkinImage.MANA_R);
|
||||
symbolLookup.put("G", FSkinImage.MANA_G);
|
||||
symbolLookup.put("W/U", FSkinImage.MANA_HYBRID_WU);
|
||||
symbolLookup.put("U/B", FSkinImage.MANA_HYBRID_UB);
|
||||
symbolLookup.put("B/R", FSkinImage.MANA_HYBRID_BR);
|
||||
symbolLookup.put("R/G", FSkinImage.MANA_HYBRID_RG);
|
||||
symbolLookup.put("G/W", FSkinImage.MANA_HYBRID_GW);
|
||||
symbolLookup.put("W/B", FSkinImage.MANA_HYBRID_WB);
|
||||
symbolLookup.put("U/R", FSkinImage.MANA_HYBRID_UR);
|
||||
symbolLookup.put("B/G", FSkinImage.MANA_HYBRID_BG);
|
||||
symbolLookup.put("R/W", FSkinImage.MANA_HYBRID_RW);
|
||||
symbolLookup.put("G/U", FSkinImage.MANA_HYBRID_GU);
|
||||
symbolLookup.put("2/W", FSkinImage.MANA_2W);
|
||||
symbolLookup.put("2/U", FSkinImage.MANA_2U);
|
||||
symbolLookup.put("2/B", FSkinImage.MANA_2B);
|
||||
symbolLookup.put("2/R", FSkinImage.MANA_2R);
|
||||
symbolLookup.put("2/G", FSkinImage.MANA_2G);
|
||||
symbolLookup.put("P/W", FSkinImage.MANA_PHRYX_W);
|
||||
symbolLookup.put("P/U", FSkinImage.MANA_PHRYX_U);
|
||||
symbolLookup.put("P/B", FSkinImage.MANA_PHRYX_B);
|
||||
symbolLookup.put("P/R", FSkinImage.MANA_PHRYX_R);
|
||||
symbolLookup.put("P/G", FSkinImage.MANA_PHRYX_G);
|
||||
symbolLookup.put("W/P", FSkinImage.MANA_PHRYX_W);
|
||||
symbolLookup.put("U/P", FSkinImage.MANA_PHRYX_U);
|
||||
symbolLookup.put("B/P", FSkinImage.MANA_PHRYX_B);
|
||||
symbolLookup.put("R/P", FSkinImage.MANA_PHRYX_R);
|
||||
symbolLookup.put("G/P", FSkinImage.MANA_PHRYX_G);
|
||||
for (int i = 0; i <= 20; i++) {
|
||||
symbolLookup.put(String.valueOf(i), FSkinImage.valueOf("MANA_" + i));
|
||||
}
|
||||
symbolLookup.put("X", FSkinImage.MANA_X);
|
||||
symbolLookup.put("Y", FSkinImage.MANA_Y);
|
||||
symbolLookup.put("Z", FSkinImage.MANA_Z);
|
||||
symbolLookup.put("CHAOS", FSkinImage.CHAOS);
|
||||
symbolLookup.put("Q", FSkinImage.UNTAP);
|
||||
symbolLookup.put("S", FSkinImage.MANA_SNOW);
|
||||
symbolLookup.put("T", FSkinImage.TAP);
|
||||
symbolLookup.put("E", FSkinImage.ENERGY);
|
||||
symbolLookup.put("AE", FSkinImage.AETHER_SHARD);
|
||||
symbolLookup.put("PW", FSkinImage.PW_BADGE_COMMON);
|
||||
symbolLookup.put("CR", FSkinImage.QUEST_COINSTACK);
|
||||
}
|
||||
|
||||
public static String startColor(Color color) {
|
||||
return "<clr " + Color.rgba8888(color) + ">";
|
||||
}
|
||||
public static String endColor() {
|
||||
return "</clr>";
|
||||
}
|
||||
|
||||
private final boolean parseReminderText;
|
||||
private String fullText = "";
|
||||
private float width, height, totalHeight;
|
||||
private FSkinFont baseFont, font;
|
||||
private boolean wrap, needClip;
|
||||
private final List<Piece> pieces = new ArrayList<>();
|
||||
private final List<Float> lineWidths = new ArrayList<>();
|
||||
private final BreakIterator boundary = BreakIterator.getLineInstance(new Locale(Forge.locale));
|
||||
|
||||
public TextRenderer() {
|
||||
this(false);
|
||||
}
|
||||
public TextRenderer(boolean parseReminderText0) {
|
||||
parseReminderText = parseReminderText0;
|
||||
}
|
||||
|
||||
//break text in pieces
|
||||
private void updatePieces(FSkinFont font0) {
|
||||
pieces.clear();
|
||||
lineWidths.clear();
|
||||
font = font0;
|
||||
needClip = false;
|
||||
if (fullText == null || fullText.isEmpty()) { return; }
|
||||
|
||||
totalHeight = font.getCapHeight();
|
||||
if (totalHeight > height) {
|
||||
//immediately try one font size smaller if no room for anything
|
||||
if (font.canShrink()) {
|
||||
updatePieces(font.shrink());
|
||||
return;
|
||||
}
|
||||
needClip = true;
|
||||
}
|
||||
|
||||
boundary.setText(fullText);
|
||||
ForgePreferences prefs = FModel.getPreferences();
|
||||
boolean hideReminderText = prefs != null && prefs.getPrefBoolean(FPref.UI_HIDE_REMINDER_TEXT);
|
||||
|
||||
char ch;
|
||||
float x = 0;
|
||||
float y = 0;
|
||||
float pieceWidth = 0;
|
||||
float lineHeight = font.getLineHeight();
|
||||
int nextSpaceIdx = boundary.first();
|
||||
int lastSpaceIdx = -1;
|
||||
int lineNum = 0;
|
||||
StringBuilder text = new StringBuilder();
|
||||
int inSymbolCount = 0;
|
||||
int consecutiveSymbols = 0;
|
||||
int inKeywordCount = 0;
|
||||
boolean atReminderTextEnd = false;
|
||||
int inReminderTextCount = 0;
|
||||
Color colorOverride = null;
|
||||
|
||||
for (int i = 0; i < fullText.length(); i++) {
|
||||
atReminderTextEnd = false;
|
||||
if (i == nextSpaceIdx) {
|
||||
lastSpaceIdx = text.length();
|
||||
nextSpaceIdx = boundary.next();
|
||||
}
|
||||
ch = fullText.charAt(i);
|
||||
switch (ch) {
|
||||
case '\r':
|
||||
continue; //skip '\r' character
|
||||
case '\n':
|
||||
if (inSymbolCount > 0) {
|
||||
inSymbolCount = 0;
|
||||
text.insert(0, '{'); //if not a symbol, render as text
|
||||
}
|
||||
lineWidths.add(x + pieceWidth);
|
||||
if (text.length() > 0) {
|
||||
addPiece(new TextPiece(text.toString(), colorOverride, inReminderTextCount > 0), lineNum, x, y, pieceWidth, lineHeight);
|
||||
pieceWidth = 0;
|
||||
text.setLength(0);
|
||||
consecutiveSymbols = 0;
|
||||
}
|
||||
lastSpaceIdx = -1;
|
||||
x = 0;
|
||||
y += lineHeight;
|
||||
totalHeight += lineHeight;
|
||||
lineNum++;
|
||||
if (totalHeight > height) {
|
||||
//try next font size down if out of space
|
||||
if (font.canShrink()) {
|
||||
updatePieces(font.shrink());
|
||||
return;
|
||||
}
|
||||
needClip = true;
|
||||
}
|
||||
continue; //skip new line character
|
||||
case '{':
|
||||
if (inSymbolCount == 0 && text.length() > 0) { //add current text if just entering symbol
|
||||
addPiece(new TextPiece(text.toString(), colorOverride,inReminderTextCount > 0), lineNum, x, y, pieceWidth, lineHeight);
|
||||
x += pieceWidth;
|
||||
pieceWidth = 0;
|
||||
text.setLength(0);
|
||||
lastSpaceIdx = -1;
|
||||
consecutiveSymbols = 0;
|
||||
}
|
||||
inSymbolCount++;
|
||||
continue; //skip '{' character
|
||||
case '}':
|
||||
if (inSymbolCount > 0) {
|
||||
inSymbolCount--;
|
||||
if (text.length() > 0) {
|
||||
FSkinImage symbol = symbolLookup.get(text.toString());
|
||||
if (symbol != null) {
|
||||
pieceWidth = lineHeight * CardFaceSymbols.FONT_SIZE_FACTOR;
|
||||
if (x + pieceWidth > width) {
|
||||
if (wrap) {
|
||||
y += lineHeight;
|
||||
totalHeight += lineHeight;
|
||||
lineNum++;
|
||||
if (totalHeight > height) {
|
||||
//try next font size down if out of space
|
||||
if (font.canShrink()) {
|
||||
updatePieces(font.shrink());
|
||||
return;
|
||||
}
|
||||
needClip = true;
|
||||
}
|
||||
if (consecutiveSymbols == 0) {
|
||||
lineWidths.add(x);
|
||||
x = 0;
|
||||
}
|
||||
else { //make previous consecutive symbols wrap too if needed
|
||||
x = 0;
|
||||
int startSymbolIdx = pieces.size() - consecutiveSymbols;
|
||||
lineWidths.add(pieces.get(startSymbolIdx).x);
|
||||
for (int j = startSymbolIdx; j < pieces.size(); j++) {
|
||||
Piece piece = pieces.get(j);
|
||||
piece.x = x;
|
||||
piece.y += lineHeight;
|
||||
piece.lineNum++;
|
||||
x += piece.w;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (font.canShrink()) {
|
||||
//try next font size down if out of space
|
||||
updatePieces(font.shrink());
|
||||
return;
|
||||
}
|
||||
else {
|
||||
needClip = true;
|
||||
}
|
||||
}
|
||||
addPiece(new SymbolPiece(symbol, inReminderTextCount > 0), lineNum, x, y - font.getAscent() + (lineHeight - pieceWidth) / 2, pieceWidth, pieceWidth);
|
||||
x += pieceWidth;
|
||||
pieceWidth = 0;
|
||||
text.setLength(0);
|
||||
lastSpaceIdx = -1;
|
||||
consecutiveSymbols++;
|
||||
continue; //skip '}' character
|
||||
}
|
||||
}
|
||||
if (!hideReminderText || inReminderTextCount == 0) {
|
||||
text.insert(0, '{'); //if not a symbol, render as text
|
||||
if (lastSpaceIdx >= 0) {
|
||||
lastSpaceIdx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case '<':
|
||||
if (inSymbolCount > 0) {
|
||||
inSymbolCount = 0;
|
||||
text.insert(0, '{'); //if not a symbol, render as text
|
||||
if (lastSpaceIdx >= 0) {
|
||||
lastSpaceIdx++;
|
||||
}
|
||||
}
|
||||
if (inKeywordCount == 0 && text.length() > 0) { //add current text if starting a keyword
|
||||
addPiece(new TextPiece(text.toString(), colorOverride,false), lineNum, x, y, pieceWidth, lineHeight);
|
||||
x += pieceWidth;
|
||||
pieceWidth = 0;
|
||||
text.setLength(0);
|
||||
lastSpaceIdx = -1;
|
||||
consecutiveSymbols = 0;
|
||||
}
|
||||
inKeywordCount++;
|
||||
break;
|
||||
case '>':
|
||||
if (inSymbolCount > 0) {
|
||||
inSymbolCount = 0;
|
||||
text.insert(0, '{'); //if not a symbol, render as text
|
||||
if (lastSpaceIdx >= 0) {
|
||||
lastSpaceIdx++;
|
||||
}
|
||||
}
|
||||
if (inKeywordCount > 0) {
|
||||
inKeywordCount--;
|
||||
if (inKeywordCount == 0 && text.length() > 0) {
|
||||
String keyword, value;
|
||||
text.deleteCharAt(0); //trim leading '<'
|
||||
if (text.charAt(0) == '/') {
|
||||
keyword = text.substring(1);
|
||||
value = null;
|
||||
}
|
||||
else {
|
||||
int idx = text.indexOf(" ");
|
||||
if (idx != -1) {
|
||||
keyword = text.substring(0, idx);
|
||||
value = text.substring(idx + 1);
|
||||
}
|
||||
else {
|
||||
keyword = text.toString();
|
||||
value = null;
|
||||
}
|
||||
}
|
||||
boolean validKeyword = true;
|
||||
switch (keyword) {
|
||||
case "clr":
|
||||
colorOverride = value != null ? new Color(Integer.parseInt(value)) : null;
|
||||
break;
|
||||
default:
|
||||
validKeyword = false;
|
||||
break;
|
||||
}
|
||||
if (validKeyword) {
|
||||
text.setLength(0);
|
||||
lastSpaceIdx = -1;
|
||||
continue; //skip '>' character
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case '(':
|
||||
if (inSymbolCount > 0) {
|
||||
inSymbolCount = 0;
|
||||
text.insert(0, '{'); //if not a symbol, render as text
|
||||
if (lastSpaceIdx >= 0) {
|
||||
lastSpaceIdx++;
|
||||
}
|
||||
}
|
||||
if (parseReminderText) {
|
||||
if (inReminderTextCount == 0 && text.length() > 0) { //add current text if just entering reminder text
|
||||
addPiece(new TextPiece(text.toString(), colorOverride,false), lineNum, x, y, pieceWidth, lineHeight);
|
||||
x += pieceWidth;
|
||||
pieceWidth = 0;
|
||||
text.setLength(0);
|
||||
lastSpaceIdx = -1;
|
||||
consecutiveSymbols = 0;
|
||||
}
|
||||
inReminderTextCount++;
|
||||
}
|
||||
break;
|
||||
case ')':
|
||||
if (inSymbolCount > 0) {
|
||||
inSymbolCount = 0;
|
||||
text.insert(0, '{'); //if not a symbol, render as text
|
||||
if (lastSpaceIdx >= 0) {
|
||||
lastSpaceIdx++;
|
||||
}
|
||||
}
|
||||
if (inReminderTextCount > 0) {
|
||||
inReminderTextCount--;
|
||||
if (inReminderTextCount == 0) {
|
||||
atReminderTextEnd = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ' ':
|
||||
if (inKeywordCount == 0 && inSymbolCount > 0) {
|
||||
inSymbolCount = 0;
|
||||
text.insert(0, '{'); //if not a symbol, render as text
|
||||
if (lastSpaceIdx >= 0) {
|
||||
lastSpaceIdx++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (hideReminderText && (inReminderTextCount > 0 || atReminderTextEnd)) {
|
||||
continue;
|
||||
}
|
||||
text.append(ch);
|
||||
if (inSymbolCount == 0 && inKeywordCount == 0) {
|
||||
pieceWidth = font.getBounds(text).width;
|
||||
if (x + pieceWidth > width) { //wrap or shrink if needed
|
||||
if (wrap && (lastSpaceIdx >= 0 || consecutiveSymbols > 0)) {
|
||||
if (lastSpaceIdx < 0) {
|
||||
//no space between symbols and end of line, wrap those symbols along with text
|
||||
x = 0;
|
||||
int startSymbolIdx = pieces.size() - consecutiveSymbols;
|
||||
lineWidths.add(pieces.get(startSymbolIdx).x);
|
||||
for (int j = startSymbolIdx; j < pieces.size(); j++) {
|
||||
Piece piece = pieces.get(j);
|
||||
piece.x = x;
|
||||
piece.y += lineHeight;
|
||||
piece.lineNum++;
|
||||
x += piece.w;
|
||||
}
|
||||
}
|
||||
else {
|
||||
int endIdx = lastSpaceIdx;
|
||||
if (lastSpaceIdx > 0 && text.charAt(lastSpaceIdx - 1) == ' ') {
|
||||
endIdx = lastSpaceIdx - 1;
|
||||
}
|
||||
String currentLineText = text.substring(0, endIdx);
|
||||
if (!currentLineText.isEmpty()) {
|
||||
pieceWidth = font.getBounds(currentLineText).width;
|
||||
addPiece(new TextPiece(currentLineText, colorOverride,inReminderTextCount > 0 || atReminderTextEnd), lineNum, x, y, pieceWidth, lineHeight);
|
||||
consecutiveSymbols = 0;
|
||||
}
|
||||
else {
|
||||
pieceWidth = 0;
|
||||
}
|
||||
lineWidths.add(x + pieceWidth);
|
||||
text.delete(0, lastSpaceIdx);
|
||||
x = 0;
|
||||
}
|
||||
lastSpaceIdx = -1;
|
||||
pieceWidth = text.length() == 0 ? 0 : font.getBounds(text).width;
|
||||
y += lineHeight;
|
||||
totalHeight += lineHeight;
|
||||
lineNum++;
|
||||
if (totalHeight > height) {
|
||||
//try next font size down if out of space
|
||||
if (font.canShrink()) {
|
||||
updatePieces(font.shrink());
|
||||
return;
|
||||
}
|
||||
needClip = true;
|
||||
}
|
||||
}
|
||||
else if (x > 0 && pieceWidth <= width) {
|
||||
//if current piece starting past beginning of line and no spaces found,
|
||||
//wrap current piece being built up along with part of previous pieces as needed
|
||||
int lastPieceIdx;
|
||||
for (lastPieceIdx = pieces.size() - 1; lastPieceIdx >= 0; lastPieceIdx--) {
|
||||
Piece lastPiece = pieces.get(lastPieceIdx);
|
||||
if (lastPiece.lineNum < lineNum) {
|
||||
lastPieceIdx = pieces.size() - 1; //don't re-wrap anything if reached previous line
|
||||
break;
|
||||
}
|
||||
if (lastPiece instanceof TextPiece) {
|
||||
TextPiece textPiece = (TextPiece)lastPiece;
|
||||
int index = textPiece.text.lastIndexOf(' ');
|
||||
if (index != -1) {
|
||||
if (index == 0) {
|
||||
textPiece.text = textPiece.text.substring(1);
|
||||
textPiece.w = font.getBounds(textPiece.text).width;
|
||||
lastPieceIdx--;
|
||||
}
|
||||
else if (index == textPiece.text.length() - 1) {
|
||||
textPiece.text = textPiece.text.substring(0, textPiece.text.length() - 1);
|
||||
textPiece.w = font.getBounds(textPiece.text).width;
|
||||
}
|
||||
else {
|
||||
TextPiece splitPiece = new TextPiece(textPiece.text.substring(index + 1), textPiece.colorOverride, textPiece.inReminderText);
|
||||
textPiece.text = textPiece.text.substring(0, index);
|
||||
textPiece.w = font.getBounds(textPiece.text).width;
|
||||
splitPiece.x = textPiece.x + textPiece.w;
|
||||
splitPiece.y = textPiece.y;
|
||||
splitPiece.w = font.getBounds(splitPiece.text).width;
|
||||
splitPiece.h = textPiece.h;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (lastPieceIdx >= 0) {
|
||||
Piece lastPiece = pieces.get(lastPieceIdx);
|
||||
lineWidths.add(lastPiece.x + lastPiece.w);
|
||||
x = 0;
|
||||
for (int j = lastPieceIdx + 1; j < pieces.size(); j++) {
|
||||
Piece piece = pieces.get(j);
|
||||
piece.x = x;
|
||||
piece.y += lineHeight;
|
||||
piece.lineNum++;
|
||||
x += piece.w;
|
||||
}
|
||||
y += lineHeight;
|
||||
totalHeight += lineHeight;
|
||||
lineNum++;
|
||||
if (totalHeight > height) {
|
||||
//try next font size down if out of space
|
||||
if (font.canShrink()) {
|
||||
updatePieces(font.shrink());
|
||||
return;
|
||||
}
|
||||
needClip = true;
|
||||
}
|
||||
} else {
|
||||
if (font.canShrink()) {
|
||||
//try next font size down if out of space
|
||||
updatePieces(font.shrink());
|
||||
return;
|
||||
}
|
||||
needClip = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (font.canShrink()) {
|
||||
//try next font size down if out of space
|
||||
updatePieces(font.shrink());
|
||||
return;
|
||||
}
|
||||
needClip = true;
|
||||
}
|
||||
}
|
||||
if (atReminderTextEnd && text.length() > 0) { //ensure final piece of reminder text added right away
|
||||
addPiece(new TextPiece(text.toString(), colorOverride, true), lineNum, x, y, pieceWidth, lineHeight);
|
||||
x += pieceWidth;
|
||||
pieceWidth = 0;
|
||||
text.setLength(0);
|
||||
lastSpaceIdx = -1;
|
||||
consecutiveSymbols = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lineWidths.add(x + pieceWidth);
|
||||
if (text.length() > 0) {
|
||||
addPiece(new TextPiece(text.toString(), colorOverride, inReminderTextCount > 0), lineNum, x, y, pieceWidth, lineHeight);
|
||||
consecutiveSymbols = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void addPiece(Piece piece, int lineNum, float x, float y, float w, float h) {
|
||||
piece.lineNum = lineNum;
|
||||
piece.x = x;
|
||||
piece.y = y;
|
||||
piece.w = w;
|
||||
piece.h = h;
|
||||
pieces.add(piece);
|
||||
}
|
||||
|
||||
private void setProps(String text, FSkinFont skinFont, float w, float h, boolean wrap0) {
|
||||
boolean needUpdate = false;
|
||||
if (!fullText.equals(text)) {
|
||||
fullText = text;
|
||||
needUpdate = true;
|
||||
}
|
||||
if (skinFont != baseFont) {
|
||||
baseFont = skinFont;
|
||||
needUpdate = true;
|
||||
}
|
||||
if (width != w) {
|
||||
width = w;
|
||||
needUpdate = true;
|
||||
}
|
||||
if (height != h) {
|
||||
height = h;
|
||||
needUpdate = true;
|
||||
}
|
||||
if (wrap != wrap0) {
|
||||
wrap = wrap0;
|
||||
needUpdate = true;
|
||||
}
|
||||
if (needUpdate) {
|
||||
updatePieces(baseFont);
|
||||
}
|
||||
}
|
||||
|
||||
private TextBounds getCurrentBounds() {
|
||||
float maxWidth = 0;
|
||||
for (Float lineWidth : lineWidths) {
|
||||
if (lineWidth > maxWidth) {
|
||||
maxWidth = lineWidth;
|
||||
}
|
||||
}
|
||||
TextBounds bounds = new TextBounds();
|
||||
bounds.width = maxWidth;
|
||||
bounds.height = totalHeight;
|
||||
return bounds;
|
||||
}
|
||||
|
||||
public TextBounds getBounds(String text, FSkinFont skinFont) {
|
||||
setProps(text, skinFont, Float.MAX_VALUE, Float.MAX_VALUE, false);
|
||||
return getCurrentBounds();
|
||||
}
|
||||
public TextBounds getWrappedBounds(String text, FSkinFont skinFont, float maxWidth) {
|
||||
setProps(text, skinFont, maxWidth, Float.MAX_VALUE, true);
|
||||
return getCurrentBounds();
|
||||
}
|
||||
|
||||
public void drawText(Graphics g, String text, FSkinFont skinFont, FSkinColor skinColor, float x, float y, float w, float h, float visibleStartY, float visibleHeight, boolean wrap0, int horzAlignment, boolean centerVertically) {
|
||||
drawText(g, text, skinFont, skinColor.getColor(), x, y, w, h, visibleStartY, visibleHeight, wrap0, horzAlignment, centerVertically);
|
||||
}
|
||||
public void drawText(Graphics g, String text, FSkinFont skinFont, Color color, float x, float y, float w, float h, float visibleStartY, float visibleHeight, boolean wrap0, int horzAlignment, boolean centerVertically) {
|
||||
setProps(text, skinFont, w, h, wrap0);
|
||||
if (needClip) { //prevent text flowing outside region if couldn't shrink it to fit
|
||||
g.startClip(x, y, w, h);
|
||||
}
|
||||
if (height > totalHeight && centerVertically) {
|
||||
y += (height - totalHeight) / 2;
|
||||
}
|
||||
float[] alignmentOffsets = new float[lineWidths.size()];
|
||||
for (int i = 0; i < lineWidths.size(); i++) {
|
||||
switch (horzAlignment) {
|
||||
case Align.left:
|
||||
alignmentOffsets[i] = 0;
|
||||
break;
|
||||
case Align.center:
|
||||
alignmentOffsets[i] = Math.max((width - lineWidths.get(i)) / 2, 0);
|
||||
break;
|
||||
case Align.right:
|
||||
alignmentOffsets[i] = Math.max(width - lineWidths.get(i), 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
visibleStartY -= y; //subtract y to make calculation quicker
|
||||
float visibleEndY = visibleStartY + visibleHeight;
|
||||
|
||||
for (Piece piece : pieces) {
|
||||
if (piece.y + piece.h < visibleStartY) {
|
||||
continue;
|
||||
}
|
||||
if (piece.y >= visibleEndY) {
|
||||
break;
|
||||
}
|
||||
piece.draw(g, color, x + alignmentOffsets[piece.lineNum], y);
|
||||
}
|
||||
if (needClip) {
|
||||
g.endClip();
|
||||
}
|
||||
}
|
||||
|
||||
private abstract class Piece {
|
||||
protected static final float ALPHA_COMPOSITE = 0.5f;
|
||||
|
||||
protected final boolean inReminderText;
|
||||
protected float x, y, w, h;
|
||||
protected int lineNum;
|
||||
|
||||
protected Piece(boolean inReminderText0) {
|
||||
inReminderText = inReminderText0;
|
||||
}
|
||||
|
||||
public abstract void draw(Graphics g, Color color, float offsetX, float offsetY);
|
||||
}
|
||||
|
||||
private class TextPiece extends Piece {
|
||||
private String text;
|
||||
private final Color colorOverride;
|
||||
|
||||
private TextPiece(String text0, Color colorOverride0, boolean inReminderText0) {
|
||||
super(inReminderText0);
|
||||
text = text0;
|
||||
colorOverride = colorOverride0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Graphics g, Color color, float offsetX, float offsetY) {
|
||||
if (colorOverride != null) {
|
||||
color = colorOverride;
|
||||
}
|
||||
else if (inReminderText) {
|
||||
color = FSkinColor.alphaColor(color, ALPHA_COMPOSITE);
|
||||
}
|
||||
g.drawText(text, font, color, x + offsetX, y + offsetY, w, h, false, Align.left, false);
|
||||
}
|
||||
}
|
||||
|
||||
private class SymbolPiece extends Piece {
|
||||
private final FSkinImage image;
|
||||
|
||||
private SymbolPiece(FSkinImage image0, boolean inReminderText0) {
|
||||
super(inReminderText0);
|
||||
image = image0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Graphics g, Color color, float offsetX, float offsetY) {
|
||||
if (inReminderText) {
|
||||
g.setAlphaComposite(ALPHA_COMPOSITE);
|
||||
}
|
||||
g.drawImage(image, x + offsetX, y + offsetY, w, h);
|
||||
if (inReminderText) {
|
||||
g.resetAlphaComposite();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package forge.adventure.libgdxgui.card;
|
||||
|
||||
import forge.adventure.libgdxgui.Graphics;
|
||||
import forge.adventure.libgdxgui.assets.FImage;
|
||||
import forge.adventure.libgdxgui.assets.ImageCache;
|
||||
import forge.item.PaperCard;
|
||||
|
||||
public class CardAvatarImage implements FImage {
|
||||
private final String imageKey;
|
||||
private FImage image;
|
||||
|
||||
public CardAvatarImage(PaperCard card0) {
|
||||
this(card0.getImageKey(false));
|
||||
}
|
||||
public CardAvatarImage(String imageKey0) {
|
||||
imageKey = imageKey0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getWidth() {
|
||||
return getHeight(); //image will be drawn at its height
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getHeight() {
|
||||
if (image != null) {
|
||||
return image.getHeight();
|
||||
}
|
||||
return ImageCache.defaultImage.getHeight() * CardRenderer.CARD_ART_HEIGHT_PERCENTAGE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Graphics g, float x, float y, float w, float h) {
|
||||
//force to get the avatar since the the cardartcache & loadingcache is always cleared on screen change or the battle bar will display black
|
||||
image = CardRenderer.getCardArt(imageKey, false, false, false);
|
||||
if (image == null) {
|
||||
return; //can't draw anything if can't be loaded yet
|
||||
}
|
||||
|
||||
//draw scaled image into clipped region so it fills box while maintain aspect ratio
|
||||
g.startClip(x, y, w, h);
|
||||
|
||||
float aspectRatio = w / h;
|
||||
float imageAspectRatio = image.getWidth() / image.getHeight();
|
||||
if (imageAspectRatio > aspectRatio) {
|
||||
float w0 = w * imageAspectRatio / aspectRatio;
|
||||
x -= (w0 - w) / 2;
|
||||
w = w0;
|
||||
}
|
||||
else {
|
||||
float h0 = h * aspectRatio / imageAspectRatio;
|
||||
y -= (h0 - h) / 2;
|
||||
h = h0;
|
||||
}
|
||||
|
||||
image.draw(g, x, y, w, h);
|
||||
|
||||
g.endClip();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,248 @@
|
||||
/*
|
||||
* Forge: Play Magic: the Gathering.
|
||||
* Copyright (C) 2011 Forge Team
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package forge.adventure.libgdxgui.card;
|
||||
|
||||
import forge.adventure.libgdxgui.Graphics;
|
||||
import forge.adventure.libgdxgui.assets.FSkinImage;
|
||||
import forge.card.ColorSet;
|
||||
import forge.card.mana.ManaCost;
|
||||
import forge.card.mana.ManaCostShard;
|
||||
import forge.gui.error.BugReporter;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
|
||||
public class CardFaceSymbols {
|
||||
public static final float FONT_SIZE_FACTOR = 0.85f;
|
||||
private static final Map<String, FSkinImage> MANA_IMAGES = new HashMap<>(128);
|
||||
|
||||
public static void loadImages() {
|
||||
for (int i = 0; i <= 20; i++) {
|
||||
MANA_IMAGES.put(String.valueOf(i), FSkinImage.valueOf("MANA_" + i));
|
||||
}
|
||||
MANA_IMAGES.put("X", FSkinImage.MANA_X);
|
||||
MANA_IMAGES.put("Y", FSkinImage.MANA_Y);
|
||||
MANA_IMAGES.put("Z", FSkinImage.MANA_Z);
|
||||
|
||||
MANA_IMAGES.put("C", FSkinImage.MANA_COLORLESS);
|
||||
MANA_IMAGES.put("B", FSkinImage.MANA_B);
|
||||
MANA_IMAGES.put("BG", FSkinImage.MANA_HYBRID_BG);
|
||||
MANA_IMAGES.put("BR", FSkinImage.MANA_HYBRID_BR);
|
||||
MANA_IMAGES.put("G", FSkinImage.MANA_G);
|
||||
MANA_IMAGES.put("GU", FSkinImage.MANA_HYBRID_GU);
|
||||
MANA_IMAGES.put("GW", FSkinImage.MANA_HYBRID_GW);
|
||||
MANA_IMAGES.put("R", FSkinImage.MANA_R);
|
||||
MANA_IMAGES.put("RG", FSkinImage.MANA_HYBRID_RG);
|
||||
MANA_IMAGES.put("RW", FSkinImage.MANA_HYBRID_RW);
|
||||
MANA_IMAGES.put("U", FSkinImage.MANA_U);
|
||||
MANA_IMAGES.put("UB", FSkinImage.MANA_HYBRID_UB);
|
||||
MANA_IMAGES.put("UR", FSkinImage.MANA_HYBRID_UR);
|
||||
MANA_IMAGES.put("W", FSkinImage.MANA_W);
|
||||
MANA_IMAGES.put("WB", FSkinImage.MANA_HYBRID_WB);
|
||||
MANA_IMAGES.put("WU", FSkinImage.MANA_HYBRID_WU);
|
||||
MANA_IMAGES.put("PW", FSkinImage.MANA_PHRYX_W);
|
||||
MANA_IMAGES.put("PR", FSkinImage.MANA_PHRYX_R);
|
||||
MANA_IMAGES.put("PU", FSkinImage.MANA_PHRYX_U);
|
||||
MANA_IMAGES.put("PB", FSkinImage.MANA_PHRYX_B);
|
||||
MANA_IMAGES.put("PG", FSkinImage.MANA_PHRYX_G);
|
||||
MANA_IMAGES.put("2W", FSkinImage.MANA_2W);
|
||||
MANA_IMAGES.put("2U", FSkinImage.MANA_2U);
|
||||
MANA_IMAGES.put("2R", FSkinImage.MANA_2R);
|
||||
MANA_IMAGES.put("2G", FSkinImage.MANA_2G);
|
||||
MANA_IMAGES.put("2B", FSkinImage.MANA_2B);
|
||||
|
||||
MANA_IMAGES.put("S", FSkinImage.MANA_SNOW);
|
||||
MANA_IMAGES.put("T", FSkinImage.TAP);
|
||||
MANA_IMAGES.put("E", FSkinImage.ENERGY);
|
||||
MANA_IMAGES.put("slash", FSkinImage.SLASH);
|
||||
MANA_IMAGES.put("attack", FSkinImage.ATTACK);
|
||||
MANA_IMAGES.put("defend", FSkinImage.DEFEND);
|
||||
MANA_IMAGES.put("summonsick", FSkinImage.SUMMONSICK);
|
||||
MANA_IMAGES.put("phasing", FSkinImage.PHASING);
|
||||
MANA_IMAGES.put("sacrifice", FSkinImage.COSTRESERVED);
|
||||
MANA_IMAGES.put("counters1", FSkinImage.COUNTERS1);
|
||||
MANA_IMAGES.put("counters2", FSkinImage.COUNTERS2);
|
||||
MANA_IMAGES.put("counters3", FSkinImage.COUNTERS3);
|
||||
MANA_IMAGES.put("countersMulti", FSkinImage.COUNTERS_MULTI);
|
||||
|
||||
MANA_IMAGES.put("foil01", FSkinImage.FOIL_01);
|
||||
MANA_IMAGES.put("foil02", FSkinImage.FOIL_02);
|
||||
MANA_IMAGES.put("foil03", FSkinImage.FOIL_03);
|
||||
MANA_IMAGES.put("foil04", FSkinImage.FOIL_04);
|
||||
MANA_IMAGES.put("foil05", FSkinImage.FOIL_05);
|
||||
MANA_IMAGES.put("foil06", FSkinImage.FOIL_06);
|
||||
MANA_IMAGES.put("foil07", FSkinImage.FOIL_07);
|
||||
MANA_IMAGES.put("foil08", FSkinImage.FOIL_08);
|
||||
MANA_IMAGES.put("foil09", FSkinImage.FOIL_09);
|
||||
MANA_IMAGES.put("foil10", FSkinImage.FOIL_10);
|
||||
|
||||
MANA_IMAGES.put("foil11", FSkinImage.FOIL_11);
|
||||
MANA_IMAGES.put("foil12", FSkinImage.FOIL_12);
|
||||
MANA_IMAGES.put("foil13", FSkinImage.FOIL_13);
|
||||
MANA_IMAGES.put("foil14", FSkinImage.FOIL_14);
|
||||
MANA_IMAGES.put("foil15", FSkinImage.FOIL_15);
|
||||
MANA_IMAGES.put("foil16", FSkinImage.FOIL_16);
|
||||
MANA_IMAGES.put("foil17", FSkinImage.FOIL_17);
|
||||
MANA_IMAGES.put("foil18", FSkinImage.FOIL_18);
|
||||
MANA_IMAGES.put("foil19", FSkinImage.FOIL_19);
|
||||
MANA_IMAGES.put("foil20", FSkinImage.FOIL_20);
|
||||
|
||||
MANA_IMAGES.put("commander", FSkinImage.IMG_ABILITY_COMMANDER);
|
||||
|
||||
MANA_IMAGES.put("deathtouch", FSkinImage.IMG_ABILITY_DEATHTOUCH);
|
||||
MANA_IMAGES.put("defender", FSkinImage.IMG_ABILITY_DEFENDER);
|
||||
MANA_IMAGES.put("doublestrike", FSkinImage.IMG_ABILITY_DOUBLE_STRIKE);
|
||||
MANA_IMAGES.put("firststrike", FSkinImage.IMG_ABILITY_FIRST_STRIKE);
|
||||
MANA_IMAGES.put("fear", FSkinImage.IMG_ABILITY_FEAR);
|
||||
MANA_IMAGES.put("flash", FSkinImage.IMG_ABILITY_FLASH);
|
||||
MANA_IMAGES.put("flying", FSkinImage.IMG_ABILITY_FLYING);
|
||||
MANA_IMAGES.put("haste", FSkinImage.IMG_ABILITY_HASTE);
|
||||
MANA_IMAGES.put("hexproof", FSkinImage.IMG_ABILITY_HEXPROOF);
|
||||
MANA_IMAGES.put("horsemanship", FSkinImage.IMG_ABILITY_HORSEMANSHIP);
|
||||
MANA_IMAGES.put("indestructible", FSkinImage.IMG_ABILITY_INDESTRUCTIBLE);
|
||||
MANA_IMAGES.put("intimidate", FSkinImage.IMG_ABILITY_INTIMIDATE);
|
||||
MANA_IMAGES.put("landwalk", FSkinImage.IMG_ABILITY_LANDWALK);
|
||||
MANA_IMAGES.put("lifelink", FSkinImage.IMG_ABILITY_LIFELINK);
|
||||
MANA_IMAGES.put("menace", FSkinImage.IMG_ABILITY_MENACE);
|
||||
MANA_IMAGES.put("reach", FSkinImage.IMG_ABILITY_REACH);
|
||||
MANA_IMAGES.put("shadow", FSkinImage.IMG_ABILITY_SHADOW);
|
||||
MANA_IMAGES.put("shroud", FSkinImage.IMG_ABILITY_SHROUD);
|
||||
MANA_IMAGES.put("trample", FSkinImage.IMG_ABILITY_TRAMPLE);
|
||||
MANA_IMAGES.put("vigilance", FSkinImage.IMG_ABILITY_VIGILANCE);
|
||||
//hexproof from
|
||||
MANA_IMAGES.put("hexproofR", FSkinImage.IMG_ABILITY_HEXPROOF_R);
|
||||
MANA_IMAGES.put("hexproofG", FSkinImage.IMG_ABILITY_HEXPROOF_G);
|
||||
MANA_IMAGES.put("hexproofB", FSkinImage.IMG_ABILITY_HEXPROOF_B);
|
||||
MANA_IMAGES.put("hexproofU", FSkinImage.IMG_ABILITY_HEXPROOF_U);
|
||||
MANA_IMAGES.put("hexproofW", FSkinImage.IMG_ABILITY_HEXPROOF_W);
|
||||
MANA_IMAGES.put("hexproofC", FSkinImage.IMG_ABILITY_HEXPROOF_C);
|
||||
MANA_IMAGES.put("hexproofUB", FSkinImage.IMG_ABILITY_HEXPROOF_UB);
|
||||
//token icon
|
||||
MANA_IMAGES.put("token", FSkinImage.IMG_ABILITY_TOKEN);
|
||||
//protection from
|
||||
MANA_IMAGES.put("protectAll", FSkinImage.IMG_ABILITY_PROTECT_ALL);
|
||||
MANA_IMAGES.put("protectB", FSkinImage.IMG_ABILITY_PROTECT_B);
|
||||
MANA_IMAGES.put("protectBU", FSkinImage.IMG_ABILITY_PROTECT_BU);
|
||||
MANA_IMAGES.put("protectBW", FSkinImage.IMG_ABILITY_PROTECT_BW);
|
||||
MANA_IMAGES.put("protectColoredSpells", FSkinImage.IMG_ABILITY_PROTECT_COLOREDSPELLS);
|
||||
MANA_IMAGES.put("protectG", FSkinImage.IMG_ABILITY_PROTECT_G);
|
||||
MANA_IMAGES.put("protectGB", FSkinImage.IMG_ABILITY_PROTECT_GB);
|
||||
MANA_IMAGES.put("protectGU", FSkinImage.IMG_ABILITY_PROTECT_GU);
|
||||
MANA_IMAGES.put("protectGW", FSkinImage.IMG_ABILITY_PROTECT_GW);
|
||||
MANA_IMAGES.put("protectGeneric", FSkinImage.IMG_ABILITY_PROTECT_GENERIC);
|
||||
MANA_IMAGES.put("protectR", FSkinImage.IMG_ABILITY_PROTECT_R);
|
||||
MANA_IMAGES.put("protectRB", FSkinImage.IMG_ABILITY_PROTECT_RB);
|
||||
MANA_IMAGES.put("protectRG", FSkinImage.IMG_ABILITY_PROTECT_RG);
|
||||
MANA_IMAGES.put("protectRU", FSkinImage.IMG_ABILITY_PROTECT_RU);
|
||||
MANA_IMAGES.put("protectRW", FSkinImage.IMG_ABILITY_PROTECT_RW);
|
||||
MANA_IMAGES.put("protectU", FSkinImage.IMG_ABILITY_PROTECT_U);
|
||||
MANA_IMAGES.put("protectUW", FSkinImage.IMG_ABILITY_PROTECT_UW);
|
||||
MANA_IMAGES.put("protectW", FSkinImage.IMG_ABILITY_PROTECT_W);
|
||||
}
|
||||
|
||||
public static void drawManaCost(Graphics g, ManaCost manaCost, float x, float y, final float imageSize) {
|
||||
if (manaCost.isNoCost()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final int genericManaCost = manaCost.getGenericCost();
|
||||
final boolean hasGeneric = (genericManaCost > 0) || manaCost.isPureGeneric();
|
||||
final float dx = imageSize;
|
||||
|
||||
if (hasGeneric) {
|
||||
for (final ManaCostShard s : manaCost) { //render X shards before generic
|
||||
if (s == ManaCostShard.X) {
|
||||
drawSymbol(s.getImageKey(), g, x, y, imageSize, imageSize);
|
||||
x += dx;
|
||||
}
|
||||
}
|
||||
|
||||
final String sGeneric = Integer.toString(genericManaCost);
|
||||
drawSymbol(sGeneric, g, x, y, imageSize, imageSize);
|
||||
x += dx;
|
||||
|
||||
for (final ManaCostShard s : manaCost) { //render non-X shards after generic
|
||||
if (s != ManaCostShard.X) {
|
||||
drawSymbol(s.getImageKey(), g, x, y, imageSize, imageSize);
|
||||
x += dx;
|
||||
}
|
||||
}
|
||||
}
|
||||
else { //if no generic, just render shards in order
|
||||
for (final ManaCostShard s : manaCost) {
|
||||
drawSymbol(s.getImageKey(), g, x, y, imageSize, imageSize);
|
||||
x += dx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void drawColorSet(Graphics g, ColorSet colorSet, float x, float y, final float imageSize) {
|
||||
drawColorSet(g, colorSet, x, y, imageSize, false);
|
||||
}
|
||||
public static void drawColorSet(Graphics g, ColorSet colorSet, float x, float y, final float imageSize, boolean vertical) {
|
||||
final float dx = imageSize;
|
||||
|
||||
for (final ManaCostShard s : colorSet.getOrderedShards()) {
|
||||
drawSymbol(s.getImageKey(), g, x, y, imageSize, imageSize);
|
||||
if (!vertical)
|
||||
x += dx;
|
||||
else
|
||||
y += dx;
|
||||
}
|
||||
}
|
||||
|
||||
public static void drawOther(final Graphics g, String s, float x, final float y, final float w, final float h, boolean rotate) {
|
||||
if (s.length() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
final float dx = w;
|
||||
|
||||
StringTokenizer tok = new StringTokenizer(s, " ");
|
||||
while (tok.hasMoreTokens()) {
|
||||
String symbol = tok.nextToken();
|
||||
FSkinImage image = MANA_IMAGES.get(symbol);
|
||||
if (image == null) {
|
||||
BugReporter.reportBug("Symbol not recognized \"" + symbol + "\" in string: " + s);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(rotate) {
|
||||
g.drawRotatedImage(image.getTextureRegion(), x, y, w, h, x+w /2, y+h /2,90);
|
||||
}
|
||||
else
|
||||
g.drawImage(image, x, y, w, h);
|
||||
|
||||
x += dx;
|
||||
}
|
||||
}
|
||||
|
||||
public static void drawSymbol(final String imageName, final Graphics g, final float x, final float y, final float w, final float h) {
|
||||
g.drawImage(MANA_IMAGES.get(imageName), x, y, w, h);
|
||||
}
|
||||
|
||||
public static float getWidth(final ManaCost manaCost, float imageSize) {
|
||||
return manaCost.getGlyphCount() * imageSize;
|
||||
}
|
||||
|
||||
public static float getWidth(final ColorSet colorSet, float imageSize) {
|
||||
return Math.max(colorSet.countColors(), 1) * imageSize;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package forge.adventure.libgdxgui.card;
|
||||
|
||||
import com.badlogic.gdx.graphics.Texture;
|
||||
import forge.adventure.libgdxgui.Forge;
|
||||
import forge.adventure.libgdxgui.Graphics;
|
||||
import forge.adventure.libgdxgui.assets.FImage;
|
||||
import forge.adventure.libgdxgui.assets.ImageCache;
|
||||
import forge.adventure.libgdxgui.card.CardRenderer.CardStackPosition;
|
||||
import forge.game.card.CardView;
|
||||
import forge.item.PaperCard;
|
||||
import forge.adventure.libgdxgui.toolbox.FCardPanel;
|
||||
|
||||
public class CardImage implements FImage {
|
||||
private final PaperCard card;
|
||||
private Texture image;
|
||||
|
||||
public CardImage(PaperCard card0) {
|
||||
card = card0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getWidth() {
|
||||
if (image != null) {
|
||||
return image.getWidth();
|
||||
}
|
||||
return ImageCache.defaultImage.getWidth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getHeight() {
|
||||
return getWidth() * FCardPanel.ASPECT_RATIO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Graphics g, float x, float y, float w, float h) {
|
||||
if (image == null) { //attempt to retrieve card image if needed
|
||||
image = ImageCache.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);
|
||||
|
||||
return; //can't draw anything if can't be loaded yet
|
||||
}
|
||||
}
|
||||
|
||||
if (image == ImageCache.defaultImage) {
|
||||
CardImageRenderer.drawCardImage(g, CardView.getCardForUi(card), false, x, y, w, h, CardStackPosition.Top);
|
||||
}
|
||||
else {
|
||||
if (Forge.enableUIMask.equals("Full")) {
|
||||
if (ImageCache.isBorderlessCardArt(image))
|
||||
g.drawImage(image, x, y, w, h);
|
||||
else {
|
||||
float radius = (h - w)/8;
|
||||
g.drawborderImage(ImageCache.borderColor(image), x, y, w, h);
|
||||
g.drawImage(ImageCache.croppedBorderImage(image), x+radius/2.2f, y+radius/2, w*0.96f, h*0.96f);
|
||||
}
|
||||
} else if (Forge.enableUIMask.equals("Crop")) {
|
||||
g.drawImage(ImageCache.croppedBorderImage(image), x, y, w, h);
|
||||
} else
|
||||
g.drawImage(image, x, y, w, h);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,587 @@
|
||||
package forge.adventure.libgdxgui.card;
|
||||
|
||||
import com.badlogic.gdx.graphics.Color;
|
||||
import com.badlogic.gdx.graphics.Texture;
|
||||
import com.badlogic.gdx.utils.Align;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import forge.adventure.libgdxgui.Forge;
|
||||
import forge.adventure.libgdxgui.Graphics;
|
||||
import forge.adventure.libgdxgui.assets.*;
|
||||
import forge.card.CardEdition;
|
||||
import forge.card.CardRarity;
|
||||
import forge.adventure.libgdxgui.card.CardRenderer.CardStackPosition;
|
||||
import forge.card.mana.ManaCost;
|
||||
import forge.game.GameView;
|
||||
import forge.game.card.CardView;
|
||||
import forge.game.card.CardView.CardStateView;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.gui.card.CardDetailUtil;
|
||||
import forge.gui.card.CardDetailUtil.DetailColors;
|
||||
import forge.localinstance.properties.ForgeConstants;
|
||||
import forge.localinstance.properties.ForgePreferences;
|
||||
import forge.model.FModel;
|
||||
import forge.adventure.libgdxgui.screens.FScreen;
|
||||
import forge.adventure.libgdxgui.screens.match.MatchController;
|
||||
import forge.util.CardTranslation;
|
||||
import forge.adventure.libgdxgui.util.Utils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static forge.adventure.libgdxgui.card.CardRenderer.CROP_MULTIPLIER;
|
||||
import static forge.adventure.libgdxgui.card.CardRenderer.isModernFrame;
|
||||
|
||||
public class CardImageRenderer {
|
||||
private static final float BASE_IMAGE_WIDTH = 360;
|
||||
private static final float BASE_IMAGE_HEIGHT = 504;
|
||||
private static float MANA_SYMBOL_SIZE, PT_BOX_WIDTH, HEADER_PADDING, BORDER_THICKNESS;
|
||||
private static FSkinFont NAME_FONT, TYPE_FONT, TEXT_FONT, PT_FONT;
|
||||
private static float prevImageWidth, prevImageHeight;
|
||||
private static final float BLACK_BORDER_THICKNESS_RATIO = 0.021f;
|
||||
|
||||
public static void forceStaticFieldUpdate() {
|
||||
//force static fields to be updated the next time a card image is rendered
|
||||
prevImageWidth = 0;
|
||||
prevImageHeight = 0;
|
||||
forgeArt.clear();
|
||||
}
|
||||
|
||||
private static void updateStaticFields(float w, float h) {
|
||||
if (w == prevImageWidth && h == prevImageHeight) {
|
||||
//for performance sake, only update static fields if card image size is different than previous rendered card
|
||||
return;
|
||||
}
|
||||
|
||||
float ratio = Math.min(w / BASE_IMAGE_WIDTH, h / BASE_IMAGE_HEIGHT);
|
||||
|
||||
MANA_SYMBOL_SIZE = 20 * ratio;
|
||||
PT_BOX_WIDTH = 56 * ratio;
|
||||
HEADER_PADDING = 5 * ratio;
|
||||
NAME_FONT = FSkinFont.forHeight(MANA_SYMBOL_SIZE);
|
||||
TYPE_FONT = FSkinFont.forHeight(MANA_SYMBOL_SIZE * 0.9f);
|
||||
TEXT_FONT = FSkinFont.forHeight(MANA_SYMBOL_SIZE * 0.95f);
|
||||
PT_FONT = NAME_FONT;
|
||||
BORDER_THICKNESS = Math.max(1.5f * ratio, 1f); //don't let border go below 1
|
||||
|
||||
prevImageWidth = w;
|
||||
prevImageHeight = h;
|
||||
}
|
||||
|
||||
public static void drawFaceDownCard(CardView card, Graphics g, float x, float y, float w, float h) {
|
||||
//try to draw the card sleeves first
|
||||
if (FSkin.getSleeves().get(card.getOwner()) != null)
|
||||
g.drawImage(FSkin.getSleeves().get(card.getOwner()), x, y, w, h);
|
||||
else
|
||||
drawArt(g, x, y, w, h);
|
||||
}
|
||||
|
||||
public static void drawCardImage(Graphics g, CardView card, boolean altState, float x, float y, float w, float h, CardStackPosition pos) {
|
||||
updateStaticFields(w, h);
|
||||
|
||||
float blackBorderThickness = w * BLACK_BORDER_THICKNESS_RATIO;
|
||||
g.fillRect(Color.BLACK, x, y, w, h);
|
||||
x += blackBorderThickness;
|
||||
y += blackBorderThickness;
|
||||
w -= 2 * blackBorderThickness;
|
||||
h -= 2 * blackBorderThickness;
|
||||
|
||||
final CardStateView state = card.getState(altState);
|
||||
final boolean canShow = MatchController.instance.mayView(card);
|
||||
|
||||
if (!canShow) {
|
||||
drawFaceDownCard(card, g, x, y, w, h);
|
||||
return;
|
||||
}
|
||||
|
||||
//determine colors for borders
|
||||
final List<DetailColors> borderColors;
|
||||
final boolean isFaceDown = card.isFaceDown();
|
||||
if (isFaceDown) {
|
||||
borderColors = ImmutableList.of(DetailColors.FACE_DOWN);
|
||||
}
|
||||
else {
|
||||
borderColors = CardDetailUtil.getBorderColors(state, canShow);
|
||||
}
|
||||
Color[] colors = fillColorBackground(g, borderColors, x, y, w, h);
|
||||
|
||||
float artInset = blackBorderThickness * 0.5f;
|
||||
float outerBorderThickness = 2 * blackBorderThickness - artInset;
|
||||
x += outerBorderThickness;
|
||||
y += outerBorderThickness;
|
||||
w -= 2 * outerBorderThickness;
|
||||
float headerHeight = Math.max(MANA_SYMBOL_SIZE + 2 * HEADER_PADDING, 2 * NAME_FONT.getCapHeight()) + 2;
|
||||
|
||||
//draw header containing name and mana cost
|
||||
Color[] headerColors = FSkinColor.tintColors(Color.WHITE, colors, CardRenderer.NAME_BOX_TINT);
|
||||
drawHeader(g, card, state, headerColors, x, y, w, headerHeight);
|
||||
|
||||
if (pos == CardStackPosition.BehindVert) { return; } //remaining rendering not needed if card is behind another card in a vertical stack
|
||||
boolean onTop = (pos == CardStackPosition.Top);
|
||||
|
||||
y += headerHeight;
|
||||
|
||||
float artWidth = w - 2 * artInset;
|
||||
float artHeight = artWidth / CardRenderer.CARD_ART_RATIO;
|
||||
float typeBoxHeight = 2 * TYPE_FONT.getCapHeight();
|
||||
float ptBoxHeight = 0;
|
||||
float textBoxHeight = h - headerHeight - artHeight - typeBoxHeight - outerBorderThickness - artInset;
|
||||
if (state.isCreature() || state.isPlaneswalker() || state.getType().hasSubtype("Vehicle")) {
|
||||
//if P/T box needed, make room for it
|
||||
ptBoxHeight = 2 * PT_FONT.getCapHeight();
|
||||
textBoxHeight -= ptBoxHeight;
|
||||
}
|
||||
else {
|
||||
textBoxHeight -= 2 * artInset;
|
||||
}
|
||||
float minTextBoxHeight = 2 * headerHeight;
|
||||
if (textBoxHeight < minTextBoxHeight) {
|
||||
if (textBoxHeight < minTextBoxHeight) {
|
||||
artHeight -= (minTextBoxHeight - textBoxHeight); //subtract from art height if text box not big enough otherwise
|
||||
textBoxHeight = minTextBoxHeight;
|
||||
if (artHeight < 0) {
|
||||
textBoxHeight += artHeight;
|
||||
artHeight = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//draw art box with Forge icon
|
||||
if (artHeight > 0) {
|
||||
drawArt(g, x + artInset, y, artWidth, artHeight);
|
||||
y += artHeight;
|
||||
}
|
||||
|
||||
//draw type line
|
||||
drawTypeLine(g, card, state, canShow, headerColors, x, y, w, typeBoxHeight);
|
||||
y += typeBoxHeight;
|
||||
|
||||
//draw text box
|
||||
Color[] textBoxColors = FSkinColor.tintColors(Color.WHITE, colors, CardRenderer.TEXT_BOX_TINT);
|
||||
drawTextBox(g, card, state, textBoxColors, x + artInset, y, w - 2 * artInset, textBoxHeight, onTop);
|
||||
y += textBoxHeight;
|
||||
|
||||
//draw P/T box
|
||||
if (onTop && ptBoxHeight > 0) {
|
||||
//only needed if on top since otherwise P/T will be hidden
|
||||
Color[] ptColors = FSkinColor.tintColors(Color.WHITE, colors, CardRenderer.PT_BOX_TINT);
|
||||
drawPtBox(g, card, state, ptColors, x, y - 2 * artInset, w, ptBoxHeight);
|
||||
}
|
||||
}
|
||||
|
||||
private static void drawHeader(Graphics g, CardView card, CardStateView state, Color[] colors, float x, float y, float w, float h) {
|
||||
fillColorBackground(g, colors, x, y, w, h);
|
||||
g.drawRect(BORDER_THICKNESS, Color.BLACK, x, y, w, h);
|
||||
|
||||
float padding = h / 8;
|
||||
|
||||
//draw mana cost for card
|
||||
float manaCostWidth = 0;
|
||||
ManaCost mainManaCost = state.getManaCost();
|
||||
if (card.isSplitCard() && card.getAlternateState() != null) {
|
||||
//handle rendering both parts of split card
|
||||
mainManaCost = card.getLeftSplitState().getManaCost();
|
||||
ManaCost otherManaCost = card.getAlternateState().getManaCost();
|
||||
manaCostWidth = CardFaceSymbols.getWidth(otherManaCost, MANA_SYMBOL_SIZE) + HEADER_PADDING;
|
||||
CardFaceSymbols.drawManaCost(g, otherManaCost, x + w - manaCostWidth, y + (h - MANA_SYMBOL_SIZE) / 2, MANA_SYMBOL_SIZE);
|
||||
//draw "//" between two parts of mana cost
|
||||
manaCostWidth += NAME_FONT.getBounds("//").width + HEADER_PADDING;
|
||||
g.drawText("//", NAME_FONT, Color.BLACK, x + w - manaCostWidth, y, w, h, false, Align.left, true);
|
||||
}
|
||||
manaCostWidth += CardFaceSymbols.getWidth(mainManaCost, MANA_SYMBOL_SIZE) + HEADER_PADDING;
|
||||
CardFaceSymbols.drawManaCost(g, mainManaCost, x + w - manaCostWidth, y + (h - MANA_SYMBOL_SIZE) / 2, MANA_SYMBOL_SIZE);
|
||||
|
||||
//draw name for card
|
||||
x += padding;
|
||||
w -= 2 * padding;
|
||||
g.drawText(CardTranslation.getTranslatedName(state.getName()), NAME_FONT, Color.BLACK, x, y, w - manaCostWidth - padding, h, false, Align.left, true);
|
||||
}
|
||||
|
||||
public static final FBufferedImage forgeArt;
|
||||
static {
|
||||
final float logoWidth = FSkinImage.LOGO.getWidth();
|
||||
final float logoHeight = FSkinImage.LOGO.getHeight();
|
||||
float h = logoHeight * 1.1f;
|
||||
float w = h * CardRenderer.CARD_ART_RATIO;
|
||||
forgeArt = new FBufferedImage(w, h) {
|
||||
@Override
|
||||
protected void draw(Graphics g, float w, float h) {
|
||||
g.drawImage(FSkinTexture.BG_TEXTURE, 0, 0, w, h);
|
||||
g.fillRect(FScreen.TEXTURE_OVERLAY_COLOR, 0, 0, w, h);
|
||||
g.drawImage(FSkinImage.LOGO, (w - logoWidth) / 2, (h - logoHeight) / 2, logoWidth, logoHeight);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static void drawArt(Graphics g, float x, float y, float w, float h) {
|
||||
g.drawImage(forgeArt, x, y, w, h);
|
||||
g.drawRect(BORDER_THICKNESS, Color.BLACK, x, y, w, h);
|
||||
}
|
||||
|
||||
private static void drawTypeLine(Graphics g, CardView card, CardStateView state, boolean canShow, Color[] colors, float x, float y, float w, float h) {
|
||||
fillColorBackground(g, colors, x, y, w, h);
|
||||
g.drawRect(BORDER_THICKNESS, Color.BLACK, x, y, w, h);
|
||||
|
||||
float padding = h / 8;
|
||||
|
||||
//draw square icon for rarity
|
||||
float iconSize = h * 0.55f;
|
||||
float iconPadding = (h - iconSize) / 2;
|
||||
w -= iconSize + iconPadding * 2;
|
||||
g.fillRect(CardRenderer.getRarityColor(state.getRarity()), x + w + iconPadding, y + (h - iconSize) / 2, iconSize, iconSize);
|
||||
|
||||
//draw type
|
||||
x += padding;
|
||||
g.drawText(CardDetailUtil.formatCardType(state, canShow), TYPE_FONT, Color.BLACK, x, y, w, h, false, Align.left, true);
|
||||
}
|
||||
|
||||
//use text renderer to handle mana symbols and reminder text
|
||||
private static final TextRenderer cardTextRenderer = new TextRenderer(true);
|
||||
|
||||
private static void drawTextBox(Graphics g, CardView card, CardStateView state, Color[] colors, float x, float y, float w, float h, boolean onTop) {
|
||||
fillColorBackground(g, colors, x, y, w, h);
|
||||
g.drawRect(BORDER_THICKNESS, Color.BLACK, x, y, w, h);
|
||||
|
||||
if (!onTop) { return; } //remaining rendering only needed if card on top
|
||||
|
||||
if (state.isBasicLand()) {
|
||||
//draw icons for basic lands
|
||||
FSkinImage image;
|
||||
switch (state.getName().replaceFirst("^Snow-Covered ", "")) {
|
||||
case "Plains":
|
||||
image = FSkinImage.MANA_W;
|
||||
break;
|
||||
case "Island":
|
||||
image = FSkinImage.MANA_U;
|
||||
break;
|
||||
case "Swamp":
|
||||
image = FSkinImage.MANA_B;
|
||||
break;
|
||||
case "Mountain":
|
||||
image = FSkinImage.MANA_R;
|
||||
break;
|
||||
case "Forest":
|
||||
image = FSkinImage.MANA_G;
|
||||
break;
|
||||
default:
|
||||
image = FSkinImage.MANA_COLORLESS;
|
||||
break;
|
||||
}
|
||||
float iconSize = h * 0.75f;
|
||||
g.drawImage(image, x + (w - iconSize) / 2, y + (h - iconSize) / 2, iconSize, iconSize);
|
||||
}
|
||||
else {
|
||||
boolean needTranslation = true;
|
||||
if (card.isToken()) {
|
||||
if (card.getCloneOrigin() == null)
|
||||
needTranslation = false;
|
||||
}
|
||||
final String text = !card.isSplitCard() ?
|
||||
card.getText(state, needTranslation ? CardTranslation.getTranslationTexts(state.getName(), "") : null) :
|
||||
card.getText(state, needTranslation ? CardTranslation.getTranslationTexts(card.getLeftSplitState().getName(), card.getRightSplitState().getName()) : null );
|
||||
if (StringUtils.isEmpty(text)) { return; }
|
||||
|
||||
float padding = TEXT_FONT.getCapHeight() * 0.75f;
|
||||
x += padding;
|
||||
y += padding;
|
||||
w -= 2 * padding;
|
||||
h -= 2 * padding;
|
||||
cardTextRenderer.drawText(g, text, TEXT_FONT, Color.BLACK, x, y, w, h, y, h, true, Align.left, true);
|
||||
}
|
||||
}
|
||||
|
||||
private static void drawPtBox(Graphics g, CardView card, CardStateView state, Color[] colors, float x, float y, float w, float h) {
|
||||
List<String> pieces = new ArrayList<>();
|
||||
if (state.isCreature()) {
|
||||
pieces.add(String.valueOf(state.getPower()));
|
||||
pieces.add("/");
|
||||
pieces.add(String.valueOf(state.getToughness()));
|
||||
}
|
||||
else if (state.isPlaneswalker()) {
|
||||
pieces.add(String.valueOf(state.getLoyalty()));
|
||||
}
|
||||
else if (state.getType().hasSubtype("Vehicle")) {
|
||||
// TODO Invert color box for Vehicles?
|
||||
pieces.add("[");
|
||||
pieces.add(String.valueOf(state.getPower()));
|
||||
pieces.add("/");
|
||||
pieces.add(String.valueOf(state.getToughness()));
|
||||
pieces.add("]");
|
||||
}
|
||||
else { return; }
|
||||
|
||||
float padding = Math.round(PT_FONT.getCapHeight() / 4);
|
||||
float totalPieceWidth = -padding;
|
||||
float[] pieceWidths = new float[pieces.size()];
|
||||
for (int i = 0; i < pieces.size(); i++) {
|
||||
float pieceWidth = PT_FONT.getBounds(pieces.get(i)).width + padding;
|
||||
pieceWidths[i] = pieceWidth;
|
||||
totalPieceWidth += pieceWidth;
|
||||
}
|
||||
float boxHeight = PT_FONT.getCapHeight() + PT_FONT.getAscent() + 3 * padding;
|
||||
|
||||
float boxWidth = Math.max(PT_BOX_WIDTH, totalPieceWidth + 2 * padding);
|
||||
x += w - boxWidth;
|
||||
y += h - boxHeight;
|
||||
w = boxWidth;
|
||||
h = boxHeight;
|
||||
|
||||
fillColorBackground(g, colors, x, y, w, h);
|
||||
g.drawRect(BORDER_THICKNESS, Color.BLACK, x, y, w, h);
|
||||
|
||||
x += (boxWidth - totalPieceWidth) / 2;
|
||||
for (int i = 0; i < pieces.size(); i++) {
|
||||
g.drawText(pieces.get(i), PT_FONT, Color.BLACK, x, y, w, h, false, Align.left, true);
|
||||
x += pieceWidths[i];
|
||||
}
|
||||
}
|
||||
|
||||
public static void drawZoom(Graphics g, CardView card, GameView gameView, boolean altState, float x, float y, float w, float h, float dispW, float dispH, boolean isCurrentCard) {
|
||||
boolean canshow = MatchController.instance.mayView(card);
|
||||
Texture image = null;
|
||||
try {
|
||||
image = ImageCache.getImage(card.getState(altState).getImageKey(), true);
|
||||
} catch (Exception ex) {
|
||||
//System.err.println(card.toString()+" : " +ex.getMessage());
|
||||
//TODO: don't know why this is needed, needs further investigation...
|
||||
if (!card.hasAlternateState()) {
|
||||
altState = false;
|
||||
image = ImageCache.getImage(card.getState(altState).getImageKey(), true);
|
||||
}
|
||||
}
|
||||
FImage sleeves = MatchController.getPlayerSleeve(card.getOwner());
|
||||
if (image == null) { //draw details if can't draw zoom
|
||||
drawDetails(g, card, gameView, altState, x, y, w, h);
|
||||
return;
|
||||
}
|
||||
if(card.isToken() && card.getCurrentState().getType().hasSubtype("Effect")
|
||||
&& FModel.getPreferences().getPrefBoolean(ForgePreferences.FPref.UI_DISABLE_IMAGES_EFFECT_CARDS)){
|
||||
drawDetails(g, card, gameView, altState, x, y, w, h);
|
||||
return;
|
||||
}
|
||||
|
||||
if (image == ImageCache.defaultImage) { //support drawing card image manually if card image not found
|
||||
drawCardImage(g, card, altState, x, y, w, h, CardStackPosition.Top);
|
||||
} else {
|
||||
float radius = (h - w)/8;
|
||||
float wh_Adj = ForgeConstants.isGdxPortLandscape && isCurrentCard ? 1.38f:1.0f;
|
||||
float new_w = w*wh_Adj;
|
||||
float new_h = h*wh_Adj;
|
||||
float new_x = ForgeConstants.isGdxPortLandscape && isCurrentCard ? (dispW - new_w) / 2:x;
|
||||
float new_y = ForgeConstants.isGdxPortLandscape && isCurrentCard ? (dispH - new_h) / 2:y;
|
||||
float new_xRotate = (dispW - new_h) /2;
|
||||
float new_yRotate = (dispH - new_w) /2;
|
||||
boolean rotateSplit = FModel.getPreferences().getPrefBoolean(ForgePreferences.FPref.UI_ROTATE_SPLIT_CARDS);
|
||||
boolean rotatePlane = FModel.getPreferences().getPrefBoolean(ForgePreferences.FPref.UI_ROTATE_PLANE_OR_PHENOMENON);
|
||||
float croppedArea = isModernFrame(card) ? CROP_MULTIPLIER : 0.97f;
|
||||
float minusxy = isModernFrame(card) ? 0.0f : 0.13f*radius;
|
||||
if (card.getCurrentState().getSetCode().equals("LEA")||card.getCurrentState().getSetCode().equals("LEB")) {
|
||||
croppedArea = 0.975f;
|
||||
minusxy = 0.135f*radius;
|
||||
}
|
||||
if (rotatePlane && (card.getCurrentState().isPhenomenon() || card.getCurrentState().isPlane())) {
|
||||
if (Forge.enableUIMask.equals("Full")){
|
||||
if (ImageCache.isBorderlessCardArt(image))
|
||||
g.drawRotatedImage(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);
|
||||
g.drawRotatedImage(ImageCache.croppedBorderImage(image), new_x+radius/2-minusxy, new_y+radius/2-minusxy, new_w*croppedArea, new_h*croppedArea, (new_x+radius/2-minusxy) + (new_w*croppedArea) / 2, (new_y+radius/2-minusxy) + (new_h*croppedArea) / 2, -90);
|
||||
}
|
||||
} else if (Forge.enableUIMask.equals("Crop")) {
|
||||
g.drawRotatedImage(ImageCache.croppedBorderImage(image), new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, -90);
|
||||
} else
|
||||
g.drawRotatedImage(image, new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, -90);
|
||||
} else if (rotateSplit && isCurrentCard && card.isSplitCard() && canshow) {
|
||||
boolean isAftermath = card.getText().contains("Aftermath") || card.getAlternateState().getOracleText().contains("Aftermath");
|
||||
if (Forge.enableUIMask.equals("Full")) {
|
||||
if (ImageCache.isBorderlessCardArt(image))
|
||||
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 {
|
||||
g.drawRotatedImage(FSkin.getBorders().get(ImageCache.getFSkinBorders(card)), new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, isAftermath ? 90 : -90);
|
||||
g.drawRotatedImage(ImageCache.croppedBorderImage(image), new_x + radius / 2-minusxy, new_y + radius / 2-minusxy, new_w * croppedArea, new_h * croppedArea, (new_x + radius / 2-minusxy) + (new_w * croppedArea) / 2, (new_y + radius / 2-minusxy) + (new_h * croppedArea) / 2, isAftermath ? 90 : -90);
|
||||
}
|
||||
} else if (Forge.enableUIMask.equals("Crop")) {
|
||||
g.drawRotatedImage(ImageCache.croppedBorderImage(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(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") && canshow) {
|
||||
if (ImageCache.isBorderlessCardArt(image))
|
||||
g.drawImage(image, x, y, w, h);
|
||||
else {
|
||||
g.drawImage(ImageCache.getBorderImage(image.toString()), ImageCache.borderColor(image), x, y, w, h);
|
||||
g.drawImage(ImageCache.croppedBorderImage(image), x + radius / 2.4f-minusxy, y + radius / 2-minusxy, w * croppedArea, h * croppedArea);
|
||||
}
|
||||
} else if (Forge.enableUIMask.equals("Crop") && canshow) {
|
||||
g.drawImage(ImageCache.croppedBorderImage(image), x, y, w, h);
|
||||
} else {
|
||||
if (canshow)
|
||||
g.drawImage(image, x, y, w, h);
|
||||
else // sleeve
|
||||
g.drawImage(sleeves, x, y, w, h);
|
||||
}
|
||||
}
|
||||
}
|
||||
CardRenderer.drawFoilEffect(g, card, x, y, w, h, isCurrentCard && canshow && image != ImageCache.defaultImage);
|
||||
}
|
||||
|
||||
public static void drawDetails(Graphics g, CardView card, GameView gameView, boolean altState, float x, float y, float w, float h) {
|
||||
updateStaticFields(w, h);
|
||||
|
||||
float blackBorderThickness = w * BLACK_BORDER_THICKNESS_RATIO;
|
||||
g.fillRect(Color.BLACK, x, y, w, h);
|
||||
x += blackBorderThickness;
|
||||
y += blackBorderThickness;
|
||||
w -= 2 * blackBorderThickness;
|
||||
h -= 2 * blackBorderThickness;
|
||||
|
||||
final CardStateView state = card.getState(altState);
|
||||
final boolean canShow = MatchController.instance.mayView(card);
|
||||
|
||||
//determine colors for borders
|
||||
final List<DetailColors> borderColors;
|
||||
final boolean isFaceDown = card.isFaceDown();
|
||||
if (isFaceDown) {
|
||||
borderColors = ImmutableList.of(DetailColors.FACE_DOWN);
|
||||
}
|
||||
else {
|
||||
borderColors = CardDetailUtil.getBorderColors(state, canShow);
|
||||
}
|
||||
Color[] colors = fillColorBackground(g, borderColors, x, y, w, h);
|
||||
|
||||
Color idForeColor = FSkinColor.getHighContrastColor(colors[0]);
|
||||
|
||||
float outerBorderThickness = 2 * blackBorderThickness;
|
||||
x += outerBorderThickness;
|
||||
y += outerBorderThickness;
|
||||
w -= 2 * outerBorderThickness;
|
||||
float cardNameBoxHeight = Math.max(MANA_SYMBOL_SIZE + 2 * HEADER_PADDING, 2 * NAME_FONT.getCapHeight()) + 2 * TYPE_FONT.getCapHeight() + 2;
|
||||
|
||||
//draw name/type box
|
||||
Color[] nameBoxColors = FSkinColor.tintColors(Color.WHITE, colors, CardRenderer.NAME_BOX_TINT);
|
||||
drawDetailsNameBox(g, card, state, canShow, nameBoxColors, x, y, w, cardNameBoxHeight);
|
||||
|
||||
float innerBorderThickness = outerBorderThickness / 2;
|
||||
float ptBoxHeight = 2 * PT_FONT.getCapHeight();
|
||||
float textBoxHeight = h - cardNameBoxHeight - ptBoxHeight - outerBorderThickness - 3 * innerBorderThickness;
|
||||
|
||||
y += cardNameBoxHeight + innerBorderThickness;
|
||||
Color[] textBoxColors = FSkinColor.tintColors(Color.WHITE, colors, CardRenderer.TEXT_BOX_TINT);
|
||||
drawDetailsTextBox(g, state, gameView, canShow, textBoxColors, x, y, w, textBoxHeight);
|
||||
|
||||
y += textBoxHeight + innerBorderThickness;
|
||||
Color[] ptColors = FSkinColor.tintColors(Color.WHITE, colors, CardRenderer.PT_BOX_TINT);
|
||||
drawDetailsIdAndPtBox(g, card, state, canShow, idForeColor, ptColors, x, y, w, ptBoxHeight);
|
||||
}
|
||||
|
||||
public static Color[] fillColorBackground(Graphics g, List<DetailColors> backColors, float x, float y, float w, float h) {
|
||||
Color[] colors = new Color[backColors.size()];
|
||||
for (int i = 0; i < colors.length; i++) {
|
||||
DetailColors dc = backColors.get(i);
|
||||
colors[i] = FSkinColor.fromRGB(dc.r, dc.g, dc.b);
|
||||
}
|
||||
fillColorBackground(g, colors, x, y, w, h);
|
||||
return colors;
|
||||
}
|
||||
public static void fillColorBackground(Graphics g, Color[] colors, float x, float y, float w, float h) {
|
||||
switch (colors.length) {
|
||||
case 1:
|
||||
g.fillRect(colors[0], x, y, w, h);
|
||||
break;
|
||||
case 2:
|
||||
g.fillGradientRect(colors[0], colors[1], false, x, y, w, h);
|
||||
break;
|
||||
case 3:
|
||||
float halfWidth = w / 2;
|
||||
g.fillGradientRect(colors[0], colors[1], false, x, y, halfWidth, h);
|
||||
g.fillGradientRect(colors[1], colors[2], false, x + halfWidth, y, halfWidth, h);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void drawDetailsNameBox(Graphics g, CardView card, CardStateView state, boolean canShow, Color[] colors, float x, float y, float w, float h) {
|
||||
fillColorBackground(g, colors, x, y, w, h);
|
||||
g.drawRect(BORDER_THICKNESS, Color.BLACK, x, y, w, h);
|
||||
|
||||
float padding = h / 8;
|
||||
|
||||
//make sure name/mana cost row height is tall enough for both
|
||||
h = Math.max(MANA_SYMBOL_SIZE + 2 * HEADER_PADDING, 2 * NAME_FONT.getCapHeight());
|
||||
|
||||
//draw mana cost for card
|
||||
float manaCostWidth = 0;
|
||||
if (canShow) {
|
||||
ManaCost mainManaCost = state.getManaCost();
|
||||
if (card.isSplitCard() && card.hasAlternateState() && !card.isFaceDown() && card.getZone() != ZoneType.Stack) { //only display current state's mana cost when on stack
|
||||
//handle rendering both parts of split card
|
||||
mainManaCost = card.getLeftSplitState().getManaCost();
|
||||
ManaCost otherManaCost = card.getAlternateState().getManaCost();
|
||||
manaCostWidth = CardFaceSymbols.getWidth(otherManaCost, MANA_SYMBOL_SIZE) + HEADER_PADDING;
|
||||
CardFaceSymbols.drawManaCost(g, otherManaCost, x + w - manaCostWidth, y + (h - MANA_SYMBOL_SIZE) / 2, MANA_SYMBOL_SIZE);
|
||||
//draw "//" between two parts of mana cost
|
||||
manaCostWidth += NAME_FONT.getBounds("//").width + HEADER_PADDING;
|
||||
g.drawText("//", NAME_FONT, Color.BLACK, x + w - manaCostWidth, y, w, h, false, Align.left, true);
|
||||
}
|
||||
manaCostWidth += CardFaceSymbols.getWidth(mainManaCost, MANA_SYMBOL_SIZE) + HEADER_PADDING;
|
||||
CardFaceSymbols.drawManaCost(g, mainManaCost, x + w - manaCostWidth, y + (h - MANA_SYMBOL_SIZE) / 2, MANA_SYMBOL_SIZE);
|
||||
}
|
||||
|
||||
//draw name for card
|
||||
x += padding;
|
||||
w -= 2 * padding;
|
||||
g.drawText(CardDetailUtil.formatCardName(card, canShow, state == card.getAlternateState()), NAME_FONT, Color.BLACK, x, y, w - manaCostWidth - padding, h, false, Align.left, true);
|
||||
|
||||
//draw type and set label for card
|
||||
y += h;
|
||||
h = 2 * TYPE_FONT.getCapHeight();
|
||||
|
||||
String set = state.getSetCode();
|
||||
CardRarity rarity = state.getRarity();
|
||||
if (!canShow) {
|
||||
set = CardEdition.UNKNOWN.getCode();
|
||||
rarity = CardRarity.Unknown;
|
||||
}
|
||||
if (!StringUtils.isEmpty(set)) {
|
||||
float setWidth = CardRenderer.getSetWidth(TYPE_FONT, set);
|
||||
CardRenderer.drawSetLabel(g, TYPE_FONT, set, rarity, x + w + padding - setWidth - HEADER_PADDING + CardRenderer.SET_BOX_MARGIN, y + CardRenderer.SET_BOX_MARGIN, setWidth, h - CardRenderer.SET_BOX_MARGIN);
|
||||
w -= setWidth; //reduce available width for type
|
||||
}
|
||||
|
||||
g.drawText(CardDetailUtil.formatCardType(state, canShow), TYPE_FONT, Color.BLACK, x, y, w, h, false, Align.left, true);
|
||||
}
|
||||
|
||||
private static void drawDetailsTextBox(Graphics g, CardStateView state, GameView gameView, boolean canShow, Color[] colors, float x, float y, float w, float h) {
|
||||
fillColorBackground(g, colors, x, y, w, h);
|
||||
g.drawRect(BORDER_THICKNESS, Color.BLACK, x, y, w, h);
|
||||
|
||||
float padX = TEXT_FONT.getCapHeight() / 2;
|
||||
float padY = padX + Utils.scale(2); //add a little more vertical padding
|
||||
x += padX;
|
||||
y += padY;
|
||||
w -= 2 * padX;
|
||||
h -= 2 * padY;
|
||||
cardTextRenderer.drawText(g, CardDetailUtil.composeCardText(state, gameView, canShow), TEXT_FONT, Color.BLACK, x, y, w, h, y, h, true, Align.left, false);
|
||||
}
|
||||
|
||||
private static void drawDetailsIdAndPtBox(Graphics g, CardView card, CardStateView state, boolean canShow, Color idForeColor, Color[] colors, float x, float y, float w, float h) {
|
||||
float idWidth = 0;
|
||||
if (canShow) {
|
||||
String idText = CardDetailUtil.formatCardId(state);
|
||||
g.drawText(idText, TYPE_FONT, idForeColor, x, y + TYPE_FONT.getCapHeight() / 2, w, h, false, Align.left, false);
|
||||
idWidth = TYPE_FONT.getBounds(idText).width;
|
||||
}
|
||||
|
||||
String ptText = CardDetailUtil.formatPowerToughness(state, canShow);
|
||||
if (StringUtils.isEmpty(ptText)) { return; }
|
||||
|
||||
float padding = PT_FONT.getCapHeight() / 2;
|
||||
float boxWidth = Math.min(PT_FONT.getBounds(ptText).width + 2 * padding,
|
||||
w - idWidth - padding); //prevent box overlapping ID
|
||||
x += w - boxWidth;
|
||||
w = boxWidth;
|
||||
|
||||
fillColorBackground(g, colors, x, y, w, h);
|
||||
g.drawRect(BORDER_THICKNESS, Color.BLACK, x, y, w, h);
|
||||
g.drawText(ptText, PT_FONT, Color.BLACK, x, y, w, h, false, Align.center, true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package forge.adventure.libgdxgui.card;
|
||||
|
||||
import com.badlogic.gdx.math.Vector2;
|
||||
import com.badlogic.gdx.utils.Align;
|
||||
import forge.item.PaperCard;
|
||||
import forge.adventure.libgdxgui.toolbox.FChoiceList;
|
||||
import forge.adventure.libgdxgui.toolbox.FLabel;
|
||||
|
||||
public class CardListPreview extends FLabel {
|
||||
public static final float CARD_PREVIEW_RATIO = 0.5f;
|
||||
|
||||
private final FChoiceList<PaperCard> list;
|
||||
|
||||
public CardListPreview(FChoiceList<PaperCard> list0) {
|
||||
super(new FLabel.Builder().iconScaleFactor(1).insets(new Vector2(0, 0))
|
||||
.iconInBackground(true).align(Align.center));
|
||||
list = list0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean tap(float x, float y, int count) {
|
||||
return zoom();
|
||||
}
|
||||
@Override
|
||||
public boolean longPress(float x, float y) {
|
||||
return zoom();
|
||||
}
|
||||
private boolean zoom() {
|
||||
int index = list.getSelectedIndex();
|
||||
if (index == -1) { return false; }
|
||||
CardZoom.show(list.extractListData(), index, list);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean fling(float velocityX, float velocityY) {
|
||||
if (Math.abs(velocityX) > Math.abs(velocityY)) {
|
||||
int selectedIndex = list.getSelectedIndex();
|
||||
if (velocityX > 0) {
|
||||
if (selectedIndex > 0) {
|
||||
list.setSelectedIndex(selectedIndex - 1);
|
||||
}
|
||||
}
|
||||
else if (selectedIndex < list.getCount() - 1) {
|
||||
list.setSelectedIndex(selectedIndex + 1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,384 @@
|
||||
package forge.adventure.libgdxgui.card;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.math.Rectangle;
|
||||
import com.badlogic.gdx.utils.Align;
|
||||
import forge.adventure.libgdxgui.Forge;
|
||||
import forge.adventure.libgdxgui.Graphics;
|
||||
import forge.adventure.libgdxgui.assets.FSkinImage;
|
||||
import forge.adventure.libgdxgui.screens.match.MatchController;
|
||||
import forge.adventure.libgdxgui.toolbox.FCardPanel;
|
||||
import forge.adventure.libgdxgui.toolbox.FDialog;
|
||||
import forge.adventure.libgdxgui.toolbox.FOverlay;
|
||||
import forge.adventure.libgdxgui.util.Utils;
|
||||
import forge.deck.ArchetypeDeckGenerator;
|
||||
import forge.deck.CardThemedDeckGenerator;
|
||||
import forge.deck.CommanderDeckGenerator;
|
||||
import forge.deck.DeckProxy;
|
||||
import forge.game.GameView;
|
||||
import forge.game.card.CardView;
|
||||
import forge.gamemodes.planarconquest.ConquestCommander;
|
||||
import forge.item.IPaperCard;
|
||||
import forge.item.InventoryItem;
|
||||
import forge.localinstance.properties.ForgePreferences;
|
||||
import forge.localinstance.properties.ForgePreferences.FPref;
|
||||
import forge.model.FModel;
|
||||
import forge.util.Localizer;
|
||||
import forge.util.collect.FCollectionView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
public class CardZoom extends FOverlay {
|
||||
private static final float REQ_AMOUNT = Utils.AVG_FINGER_WIDTH;
|
||||
|
||||
private static final CardZoom cardZoom = new CardZoom();
|
||||
private static final ForgePreferences prefs = FModel.getPreferences();
|
||||
private static List<?> items;
|
||||
private static int currentIndex, initialIndex;
|
||||
private static CardView currentCard, prevCard, nextCard;
|
||||
private static boolean zoomMode = true;
|
||||
private static boolean oneCardView = prefs.getPrefBoolean(FPref.UI_SINGLE_CARD_ZOOM);
|
||||
private float totalZoomAmount;
|
||||
private static ActivateHandler activateHandler;
|
||||
private static String currentActivateAction;
|
||||
private static Rectangle flipIconBounds;
|
||||
private static Rectangle mutateIconBounds;
|
||||
private static boolean showAltState;
|
||||
private static boolean showBackSide = false;
|
||||
private static boolean showMerged = false;
|
||||
|
||||
public static void show(Object item) {
|
||||
show(item, false);
|
||||
}
|
||||
public static void show(Object item, boolean showbackside) {
|
||||
List<Object> items0 = new ArrayList<>();
|
||||
items0.add(item);
|
||||
showBackSide = showbackside; //reverse the displayed zoomed card for the choice list
|
||||
show(items0, 0, null);
|
||||
}
|
||||
public static void show(FCollectionView<?> items0, int currentIndex0, ActivateHandler activateHandler0) {
|
||||
show((List<?>)items0, currentIndex0, activateHandler0);
|
||||
}
|
||||
public static void show(final List<?> items0, int currentIndex0, ActivateHandler activateHandler0) {
|
||||
items = items0;
|
||||
activateHandler = activateHandler0;
|
||||
currentIndex = currentIndex0;
|
||||
initialIndex = currentIndex0;
|
||||
currentCard = getCardView(items.get(currentIndex));
|
||||
prevCard = currentIndex > 0 ? getCardView(items.get(currentIndex - 1)) : null;
|
||||
nextCard = currentIndex < items.size() - 1 ? getCardView(items.get(currentIndex + 1)) : null;
|
||||
onCardChanged();
|
||||
cardZoom.show();
|
||||
}
|
||||
|
||||
public static boolean isOpen() {
|
||||
return cardZoom.isVisible();
|
||||
}
|
||||
|
||||
public static void hideZoom() {
|
||||
cardZoom.hide();
|
||||
}
|
||||
|
||||
private CardZoom() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVisible(boolean visible0) {
|
||||
if (this.isVisible() == visible0) { return; }
|
||||
|
||||
super.setVisible(visible0);
|
||||
|
||||
//update selected index when hidden if current index is different than initial index
|
||||
if (!visible0 && activateHandler != null && currentIndex != initialIndex) {
|
||||
activateHandler.setSelectedIndex(currentIndex);
|
||||
}
|
||||
}
|
||||
|
||||
private static void incrementCard(int dir) {
|
||||
if (dir > 0) {
|
||||
if (currentIndex == items.size() - 1) { return; }
|
||||
currentIndex++;
|
||||
|
||||
prevCard = currentCard;
|
||||
currentCard = nextCard;
|
||||
nextCard = currentIndex < items.size() - 1 ? getCardView(items.get(currentIndex + 1)) : null;
|
||||
}
|
||||
else {
|
||||
if (currentIndex == 0) { return; }
|
||||
currentIndex--;
|
||||
|
||||
nextCard = currentCard;
|
||||
currentCard = prevCard;
|
||||
prevCard = currentIndex > 0 ? getCardView(items.get(currentIndex - 1)) : null;
|
||||
}
|
||||
onCardChanged();
|
||||
}
|
||||
|
||||
private static void onCardChanged() {
|
||||
mutateIconBounds = null;
|
||||
if (activateHandler != null) {
|
||||
currentActivateAction = activateHandler.getActivateAction(currentIndex);
|
||||
}
|
||||
if (MatchController.instance.mayFlip(currentCard)) {
|
||||
flipIconBounds = new Rectangle();
|
||||
} else {
|
||||
flipIconBounds = null;
|
||||
}
|
||||
if (currentCard != null) {
|
||||
if (currentCard.getMergedCardsCollection() != null )
|
||||
if (currentCard.getMergedCardsCollection().size() > 0)
|
||||
mutateIconBounds = new Rectangle();
|
||||
}
|
||||
showAltState = false;
|
||||
}
|
||||
|
||||
private static CardView getCardView(Object item) {
|
||||
if (item instanceof Entry) {
|
||||
item = ((Entry<?, ?>)item).getKey();
|
||||
}
|
||||
if (item instanceof CardView) {
|
||||
return (CardView)item;
|
||||
}
|
||||
if (item instanceof DeckProxy) {
|
||||
if (item instanceof CardThemedDeckGenerator){
|
||||
return CardView.getCardForUi(((CardThemedDeckGenerator)item).getPaperCard());
|
||||
}else if (item instanceof CommanderDeckGenerator){
|
||||
return CardView.getCardForUi(((CommanderDeckGenerator)item).getPaperCard());
|
||||
}else if (item instanceof ArchetypeDeckGenerator){
|
||||
return CardView.getCardForUi(((ArchetypeDeckGenerator)item).getPaperCard());
|
||||
}else{
|
||||
DeckProxy deck = ((DeckProxy)item);
|
||||
return new CardView(-1, null, deck.getName(), null, deck.getImageKey(false));
|
||||
}
|
||||
|
||||
}
|
||||
if (item instanceof IPaperCard) {
|
||||
return CardView.getCardForUi((IPaperCard)item);
|
||||
}
|
||||
if (item instanceof ConquestCommander) {
|
||||
return CardView.getCardForUi(((ConquestCommander)item).getCard());
|
||||
}
|
||||
if (item instanceof InventoryItem) {
|
||||
InventoryItem ii = (InventoryItem)item;
|
||||
return new CardView(-1, null, ii.getName(), null, ii.getImageKey(false));
|
||||
}
|
||||
return new CardView(-1, null, item.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean tap(float x, float y, int count) {
|
||||
if (mutateIconBounds != null && mutateIconBounds.contains(x, y)) {
|
||||
if(showMerged) {
|
||||
showMerged = false;
|
||||
} else {
|
||||
showMerged = true;
|
||||
show(currentCard.getMergedCardsCollection(), 0, null);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (flipIconBounds != null && flipIconBounds.contains(x, y)) {
|
||||
if (currentCard.isFaceDown() && currentCard.getBackup() != null) {
|
||||
if (currentCard.getBackup().hasBackSide()) {
|
||||
show(currentCard.getBackup());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (!showBackSide)
|
||||
showAltState = !showAltState;
|
||||
else
|
||||
showBackSide = !showBackSide;
|
||||
return true;
|
||||
}
|
||||
hide();
|
||||
showBackSide = false;
|
||||
showAltState = false;
|
||||
showMerged = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean fling(float velocityX, float velocityY) {
|
||||
if (Math.abs(velocityX) > Math.abs(velocityY)) {
|
||||
incrementCard(velocityX > 0 ? -1 : 1);
|
||||
showBackSide = false;
|
||||
showAltState = false;
|
||||
return true;
|
||||
}
|
||||
if (velocityY > 0) {
|
||||
zoomMode = !zoomMode;
|
||||
showBackSide = false;
|
||||
showAltState = false;
|
||||
return true;
|
||||
}
|
||||
if (currentActivateAction != null && activateHandler != null) {
|
||||
hide();
|
||||
showBackSide = false;
|
||||
showAltState = false;
|
||||
activateHandler.activate(currentIndex);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void setOneCardView(boolean oneCardView0) {
|
||||
if (oneCardView == oneCardView0 || Forge.isLandscapeMode()) { return; } //don't allow changing this when in landscape mode
|
||||
|
||||
oneCardView = oneCardView0;
|
||||
prefs.setPref(FPref.UI_SINGLE_CARD_ZOOM, oneCardView0);
|
||||
prefs.save();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean zoom(float x, float y, float amount) {
|
||||
totalZoomAmount += amount;
|
||||
|
||||
if (totalZoomAmount >= REQ_AMOUNT) {
|
||||
setOneCardView(true);
|
||||
totalZoomAmount = 0;
|
||||
}
|
||||
else if (totalZoomAmount <= -REQ_AMOUNT) {
|
||||
setOneCardView(false);
|
||||
totalZoomAmount = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean longPress(float x, float y) {
|
||||
setOneCardView(!oneCardView);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawOverlay(Graphics g) {
|
||||
final GameView gameView = MatchController.instance.getGameView();
|
||||
|
||||
float w = getWidth();
|
||||
float h = getHeight();
|
||||
float messageHeight = FDialog.MSG_HEIGHT;
|
||||
float AspectRatioMultiplier;
|
||||
switch (Forge.extrawide) {
|
||||
case "default":
|
||||
AspectRatioMultiplier = 3; //good for tablets with 16:10 or similar
|
||||
break;
|
||||
case "wide":
|
||||
AspectRatioMultiplier = 2.5f;
|
||||
break;
|
||||
case "extrawide":
|
||||
AspectRatioMultiplier = 2; //good for tall phones with 21:9 or similar
|
||||
break;
|
||||
default:
|
||||
AspectRatioMultiplier = 3;
|
||||
break;
|
||||
}
|
||||
float maxCardHeight = h - AspectRatioMultiplier * messageHeight; //maxheight of currently zoomed card
|
||||
|
||||
float cardWidth, cardHeight, y;
|
||||
|
||||
if (oneCardView && !Forge.isLandscapeMode()) {
|
||||
|
||||
cardWidth = w;
|
||||
cardHeight = FCardPanel.ASPECT_RATIO * cardWidth;
|
||||
|
||||
boolean rotateSplit = FModel.getPreferences().getPrefBoolean(FPref.UI_ROTATE_SPLIT_CARDS);
|
||||
if (currentCard.isSplitCard() && rotateSplit) {
|
||||
// card will be rotated. Make sure that the height does not exceed the width of the view
|
||||
if (cardHeight > Gdx.graphics.getWidth())
|
||||
{
|
||||
cardHeight = Gdx.graphics.getWidth();
|
||||
cardWidth = cardHeight / FCardPanel.ASPECT_RATIO;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
cardWidth = w * 0.5f;
|
||||
cardHeight = FCardPanel.ASPECT_RATIO * cardWidth;
|
||||
|
||||
float maxSideCardHeight = maxCardHeight * 5 / 7;
|
||||
if (cardHeight > maxSideCardHeight) { //prevent card overlapping message bars
|
||||
cardHeight = maxSideCardHeight;
|
||||
cardWidth = cardHeight / FCardPanel.ASPECT_RATIO;
|
||||
}
|
||||
y = (h - cardHeight) / 2;
|
||||
if (prevCard != null) {
|
||||
CardImageRenderer.drawZoom(g, prevCard, gameView, false, 0, y, cardWidth, cardHeight, getWidth(), getHeight(), false);
|
||||
}
|
||||
if (nextCard != null) {
|
||||
CardImageRenderer.drawZoom(g, nextCard, gameView, false, w - cardWidth, y, cardWidth, cardHeight, getWidth(), getHeight(), false);
|
||||
}
|
||||
|
||||
cardWidth = w * 0.7f;
|
||||
cardHeight = FCardPanel.ASPECT_RATIO * cardWidth;
|
||||
}
|
||||
|
||||
if (cardHeight > maxCardHeight) { //prevent card overlapping message bars
|
||||
cardHeight = maxCardHeight;
|
||||
cardWidth = cardHeight / FCardPanel.ASPECT_RATIO;
|
||||
}
|
||||
float x = (w - cardWidth) / 2;
|
||||
y = (h - cardHeight) / 2;
|
||||
if (zoomMode) {
|
||||
CardImageRenderer.drawZoom(g, currentCard, gameView, showBackSide? showBackSide : showAltState, x, y, cardWidth, cardHeight, getWidth(), getHeight(), true);
|
||||
} else {
|
||||
CardImageRenderer.drawDetails(g, currentCard, gameView, showBackSide? showBackSide : showAltState, x, y, cardWidth, cardHeight);
|
||||
}
|
||||
|
||||
if (!showMerged) {
|
||||
if (mutateIconBounds != null) {
|
||||
float oldAlpha = g.getfloatAlphaComposite();
|
||||
try {
|
||||
g.setAlphaComposite(0.6f);
|
||||
drawIconBounds(g, mutateIconBounds, Forge.hdbuttons ? FSkinImage.HDLIBRARY : FSkinImage.LIBRARY, x, y, cardWidth, cardHeight);
|
||||
g.setAlphaComposite(oldAlpha);
|
||||
} catch (Exception e) {
|
||||
mutateIconBounds = null;
|
||||
g.setAlphaComposite(oldAlpha);
|
||||
}
|
||||
} else if (flipIconBounds != null) {
|
||||
drawIconBounds(g, flipIconBounds, Forge.hdbuttons ? FSkinImage.HDFLIPCARD : FSkinImage.FLIPCARD, x, y, cardWidth, cardHeight);
|
||||
}
|
||||
} else if (flipIconBounds != null) {
|
||||
drawIconBounds(g, flipIconBounds, Forge.hdbuttons ? FSkinImage.HDFLIPCARD : FSkinImage.FLIPCARD, x, y, cardWidth, cardHeight);
|
||||
}
|
||||
|
||||
if (currentActivateAction != null) {
|
||||
g.fillRect(FDialog.MSG_BACK_COLOR, 0, 0, w, messageHeight);
|
||||
g.drawText(Localizer.getInstance().getMessage("lblSwipeUpTo").replace("%s", currentActivateAction), FDialog.MSG_FONT, FDialog.MSG_FORE_COLOR, 0, 0, w, messageHeight, false, Align.center, true);
|
||||
}
|
||||
g.fillRect(FDialog.MSG_BACK_COLOR, 0, h - messageHeight, w, messageHeight);
|
||||
g.drawText(zoomMode ? Localizer.getInstance().getMessage("lblSwipeDownDetailView") : Localizer.getInstance().getMessage("lblSwipeDownPictureView"), FDialog.MSG_FONT, FDialog.MSG_FORE_COLOR, 0, h - messageHeight, w, messageHeight, false, Align.center, true);
|
||||
|
||||
interrupt(false);
|
||||
}
|
||||
|
||||
private void drawIconBounds(Graphics g, Rectangle iconBounds, FSkinImage skinImage, float x, float y, float cardWidth, float cardHeight) {
|
||||
float imageWidth = cardWidth / 2;
|
||||
float imageHeight = imageWidth * skinImage.getHeight() / skinImage.getWidth();
|
||||
iconBounds.set(x + (cardWidth - imageWidth) / 2, y + (cardHeight - imageHeight) / 2, imageWidth, imageHeight);
|
||||
g.drawImage(skinImage, iconBounds.x, iconBounds.y, iconBounds.width, iconBounds.height);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doLayout(float width, float height) {
|
||||
}
|
||||
|
||||
public interface ActivateHandler {
|
||||
String getActivateAction(int index);
|
||||
void setSelectedIndex(int index);
|
||||
void activate(int index);
|
||||
}
|
||||
|
||||
public void interrupt(boolean resume) {
|
||||
if (MatchController.instance.hasLocalPlayers())
|
||||
return;
|
||||
if(resume && MatchController.instance.isGamePaused()) {
|
||||
MatchController.instance.resumeMatch();
|
||||
return;
|
||||
}
|
||||
if(!MatchController.instance.isGamePaused())
|
||||
MatchController.instance.pauseMatch();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package forge.adventure.libgdxgui.card;
|
||||
|
||||
import forge.adventure.libgdxgui.Graphics;
|
||||
import forge.adventure.libgdxgui.assets.FImage;
|
||||
import forge.adventure.libgdxgui.assets.FSkinImage;
|
||||
import forge.card.ColorSet;
|
||||
|
||||
public class ColorSetImage implements FImage {
|
||||
private final ColorSet colorSet;
|
||||
private final int shardCount;
|
||||
|
||||
public ColorSetImage(ColorSet colorSet0) {
|
||||
colorSet = colorSet0;
|
||||
shardCount = colorSet.getOrderedShards().length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getWidth() {
|
||||
return FSkinImage.MANA_W.getWidth() * shardCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getHeight() {
|
||||
return FSkinImage.MANA_W.getHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Graphics g, float x, float y, float w, float h) {
|
||||
float imageSize = w / shardCount;
|
||||
if (imageSize > h) {
|
||||
imageSize = h;
|
||||
float w0 = imageSize * shardCount;
|
||||
x += (w - w0) / 2;
|
||||
w = w0;
|
||||
}
|
||||
CardFaceSymbols.drawColorSet(g, colorSet, x, y, imageSize);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
package forge.adventure.libgdxgui.card;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import forge.adventure.libgdxgui.Forge;
|
||||
import forge.adventure.libgdxgui.Graphics;
|
||||
import forge.adventure.libgdxgui.assets.FImage;
|
||||
import forge.adventure.libgdxgui.assets.FSkinFont;
|
||||
import forge.adventure.libgdxgui.assets.FSkinImage;
|
||||
import forge.adventure.libgdxgui.screens.FScreen;
|
||||
import forge.adventure.libgdxgui.screens.TabPageScreen;
|
||||
import forge.adventure.libgdxgui.toolbox.FChoiceList;
|
||||
import forge.adventure.libgdxgui.toolbox.FEvent;
|
||||
import forge.adventure.libgdxgui.toolbox.FEvent.FEventHandler;
|
||||
import forge.adventure.libgdxgui.toolbox.FOptionPane;
|
||||
import forge.adventure.libgdxgui.toolbox.FTextField;
|
||||
import forge.game.GameEntityView;
|
||||
import forge.game.card.CardView;
|
||||
import forge.util.Callback;
|
||||
import forge.util.Localizer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public class GameEntityPicker extends TabPageScreen<GameEntityPicker> {
|
||||
private final FOptionPane optionPane;
|
||||
|
||||
public GameEntityPicker(String title, Collection<? extends GameEntityView> choiceList, Collection<CardView> revealList, String revealListCaption, FImage revealListImage, boolean isOptional, final Callback<GameEntityView> callback) {
|
||||
super(new PickerTab[] {
|
||||
new PickerTab(choiceList, Localizer.getInstance().getMessage("lblChoices"), Forge.hdbuttons ? FSkinImage.HDCHOICE : FSkinImage.DECKLIST, 1),
|
||||
new PickerTab(revealList, revealListCaption, revealListImage, 0)
|
||||
}, false);
|
||||
|
||||
setHeight(FOptionPane.getMaxDisplayObjHeight());
|
||||
|
||||
optionPane = new FOptionPane(null, null, title, null, this,
|
||||
isOptional ? ImmutableList.of(Localizer.getInstance().getMessage("lblOK"), Localizer.getInstance().getMessage("lblCancel")) : ImmutableList.of(Localizer.getInstance().getMessage("lblOK")), 0, new Callback<Integer>() {
|
||||
@Override
|
||||
public void run(Integer result) {
|
||||
if (result == 0) {
|
||||
callback.run(((PickerTab)tabPages[0]).list.getSelectedItem());
|
||||
}
|
||||
else {
|
||||
callback.run(null);
|
||||
}
|
||||
}
|
||||
}) {
|
||||
@Override
|
||||
protected boolean padAboveAndBelow() {
|
||||
return false; //allow list to go straight up against buttons
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void show() {
|
||||
optionPane.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canActivateTabPage() {
|
||||
return true; //always allow activating tab pages while this is open
|
||||
}
|
||||
|
||||
@Override
|
||||
public FScreen getLandscapeBackdropScreen() {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static class PickerTab extends TabPage<GameEntityPicker> {
|
||||
private final FTextField txtSearch;
|
||||
private final FChoiceList<GameEntityView> list;
|
||||
|
||||
private PickerTab(final Collection<? extends GameEntityView> items, String caption0, FImage icon0, final int maxChoices) {
|
||||
super(caption0 + " (" + items.size() + ")", icon0);
|
||||
txtSearch = add(new FTextField());
|
||||
txtSearch.setFont(FSkinFont.get(12));
|
||||
txtSearch.setGhostText(Localizer.getInstance().getMessage("lblSearch"));
|
||||
txtSearch.setChangedHandler(new FEventHandler() {
|
||||
@Override
|
||||
public void handleEvent(FEvent e) {
|
||||
String pattern = txtSearch.getText().toLowerCase();
|
||||
list.clearSelection();
|
||||
if (pattern.isEmpty()) {
|
||||
list.setListData(items);
|
||||
}
|
||||
else {
|
||||
List<GameEntityView> filteredList = new ArrayList<>();
|
||||
for (GameEntityView option : items) {
|
||||
if (option.toString().toLowerCase().contains(pattern)) {
|
||||
filteredList.add(option);
|
||||
}
|
||||
}
|
||||
list.setListData(filteredList);
|
||||
}
|
||||
if (!list.isEmpty() && maxChoices > 0) {
|
||||
list.addSelectedIndex(0);
|
||||
}
|
||||
list.setScrollTop(0);
|
||||
}
|
||||
});
|
||||
list = add(new FChoiceList<GameEntityView>(items, maxChoices, maxChoices) {
|
||||
@Override
|
||||
protected void onItemActivate(Integer index, GameEntityView value) {
|
||||
if (maxChoices > 0) {
|
||||
parentScreen.optionPane.setResult(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawOverlay(Graphics g) {
|
||||
//don't draw border
|
||||
}
|
||||
});
|
||||
if (maxChoices > 0) {
|
||||
list.addSelectedIndex(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivate() {
|
||||
if (parentScreen.optionPane != null) {
|
||||
parentScreen.optionPane.setButtonEnabled(0, list.getMaxChoices() > 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doLayout(float width, float height) {
|
||||
float padding = txtSearch.getHeight() * 0.25f;
|
||||
float y = padding;
|
||||
txtSearch.setBounds(0, y, width, txtSearch.getHeight());
|
||||
y += txtSearch.getHeight() + padding;
|
||||
list.setBounds(0, y, width, height - y);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,424 @@
|
||||
/*
|
||||
* Forge: Play Magic: the Gathering.
|
||||
* Copyright (C) 2011 Forge Team
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package forge.adventure.libgdxgui.deck;
|
||||
|
||||
import com.badlogic.gdx.utils.Align;
|
||||
import com.google.common.collect.Iterables;
|
||||
import forge.StaticData;
|
||||
import forge.adventure.libgdxgui.Forge;
|
||||
import forge.adventure.libgdxgui.Graphics;
|
||||
import forge.adventure.libgdxgui.assets.FSkinFont;
|
||||
import forge.adventure.libgdxgui.assets.FSkinImage;
|
||||
import forge.adventure.libgdxgui.card.CardRenderer;
|
||||
import forge.adventure.libgdxgui.card.CardRenderer.CardStackPosition;
|
||||
import forge.adventure.libgdxgui.card.CardZoom;
|
||||
import forge.adventure.libgdxgui.toolbox.*;
|
||||
import forge.adventure.libgdxgui.toolbox.FEvent.FEventHandler;
|
||||
import forge.adventure.libgdxgui.util.Utils;
|
||||
import forge.card.CardEdition;
|
||||
import forge.card.CardRules;
|
||||
import forge.card.mana.ManaCostShard;
|
||||
import forge.deck.CardPool;
|
||||
import forge.deck.Deck;
|
||||
import forge.deck.DeckgenUtil;
|
||||
import forge.item.PaperCard;
|
||||
import forge.model.FModel;
|
||||
import forge.util.Callback;
|
||||
import forge.util.Localizer;
|
||||
|
||||
import java.text.NumberFormat;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
|
||||
public class AddBasicLandsDialog extends FDialog {
|
||||
private static final float ADD_BTN_SIZE = Utils.AVG_FINGER_HEIGHT * 0.75f;
|
||||
private static final float LAND_PANEL_PADDING = Utils.scale(3);
|
||||
|
||||
private final Deck currentDeck;
|
||||
|
||||
private final Callback<CardPool> callback;
|
||||
|
||||
private final FLabel lblLandSet = add(new FLabel.Builder().text(Localizer.getInstance().getMessage("lblLandSet") + ":").font(FSkinFont.get(12)).textColor(FLabel.INLINE_LABEL_COLOR).build());
|
||||
private final FComboBox<CardEdition> cbLandSet = add(new FComboBox<>(Iterables.filter(StaticData.instance().getEditions(), CardEdition.Predicates.hasBasicLands)));
|
||||
|
||||
private final FScrollPane scroller = add(new FScrollPane() {
|
||||
@Override
|
||||
protected ScrollBounds layoutAndGetScrollBounds(float visibleWidth, float visibleHeight) {
|
||||
float padding = FOptionPane.PADDING;
|
||||
float x = padding;
|
||||
float totalWidth = Forge.isLandscapeMode() ? visibleWidth : 2 * visibleWidth - ADD_BTN_SIZE;
|
||||
float panelWidth = (totalWidth - 6 * padding) / 5;
|
||||
|
||||
pnlPlains.setBounds(x, 0, panelWidth, visibleHeight);
|
||||
x += panelWidth + padding;
|
||||
pnlIsland.setBounds(x, 0, panelWidth, visibleHeight);
|
||||
x += panelWidth + padding;
|
||||
pnlSwamp.setBounds(x, 0, panelWidth, visibleHeight);
|
||||
x += panelWidth + padding;
|
||||
pnlMountain.setBounds(x, 0, panelWidth, visibleHeight);
|
||||
x += panelWidth + padding;
|
||||
pnlForest.setBounds(x, 0, panelWidth, visibleHeight);
|
||||
|
||||
return new ScrollBounds(totalWidth, visibleHeight);
|
||||
}
|
||||
});
|
||||
private final LandPanel pnlPlains = scroller.add(new LandPanel("Plains"));
|
||||
private final LandPanel pnlIsland = scroller.add(new LandPanel("Island"));
|
||||
private final LandPanel pnlSwamp = scroller.add(new LandPanel("Swamp"));
|
||||
private final LandPanel pnlMountain = scroller.add(new LandPanel("Mountain"));
|
||||
private final LandPanel pnlForest = scroller.add(new LandPanel("Forest"));
|
||||
|
||||
private final FTextArea lblDeckInfo = add(new FTextArea(true) {
|
||||
@Override
|
||||
public boolean tap(float x, float y, int count) {
|
||||
if (count == 2) {
|
||||
Map<ManaCostShard, Integer> suggestionMap = DeckgenUtil.suggestBasicLandCount(currentDeck);
|
||||
pnlPlains.count = suggestionMap.get(ManaCostShard.WHITE);
|
||||
pnlIsland.count = suggestionMap.get(ManaCostShard.BLUE);
|
||||
pnlSwamp.count = suggestionMap.get(ManaCostShard.BLACK);
|
||||
pnlMountain.count = suggestionMap.get(ManaCostShard.RED);
|
||||
pnlForest.count = suggestionMap.get(ManaCostShard.GREEN);
|
||||
|
||||
pnlPlains.lblCount.setText(String.valueOf(pnlPlains.count));
|
||||
pnlIsland.lblCount.setText(String.valueOf(pnlIsland.count));
|
||||
pnlSwamp.lblCount.setText(String.valueOf(pnlSwamp.count));
|
||||
pnlMountain.lblCount.setText(String.valueOf(pnlMountain.count));
|
||||
pnlForest.lblCount.setText(String.valueOf(pnlForest.count));
|
||||
|
||||
updateDeckInfoLabel();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
private int nonLandCount, oldLandCount;
|
||||
private CardEdition landSet;
|
||||
|
||||
public AddBasicLandsDialog(Deck deck, CardEdition defaultLandSet, final Callback<CardPool> callback0) {
|
||||
super(Localizer.getInstance().getMessage("lblAddBasicLandsAutoSuggest").replace("%s", deck.getName()), 2);
|
||||
|
||||
callback = callback0;
|
||||
currentDeck = deck;
|
||||
|
||||
lblDeckInfo.setAlignment(Align.center);
|
||||
lblDeckInfo.setFont(FSkinFont.get(12));
|
||||
|
||||
cbLandSet.setFont(lblLandSet.getFont());
|
||||
cbLandSet.setChangedHandler(new FEventHandler() {
|
||||
@Override
|
||||
public void handleEvent(FEvent e) {
|
||||
landSet = cbLandSet.getSelectedItem();
|
||||
pnlPlains.refreshArtChoices();
|
||||
pnlIsland.refreshArtChoices();
|
||||
pnlSwamp.refreshArtChoices();
|
||||
pnlMountain.refreshArtChoices();
|
||||
pnlForest.refreshArtChoices();
|
||||
}
|
||||
});
|
||||
cbLandSet.setSelectedItem(defaultLandSet);
|
||||
|
||||
initButton(0, Localizer.getInstance().getMessage("lblOK"), new FEventHandler() {
|
||||
@Override
|
||||
public void handleEvent(FEvent e) {
|
||||
CardPool landsToAdd = new CardPool();
|
||||
pnlPlains.addToCardPool(landsToAdd);
|
||||
pnlIsland.addToCardPool(landsToAdd);
|
||||
pnlSwamp.addToCardPool(landsToAdd);
|
||||
pnlMountain.addToCardPool(landsToAdd);
|
||||
pnlForest.addToCardPool(landsToAdd);
|
||||
|
||||
hide();
|
||||
|
||||
if (landsToAdd.countAll() > 0) {
|
||||
callback.run(landsToAdd);
|
||||
}
|
||||
}
|
||||
});
|
||||
initButton(1, Localizer.getInstance().getMessage("lblCancel"), new FEventHandler() {
|
||||
@Override
|
||||
public void handleEvent(FEvent e) {
|
||||
hide();
|
||||
}
|
||||
});
|
||||
|
||||
//initialize land counts based on current deck contents
|
||||
int halfCountW = 0; //track half shard count for each color to add to symbol count only if a full symbol is also found
|
||||
int halfCountU = 0;
|
||||
int halfCountB = 0;
|
||||
int halfCountR = 0;
|
||||
int halfCountG = 0;
|
||||
for (Entry<PaperCard, Integer> entry : deck.getMain()) {
|
||||
CardRules cardRules = entry.getKey().getRules();
|
||||
int count = entry.getValue();
|
||||
if (cardRules.getType().isLand()) {
|
||||
oldLandCount += count;
|
||||
}
|
||||
else {
|
||||
nonLandCount += count;
|
||||
|
||||
for (ManaCostShard shard : cardRules.getManaCost()) {
|
||||
boolean isMonoColor = shard.isMonoColor();
|
||||
if (shard.isWhite()) {
|
||||
if (isMonoColor) {
|
||||
pnlPlains.symbolCount += count;
|
||||
continue;
|
||||
}
|
||||
halfCountW += count;
|
||||
}
|
||||
if (shard.isBlue()) {
|
||||
if (isMonoColor) {
|
||||
pnlIsland.symbolCount += count;
|
||||
continue;
|
||||
}
|
||||
halfCountU += count;
|
||||
}
|
||||
if (shard.isBlack()) {
|
||||
if (isMonoColor) {
|
||||
pnlSwamp.symbolCount += count;
|
||||
continue;
|
||||
}
|
||||
halfCountB += count;
|
||||
}
|
||||
if (shard.isRed()) {
|
||||
if (isMonoColor) {
|
||||
pnlMountain.symbolCount += count;
|
||||
continue;
|
||||
}
|
||||
halfCountR += count;
|
||||
}
|
||||
if (shard.isGreen()) {
|
||||
if (isMonoColor) {
|
||||
pnlForest.symbolCount += count;
|
||||
continue;
|
||||
}
|
||||
halfCountG += count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//only account for half shards if full shards exist for a given color
|
||||
if (pnlPlains.symbolCount > 0 && halfCountW > 0) {
|
||||
pnlPlains.symbolCount += halfCountW * 0.5;
|
||||
}
|
||||
if (pnlIsland.symbolCount > 0 && halfCountU > 0) {
|
||||
pnlIsland.symbolCount += halfCountU * 0.5;
|
||||
}
|
||||
if (pnlSwamp.symbolCount > 0 && halfCountB > 0) {
|
||||
pnlSwamp.symbolCount += halfCountB * 0.5;
|
||||
}
|
||||
if (pnlMountain.symbolCount > 0 && halfCountR > 0) {
|
||||
pnlMountain.symbolCount += halfCountR * 0.5;
|
||||
}
|
||||
if (pnlForest.symbolCount > 0 && halfCountG > 0) {
|
||||
pnlForest.symbolCount += halfCountG * 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
updateDeckInfoLabel();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float layoutAndGetHeight(float width, float maxHeight) {
|
||||
float padding = FOptionPane.PADDING;
|
||||
float x = padding;
|
||||
float y = padding;
|
||||
float w = width - 2 * padding;
|
||||
|
||||
//layout land set combo box
|
||||
float comboBoxHeight = cbLandSet.getHeight();
|
||||
lblLandSet.setBounds(x, y, lblLandSet.getAutoSizeBounds().width, comboBoxHeight);
|
||||
cbLandSet.setBounds(x + lblLandSet.getWidth(), y, w - lblLandSet.getWidth(), comboBoxHeight);
|
||||
|
||||
//layout card panel scroller
|
||||
y += comboBoxHeight + padding;
|
||||
float panelExtraHeight = pnlPlains.cbLandArt.getHeight() + ADD_BTN_SIZE + 2 * LAND_PANEL_PADDING;
|
||||
float panelWidth;
|
||||
if (Forge.isLandscapeMode()) {
|
||||
panelWidth = (width - 6 * padding) / 5;
|
||||
}
|
||||
else {
|
||||
panelWidth = (2 * width - ADD_BTN_SIZE - 6 * padding) / 5;
|
||||
}
|
||||
float panelHeight = panelWidth * FCardPanel.ASPECT_RATIO + panelExtraHeight;
|
||||
scroller.setBounds(0, y, width, panelHeight);
|
||||
|
||||
//adjust scroll based on prevalent colors in deck
|
||||
if (pnlMountain.symbolCount + pnlForest.symbolCount > pnlPlains.symbolCount + pnlIsland.symbolCount) {
|
||||
scroller.scrollToRight();
|
||||
}
|
||||
else {
|
||||
scroller.scrollToLeft();
|
||||
}
|
||||
|
||||
//layout info label
|
||||
y += panelHeight + padding;
|
||||
lblDeckInfo.setBounds(x, y, w, lblDeckInfo.getPreferredHeight(w));
|
||||
|
||||
return y + lblDeckInfo.getHeight() + padding;
|
||||
}
|
||||
|
||||
private void updateDeckInfoLabel() {
|
||||
NumberFormat integer = NumberFormat.getIntegerInstance();
|
||||
NumberFormat percent = NumberFormat.getPercentInstance();
|
||||
int newLandCount = pnlPlains.count + pnlIsland.count + pnlSwamp.count + pnlMountain.count + pnlForest.count;
|
||||
double totalSymbolCount = pnlPlains.symbolCount + pnlIsland.symbolCount + pnlSwamp.symbolCount + pnlMountain.symbolCount + pnlForest.symbolCount;
|
||||
if (totalSymbolCount == 0) {
|
||||
totalSymbolCount = 1; //prevent divide by 0 error
|
||||
}
|
||||
int newTotalCount = nonLandCount + oldLandCount + newLandCount;
|
||||
lblDeckInfo.setText(
|
||||
String.format(Localizer.getInstance().getMessage("lblNonLandCount"), nonLandCount) + " + " +
|
||||
String.format(Localizer.getInstance().getMessage("lblOldLandCount"), oldLandCount) + " + " +
|
||||
String.format(Localizer.getInstance().getMessage("lblNewLandCount"), newLandCount) + " = " +
|
||||
String.format(Localizer.getInstance().getMessage("lblNewTotalCount"), newTotalCount) + "\n" +
|
||||
"{W} " + integer.format(pnlPlains.symbolCount) + " (" + percent.format(pnlPlains.symbolCount / totalSymbolCount) + ") | " +
|
||||
"{U} " + integer.format(pnlIsland.symbolCount) + " (" + percent.format(pnlIsland.symbolCount / totalSymbolCount) + ") | " +
|
||||
"{B} " + integer.format(pnlSwamp.symbolCount) + " (" + percent.format(pnlSwamp.symbolCount / totalSymbolCount) + ") | " +
|
||||
"{R} " + integer.format(pnlMountain.symbolCount) + " (" + percent.format(pnlMountain.symbolCount / totalSymbolCount) + ") | " +
|
||||
"{G} " + integer.format(pnlForest.symbolCount) + " (" + percent.format(pnlForest.symbolCount / totalSymbolCount) + ")");
|
||||
}
|
||||
|
||||
private class LandPanel extends FContainer {
|
||||
private final LandCardPanel cardPanel;
|
||||
private final FLabel lblCount, btnSubtract, btnAdd;
|
||||
private final FComboBox<String> cbLandArt;
|
||||
private final String cardName;
|
||||
private PaperCard card;
|
||||
private int count, maxCount;
|
||||
private double symbolCount;
|
||||
|
||||
private LandPanel(String cardName0) {
|
||||
cardName = cardName0;
|
||||
cardPanel = add(new LandCardPanel());
|
||||
cbLandArt = add(new FComboBox<>());
|
||||
cbLandArt.setFont(cbLandSet.getFont());
|
||||
cbLandArt.setChangedHandler(new FEventHandler() {
|
||||
@Override
|
||||
public void handleEvent(FEvent e) {
|
||||
int artIndex = cbLandArt.getSelectedIndex();
|
||||
if (artIndex < 0) { return; }
|
||||
card = generateCard(artIndex); //generate card for display
|
||||
}
|
||||
});
|
||||
lblCount = add(new FLabel.Builder().text("0").font(FSkinFont.get(18)).align(Align.center).build());
|
||||
btnSubtract = add(new FLabel.ButtonBuilder().icon(Forge.hdbuttons ? FSkinImage.HDMINUS : FSkinImage.MINUS).command(new FEventHandler() {
|
||||
@Override
|
||||
public void handleEvent(FEvent e) {
|
||||
if (count > 0) {
|
||||
count--;
|
||||
lblCount.setText(String.valueOf(count));
|
||||
updateDeckInfoLabel();
|
||||
}
|
||||
}
|
||||
}).build());
|
||||
btnAdd = add(new FLabel.ButtonBuilder().icon(Forge.hdbuttons ? FSkinImage.HDPLUS : FSkinImage.PLUS).command(new FEventHandler() {
|
||||
@Override
|
||||
public void handleEvent(FEvent e) {
|
||||
if (maxCount == 0 || count < maxCount) {
|
||||
count++;
|
||||
lblCount.setText(String.valueOf(count));
|
||||
updateDeckInfoLabel();
|
||||
}
|
||||
}
|
||||
}).build());
|
||||
}
|
||||
|
||||
private void addToCardPool(CardPool pool) {
|
||||
if (count == 0) { return; }
|
||||
int artIndex = cbLandArt.getSelectedIndex();
|
||||
if (artIndex < 0) { return; }
|
||||
|
||||
if (artIndex > 0 && card != null) {
|
||||
pool.add(card, count); //simplify things if art index specified
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < count; i++) {
|
||||
pool.add(generateCard(artIndex));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private PaperCard generateCard(int artIndex) {
|
||||
PaperCard c = FModel.getMagicDb().getCommonCards().getCard(cardName, landSet.getCode(), artIndex);
|
||||
if (c == null) {
|
||||
//if can't find land for this set, fall back to Zendikar lands
|
||||
c = FModel.getMagicDb().getCommonCards().getCard(cardName, "ZEN");
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
private void refreshArtChoices() {
|
||||
cbLandArt.removeAllItems();
|
||||
if (landSet == null) { return; }
|
||||
|
||||
int artChoiceCount = FModel.getMagicDb().getCommonCards().getArtCount(cardName, landSet.getCode());
|
||||
cbLandArt.addItem(Localizer.getInstance().getMessage("lblAssortedArt"));
|
||||
for (int i = 1; i <= artChoiceCount; i++) {
|
||||
cbLandArt.addItem(Localizer.getInstance().getMessage("lblCardArtN", String.valueOf(i)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doLayout(float width, float height) {
|
||||
float y = height - ADD_BTN_SIZE;
|
||||
float buttonWidth = ADD_BTN_SIZE;
|
||||
float labelWidth = width - 2 * ADD_BTN_SIZE;
|
||||
float minLabelWidth = lblCount.getFont().getBounds("0").width + 2 * lblCount.getInsets().x;
|
||||
if (labelWidth < minLabelWidth) { //ensure count label has enough room for display a single digit count at normal font size
|
||||
labelWidth = minLabelWidth;
|
||||
buttonWidth = (width - labelWidth) / 2;
|
||||
}
|
||||
btnSubtract.setBounds(0, y, buttonWidth, ADD_BTN_SIZE);
|
||||
lblCount.setBounds(buttonWidth, y, labelWidth, ADD_BTN_SIZE);
|
||||
btnAdd.setBounds(width - buttonWidth, y, buttonWidth, ADD_BTN_SIZE);
|
||||
|
||||
y -= cbLandArt.getHeight() + LAND_PANEL_PADDING;
|
||||
cbLandArt.setBounds(0, y, width, cbLandArt.getHeight());
|
||||
|
||||
float cardPanelHeight = y - LAND_PANEL_PADDING;
|
||||
float cardPanelWidth = cardPanelHeight / FCardPanel.ASPECT_RATIO;
|
||||
cardPanel.setBounds((width - cardPanelWidth) / 2, 0, cardPanelWidth, cardPanelHeight);
|
||||
}
|
||||
|
||||
private class LandCardPanel extends FDisplayObject {
|
||||
private LandCardPanel() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean tap(float x, float y, int count) {
|
||||
if (card == null) { return false; }
|
||||
CardZoom.show(card);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean longPress(float x, float y) {
|
||||
if (card == null) { return false; }
|
||||
CardZoom.show(card);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Graphics g) {
|
||||
if (card == null) { return; }
|
||||
CardRenderer.drawCard(g, card, 0, 0, getWidth(), getHeight(), CardStackPosition.Top);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
* Forge: Play Magic: the Gathering.
|
||||
* Copyright (C) 2011 Forge Team
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package forge.adventure.libgdxgui.deck;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import forge.adventure.libgdxgui.Forge;
|
||||
import forge.adventure.libgdxgui.Graphics;
|
||||
import forge.deck.Deck;
|
||||
import forge.deck.DeckImportController;
|
||||
import forge.deck.DeckRecognizer;
|
||||
import forge.deck.DeckRecognizer.TokenType;
|
||||
import forge.gui.FThreads;
|
||||
import forge.gui.util.SOptionPane;
|
||||
import forge.adventure.libgdxgui.toolbox.*;
|
||||
import forge.adventure.libgdxgui.toolbox.FEvent.FEventHandler;
|
||||
import forge.util.Callback;
|
||||
import forge.util.Localizer;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public class FDeckImportDialog extends FDialog {
|
||||
private final Callback<Deck> callback;
|
||||
|
||||
private final FTextArea txtInput = add(new FTextArea(true));
|
||||
private final FCheckBox newEditionCheck = add(new FCheckBox(Localizer.getInstance().getMessage("lblImportLatestVersionCard"), true));
|
||||
private final FCheckBox dateTimeCheck = add(new FCheckBox(Localizer.getInstance().getMessage("lblUseOnlySetsReleasedBefore"), false));
|
||||
/*setting onlyCoreExpCheck to false allow the copied cards to pass the check of deck contents
|
||||
forge-core\src\main\java\forge\deck\Deck.javaDeck.java starting @ Line 320 which is called by
|
||||
forge-gui-mobile\src\forge\deck\FDeckEditor.java starting @ Line 373
|
||||
(as of latest commit: 8e6655e3ee67688cff66b422d4722c58392eaa7e)
|
||||
*/
|
||||
private final FCheckBox onlyCoreExpCheck = add(new FCheckBox(Localizer.getInstance().getMessage("lblUseOnlyCoreAndExpansionSets"), false));
|
||||
|
||||
private final FComboBox<String> monthDropdown = add(new FComboBox<>()); //don't need wrappers since skin can't change while this dialog is open
|
||||
private final FComboBox<Integer> yearDropdown = add(new FComboBox<>());
|
||||
|
||||
private final boolean showOptions;
|
||||
private final DeckImportController controller;
|
||||
|
||||
private final static ImmutableList<String> importOrCancel = ImmutableList.of(Localizer.getInstance().getMessage("lblImport"), Localizer.getInstance().getMessage("lblCancel"));
|
||||
|
||||
public FDeckImportDialog(final boolean replacingDeck, final Callback<Deck> callback0) {
|
||||
super(Localizer.getInstance().getMessage("lblImportFromClipboard"), 2);
|
||||
|
||||
callback = callback0;
|
||||
controller = new DeckImportController(replacingDeck, newEditionCheck, dateTimeCheck, onlyCoreExpCheck, monthDropdown, yearDropdown);
|
||||
txtInput.setText(Forge.getClipboard().getContents()); //just pull import directly off the clipboard
|
||||
|
||||
initButton(0, Localizer.getInstance().getMessage("lblImport"), new FEventHandler() {
|
||||
@Override
|
||||
public void handleEvent(FEvent e) {
|
||||
FThreads.invokeInBackgroundThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
List<DeckRecognizer.Token> tokens = controller.parseInput(txtInput.getText()); //ensure deck updated based on any changes to options
|
||||
|
||||
//if there are any unknown cards, let user know this and give them the option to cancel
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (DeckRecognizer.Token token : tokens) {
|
||||
if (token.getType() == TokenType.UnknownCard) {
|
||||
if (sb.length() > 0) {
|
||||
sb.append("\n");
|
||||
}
|
||||
sb.append(token.getNumber()).append(" ").append(token.getText());
|
||||
}
|
||||
}
|
||||
if (sb.length() > 0) {
|
||||
if (SOptionPane.showOptionDialog(Localizer.getInstance().getMessage("lblFollowingCardsCannotBeImported") + "\n\n" + sb, Localizer.getInstance().getMessage("lblImportRemainingCards"), SOptionPane.INFORMATION_ICON, importOrCancel) == 1) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final Deck deck = controller.accept(); //must accept in background thread in case a dialog is shown
|
||||
if (deck == null) { return; }
|
||||
|
||||
FThreads.invokeInEdtLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
hide();
|
||||
callback.run(deck);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
initButton(1, Localizer.getInstance().getMessage("lblCancel"), new FEventHandler() {
|
||||
@Override
|
||||
public void handleEvent(FEvent e) {
|
||||
hide();
|
||||
}
|
||||
});
|
||||
|
||||
List<DeckRecognizer.Token> tokens = controller.parseInput(txtInput.getText());
|
||||
|
||||
//ensure at least one known card found on clipboard
|
||||
for (DeckRecognizer.Token token : tokens) {
|
||||
if (token.getType() == TokenType.KnownCard) {
|
||||
showOptions = true;
|
||||
|
||||
dateTimeCheck.setCommand(new FEventHandler() {
|
||||
@Override
|
||||
public void handleEvent(FEvent e) {
|
||||
updateDropDownEnabled();
|
||||
}
|
||||
});
|
||||
updateDropDownEnabled();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
showOptions = false;
|
||||
setButtonEnabled(0, false);
|
||||
txtInput.setText(Localizer.getInstance().getMessage("lblNoKnownCardsOnClipboard"));
|
||||
}
|
||||
|
||||
private void updateDropDownEnabled() {
|
||||
boolean enabled = dateTimeCheck.isSelected();
|
||||
monthDropdown.setEnabled(enabled);
|
||||
yearDropdown.setEnabled(enabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawOverlay(Graphics g) {
|
||||
super.drawOverlay(g);
|
||||
if (showOptions) {
|
||||
float y = txtInput.getTop() - FOptionPane.PADDING;
|
||||
g.drawLine(BORDER_THICKNESS, BORDER_COLOR, 0, y, getWidth(), y);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float layoutAndGetHeight(float width, float maxHeight) {
|
||||
float padding = FOptionPane.PADDING;
|
||||
float x = padding;
|
||||
float y = padding;
|
||||
float w = width - 2 * padding;
|
||||
float h;
|
||||
if (showOptions) {
|
||||
h = monthDropdown.getHeight();
|
||||
float fieldPadding = padding / 2;
|
||||
|
||||
newEditionCheck.setBounds(x, y, w, h);
|
||||
y += h + fieldPadding;
|
||||
dateTimeCheck.setBounds(x, y, w, h);
|
||||
y += h + fieldPadding;
|
||||
|
||||
float dropDownWidth = (w - fieldPadding) / 2;
|
||||
monthDropdown.setBounds(x, y, dropDownWidth, h);
|
||||
yearDropdown.setBounds(x + dropDownWidth + fieldPadding, y, dropDownWidth, h);
|
||||
y += h + fieldPadding;
|
||||
|
||||
onlyCoreExpCheck.setBounds(x, y, w, h);
|
||||
y += h + 2 * padding;
|
||||
}
|
||||
h = txtInput.getPreferredHeight(w);
|
||||
float maxTextBoxHeight = maxHeight - y - padding;
|
||||
if (h > maxTextBoxHeight) {
|
||||
h = maxTextBoxHeight;
|
||||
}
|
||||
txtInput.setBounds(x, y, w, h);
|
||||
y += h + padding;
|
||||
if (showOptions) {
|
||||
h = newEditionCheck.getHeight();
|
||||
}
|
||||
return y;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
package forge.adventure.libgdxgui.deck;
|
||||
|
||||
import forge.adventure.libgdxgui.Forge;
|
||||
import forge.adventure.libgdxgui.assets.*;
|
||||
import forge.deck.CardPool;
|
||||
import forge.deck.Deck;
|
||||
import forge.deck.DeckSection;
|
||||
import forge.item.PaperCard;
|
||||
import forge.adventure.libgdxgui.itemmanager.CardManager;
|
||||
import forge.itemmanager.ItemManagerConfig;
|
||||
import forge.adventure.libgdxgui.itemmanager.filters.ItemFilter;
|
||||
import forge.adventure.libgdxgui.menu.FMenuItem;
|
||||
import forge.adventure.libgdxgui.menu.FPopupMenu;
|
||||
import forge.adventure.libgdxgui.screens.FScreen;
|
||||
import forge.adventure.libgdxgui.screens.match.MatchController;
|
||||
import forge.adventure.libgdxgui.toolbox.FEvent;
|
||||
import forge.adventure.libgdxgui.toolbox.FEvent.FEventHandler;
|
||||
import forge.adventure.libgdxgui.toolbox.FOptionPane;
|
||||
import forge.util.Localizer;
|
||||
|
||||
import java.util.Map.Entry;
|
||||
|
||||
public class FDeckViewer extends FScreen {
|
||||
private static FDeckViewer deckViewer;
|
||||
private static final FPopupMenu menu = new FPopupMenu() {
|
||||
@Override
|
||||
protected void buildMenu() {
|
||||
Deck deck = deckViewer.deck;
|
||||
for (Entry<DeckSection, CardPool> entry : deck) {
|
||||
final DeckSection section = entry.getKey();
|
||||
final CardPool pool = entry.getValue();
|
||||
int count = pool.countAll();
|
||||
if (count == 0) { continue; }
|
||||
|
||||
final String captionPrefix;
|
||||
final FImage icon;
|
||||
switch (section) {
|
||||
default:
|
||||
case Main:
|
||||
captionPrefix = Localizer.getInstance().getMessage("ttMain");
|
||||
icon = FDeckEditor.MAIN_DECK_ICON;
|
||||
break;
|
||||
case Sideboard:
|
||||
captionPrefix = Localizer.getInstance().getMessage("lblSideboard");
|
||||
icon = FDeckEditor.SIDEBOARD_ICON;
|
||||
break;
|
||||
case Commander:
|
||||
captionPrefix = Localizer.getInstance().getMessage("lblCommander");
|
||||
icon = FSkinImage.COMMANDER;
|
||||
break;
|
||||
case Avatar:
|
||||
captionPrefix = Localizer.getInstance().getMessage("lblAvatar");
|
||||
icon = new FTextureRegionImage(FSkin.getAvatars().get(0));
|
||||
break;
|
||||
case Planes:
|
||||
captionPrefix = Localizer.getInstance().getMessage("lblPlanes");
|
||||
icon = FSkinImage.CHAOS;
|
||||
break;
|
||||
case Schemes:
|
||||
captionPrefix = Localizer.getInstance().getMessage("lblSchemes");
|
||||
icon = FSkinImage.POISON;
|
||||
break;
|
||||
}
|
||||
|
||||
FMenuItem item = new FMenuItem(captionPrefix + " (" + count + ")", icon, new FEventHandler() {
|
||||
@Override
|
||||
public void handleEvent(FEvent e) {
|
||||
deckViewer.setCurrentSection(section);
|
||||
}
|
||||
});
|
||||
if (section == deckViewer.currentSection) {
|
||||
item.setSelected(true);
|
||||
}
|
||||
addItem(item);
|
||||
}
|
||||
addItem(new FMenuItem(Localizer.getInstance().getMessage("btnCopyToClipboard"), Forge.hdbuttons ? FSkinImage.HDEXPORT : FSkinImage.BLANK, new FEventHandler() {
|
||||
@Override
|
||||
public void handleEvent(FEvent e) {
|
||||
copyDeckToClipboard(deckViewer.deck);
|
||||
}
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
public static void copyDeckToClipboard(Deck deck) {
|
||||
final String nl = System.getProperty("line.separator");
|
||||
final StringBuilder deckList = new StringBuilder();
|
||||
String dName = deck.getName();
|
||||
//fix copying a commander netdeck then importing it again...
|
||||
if (dName.startsWith("[Commander")||dName.contains("Commander"))
|
||||
dName = "";
|
||||
deckList.append(dName == null ? "" : dName + nl + nl);
|
||||
|
||||
for (DeckSection s : DeckSection.values()){
|
||||
CardPool cp = deck.get(s);
|
||||
if (cp == null || cp.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
deckList.append(s.toString()).append(": ");
|
||||
deckList.append(nl);
|
||||
for (final Entry<PaperCard, Integer> ev : cp) {
|
||||
deckList.append(ev.getValue()).append(" ").append(ev.getKey()).append(nl);
|
||||
}
|
||||
deckList.append(nl);
|
||||
}
|
||||
|
||||
Forge.getClipboard().setContents(deckList.toString());
|
||||
FOptionPane.showMessageDialog(Localizer.getInstance().getMessage("lblDeckListCopiedClipboard", deck.getName()));
|
||||
}
|
||||
|
||||
private final Deck deck;
|
||||
private final CardManager cardManager;
|
||||
private DeckSection currentSection;
|
||||
|
||||
public static void show(final Deck deck0) {
|
||||
show(deck0, false);
|
||||
}
|
||||
public static void show(final Deck deck0, boolean noPreload) {
|
||||
if (deck0 == null) { return; }
|
||||
|
||||
if (!noPreload){
|
||||
/*preload deck to cache*/
|
||||
ImageCache.preloadCache(deck0);
|
||||
}
|
||||
|
||||
deckViewer = new FDeckViewer(deck0);
|
||||
deckViewer.setRotate180(MatchController.getView() != null && MatchController.getView().isTopHumanPlayerActive());
|
||||
Forge.openScreen(deckViewer);
|
||||
}
|
||||
|
||||
private FDeckViewer(Deck deck0) {
|
||||
super(new MenuHeader(deck0.getName(), menu) {
|
||||
@Override
|
||||
protected boolean displaySidebarForLandscapeMode() {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
deck = deck0;
|
||||
cardManager = new CardManager(false);
|
||||
cardManager.setPool(deck.getMain());
|
||||
|
||||
currentSection = DeckSection.Main;
|
||||
updateCaption();
|
||||
|
||||
add(cardManager);
|
||||
|
||||
cardManager.setup(ItemManagerConfig.DECK_VIEWER);
|
||||
}
|
||||
|
||||
private void setCurrentSection(DeckSection currentSection0) {
|
||||
if (currentSection == currentSection0) { return; }
|
||||
currentSection = currentSection0;
|
||||
cardManager.setPool(deck.get(currentSection));
|
||||
updateCaption();
|
||||
}
|
||||
|
||||
private void updateCaption() {
|
||||
cardManager.setCaption(currentSection.name());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doLayout(float startY, float width, float height) {
|
||||
float x = 0;
|
||||
if (Forge.isLandscapeMode()) { //add some horizontal padding in landscape mode
|
||||
x = ItemFilter.PADDING;
|
||||
width -= 2 * x;
|
||||
}
|
||||
cardManager.setBounds(x, startY, width, height - startY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FScreen getLandscapeBackdropScreen() {
|
||||
return null; //never use backdrop for editor
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
package forge.adventure.libgdxgui.deck;
|
||||
|
||||
import forge.adventure.libgdxgui.assets.FImage;
|
||||
import forge.deck.CardPool;
|
||||
import forge.item.PaperCard;
|
||||
import forge.adventure.libgdxgui.itemmanager.CardManager;
|
||||
import forge.adventure.libgdxgui.itemmanager.ItemManager.ContextMenuBuilder;
|
||||
import forge.itemmanager.ItemManagerConfig;
|
||||
import forge.adventure.libgdxgui.menu.FDropDownMenu;
|
||||
import forge.adventure.libgdxgui.menu.FMenuItem;
|
||||
import forge.adventure.libgdxgui.screens.FScreen;
|
||||
import forge.adventure.libgdxgui.screens.TabPageScreen;
|
||||
import forge.adventure.libgdxgui.toolbox.FDialog;
|
||||
import forge.adventure.libgdxgui.toolbox.FEvent;
|
||||
import forge.adventure.libgdxgui.toolbox.FEvent.FEventHandler;
|
||||
import forge.adventure.libgdxgui.toolbox.GuiChoose;
|
||||
import forge.util.Callback;
|
||||
import forge.util.Localizer;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class FSideboardDialog extends FDialog {
|
||||
private final SideboardTabs tabs;
|
||||
private final Callback<List<PaperCard>> callback;
|
||||
|
||||
public FSideboardDialog(CardPool sideboard, CardPool main, final Callback<List<PaperCard>> callback0, String message) {
|
||||
super(String.format(Localizer.getInstance().getMessage("lblUpdateMainFromSideboard"), message), 1);
|
||||
|
||||
callback = callback0;
|
||||
tabs = add(new SideboardTabs(sideboard, main));
|
||||
initButton(0, Localizer.getInstance().getMessage("lblOK"), new FEventHandler() {
|
||||
@Override
|
||||
public void handleEvent(FEvent e) {
|
||||
hide();
|
||||
}
|
||||
});
|
||||
if (sideboard.isEmpty()) { //show main deck by default if sideboard is empty
|
||||
tabs.setSelectedPage(tabs.getMainDeckPage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVisible(boolean visible0) {
|
||||
super.setVisible(visible0);
|
||||
if (!visible0) { //do callback when hidden to ensure you don't get stuck if Back pressed
|
||||
callback.run(tabs.getMainDeckPage().cardManager.getPool().toFlatList());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float layoutAndGetHeight(float width, float maxHeight) {
|
||||
tabs.setBounds(0, 0, width, maxHeight);
|
||||
return maxHeight;
|
||||
}
|
||||
|
||||
private static class SideboardTabs extends TabPageScreen<SideboardTabs> {
|
||||
private SideboardTabs(CardPool sideboard, CardPool main) {
|
||||
super(new TabPageBase[] {
|
||||
new SideboardPage(sideboard),
|
||||
new MainDeckPage(main)
|
||||
}, false);
|
||||
((SideboardPage)tabPages[0]).parent = this;
|
||||
((MainDeckPage)tabPages[1]).parent = this;
|
||||
}
|
||||
|
||||
private SideboardPage getSideboardPage() {
|
||||
return ((SideboardPage)tabPages[0]);
|
||||
}
|
||||
|
||||
private MainDeckPage getMainDeckPage() {
|
||||
return ((MainDeckPage)tabPages[1]);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canActivateTabPage() {
|
||||
return true; //always allow activating tab pages while this is open
|
||||
}
|
||||
|
||||
@Override
|
||||
public FScreen getLandscapeBackdropScreen() {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static abstract class TabPageBase extends TabPage<SideboardTabs> {
|
||||
protected SideboardTabs parent;
|
||||
protected final CardManager cardManager = add(new CardManager(false));
|
||||
|
||||
protected TabPageBase(CardPool cardPool, FImage icon0) {
|
||||
super("", icon0);
|
||||
|
||||
cardManager.setItemActivateHandler(new FEventHandler() {
|
||||
@Override
|
||||
public void handleEvent(FEvent e) {
|
||||
onCardActivated(cardManager.getSelectedItem());
|
||||
}
|
||||
});
|
||||
cardManager.setContextMenuBuilder(new ContextMenuBuilder<PaperCard>() {
|
||||
@Override
|
||||
public void buildMenu(final FDropDownMenu menu, final PaperCard card) {
|
||||
TabPageBase.this.buildMenu(menu, card);
|
||||
}
|
||||
});
|
||||
cardManager.setup(ItemManagerConfig.SIDEBOARD);
|
||||
cardManager.setPool(new CardPool(cardPool)); //create copy of card pool to avoid modifying the original card pool
|
||||
updateCaption();
|
||||
}
|
||||
|
||||
protected void addCard(PaperCard card, int qty) {
|
||||
cardManager.addItem(card, qty);
|
||||
updateCaption();
|
||||
}
|
||||
|
||||
protected void removeCard(PaperCard card, int qty) {
|
||||
cardManager.removeItem(card, qty);
|
||||
updateCaption();
|
||||
}
|
||||
|
||||
protected void addItem(FDropDownMenu menu, final String verb, String dest, FImage icon, final Callback<Integer> callback) {
|
||||
String label = verb;
|
||||
if (!StringUtils.isEmpty(dest)) {
|
||||
label += " " + dest;
|
||||
}
|
||||
menu.addItem(new FMenuItem(label, icon, new FEventHandler() {
|
||||
@Override
|
||||
public void handleEvent(FEvent e) {
|
||||
PaperCard card = cardManager.getSelectedItem();
|
||||
int max = cardManager.getItemCount(card);
|
||||
if (max == 1) {
|
||||
callback.run(max);
|
||||
}
|
||||
else {
|
||||
GuiChoose.getInteger(card + " - " + verb + " " + Localizer.getInstance().getMessage("lblHowMany"), 1, max, 20, callback);
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
protected abstract void updateCaption();
|
||||
protected abstract void onCardActivated(PaperCard card);
|
||||
protected abstract void buildMenu(final FDropDownMenu menu, final PaperCard card);
|
||||
|
||||
@Override
|
||||
protected void doLayout(float width, float height) {
|
||||
cardManager.setBounds(0, 0, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
private static class SideboardPage extends TabPageBase {
|
||||
protected SideboardPage(CardPool cardPool) {
|
||||
super(cardPool, FDeckEditor.SIDEBOARD_ICON);
|
||||
cardManager.setCaption(Localizer.getInstance().getMessage("lblSideboard"));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateCaption() {
|
||||
caption = Localizer.getInstance().getMessage("lblSideboard") + " (" + cardManager.getPool().countAll() + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCardActivated(PaperCard card) {
|
||||
removeCard(card, 1);
|
||||
parent.getMainDeckPage().addCard(card, 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void buildMenu(FDropDownMenu menu, final PaperCard card) {
|
||||
addItem(menu, Localizer.getInstance().getMessage("lblMove"), Localizer.getInstance().getMessage("lblToMainDeck"), FDeckEditor.MAIN_DECK_ICON, new Callback<Integer>() {
|
||||
@Override
|
||||
public void run(Integer result) {
|
||||
if (result == null || result <= 0) { return; }
|
||||
|
||||
removeCard(card, result);
|
||||
parent.getMainDeckPage().addCard(card, result);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static class MainDeckPage extends TabPageBase {
|
||||
protected MainDeckPage(CardPool cardPool) {
|
||||
super(cardPool, FDeckEditor.MAIN_DECK_ICON);
|
||||
cardManager.setCaption(Localizer.getInstance().getMessage("ttMain"));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateCaption() {
|
||||
caption = Localizer.getInstance().getMessage("ttMain") + " (" + cardManager.getPool().countAll() + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCardActivated(PaperCard card) {
|
||||
removeCard(card, 1);
|
||||
parent.getSideboardPage().addCard(card, 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void buildMenu(FDropDownMenu menu, final PaperCard card) {
|
||||
addItem(menu, Localizer.getInstance().getMessage("lblMove"), Localizer.getInstance().getMessage("lbltosideboard"), FDeckEditor.SIDEBOARD_ICON, new Callback<Integer>() {
|
||||
@Override
|
||||
public void run(Integer result) {
|
||||
if (result == null || result <= 0) { return; }
|
||||
|
||||
removeCard(card, result);
|
||||
parent.getSideboardPage().addCard(card, result);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package forge.adventure.libgdxgui.deck;
|
||||
|
||||
import forge.adventure.libgdxgui.Forge;
|
||||
import forge.deck.CardPool;
|
||||
import forge.item.PaperCard;
|
||||
import forge.adventure.libgdxgui.itemmanager.CardManager;
|
||||
import forge.itemmanager.ItemManagerConfig;
|
||||
import forge.model.FModel;
|
||||
import forge.adventure.libgdxgui.screens.FScreen;
|
||||
import forge.adventure.libgdxgui.toolbox.FButton;
|
||||
import forge.adventure.libgdxgui.toolbox.FEvent;
|
||||
import forge.adventure.libgdxgui.toolbox.FEvent.FEventHandler;
|
||||
import forge.util.Aggregates;
|
||||
import forge.util.Localizer;
|
||||
import forge.adventure.libgdxgui.util.Utils;
|
||||
|
||||
public class FVanguardChooser extends FScreen {
|
||||
public static final float PADDING = Utils.scale(5);
|
||||
|
||||
private static final CardPool allHumanAvatars = new CardPool();
|
||||
private static final CardPool allAiAvatars = new CardPool();
|
||||
private static final CardPool nonRandomHumanAvatars = new CardPool();
|
||||
private static final CardPool nonRandomAiAvatars = new CardPool();
|
||||
|
||||
static {
|
||||
for (PaperCard c : FModel.getMagicDb().getVariantCards().getAllCards()) {
|
||||
if (c.getRules().getType().isVanguard()) {
|
||||
allHumanAvatars.add(c);
|
||||
if (!c.getRules().getAiHints().getRemRandomDecks()) {
|
||||
nonRandomHumanAvatars.add(c);
|
||||
}
|
||||
if (!c.getRules().getAiHints().getRemAIDecks()) {
|
||||
allAiAvatars.add(c);
|
||||
if (!c.getRules().getAiHints().getRemRandomDecks()) {
|
||||
nonRandomAiAvatars.add(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final CardManager lstVanguards = add(new CardManager(true));
|
||||
private final FButton btnRandom = add(new FButton(Localizer.getInstance().getMessage("lblRandomVanguard")));
|
||||
private boolean isAi;
|
||||
|
||||
public FVanguardChooser(boolean isAi0, FEventHandler selectionChangedHandler) {
|
||||
super("");
|
||||
isAi = isAi0;
|
||||
lstVanguards.setItemActivateHandler(new FEventHandler() {
|
||||
@Override
|
||||
public void handleEvent(FEvent e) {
|
||||
Forge.back();
|
||||
}
|
||||
});
|
||||
btnRandom.setCommand(new FEventHandler() {
|
||||
@Override
|
||||
public void handleEvent(FEvent e) {
|
||||
selectRandom();
|
||||
Forge.back();
|
||||
}
|
||||
});
|
||||
lstVanguards.setup(ItemManagerConfig.VANGUARDS);
|
||||
lstVanguards.setPool(isAi ? allAiAvatars : allHumanAvatars, true);
|
||||
lstVanguards.setSelectionChangedHandler(selectionChangedHandler);
|
||||
selectRandom();
|
||||
}
|
||||
|
||||
private void selectRandom() {
|
||||
if (lstVanguards.getItemCount() == 0) { return; }
|
||||
|
||||
if (isAi) {
|
||||
lstVanguards.setSelectedItem(Aggregates.random(nonRandomAiAvatars).getKey());
|
||||
}
|
||||
else {
|
||||
lstVanguards.setSelectedItem(Aggregates.random(nonRandomHumanAvatars).getKey());
|
||||
}
|
||||
}
|
||||
|
||||
public void setIsAi(boolean isAi0) {
|
||||
if (isAi == isAi0) { return; }
|
||||
isAi = isAi0;
|
||||
|
||||
PaperCard lastSelection = lstVanguards.getSelectedItem();
|
||||
|
||||
lstVanguards.setPool(isAi ? allAiAvatars : allHumanAvatars, true);
|
||||
|
||||
if (lastSelection != null) {
|
||||
lstVanguards.setSelectedItem(lastSelection);
|
||||
}
|
||||
if (lstVanguards.getSelectedIndex() == -1) {
|
||||
selectRandom();
|
||||
}
|
||||
}
|
||||
|
||||
public CardManager getLstVanguards() {
|
||||
return lstVanguards;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doLayout(float startY, float width, float height) {
|
||||
float x = PADDING;
|
||||
float y = startY;
|
||||
width -= 2 * x;
|
||||
|
||||
float buttonHeight = Utils.AVG_FINGER_HEIGHT;
|
||||
lstVanguards.setBounds(x, y, width, height - y - buttonHeight - 2 * PADDING); //leave room for buttons at bottom
|
||||
|
||||
y += lstVanguards.getHeight() + PADDING;
|
||||
btnRandom.setBounds(x, y, width, buttonHeight);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
package forge.adventure.libgdxgui.error;
|
||||
|
||||
import com.badlogic.gdx.utils.Align;
|
||||
import forge.adventure.libgdxgui.Forge;
|
||||
import forge.adventure.libgdxgui.Graphics;
|
||||
import forge.adventure.libgdxgui.assets.FSkinColor;
|
||||
import forge.adventure.libgdxgui.assets.FSkinFont;
|
||||
import forge.adventure.libgdxgui.screens.FScreen;
|
||||
import forge.adventure.libgdxgui.toolbox.FButton;
|
||||
import forge.adventure.libgdxgui.toolbox.FEvent;
|
||||
import forge.adventure.libgdxgui.toolbox.FScrollPane;
|
||||
import forge.adventure.libgdxgui.toolbox.FTextArea;
|
||||
import forge.adventure.libgdxgui.util.TextBounds;
|
||||
import forge.adventure.libgdxgui.util.Utils;
|
||||
import forge.gui.error.BugReporter;
|
||||
import forge.util.Callback;
|
||||
|
||||
public class BugReportDialog extends FScreen { //use screen rather than dialog so screen with bug isn't rendered
|
||||
private static final float PADDING = Utils.scale(5);
|
||||
private static final float BUTTON_HEIGHT = Utils.AVG_FINGER_HEIGHT * 0.75f;
|
||||
private static boolean isOpen;
|
||||
|
||||
public static void show(String title, String text, boolean showExitAppBtn) {
|
||||
if (isOpen || Forge.getCurrentScreen() == null) { return; } //don't allow showing if Forge not finished initializing yet
|
||||
|
||||
isOpen = true;
|
||||
Forge.openScreen(new BugReportDialog(title, text, showExitAppBtn));
|
||||
}
|
||||
|
||||
private final FTextArea lblHeader = add(new FTextArea(false, "Report Bug"));
|
||||
private final TemplateView tvDetails;
|
||||
private final FButton btnReport = add(new FButton(BugReporter.REPORT));
|
||||
private final FButton btnSave = add(new FButton(BugReporter.SAVE));
|
||||
private final FButton btnDiscard = add(new FButton(BugReporter.DISCARD));
|
||||
private final FButton btnExit = add(new FButton(BugReporter.EXIT));
|
||||
|
||||
private BugReportDialog(String title, String text0, boolean showExitAppBtn) {
|
||||
super(title);
|
||||
lblHeader.setFont(FSkinFont.get(12));
|
||||
tvDetails = add(new TemplateView(text0));
|
||||
btnReport.setCommand(new FEvent.FEventHandler() {
|
||||
@Override
|
||||
public void handleEvent(FEvent e) {
|
||||
BugReporter.sendSentry();
|
||||
Forge.back();
|
||||
}
|
||||
});
|
||||
btnSave.setCommand(new FEvent.FEventHandler() {
|
||||
@Override
|
||||
public void handleEvent(FEvent e) {
|
||||
BugReporter.saveToFile(tvDetails.text);
|
||||
}
|
||||
});
|
||||
btnDiscard.setCommand(new FEvent.FEventHandler() {
|
||||
@Override
|
||||
public void handleEvent(FEvent e) {
|
||||
Forge.back();
|
||||
}
|
||||
});
|
||||
if (showExitAppBtn) {
|
||||
btnExit.setCommand(new FEvent.FEventHandler() {
|
||||
@Override
|
||||
public void handleEvent(FEvent e) {
|
||||
Forge.exit(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
btnExit.setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public FScreen getLandscapeBackdropScreen() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose(Callback<Boolean> canCloseCallback) {
|
||||
super.onClose(canCloseCallback);
|
||||
isOpen = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doLayout(float startY, float width, float height) {
|
||||
float x = PADDING;
|
||||
float y = startY + PADDING;
|
||||
float w = width - 2 * PADDING;
|
||||
|
||||
lblHeader.setBounds(x, y, w, lblHeader.getPreferredHeight(w));
|
||||
y += lblHeader.getHeight() + PADDING;
|
||||
|
||||
float buttonWidth, totalButtonHeight;
|
||||
float buttonHeight = BUTTON_HEIGHT;
|
||||
boolean landscapeMode = Forge.isLandscapeMode();
|
||||
if (landscapeMode) {
|
||||
buttonWidth = (w - 3 * PADDING) / 4;
|
||||
totalButtonHeight = buttonHeight;
|
||||
}
|
||||
else {
|
||||
buttonWidth = (w - PADDING) / 2;
|
||||
totalButtonHeight = 2 * buttonHeight + PADDING;
|
||||
}
|
||||
|
||||
tvDetails.setBounds(x, y, w, height - totalButtonHeight - 2 * PADDING - y);
|
||||
y += tvDetails.getHeight() + PADDING;
|
||||
|
||||
btnReport.setBounds(x, y, buttonWidth, buttonHeight);
|
||||
btnSave.setBounds(x + buttonWidth + PADDING, y, buttonWidth, buttonHeight);
|
||||
if (landscapeMode) {
|
||||
x += 2 * (buttonWidth + PADDING);
|
||||
}
|
||||
else {
|
||||
y += buttonHeight + PADDING;
|
||||
}
|
||||
if (btnExit.isVisible()) {
|
||||
btnDiscard.setBounds(x, y, buttonWidth, buttonHeight);
|
||||
btnExit.setBounds(x + buttonWidth + PADDING, y, buttonWidth, buttonHeight);
|
||||
}
|
||||
else {
|
||||
btnDiscard.setBounds(x, y, 2 * buttonWidth + PADDING, buttonHeight);
|
||||
}
|
||||
}
|
||||
|
||||
private static class TemplateView extends FScrollPane {
|
||||
private static final FSkinFont FONT = FSkinFont.get(11);
|
||||
private static final FSkinColor BACK_COLOR = FSkinColor.get(FSkinColor.Colors.CLR_ZEBRA);
|
||||
private static final FSkinColor FORE_COLOR = FSkinColor.get(FSkinColor.Colors.CLR_TEXT);
|
||||
private static final FSkinColor BORDER_COLOR = FSkinColor.get(FSkinColor.Colors.CLR_BORDERS);
|
||||
private static final float PADDING = Utils.scale(3);
|
||||
|
||||
private final String text;
|
||||
|
||||
private TemplateView(String text0) {
|
||||
text = text0;
|
||||
setHeight(Forge.getScreenHeight() / 3);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ScrollBounds layoutAndGetScrollBounds(float visibleWidth, float visibleHeight) {
|
||||
TextBounds bounds = FONT.getMultiLineBounds(text);
|
||||
return new ScrollBounds(bounds.width + 2 * PADDING, bounds.height + 2 * PADDING +
|
||||
FONT.getLineHeight() - FONT.getCapHeight()); //account for height below baseline of final line);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawBackground(Graphics g) {
|
||||
g.fillRect(BACK_COLOR, 0, 0, getWidth(), getHeight());
|
||||
g.drawText(text, FONT, FORE_COLOR, PADDING - getScrollLeft(), PADDING - getScrollTop(), getScrollWidth() - 2 * PADDING, getScrollHeight() - 2 * PADDING, false, Align.left, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawOverlay(Graphics g) {
|
||||
super.drawOverlay(g);
|
||||
g.drawRect(1, BORDER_COLOR, 0, 0, getWidth(), getHeight());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package forge.adventure.libgdxgui.itemmanager;
|
||||
|
||||
import forge.adventure.libgdxgui.Graphics;
|
||||
import forge.adventure.libgdxgui.assets.FSkinColor;
|
||||
import forge.adventure.libgdxgui.assets.FSkinFont;
|
||||
import forge.adventure.libgdxgui.card.CardRenderer;
|
||||
import forge.adventure.libgdxgui.card.CardZoom;
|
||||
import forge.item.PaperCard;
|
||||
import forge.adventure.libgdxgui.itemmanager.filters.*;
|
||||
import forge.adventure.libgdxgui.toolbox.FList;
|
||||
import forge.adventure.libgdxgui.toolbox.FList.CompactModeHandler;
|
||||
|
||||
import java.util.Map.Entry;
|
||||
|
||||
/**
|
||||
* ItemManager for cards
|
||||
*/
|
||||
public class CardManager extends ItemManager<PaperCard> {
|
||||
public CardManager(boolean wantUnique0) {
|
||||
super(PaperCard.class, wantUnique0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addDefaultFilters() {
|
||||
addDefaultFilters(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TextSearchFilter<PaperCard> createSearchFilter() {
|
||||
return createSearchFilter(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AdvancedSearchFilter<PaperCard> createAdvancedSearchFilter() {
|
||||
return createAdvancedSearchFilter(this);
|
||||
}
|
||||
|
||||
protected void onCardLongPress(int index, Entry<PaperCard, Integer> value, float x, float y) {
|
||||
CardZoom.show(model.getOrderedList(), index, CardManager.this);
|
||||
}
|
||||
|
||||
/* Static overrides shared with SpellShopManager*/
|
||||
|
||||
public static void addDefaultFilters(final ItemManager<? super PaperCard> itemManager) {
|
||||
itemManager.addFilter(new CardColorFilter(itemManager));
|
||||
itemManager.addFilter(new CardFormatFilter(itemManager));
|
||||
itemManager.addFilter(new CardTypeFilter(itemManager));
|
||||
}
|
||||
|
||||
public static TextSearchFilter<PaperCard> createSearchFilter(final ItemManager<? super PaperCard> itemManager) {
|
||||
return new CardSearchFilter(itemManager);
|
||||
}
|
||||
|
||||
public static AdvancedSearchFilter<PaperCard> createAdvancedSearchFilter(final ItemManager<? super PaperCard> itemManager) {
|
||||
return new AdvancedSearchFilter<>(itemManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemRenderer getListItemRenderer(final CompactModeHandler compactModeHandler) {
|
||||
return new ItemRenderer() {
|
||||
@Override
|
||||
public float getItemHeight() {
|
||||
return CardRenderer.getCardListItemHeight(compactModeHandler.isCompactMode());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawValue(Graphics g, Entry<PaperCard, Integer> value, FSkinFont font, FSkinColor foreColor, FSkinColor backColor, boolean pressed, float x, float y, float w, float h) {
|
||||
CardRenderer.drawCardListItem(g, font, foreColor, value.getKey(), isInfinite() ? 0 : value.getValue(), getItemSuffix(value), x, y, w, h, compactModeHandler.isCompactMode());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean tap(Integer index, Entry<PaperCard, Integer> value, float x, float y, int count) {
|
||||
return CardRenderer.cardListItemTap(model.getOrderedList(), index, CardManager.this, x, y, count, compactModeHandler.isCompactMode());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean longPress(Integer index, Entry<PaperCard, Integer> value, float x, float y) {
|
||||
if (CardRenderer.cardListItemTap(model.getOrderedList(), index, CardManager.this, x, y, 1, compactModeHandler.isCompactMode())) {
|
||||
return true; //avoid calling onCardLongPress if user long presses on card art
|
||||
}
|
||||
onCardLongPress(index, value, x, y);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean allowPressEffect(FList<Entry<PaperCard, Integer>> list, float x, float y) {
|
||||
//only allow press effect if right of card art
|
||||
return x > CardRenderer.getCardListItemHeight(compactModeHandler.isCompactMode()) * CardRenderer.CARD_ART_RATIO;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
package forge.adventure.libgdxgui.itemmanager;
|
||||
|
||||
import com.badlogic.gdx.utils.Align;
|
||||
import forge.adventure.libgdxgui.Forge;
|
||||
import forge.adventure.libgdxgui.Graphics;
|
||||
import forge.adventure.libgdxgui.assets.FSkinColor;
|
||||
import forge.adventure.libgdxgui.assets.FSkinFont;
|
||||
import forge.adventure.libgdxgui.assets.FSkinImage;
|
||||
import forge.adventure.libgdxgui.card.CardFaceSymbols;
|
||||
import forge.adventure.libgdxgui.card.CardRenderer;
|
||||
import forge.adventure.libgdxgui.deck.FDeckViewer;
|
||||
import forge.adventure.libgdxgui.itemmanager.filters.AdvancedSearchFilter;
|
||||
import forge.adventure.libgdxgui.itemmanager.filters.DeckColorFilter;
|
||||
import forge.adventure.libgdxgui.itemmanager.filters.DeckFormatFilter;
|
||||
import forge.adventure.libgdxgui.itemmanager.filters.TextSearchFilter;
|
||||
import forge.adventure.libgdxgui.toolbox.FList;
|
||||
import forge.adventure.libgdxgui.toolbox.FList.CompactModeHandler;
|
||||
import forge.adventure.libgdxgui.util.Utils;
|
||||
import forge.card.ColorSet;
|
||||
import forge.deck.DeckProxy;
|
||||
import forge.deck.io.DeckPreferences;
|
||||
import forge.game.GameType;
|
||||
import forge.game.IHasGameType;
|
||||
import forge.itemmanager.ItemManagerConfig;
|
||||
import forge.util.Localizer;
|
||||
|
||||
import java.util.Map.Entry;
|
||||
|
||||
/**
|
||||
* ItemManager for decks
|
||||
*/
|
||||
public final class DeckManager extends ItemManager<DeckProxy> implements IHasGameType {
|
||||
private final GameType gameType;
|
||||
|
||||
/**
|
||||
* Creates deck list for selected decks for quick deleting, editing, and
|
||||
* basic info. "selectable" and "editable" assumed true.
|
||||
*
|
||||
* @param gt
|
||||
*/
|
||||
public DeckManager(final GameType gt) {
|
||||
super(DeckProxy.class, true);
|
||||
gameType = gt;
|
||||
setCaption(Localizer.getInstance().getMessage("lblDecks"));
|
||||
}
|
||||
|
||||
public GameType getGameType() {
|
||||
return gameType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setup(ItemManagerConfig config0) {
|
||||
boolean wasStringOnly = (getConfig() == ItemManagerConfig.STRING_ONLY);
|
||||
boolean isStringOnly = (config0 == ItemManagerConfig.STRING_ONLY);
|
||||
|
||||
super.setup(config0, null);
|
||||
|
||||
if (isStringOnly != wasStringOnly) {
|
||||
restoreDefaultFilters();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addDefaultFilters() {
|
||||
if (getConfig() == ItemManagerConfig.STRING_ONLY) { return; }
|
||||
|
||||
addFilter(new DeckColorFilter(this));
|
||||
addFilter(new DeckFormatFilter(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TextSearchFilter<DeckProxy> createSearchFilter() {
|
||||
return new TextSearchFilter<>(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AdvancedSearchFilter<DeckProxy> createAdvancedSearchFilter() {
|
||||
return new AdvancedSearchFilter<>(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean allowSortChange() {
|
||||
return false;
|
||||
}
|
||||
|
||||
private static final float IMAGE_SIZE = CardRenderer.MANA_SYMBOL_SIZE;
|
||||
|
||||
@Override
|
||||
public ItemRenderer getListItemRenderer(final CompactModeHandler compactModeHandler) {
|
||||
return new ItemRenderer() {
|
||||
@Override
|
||||
public float getItemHeight() {
|
||||
if (DeckManager.this.getConfig().getCols().size() == 1) {
|
||||
//if just string column, use normal list item height
|
||||
return Utils.AVG_FINGER_HEIGHT;
|
||||
}
|
||||
return CardRenderer.getCardListItemHeight(compactModeHandler.isCompactMode()); //use same height for decks as for cards
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean tap(Integer index, Entry<DeckProxy, Integer> value, float x, float y, int count) {
|
||||
float bottomRight = IMAGE_SIZE + 2 * FList.PADDING;
|
||||
if (x <= bottomRight && y <= bottomRight) {
|
||||
DeckPreferences prefs = DeckPreferences.getPrefs(value.getKey());
|
||||
prefs.setStarCount((prefs.getStarCount() + 1) % 2); //TODO: consider supporting more than 1 star
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean longPress(Integer index, Entry<DeckProxy, Integer> value, float x, float y) {
|
||||
FDeckViewer.show(value.getKey().getDeck());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawValue(Graphics g, Entry<DeckProxy, Integer> value, FSkinFont font, FSkinColor foreColor, FSkinColor backColor, boolean pressed, float x, float y, float w, float h) {
|
||||
DeckProxy deck = value.getKey();
|
||||
|
||||
if (DeckManager.this.getConfig().getCols().size() == 1) {
|
||||
//if just string column, just draw deck string value
|
||||
g.drawText(deck.toString(), font, foreColor, x, y, w, h, false, Align.left, true);
|
||||
return;
|
||||
}
|
||||
|
||||
//draw favorite, name, path and color on first line
|
||||
if (Forge.hdbuttons)
|
||||
g.drawImage(DeckPreferences.getPrefs(deck).getStarCount() > 0 ?
|
||||
FSkinImage.HDSTAR_FILLED : FSkinImage.HDSTAR_OUTLINE, x, y, IMAGE_SIZE, IMAGE_SIZE);
|
||||
else
|
||||
g.drawImage(DeckPreferences.getPrefs(deck).getStarCount() > 0 ? FSkinImage.STAR_FILLED : FSkinImage.STAR_OUTLINE, x, y, IMAGE_SIZE, IMAGE_SIZE);
|
||||
|
||||
x += IMAGE_SIZE + FList.PADDING;
|
||||
ColorSet deckColor = deck.getColor();
|
||||
float availableNameWidth = w - CardFaceSymbols.getWidth(deckColor, IMAGE_SIZE) - IMAGE_SIZE - 2 * FList.PADDING;
|
||||
String name = deck.getName();
|
||||
if (!deck.getPath().isEmpty()) { //render path after name if needed
|
||||
name += " (" + deck.getPath().substring(1) + ")";
|
||||
}
|
||||
g.drawText(name, font, foreColor, x, y, availableNameWidth, IMAGE_SIZE, false, Align.left, true);
|
||||
x += availableNameWidth + FList.PADDING;
|
||||
CardFaceSymbols.drawColorSet(g, deckColor, x, y, IMAGE_SIZE);
|
||||
|
||||
if (compactModeHandler.isCompactMode()) {
|
||||
return; //skip second line if compact mode
|
||||
}
|
||||
|
||||
//draw formats, main/side, and set/highest rarity on second line
|
||||
font = FSkinFont.get(12);
|
||||
float lineHeight = font.getLineHeight();
|
||||
|
||||
x = FList.PADDING;
|
||||
y += IMAGE_SIZE + FList.PADDING + CardRenderer.SET_BOX_MARGIN;
|
||||
String set = deck.getEdition().getCode();
|
||||
float setWidth = CardRenderer.getSetWidth(font, set);
|
||||
float availableFormatWidth = w - setWidth + CardRenderer.SET_BOX_MARGIN;
|
||||
|
||||
int mainSize = deck.getMainSize();
|
||||
if (mainSize < 0) {
|
||||
mainSize = 0; //show main as 0 if empty
|
||||
}
|
||||
int sideSize = deck.getSideSize();
|
||||
if (sideSize < 0) {
|
||||
sideSize = 0; //show sideboard as 0 if empty
|
||||
}
|
||||
|
||||
g.drawText(deck.getFormatsString() + " (" + mainSize + " / " + sideSize + ")", font, foreColor, x, y, availableFormatWidth, lineHeight, false, Align.left, true);
|
||||
|
||||
x += availableFormatWidth + CardRenderer.SET_BOX_MARGIN;
|
||||
y -= CardRenderer.SET_BOX_MARGIN;
|
||||
CardRenderer.drawSetLabel(g, font, set, deck.getHighestRarity(), x, y, setWidth, lineHeight + 2 * CardRenderer.SET_BOX_MARGIN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean allowPressEffect(FList<Entry<DeckProxy, Integer>> list, float x, float y) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user