diff --git a/AndorsTrail/app/src/main/java/com/gpl/rpg/AndorsTrail/controller/VisualEffectController.java b/AndorsTrail/app/src/main/java/com/gpl/rpg/AndorsTrail/controller/VisualEffectController.java index 8af21936a..b88b7a3c0 100644 --- a/AndorsTrail/app/src/main/java/com/gpl/rpg/AndorsTrail/controller/VisualEffectController.java +++ b/AndorsTrail/app/src/main/java/com/gpl/rpg/AndorsTrail/controller/VisualEffectController.java @@ -19,11 +19,15 @@ import com.gpl.rpg.AndorsTrail.resource.VisualEffectCollection.VisualEffect; import com.gpl.rpg.AndorsTrail.resource.tiles.TileManager; import com.gpl.rpg.AndorsTrail.util.Coord; import com.gpl.rpg.AndorsTrail.util.CoordRect; +import com.gpl.rpg.AndorsTrail.util.L; import com.gpl.rpg.AndorsTrail.util.Size; +import java.util.ArrayList; +import java.util.List; + public final class VisualEffectController { private int effectCount = 0; - + private final List activeAnimations = new ArrayList<>(); private final ControllerContext controllers; private final WorldContext world; private final VisualEffectCollection effectTypes; @@ -38,8 +42,24 @@ public final class VisualEffectController { public void startEffect(Coord position, VisualEffectCollection.VisualEffectID effectID, String displayValue, VisualEffectCompletedCallback callback, int callbackValue) { ++effectCount; - (new VisualEffectAnimation(effectTypes.getVisualEffect(effectID), position, displayValue, callback, callbackValue)) - .start(); + VisualEffectAnimation animation = new VisualEffectAnimation(effectTypes.getVisualEffect(effectID), position, displayValue, callback, callbackValue); + activeAnimations.add(animation); + animation.start(); + } + + public void collectAndSendAnimations(VisualEffectAnimation animation) { + List tileIDs = new ArrayList<>(); + List yOffsets = new ArrayList<>(); + //There are more elegant ways to fix this, but to get multiple effects to work, this is the quickest fix: increase all other effects that are being played + //TODO: if someone got time to make this look nice, be my guest + for (VisualEffectAnimation vanimation : activeAnimations) { + if (vanimation != animation && vanimation.currentFrame < vanimation.effect.lastFrame) vanimation.currentFrame++; + tileIDs.add(vanimation.effect.frameIconIDs[vanimation.currentFrame]); + yOffsets.add(-2 * vanimation.currentFrame); + L.log("VisualEffectController: collectAndSendAnimations: " + vanimation.currentFrame + " currentFrame, effectCount = " + effectCount); + } + L.log("VisualEffectController: collectAndSendAnimations: " + activeAnimations.size() + " animations to send, effectCount = " + effectCount); + visualEffectFrameListeners.onNewAnimationFrames(activeAnimations, tileIDs, yOffsets); } private VisualEffectCollection.VisualEffectID enqueuedEffectID = null; @@ -111,7 +131,7 @@ public final class VisualEffectController { if (callback != null) callback.onVisualEffectCompleted(callbackValue); visualEffectFrameListeners.onSpriteMoveCompleted(this); } - + public void start() { actor.hasVFXRunning = true; @@ -134,7 +154,19 @@ public final class VisualEffectController { textPaint.setAlpha(255); textPaint.setTextAlign(Align.CENTER); } - + + public static class VisualEffectData { + public final VisualEffectAnimation effect; + public final int tileID; + public final int textYOffset; + + public VisualEffectData(VisualEffectAnimation effect, int tileID, int textYOffset) { + this.effect = effect; + this.tileID = tileID; + this.textYOffset = textYOffset; + } + } + public final class VisualEffectAnimation extends Handler implements Runnable { @Override @@ -151,17 +183,23 @@ public final class VisualEffectController { ++currentFrame; int frame = currentFrame; - int tileID = effect.frameIconIDs[frame]; - int textYOffset = -2 * (frame); +// int tileID = effect.frameIconIDs[frame]; +// int textYOffset = -2 * (frame); + if (frame >= beginFadeAtFrame && displayText != null) { textPaint.setAlpha(255 * (effect.lastFrame - frame) / (effect.lastFrame - beginFadeAtFrame)); } + area.topLeft.y = position.y - 1; - visualEffectFrameListeners.onNewAnimationFrame(this, tileID, textYOffset); + + //Fall back to onNewAnimationFrame if the new effect handle process is no longer desired +// visualEffectFrameListeners.onNewAnimationFrame(this, tileID, textYOffset); + collectAndSendAnimations(this); } private void onCompleted() { --effectCount; + activeAnimations.remove(this); visualEffectFrameListeners.onAnimationCompleted(this); if (callback != null) callback.onVisualEffectCompleted(callbackValue); } diff --git a/AndorsTrail/app/src/main/java/com/gpl/rpg/AndorsTrail/controller/listeners/VisualEffectFrameListener.java b/AndorsTrail/app/src/main/java/com/gpl/rpg/AndorsTrail/controller/listeners/VisualEffectFrameListener.java index 34149225a..5d06aef20 100644 --- a/AndorsTrail/app/src/main/java/com/gpl/rpg/AndorsTrail/controller/listeners/VisualEffectFrameListener.java +++ b/AndorsTrail/app/src/main/java/com/gpl/rpg/AndorsTrail/controller/listeners/VisualEffectFrameListener.java @@ -4,8 +4,11 @@ import com.gpl.rpg.AndorsTrail.controller.VisualEffectController.SpriteMoveAnima import com.gpl.rpg.AndorsTrail.controller.VisualEffectController.VisualEffectAnimation; import com.gpl.rpg.AndorsTrail.util.CoordRect; +import java.util.List; + public interface VisualEffectFrameListener { void onNewAnimationFrame(VisualEffectAnimation animation, int tileID, int textYOffset); + void onNewAnimationFrames(List animations, List tileIDs, List textYOffsets); void onAnimationCompleted(VisualEffectAnimation animation); void onSpriteMoveStarted(SpriteMoveAnimation animation); void onNewSpriteMoveFrame(SpriteMoveAnimation animation); diff --git a/AndorsTrail/app/src/main/java/com/gpl/rpg/AndorsTrail/controller/listeners/VisualEffectFrameListeners.java b/AndorsTrail/app/src/main/java/com/gpl/rpg/AndorsTrail/controller/listeners/VisualEffectFrameListeners.java index cbeb221bb..60b6614aa 100644 --- a/AndorsTrail/app/src/main/java/com/gpl/rpg/AndorsTrail/controller/listeners/VisualEffectFrameListeners.java +++ b/AndorsTrail/app/src/main/java/com/gpl/rpg/AndorsTrail/controller/listeners/VisualEffectFrameListeners.java @@ -5,12 +5,18 @@ import com.gpl.rpg.AndorsTrail.controller.VisualEffectController.VisualEffectAni import com.gpl.rpg.AndorsTrail.util.CoordRect; import com.gpl.rpg.AndorsTrail.util.ListOfListeners; +import java.util.List; + public final class VisualEffectFrameListeners extends ListOfListeners implements VisualEffectFrameListener { private final Function3 onNewAnimationFrame = new Function3() { @Override public void call(VisualEffectFrameListener listener, VisualEffectAnimation animation, Integer tileID, Integer textYOffset) { listener.onNewAnimationFrame(animation, tileID, textYOffset); } }; + private final Function3, List, List> onNewAnimationFrames = new Function3, List, List>() { + @Override public void call(VisualEffectFrameListener listener, List animations, List tileIDs, List textYOffsets) { listener.onNewAnimationFrames(animations, tileIDs, textYOffsets); } + }; + private final Function1 onAnimationCompleted = new Function1() { @Override public void call(VisualEffectFrameListener listener, VisualEffectAnimation animation) { listener.onAnimationCompleted(animation); } }; @@ -36,6 +42,11 @@ public final class VisualEffectFrameListeners extends ListOfListeners animations, List tileIDs, List textYOffsets) { + callAllListeners(this.onNewAnimationFrames, animations, tileIDs, textYOffsets); + } + @Override public void onAnimationCompleted(VisualEffectAnimation animation) { callAllListeners(this.onAnimationCompleted, animation); diff --git a/AndorsTrail/app/src/main/java/com/gpl/rpg/AndorsTrail/util/CoordRect.java b/AndorsTrail/app/src/main/java/com/gpl/rpg/AndorsTrail/util/CoordRect.java index d83843dfc..841f0306e 100644 --- a/AndorsTrail/app/src/main/java/com/gpl/rpg/AndorsTrail/util/CoordRect.java +++ b/AndorsTrail/app/src/main/java/com/gpl/rpg/AndorsTrail/util/CoordRect.java @@ -30,6 +30,18 @@ public final class CoordRect { return true; } + public static CoordRect union(CoordRect r1, CoordRect r2) { + int left = Math.min(r1.topLeft.x, r2.topLeft.x); + int top = Math.min(r1.topLeft.y, r2.topLeft.y); + int right = Math.max(r1.topLeft.x + r1.size.width, r2.topLeft.x + r2.size.width); + int bottom = Math.max(r1.topLeft.y + r1.size.height, r2.topLeft.y + r2.size.height); + + int width = right - left; + int height = bottom - top; + + return new CoordRect(new Coord(left, top), new Size(width, height)); + } + /* public static boolean contains(final int x, final int y, final Size size, final Coord p) { if (p.x < x) return false; diff --git a/AndorsTrail/app/src/main/java/com/gpl/rpg/AndorsTrail/view/MainView.java b/AndorsTrail/app/src/main/java/com/gpl/rpg/AndorsTrail/view/MainView.java index 89fbcff33..1e3adf451 100644 --- a/AndorsTrail/app/src/main/java/com/gpl/rpg/AndorsTrail/view/MainView.java +++ b/AndorsTrail/app/src/main/java/com/gpl/rpg/AndorsTrail/view/MainView.java @@ -1,6 +1,8 @@ package com.gpl.rpg.AndorsTrail.view; import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.List; import com.gpl.rpg.AndorsTrail.AndorsTrailApplication; import com.gpl.rpg.AndorsTrail.AndorsTrailPreferences; @@ -8,6 +10,7 @@ import com.gpl.rpg.AndorsTrail.context.ControllerContext; import com.gpl.rpg.AndorsTrail.context.WorldContext; import com.gpl.rpg.AndorsTrail.controller.Constants; import com.gpl.rpg.AndorsTrail.controller.InputController; +import com.gpl.rpg.AndorsTrail.controller.VisualEffectController; import com.gpl.rpg.AndorsTrail.controller.VisualEffectController.BloodSplatter; import com.gpl.rpg.AndorsTrail.controller.VisualEffectController.SpriteMoveAnimation; import com.gpl.rpg.AndorsTrail.controller.VisualEffectController.VisualEffectAnimation; @@ -29,6 +32,7 @@ import com.gpl.rpg.AndorsTrail.resource.tiles.TileCollection; import com.gpl.rpg.AndorsTrail.resource.tiles.TileManager; import com.gpl.rpg.AndorsTrail.util.Coord; import com.gpl.rpg.AndorsTrail.util.CoordRect; +import com.gpl.rpg.AndorsTrail.util.L; import com.gpl.rpg.AndorsTrail.util.Size; import android.content.Context; @@ -76,6 +80,8 @@ public final class MainView extends SurfaceView private final int[] debugColors = {Color.MAGENTA, Color.BLUE, Color.CYAN, Color.GREEN, Color.YELLOW, Color.RED, Color.WHITE}; private final CoordRect p1x1 = new CoordRect(new Coord(), new Size(1,1)); private boolean hasSurface = false; + + private List activeEffects = new ArrayList<>(); //DEBUG // private Coord touchedTile = null; @@ -301,6 +307,68 @@ public final class MainView extends SurfaceView if (c != null) holder.unlockCanvasAndPost(c); } } + + private void redrawArea_(CoordRect area, final List effects, List tileIDs, List textYOffsets) { + if (!hasSurface) return; + + + if (!currentMap.intersects(area)) return; + if (!mapViewArea.intersects(area)) return; + + if (shouldRedrawEverything()) { + area = mapViewArea; + } + + calculateRedrawRect(area); + redrawRect.intersect(redrawClip); + Canvas c = null; + try { + c = holder.lockCanvas(redrawRect); + if (area != mapViewArea) { + if (isRedrawRectWholeScreen(redrawRect)) { + area = mapViewArea; + } + } + if (area == mapViewArea) { + area = adaptAreaToScrolling(area); + } + + synchronized (holder) { synchronized (tiles) { + int xScroll = 0; + int yScroll = 0; + if (scrolling && scrollVector != null) { + xScroll = (int) (tileSize - (tileSize * (System.currentTimeMillis() - scrollStartTime) / SCROLL_DURATION)); + xScroll = Math.max(0, Math.min(tileSize, xScroll)) * scrollVector.x; + yScroll = (int) (tileSize - (tileSize * (System.currentTimeMillis() - scrollStartTime) / SCROLL_DURATION)); + yScroll = Math.max(0, Math.min(tileSize, yScroll)) * scrollVector.y; + } + c.clipRect(redrawClip); + c.translate(screenOffset.x + xScroll, screenOffset.y + yScroll); + doDrawRect(c, area); + // Render each effect + renderEffects(c); + } } + } finally { + if (c != null) holder.unlockCanvasAndPost(c); + } + } + + private void renderEffects(Canvas canvas) { + for (VisualEffectController.VisualEffectData data : activeEffects) { + VisualEffectAnimation effect = data.effect; + int tileID = data.tileID; + int textYOffset = data.textYOffset; + + L.log("Rendering effect at position: " + effect.position + " with tileID: " + tileID + " and textYOffset: " + textYOffset); + + drawFromMapPosition(canvas, effect.area, effect.position, tileID); + if (effect.displayText != null) { + drawEffectText(canvas, effect.area, effect, textYOffset, effect.getTextPaint()); + } + } + L.log("Total effects rendered: " + activeEffects.size()); + activeEffects.clear(); + } private boolean isRedrawRectWholeScreen(Rect redrawRect) { // if (redrawRect.width() < mapViewArea.size.width * scaledTileSize) return false; @@ -322,6 +390,22 @@ public final class MainView extends SurfaceView // if (shouldRedrawEverythingForVisualEffect()) area = mapViewArea; redrawArea_(area, effect, tileID, textYOffset); } + private void redrawAreaWithEffect(List effects, List tileIDs, List textYOffsets) { + CoordRect area = null; + for (int i = 0; i < effects.size(); i++) { + VisualEffectAnimation effect = effects.get(i); + activeEffects.add(new VisualEffectController.VisualEffectData(effect, tileIDs.get(i), textYOffsets.get(i))); + if (area == null) { + area = effect.area; + } else { + area = CoordRect.union(area, effect.area); + } + } + if (area != null) { + redrawArea_(area, effects, tileIDs, textYOffsets); + } + } + private void clearCanvas() { if (!hasSurface) return; Canvas c = null; @@ -798,6 +882,12 @@ public final class MainView extends SurfaceView redrawAreaWithEffect(animation, tileID, textYOffset); } + @Override + public void onNewAnimationFrames(List animations, List tileIDs, List textYOffsets) { + L.log("Rendering " + animations.size() + " effects"); + redrawAreaWithEffect(animations, tileIDs, textYOffsets); + } + @Override public void onAnimationCompleted(VisualEffectAnimation animation) { redrawArea(animation.area, RedrawAreaDebugReason.EffectCompleted);