diff --git a/.gitattributes b/.gitattributes index c1895a95e1f..a30e915ca2a 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1107,6 +1107,10 @@ forge-gui-mobile/src/forge/Forge.java -text forge-gui-mobile/src/forge/Graphics.java -text forge-gui-mobile/src/forge/GuiMobile.java -text forge-gui-mobile/src/forge/TextureRenderer.java -text +forge-gui-mobile/src/forge/animation/AbilityEffect.java -text +forge-gui-mobile/src/forge/animation/ForgeAnimation.java -text +forge-gui-mobile/src/forge/animation/GifAnimation.java -text +forge-gui-mobile/src/forge/animation/GifDecoder.java -text forge-gui-mobile/src/forge/assets/AssetsDownloader.java -text forge-gui-mobile/src/forge/assets/BitmapFontWriter.java -text forge-gui-mobile/src/forge/assets/FImage.java -text @@ -15440,6 +15444,7 @@ forge-gui/res/editions/Visions.txt -text forge-gui/res/editions/Weatherlight.txt -text forge-gui/res/editions/Worldwake.txt -text forge-gui/res/editions/Zendikar.txt -text +forge-gui/res/effects/lightning.gif -text forge-gui/res/howto.txt svneol=native#text/plain forge-gui/res/licenses/java-yield-license.txt svneol=native#text/plain forge-gui/res/licenses/log4j-license.txt svneol=native#text/plain diff --git a/forge-gui-mobile/src/forge/Forge.java b/forge-gui-mobile/src/forge/Forge.java index 2916e6b7081..d6b18ee0990 100644 --- a/forge-gui-mobile/src/forge/Forge.java +++ b/forge-gui-mobile/src/forge/Forge.java @@ -1,7 +1,6 @@ package forge; import java.util.ArrayList; -import java.util.List; import java.util.Stack; import com.badlogic.gdx.ApplicationListener; @@ -10,6 +9,7 @@ import com.badlogic.gdx.Input.Keys; import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.utils.Clipboard; +import forge.animation.ForgeAnimation; import forge.assets.AssetsDownloader; import forge.assets.FSkin; import forge.assets.FSkinFont; @@ -187,7 +187,7 @@ public class Forge implements ApplicationListener { private static void setCurrentScreen(FScreen screen0) { try { endKeyInput(); //end key input before switching screens - Animation.endAll(); //end all active animations before switching screens + ForgeAnimation.endAll(); //end all active animations before switching screens currentScreen = screen0; currentScreen.setSize(screenWidth, screenHeight); @@ -207,8 +207,8 @@ public class Forge implements ApplicationListener { public void render() { try { ImageCache.allowSingleLoad(); - Animation.advanceAll(); - + ForgeAnimation.advanceAll(); + Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); // Clear the screen. FContainer screen = currentScreen; @@ -569,43 +569,4 @@ public class Forge implements ApplicationListener { return handled; } } - - public static abstract class Animation { - private static final List activeAnimations = new ArrayList(); - - public void start() { - if (activeAnimations.contains(this)) { return; } //prevent starting the same animation multiple times - - activeAnimations.add(this); - if (activeAnimations.size() == 1) { //if first animation being started, ensure continuous rendering turned on - Gdx.graphics.setContinuousRendering(true); - } - } - - private 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)) { - activeAnimations.remove(i); - i--; - } - } - - if (activeAnimations.isEmpty()) { //when all animations have ended, turn continuous rendering back off - Gdx.graphics.setContinuousRendering(false); - } - } - - private static void endAll() { - if (activeAnimations.isEmpty()) { return; } - - activeAnimations.clear(); - Gdx.graphics.setContinuousRendering(false); - } - - //return true if animation should continue, false to stop the animation - protected abstract boolean advance(float dt); - } } diff --git a/forge-gui-mobile/src/forge/GuiMobile.java b/forge-gui-mobile/src/forge/GuiMobile.java index 063bc6621fd..5adf3da1753 100644 --- a/forge-gui-mobile/src/forge/GuiMobile.java +++ b/forge-gui-mobile/src/forge/GuiMobile.java @@ -470,7 +470,7 @@ public class GuiMobile implements IGuiBase { @Override public IAudioClip createAudioClip(String filename) { - return AudioClip.createClip(filename); + return AudioClip.createClip(ForgeConstants.SOUND_DIR + filename); } @Override diff --git a/forge-gui-mobile/src/forge/animation/AbilityEffect.java b/forge-gui-mobile/src/forge/animation/AbilityEffect.java new file mode 100644 index 00000000000..00e91225972 --- /dev/null +++ b/forge-gui-mobile/src/forge/animation/AbilityEffect.java @@ -0,0 +1,35 @@ +package forge.animation; + +import forge.Graphics; +import forge.properties.ForgeConstants; +import forge.sound.AudioClip; + +public enum AbilityEffect { + LIGHTNING("lightning.gif", "lightning.wav"); + + private final String gif, wav; + private GifAnimation animation; + private AudioClip soundClip; + + private 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(); + 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); + } + } +} diff --git a/forge-gui-mobile/src/forge/animation/ForgeAnimation.java b/forge-gui-mobile/src/forge/animation/ForgeAnimation.java new file mode 100644 index 00000000000..a2eb875a1c6 --- /dev/null +++ b/forge-gui-mobile/src/forge/animation/ForgeAnimation.java @@ -0,0 +1,45 @@ +package forge.animation; + +import java.util.ArrayList; +import java.util.List; + +import com.badlogic.gdx.Gdx; + +public abstract class ForgeAnimation { + private static final List activeAnimations = new ArrayList(); + + public void start() { + if (activeAnimations.contains(this)) { return; } //prevent starting the same animation multiple times + + activeAnimations.add(this); + if (activeAnimations.size() == 1) { //if first animation being started, ensure continuous rendering turned on + Gdx.graphics.setContinuousRendering(true); + } + } + + 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)) { + activeAnimations.remove(i); + i--; + } + } + + if (activeAnimations.isEmpty()) { //when all animations have ended, turn continuous rendering back off + Gdx.graphics.setContinuousRendering(false); + } + } + + public static void endAll() { + if (activeAnimations.isEmpty()) { return; } + + activeAnimations.clear(); + Gdx.graphics.setContinuousRendering(false); + } + + //return true if animation should continue, false to stop the animation + protected abstract boolean advance(float dt); +} diff --git a/forge-gui-mobile/src/forge/animation/GifAnimation.java b/forge-gui-mobile/src/forge/animation/GifAnimation.java new file mode 100644 index 00000000000..6174e11f350 --- /dev/null +++ b/forge-gui-mobile/src/forge/animation/GifAnimation.java @@ -0,0 +1,37 @@ +package forge.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.Graphics; + +public class GifAnimation extends ForgeAnimation { + private final Animation 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); + } + } +} diff --git a/forge-gui-mobile/src/forge/animation/GifDecoder.java b/forge-gui-mobile/src/forge/animation/GifDecoder.java new file mode 100644 index 00000000000..22ef363a3dd --- /dev/null +++ b/forge-gui-mobile/src/forge/animation/GifDecoder.java @@ -0,0 +1,738 @@ +package forge.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 java.io.InputStream; +import java.util.Vector; + +import com.badlogic.gdx.graphics.Pixmap; +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.graphics.Texture; +import com.badlogic.gdx.utils.Array; + +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 frames; // frames read from current file + protected int frameCount; + + private static class DixieMap extends Pixmap { + DixieMap(int w, int h, Pixmap.Format f) { + super(w, h, f); + } + + DixieMap(int[] data, int w, int h, Pixmap.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 ((GifFrame) 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(); + String app = ""; + for (int i = 0; i < 11; i++) { + app += (char) block[i]; + } + if (app.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() { + String id = ""; + for (int i = 0; i < 6; i++) { + id += (char) read(); + } + if (!id.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()); + } + + public Animation getAnimation(PlayMode playType) { + int nrFrames = getFrameCount(); + Pixmap frame = getFrame(0); + int width = frame.getWidth(); + int height = frame.getHeight(); + int vzones = (int)Math.sqrt((double)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 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 + Animation result = new Animation(frameDuration, texReg, playType); + + return result; + } + + public static Animation loadGIFAnimation(PlayMode playType, InputStream is) { + GifDecoder gdec = new GifDecoder(); + gdec.read(is); + return gdec.getAnimation(playType); + } +} diff --git a/forge-gui-mobile/src/forge/screens/match/MatchScreen.java b/forge-gui-mobile/src/forge/screens/match/MatchScreen.java index d4641d451e7..3b84ba3b9b4 100644 --- a/forge-gui-mobile/src/forge/screens/match/MatchScreen.java +++ b/forge-gui-mobile/src/forge/screens/match/MatchScreen.java @@ -31,6 +31,7 @@ import forge.screens.match.views.VStack; import forge.sound.MusicPlaylist; import forge.Forge.KeyInputAdapter; import forge.Graphics; +import forge.animation.AbilityEffect; import forge.assets.FSkinColor; import forge.assets.FSkinTexture; import forge.assets.FSkinColor.Colors; @@ -54,6 +55,7 @@ public class MatchScreen extends FScreen { private final VDevMenu devMenu; private final FieldScroller scroller; private VPlayerPanel bottomPlayerPanel, topPlayerPanel; + private AbilityEffect activeEffect; public MatchScreen(Game game, LobbyPlayer localPlayer, List playerPanels0) { super(new FMenuBar()); @@ -174,6 +176,10 @@ public class MatchScreen extends FScreen { } } } + + if (activeEffect != null) { + activeEffect.draw(g, 10, 10, 100, 100); + } } @Override diff --git a/forge-gui-mobile/src/forge/sound/AudioClip.java b/forge-gui-mobile/src/forge/sound/AudioClip.java index f2a1ce074db..66dbe702f7b 100644 --- a/forge-gui-mobile/src/forge/sound/AudioClip.java +++ b/forge-gui-mobile/src/forge/sound/AudioClip.java @@ -18,8 +18,6 @@ package forge.sound; -import forge.properties.ForgeConstants; - import com.badlogic.gdx.audio.Sound; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.files.FileHandle; @@ -28,7 +26,7 @@ public class AudioClip implements IAudioClip { private Sound clip; public static AudioClip createClip(String filename) { - FileHandle fileHandle = Gdx.files.absolute(ForgeConstants.SOUND_DIR + filename); + FileHandle fileHandle = Gdx.files.absolute(filename); if (!fileHandle.exists()) { return null; } return new AudioClip(fileHandle); } diff --git a/forge-gui-mobile/src/forge/toolbox/FScrollPane.java b/forge-gui-mobile/src/forge/toolbox/FScrollPane.java index 33eecda49aa..29a03390118 100644 --- a/forge-gui-mobile/src/forge/toolbox/FScrollPane.java +++ b/forge-gui-mobile/src/forge/toolbox/FScrollPane.java @@ -4,7 +4,7 @@ import java.util.ArrayList; import com.badlogic.gdx.math.Vector2; -import forge.Forge.Animation; +import forge.animation.ForgeAnimation; import forge.Graphics; import forge.util.PhysicsObject; import forge.util.Utils; @@ -219,7 +219,7 @@ public abstract class FScrollPane extends FContainer { private FlingAnimation activeFlingAnimation; - private class FlingAnimation extends Animation { + private class FlingAnimation extends ForgeAnimation { private final PhysicsObject physicsObj; private FlingAnimation(float velocityX, float velocityY) { diff --git a/forge-gui/res/effects/lightning.gif b/forge-gui/res/effects/lightning.gif new file mode 100644 index 00000000000..b2f7c185f0f Binary files /dev/null and b/forge-gui/res/effects/lightning.gif differ diff --git a/forge-gui/src/main/java/forge/properties/ForgeConstants.java b/forge-gui/src/main/java/forge/properties/ForgeConstants.java index 7b6b1741bc7..bab74503353 100644 --- a/forge-gui/src/main/java/forge/properties/ForgeConstants.java +++ b/forge-gui/src/main/java/forge/properties/ForgeConstants.java @@ -56,6 +56,7 @@ public final class ForgeConstants { public static final String AI_PROFILE_DIR = RES_DIR + "ai/"; public static final String SOUND_DIR = RES_DIR + "sound/"; public static final String MUSIC_DIR = RES_DIR + "music/"; + public static final String EFFECTS_DIR = RES_DIR + "effects/"; private static final String QUEST_DIR = RES_DIR + "quest/"; public static final String QUEST_WORLD_DIR = QUEST_DIR + "world/";