From 3ad385e02f915f7435a9f5272f9ef06ef001c54d Mon Sep 17 00:00:00 2001 From: Zukero Date: Fri, 28 Sep 2018 13:33:30 +0200 Subject: [PATCH] Attempt to fix perf issue with filters that plagues some devices only. --- .../res/raw/conversationlist_debug.json | 115 +- AndorsTrail/res/values/strings.xml | 4 + AndorsTrail/res/xml/preferences.xml | 5 + .../AndorsTrail/AndorsTrailApplication.java | 15 +- .../AndorsTrail/AndorsTrailPreferences.java | 207 +- .../controller/WorldMapController.java | 2 +- .../AndorsTrail/model/map/LayeredTileMap.java | 42 +- .../gpl/rpg/AndorsTrail/view/MainView.java | 1688 ++++++++--------- 8 files changed, 1092 insertions(+), 986 deletions(-) diff --git a/AndorsTrail/res/raw/conversationlist_debug.json b/AndorsTrail/res/raw/conversationlist_debug.json index ead482671..50cc54102 100644 --- a/AndorsTrail/res/raw/conversationlist_debug.json +++ b/AndorsTrail/res/raw/conversationlist_debug.json @@ -193,18 +193,46 @@ { "nextPhraseID":"npc3_1", "text":"Beer!" - }, - { - "nextPhraseID":"npc3_2", - "text":"Lights out!" }, { "nextPhraseID":"npc3_3", - "text":"Lights on!" + "text":"No filter!" + }, + { + "nextPhraseID":"npc3_5", + "text":"Black 20%!" + }, + { + "nextPhraseID":"npc3_6", + "text":"Black 40%!" + }, + { + "nextPhraseID":"npc3_7", + "text":"Black 60%!" + }, + { + "nextPhraseID":"npc3_2", + "text":"Black 80%!" }, { "nextPhraseID":"npc3_4", "text":"Red ligths!" + }, + { + "nextPhraseID":"npc3_9", + "text":"Green ligths!" + }, + { + "nextPhraseID":"npc3_10", + "text":"Blue ligths!" + }, + { + "nextPhraseID":"npc3_11", + "text":"Black & White!" + }, + { + "nextPhraseID":"npc3_8", + "text":"Invert!" } ] }, @@ -266,6 +294,83 @@ } ] }, + { + "id":"npc3_5", + "message":"Ok.", + "rewards":[ + { + "rewardType":"changeMapFilter", + "rewardID":"black20", + "mapName":"debugmap" + } + ] + }, + { + "id":"npc3_6", + "message":"Ok.", + "rewards":[ + { + "rewardType":"changeMapFilter", + "rewardID":"black40", + "mapName":"debugmap" + } + ] + }, + { + "id":"npc3_7", + "message":"Ok.", + "rewards":[ + { + "rewardType":"changeMapFilter", + "rewardID":"black60", + "mapName":"debugmap" + } + ] + }, + { + "id":"npc3_8", + "message":"Ok.", + "rewards":[ + { + "rewardType":"changeMapFilter", + "rewardID":"invert", + "mapName":"debugmap" + } + ] + }, + { + "id":"npc3_9", + "message":"Ok.", + "rewards":[ + { + "rewardType":"changeMapFilter", + "rewardID":"greentint", + "mapName":"debugmap" + } + ] + }, + { + "id":"npc3_10", + "message":"Ok.", + "rewards":[ + { + "rewardType":"changeMapFilter", + "rewardID":"bluetint", + "mapName":"debugmap" + } + ] + }, + { + "id":"npc3_11", + "message":"Ok.", + "rewards":[ + { + "rewardType":"changeMapFilter", + "rewardID":"bw", + "mapName":"debugmap" + } + ] + }, { "id":"chaotic_rewarder_0", "message":"What do you want ?", diff --git a/AndorsTrail/res/values/strings.xml b/AndorsTrail/res/values/strings.xml index 7867fb831..8936ab5ee 100644 --- a/AndorsTrail/res/values/strings.xml +++ b/AndorsTrail/res/values/strings.xml @@ -475,6 +475,10 @@ Optimized drawing Disable this if you see graphical artifacts. Enabling this option will make the game only redraw changed parts of the screen every frame. + High quality filters + Disable this if you experience performance issues on filtered maps (dark caves for example). Enabling this option will make the game use advanced color filters, instead of solid color overlays. + + diff --git a/AndorsTrail/res/xml/preferences.xml b/AndorsTrail/res/xml/preferences.xml index 5fdd0caf8..aaa992698 100644 --- a/AndorsTrail/res/xml/preferences.xml +++ b/AndorsTrail/res/xml/preferences.xml @@ -26,6 +26,11 @@ android:defaultValue="false" android:summary="@string/preferences_optimized_drawing" android:key="optimized_drawing" /> + diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/AndorsTrailApplication.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/AndorsTrailApplication.java index 2cf7f9ce0..b71d932de 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/AndorsTrailApplication.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/AndorsTrailApplication.java @@ -3,28 +3,27 @@ package com.gpl.rpg.AndorsTrail; import java.io.File; import java.io.IOException; import java.util.Locale; + +import com.gpl.rpg.AndorsTrail.context.ControllerContext; +import com.gpl.rpg.AndorsTrail.context.WorldContext; +import com.gpl.rpg.AndorsTrail.controller.Constants; + import android.annotation.SuppressLint; import android.app.Activity; import android.app.Application; import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; -import android.os.Build; import android.os.Environment; import android.view.Window; import android.view.WindowManager; -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.util.L; - public final class AndorsTrailApplication extends Application { - public static final boolean DEVELOPMENT_DEBUGRESOURCES = false; + public static final boolean DEVELOPMENT_DEBUGRESOURCES = true; public static final boolean DEVELOPMENT_FORCE_STARTNEWGAME = false; public static final boolean DEVELOPMENT_FORCE_CONTINUEGAME = false; - public static final boolean DEVELOPMENT_DEBUGBUTTONS = false; + public static final boolean DEVELOPMENT_DEBUGBUTTONS = true; public static final boolean DEVELOPMENT_FASTSPEED = false; public static final boolean DEVELOPMENT_VALIDATEDATA = true; public static final boolean DEVELOPMENT_DEBUGMESSAGES = true; diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/AndorsTrailPreferences.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/AndorsTrailPreferences.java index d90d5ec01..e9af916a5 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/AndorsTrailPreferences.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/AndorsTrailPreferences.java @@ -1,102 +1,105 @@ -package com.gpl.rpg.AndorsTrail; - -import com.gpl.rpg.AndorsTrail.util.ThemeHelper; - -import android.content.Context; -import android.content.SharedPreferences; -import android.preference.PreferenceManager; - -public final class AndorsTrailPreferences { - public static final int DISPLAYLOOT_DIALOG_ALWAYS = 0; - public static final int DISPLAYLOOT_DIALOG_FOR_ITEMS = 3; - public static final int DISPLAYLOOT_DIALOG_FOR_ITEMS_ELSE_TOAST = 4; - public static final int DISPLAYLOOT_TOAST = 1; - public static final int DISPLAYLOOT_TOAST_FOR_ITEMS = 5; - public static final int DISPLAYLOOT_NONE = 2; - public static final int MOVEMENTMETHOD_STRAIGHT = 0; - public static final int MOVEMENTMETHOD_DIRECTIONAL = 1; - public static final int MOVEMENTAGGRESSIVENESS_NORMAL = 0; - public static final int MOVEMENTAGGRESSIVENESS_AGGRESSIVE = 1; - public static final int MOVEMENTAGGRESSIVENESS_DEFENSIVE = 2; - public static final int DPAD_POSITION_DISABLED = 0; - public static final int DPAD_POSITION_LOWER_RIGHT = 1; - public static final int DPAD_POSITION_LOWER_LEFT = 2; - public static final int DPAD_POSITION_LOWER_CENTER = 3; - public static final int DPAD_POSITION_CENTER_LEFT = 4; - public static final int DPAD_POSITION_CENTER_RIGHT = 5; - public static final int DPAD_POSITION_UPPER_LEFT = 6; - public static final int DPAD_POSITION_UPPER_RIGHT = 7; - public static final int DPAD_POSITION_UPPER_CENTER = 8; - public static final int CONFIRM_OVERWRITE_SAVEGAME_ALWAYS = 0; - public static final int CONFIRM_OVERWRITE_SAVEGAME_WHEN_PLAYERNAME_DIFFERS = 1; - public static final int CONFIRM_OVERWRITE_SAVEGAME_NEVER = 2; - public static final int QUICKSLOTS_POSITION_HORIZONTAL_CENTER_BOTTOM = 0; - public static final int QUICKSLOTS_POSITION_VERTICAL_CENTER_LEFT = 1; - public static final int QUICKSLOTS_POSITION_VERTICAL_CENTER_RIGHT = 2; - public static final int QUICKSLOTS_POSITION_VERTICAL_BOTTOM_LEFT = 3; - public static final int QUICKSLOTS_POSITION_HORIZONTAL_BOTTOM_LEFT = 4; - public static final int QUICKSLOTS_POSITION_HORIZONTAL_BOTTOM_RIGHT = 5; - public static final int QUICKSLOTS_POSITION_VERTICAL_BOTTOM_RIGHT = 6; - public static final int ATTACKSPEED_DEFAULT_MILLISECONDS = 1000; - - public boolean confirmRest = true; - public boolean confirmAttack = true; - public int displayLoot = DISPLAYLOOT_DIALOG_ALWAYS; - public boolean fullscreen = true; - public int attackspeed_milliseconds = 1000; - public int movementMethod = MOVEMENTMETHOD_STRAIGHT; - public int movementAggressiveness = MOVEMENTAGGRESSIVENESS_NORMAL; - public float scalingFactor = 1.0f; - public int dpadPosition; - public boolean dpadMinimizeable = true; - public boolean optimizedDrawing = false; - public boolean enableUiAnimations = true; - public int displayOverwriteSavegame = CONFIRM_OVERWRITE_SAVEGAME_ALWAYS; - public int quickslotsPosition = QUICKSLOTS_POSITION_HORIZONTAL_CENTER_BOTTOM; - public boolean showQuickslotsWhenToolboxIsVisible = false; - public boolean useLocalizedResources = true; - public int selectedTheme = 0; - - public void read(final Context androidContext) { - AndorsTrailPreferences dest = this; - try { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(androidContext); - dest.confirmRest = prefs.getBoolean("confirm_rest", true); - dest.confirmAttack = prefs.getBoolean("confirm_attack", true); - dest.displayLoot = Integer.parseInt(prefs.getString("display_lootdialog", Integer.toString(DISPLAYLOOT_DIALOG_ALWAYS))); - dest.fullscreen = prefs.getBoolean("fullscreen", true); - dest.attackspeed_milliseconds = Integer.parseInt(prefs.getString("attackspeed", "1000")); - dest.movementMethod = Integer.parseInt(prefs.getString("movementmethod", Integer.toString(MOVEMENTMETHOD_STRAIGHT))); - dest.scalingFactor = Float.parseFloat(prefs.getString("scaling_factor", "1.0f")); - dest.dpadPosition = Integer.parseInt(prefs.getString("dpadposition", Integer.toString(DPAD_POSITION_DISABLED))); - dest.dpadMinimizeable = prefs.getBoolean("dpadMinimizeable", true); - dest.optimizedDrawing = prefs.getBoolean("optimized_drawing", false); - dest.enableUiAnimations = prefs.getBoolean("enableUiAnimations", true); - dest.displayOverwriteSavegame = Integer.parseInt(prefs.getString("display_overwrite_savegame", Integer.toString(CONFIRM_OVERWRITE_SAVEGAME_ALWAYS))); - dest.quickslotsPosition = Integer.parseInt(prefs.getString("quickslots_placement", Integer.toString(QUICKSLOTS_POSITION_HORIZONTAL_CENTER_BOTTOM))); - dest.showQuickslotsWhenToolboxIsVisible = prefs.getBoolean("showQuickslotsWhenToolboxIsVisible", false); - dest.useLocalizedResources = prefs.getBoolean("useLocalizedResources", true); - dest.selectedTheme = Integer.parseInt(prefs.getString("selectedTheme", Integer.toString(0))); - // This might be implemented as a skill in the future. - //dest.movementAggressiveness = Integer.parseInt(prefs.getString("movementaggressiveness", Integer.toString(MOVEMENTAGGRESSIVENESS_NORMAL))); - } catch (Exception e) { - dest.confirmRest = true; - dest.confirmAttack = true; - dest.displayLoot = DISPLAYLOOT_DIALOG_ALWAYS; - dest.fullscreen = true; - dest.attackspeed_milliseconds = 1000; - dest.movementMethod = MOVEMENTMETHOD_STRAIGHT; - dest.movementAggressiveness = MOVEMENTAGGRESSIVENESS_NORMAL; - dest.scalingFactor = 1.0f; - dest.dpadPosition = DPAD_POSITION_DISABLED; - dest.dpadMinimizeable = true; - dest.optimizedDrawing = false; - dest.enableUiAnimations = true; - dest.displayOverwriteSavegame = CONFIRM_OVERWRITE_SAVEGAME_ALWAYS; - dest.quickslotsPosition = QUICKSLOTS_POSITION_HORIZONTAL_CENTER_BOTTOM; - dest.showQuickslotsWhenToolboxIsVisible = false; - dest.useLocalizedResources = true; - dest.selectedTheme = 0; - } - } -} +package com.gpl.rpg.AndorsTrail; + +import com.gpl.rpg.AndorsTrail.util.ThemeHelper; + +import android.content.Context; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; + +public final class AndorsTrailPreferences { + public static final int DISPLAYLOOT_DIALOG_ALWAYS = 0; + public static final int DISPLAYLOOT_DIALOG_FOR_ITEMS = 3; + public static final int DISPLAYLOOT_DIALOG_FOR_ITEMS_ELSE_TOAST = 4; + public static final int DISPLAYLOOT_TOAST = 1; + public static final int DISPLAYLOOT_TOAST_FOR_ITEMS = 5; + public static final int DISPLAYLOOT_NONE = 2; + public static final int MOVEMENTMETHOD_STRAIGHT = 0; + public static final int MOVEMENTMETHOD_DIRECTIONAL = 1; + public static final int MOVEMENTAGGRESSIVENESS_NORMAL = 0; + public static final int MOVEMENTAGGRESSIVENESS_AGGRESSIVE = 1; + public static final int MOVEMENTAGGRESSIVENESS_DEFENSIVE = 2; + public static final int DPAD_POSITION_DISABLED = 0; + public static final int DPAD_POSITION_LOWER_RIGHT = 1; + public static final int DPAD_POSITION_LOWER_LEFT = 2; + public static final int DPAD_POSITION_LOWER_CENTER = 3; + public static final int DPAD_POSITION_CENTER_LEFT = 4; + public static final int DPAD_POSITION_CENTER_RIGHT = 5; + public static final int DPAD_POSITION_UPPER_LEFT = 6; + public static final int DPAD_POSITION_UPPER_RIGHT = 7; + public static final int DPAD_POSITION_UPPER_CENTER = 8; + public static final int CONFIRM_OVERWRITE_SAVEGAME_ALWAYS = 0; + public static final int CONFIRM_OVERWRITE_SAVEGAME_WHEN_PLAYERNAME_DIFFERS = 1; + public static final int CONFIRM_OVERWRITE_SAVEGAME_NEVER = 2; + public static final int QUICKSLOTS_POSITION_HORIZONTAL_CENTER_BOTTOM = 0; + public static final int QUICKSLOTS_POSITION_VERTICAL_CENTER_LEFT = 1; + public static final int QUICKSLOTS_POSITION_VERTICAL_CENTER_RIGHT = 2; + public static final int QUICKSLOTS_POSITION_VERTICAL_BOTTOM_LEFT = 3; + public static final int QUICKSLOTS_POSITION_HORIZONTAL_BOTTOM_LEFT = 4; + public static final int QUICKSLOTS_POSITION_HORIZONTAL_BOTTOM_RIGHT = 5; + public static final int QUICKSLOTS_POSITION_VERTICAL_BOTTOM_RIGHT = 6; + public static final int ATTACKSPEED_DEFAULT_MILLISECONDS = 1000; + + public boolean confirmRest = true; + public boolean confirmAttack = true; + public int displayLoot = DISPLAYLOOT_DIALOG_ALWAYS; + public boolean fullscreen = true; + public int attackspeed_milliseconds = 1000; + public int movementMethod = MOVEMENTMETHOD_STRAIGHT; + public int movementAggressiveness = MOVEMENTAGGRESSIVENESS_NORMAL; + public float scalingFactor = 1.0f; + public int dpadPosition; + public boolean dpadMinimizeable = true; + public boolean optimizedDrawing = false; + public boolean highQualityFilters = true; + public boolean enableUiAnimations = true; + public int displayOverwriteSavegame = CONFIRM_OVERWRITE_SAVEGAME_ALWAYS; + public int quickslotsPosition = QUICKSLOTS_POSITION_HORIZONTAL_CENTER_BOTTOM; + public boolean showQuickslotsWhenToolboxIsVisible = false; + public boolean useLocalizedResources = true; + public int selectedTheme = 0; + + public void read(final Context androidContext) { + AndorsTrailPreferences dest = this; + try { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(androidContext); + dest.confirmRest = prefs.getBoolean("confirm_rest", true); + dest.confirmAttack = prefs.getBoolean("confirm_attack", true); + dest.displayLoot = Integer.parseInt(prefs.getString("display_lootdialog", Integer.toString(DISPLAYLOOT_DIALOG_ALWAYS))); + dest.fullscreen = prefs.getBoolean("fullscreen", true); + dest.attackspeed_milliseconds = Integer.parseInt(prefs.getString("attackspeed", "1000")); + dest.movementMethod = Integer.parseInt(prefs.getString("movementmethod", Integer.toString(MOVEMENTMETHOD_STRAIGHT))); + dest.scalingFactor = Float.parseFloat(prefs.getString("scaling_factor", "1.0f")); + dest.dpadPosition = Integer.parseInt(prefs.getString("dpadposition", Integer.toString(DPAD_POSITION_DISABLED))); + dest.dpadMinimizeable = prefs.getBoolean("dpadMinimizeable", true); + dest.optimizedDrawing = prefs.getBoolean("optimized_drawing", false); + dest.highQualityFilters = prefs.getBoolean("high_quality_filters", true); + dest.enableUiAnimations = prefs.getBoolean("enableUiAnimations", true); + dest.displayOverwriteSavegame = Integer.parseInt(prefs.getString("display_overwrite_savegame", Integer.toString(CONFIRM_OVERWRITE_SAVEGAME_ALWAYS))); + dest.quickslotsPosition = Integer.parseInt(prefs.getString("quickslots_placement", Integer.toString(QUICKSLOTS_POSITION_HORIZONTAL_CENTER_BOTTOM))); + dest.showQuickslotsWhenToolboxIsVisible = prefs.getBoolean("showQuickslotsWhenToolboxIsVisible", false); + dest.useLocalizedResources = prefs.getBoolean("useLocalizedResources", true); + dest.selectedTheme = Integer.parseInt(prefs.getString("selectedTheme", Integer.toString(0))); + // This might be implemented as a skill in the future. + //dest.movementAggressiveness = Integer.parseInt(prefs.getString("movementaggressiveness", Integer.toString(MOVEMENTAGGRESSIVENESS_NORMAL))); + } catch (Exception e) { + dest.confirmRest = true; + dest.confirmAttack = true; + dest.displayLoot = DISPLAYLOOT_DIALOG_ALWAYS; + dest.fullscreen = true; + dest.attackspeed_milliseconds = 1000; + dest.movementMethod = MOVEMENTMETHOD_STRAIGHT; + dest.movementAggressiveness = MOVEMENTAGGRESSIVENESS_NORMAL; + dest.scalingFactor = 1.0f; + dest.dpadPosition = DPAD_POSITION_DISABLED; + dest.dpadMinimizeable = true; + dest.optimizedDrawing = false; + dest.highQualityFilters = true; + dest.enableUiAnimations = true; + dest.displayOverwriteSavegame = CONFIRM_OVERWRITE_SAVEGAME_ALWAYS; + dest.quickslotsPosition = QUICKSLOTS_POSITION_HORIZONTAL_CENTER_BOTTOM; + dest.showQuickslotsWhenToolboxIsVisible = false; + dest.useLocalizedResources = true; + dest.selectedTheme = 0; + } + } +} diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/WorldMapController.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/WorldMapController.java index 32626c5c7..3e1edb8e3 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/WorldMapController.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/WorldMapController.java @@ -115,7 +115,7 @@ public final class WorldMapController { this.cachedTiles = cachedTiles; this.tileSize = world.tileManager.tileSize; this.scale = (float) WORLDMAP_SCREENSHOT_TILESIZE / world.tileManager.tileSize; - mapTiles.setColorFilter(mPaint); + mapTiles.setColorFilter(mPaint, null, true); } public Bitmap drawMap() { diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/LayeredTileMap.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/LayeredTileMap.java index c35be2452..11eba43c0 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/LayeredTileMap.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/LayeredTileMap.java @@ -2,14 +2,14 @@ package com.gpl.rpg.AndorsTrail.model.map; import java.util.Collection; -import android.graphics.ColorFilter; -import android.graphics.ColorMatrixColorFilter; -import android.graphics.Paint; - import com.gpl.rpg.AndorsTrail.util.Coord; import com.gpl.rpg.AndorsTrail.util.CoordRect; import com.gpl.rpg.AndorsTrail.util.Size; +import android.graphics.ColorFilter; +import android.graphics.ColorMatrixColorFilter; +import android.graphics.Paint; + public final class LayeredTileMap { private static final ColorFilter colorFilterBlack20 = createGrayScaleColorFilter(0.8f); private static final ColorFilter colorFilterBlack40 = createGrayScaleColorFilter(0.6f); @@ -20,7 +20,6 @@ public final class LayeredTileMap { private static final ColorFilter colorFilterRedTint = createRedTintColorFilter(); private static final ColorFilter colorFilterGreenTint = createGreenTintColorFilter(); private static final ColorFilter colorFilterBlueTint = createBlueTintColorFilter(); - public enum ColorFilterId { none, @@ -89,8 +88,9 @@ public final class LayeredTileMap { return false; } - public void setColorFilter(Paint mPaint) { - mPaint.setColorFilter(getColorFilter()); + public void setColorFilter(Paint mPaint, Paint alternateColorFilterPaint, boolean highQuality) { + if (highQuality) mPaint.setColorFilter(getColorFilter()); + else setColor(alternateColorFilterPaint); } public ColorFilter getColorFilter() { @@ -120,6 +120,34 @@ public final class LayeredTileMap { } } + + + public void setColor(Paint p) { + if (colorFilter == null) { + p.setARGB(0, 0, 0, 0); + return; + } + switch (colorFilter) { + case black20: + p.setARGB(51, 0, 0, 0); return; + case black40: + p.setARGB(102, 0, 0, 0); return; + case black60: + p.setARGB(153, 0, 0, 0); return; + case black80: + p.setARGB(204, 0, 0, 0); return; + case redtint: + p.setARGB(50, 200, 0, 0); return; + case greentint: + p.setARGB(50, 0, 200, 0); return; + case bluetint: + p.setARGB(50, 0, 0, 200); return; + default: + p.setARGB(0, 0, 0, 0); return; + + } + + } private static ColorMatrixColorFilter createGrayScaleColorFilter(float blackOpacity) { final float f = blackOpacity; diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/view/MainView.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/view/MainView.java index 08137b3d4..ede798575 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/view/MainView.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/view/MainView.java @@ -1,863 +1,825 @@ -package com.gpl.rpg.AndorsTrail.view; - -import java.lang.ref.WeakReference; - -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Rect; -import android.os.Handler; -import android.util.AttributeSet; -import android.view.KeyEvent; -import android.view.MotionEvent; -import android.view.SurfaceHolder; -import android.view.SurfaceView; - -import com.gpl.rpg.AndorsTrail.AndorsTrailApplication; -import com.gpl.rpg.AndorsTrail.AndorsTrailPreferences; -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.BloodSplatter; -import com.gpl.rpg.AndorsTrail.controller.VisualEffectController.SpriteMoveAnimation; -import com.gpl.rpg.AndorsTrail.controller.VisualEffectController.VisualEffectAnimation; -import com.gpl.rpg.AndorsTrail.controller.listeners.CombatSelectionListener; -import com.gpl.rpg.AndorsTrail.controller.listeners.GameRoundListener; -import com.gpl.rpg.AndorsTrail.controller.listeners.MapLayoutListener; -import com.gpl.rpg.AndorsTrail.controller.listeners.MonsterMovementListener; -import com.gpl.rpg.AndorsTrail.controller.listeners.MonsterSpawnListener; -import com.gpl.rpg.AndorsTrail.controller.listeners.PlayerMovementListener; -import com.gpl.rpg.AndorsTrail.controller.listeners.VisualEffectFrameListener; -import com.gpl.rpg.AndorsTrail.model.ModelContainer; -import com.gpl.rpg.AndorsTrail.model.actor.Monster; -import com.gpl.rpg.AndorsTrail.model.item.Loot; -import com.gpl.rpg.AndorsTrail.model.map.LayeredTileMap; -import com.gpl.rpg.AndorsTrail.model.map.MapLayer; -import com.gpl.rpg.AndorsTrail.model.map.MonsterSpawnArea; -import com.gpl.rpg.AndorsTrail.model.map.PredefinedMap; -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; - -public final class MainView extends SurfaceView - implements SurfaceHolder.Callback, - PlayerMovementListener, - CombatSelectionListener, - MonsterSpawnListener, - MonsterMovementListener, - MapLayoutListener, - VisualEffectFrameListener, - GameRoundListener { - - private final int tileSize; - private float scale; - private int scaledTileSize; - - private Size screenSizeTileCount = null; - private final Coord screenOffset = new Coord(); // pixel offset where the image begins - private final Coord mapTopLeft = new Coord(); // Map coords of visible map - private CoordRect mapViewArea; // Area in mapcoordinates containing the visible map. topleft == this.topleft - private Rect redrawClip = new Rect(); //Area in screen coordinates containing the visible map. - - private final ModelContainer model; - private final WorldContext world; - private final ControllerContext controllers; - private final InputController inputController; - private final AndorsTrailPreferences preferences; - - private final SurfaceHolder holder; - private final Paint mPaint = new Paint(); - private final CoordRect p1x1 = new CoordRect(new Coord(), new Size(1,1)); - private boolean hasSurface = false; - - //DEBUG -// private Coord touchedTile = null; -// private static Paint touchHighlight = new Paint(); -// private static Paint redrawHighlight = new Paint(); -// -// static { -// touchHighlight.setColor(Color.RED); -// touchHighlight.setStrokeWidth(0f); -// touchHighlight.setStyle(Style.STROKE); -// redrawHighlight.setColor(Color.CYAN); -// redrawHighlight.setStrokeWidth(0f); -// redrawHighlight.setStyle(Style.STROKE); -// } - - private PredefinedMap currentMap; - private LayeredTileMap currentTileMap; - private TileCollection tiles; - - private final Coord playerPosition = new Coord(); - private Size surfaceSize; - private boolean redrawNextTick = false; - - private boolean scrolling = false; - private Coord scrollVector; - private long scrollStartTime; - //TODO restore private final modifiers before release - public static long SCROLL_DURATION = Constants.MINIMUM_INPUT_INTERVAL; - private int movingSprites = 0; - private SpriteMoveAnimationHandler movingSpritesRedrawTick = new SpriteMoveAnimationHandler(this); - - - public MainView(Context context, AttributeSet attr) { - super(context, attr); - this.holder = getHolder(); - - AndorsTrailApplication app = AndorsTrailApplication.getApplicationFromActivityContext(context); - this.controllers = app.getControllerContext(); - this.world = app.getWorld(); - this.model = world.model; - this.tileSize = world.tileManager.tileSize; - this.inputController = controllers.inputController; - this.preferences = app.getPreferences(); - - holder.addCallback(this); - - setFocusable(true); - requestFocus(); - setOnClickListener(this.inputController); - setOnLongClickListener(this.inputController); - - - } - - @Override - public boolean onKeyDown(int keyCode, KeyEvent msg) { - if (!canAcceptInput()) return true; - - if (inputController.onKeyboardAction(keyCode)) return true; - else return super.onKeyDown(keyCode, msg); - } - - @Override - public boolean onKeyUp(int keyCode, KeyEvent msg) { - if (!canAcceptInput()) return true; - - inputController.onKeyboardCancel(); - - return super.onKeyUp(keyCode, msg); - } - - @Override - public void surfaceChanged(SurfaceHolder sh, int format, int w, int h) { - if (w <= 0 || h <= 0) return; - - - this.scale = world.tileManager.scale; - this.mPaint.setFilterBitmap(scale != 1); - this.scaledTileSize = world.tileManager.viewTileSize; -// this.surfaceSize = new Size(w, h); - this.surfaceSize = new Size((int) (getWidth() / scale), (int) (getHeight() / scale)); - this.screenSizeTileCount = new Size( - (int) Math.floor(getWidth() / scaledTileSize) - ,(int) Math.floor(getHeight() / scaledTileSize) - ); - - if (sh.getSurfaceFrame().right != surfaceSize.width || sh.getSurfaceFrame().bottom != surfaceSize.height) { - sh.setFixedSize(surfaceSize.width, surfaceSize.height); - } - - if (model.currentMap != null) { - onPlayerEnteredNewMap(model.currentMap, model.player.position); - } else { - redrawAll(RedrawAllDebugReason.SurfaceChanged); - } - } - - @Override - public void surfaceCreated(SurfaceHolder sh) { - hasSurface = true; - } - - @Override - public void surfaceDestroyed(SurfaceHolder sh) { - hasSurface = false; - movingSpritesRedrawTick.stop(); - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - if (!canAcceptInput()) return true; - - switch (event.getAction()) { - case MotionEvent.ACTION_DOWN: - case MotionEvent.ACTION_MOVE: - final int tile_x = (int) Math.floor(((int)event.getX() - screenOffset.x * scale) / scaledTileSize) + mapTopLeft.x; - final int tile_y = (int) Math.floor(((int)event.getY() - screenOffset.y * scale) / scaledTileSize) + mapTopLeft.y; -// touchedTile = new Coord(tile_x, tile_y); - if (inputController.onTouchedTile(tile_x, tile_y)) return true; - break; - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: - case MotionEvent.ACTION_OUTSIDE: - inputController.onTouchCancel(); - break; - } - return super.onTouchEvent(event); - } - - private boolean canAcceptInput() { - if (!model.uiSelections.isMainActivityVisible) return false; - if (!hasSurface) return false; - return true; - } - - private static enum RedrawAllDebugReason { - SurfaceChanged, MapChanged, PlayerMoved, SpriteMoved, MapScrolling, FilterAnimation - } - private static enum RedrawAreaDebugReason { - MonsterMoved, MonsterKilled, EffectCompleted, AsyncRequest - } - private static enum RedrawTileDebugReason { - SelectionRemoved, SelectionAdded, Bag - } - - private void redrawAll(RedrawAllDebugReason why) { - if (preferences.enableUiAnimations) { - if (scrolling && why != RedrawAllDebugReason.MapScrolling) return; - if (!scrolling && movingSprites > 0 && why != RedrawAllDebugReason.SpriteMoved) return; - } - redrawArea_(mapViewArea, null, 0, 0); - } - private void redrawTile(final Coord p, RedrawTileDebugReason why) { - if (scrolling) return; - p1x1.topLeft.set(p); - redrawArea_(p1x1, null, 0, 0); - } - private void redrawArea(final CoordRect area, RedrawAreaDebugReason why) { - if (scrolling) return; - redrawArea_(area, null, 0, 0); - } - private void redrawArea_(CoordRect area, final VisualEffectAnimation effect, int tileID, int textYOffset) { - 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); - // lockCanvas sometimes changes redrawRect, when the double-buffer has not been - // sufficiently filled beforehand. In those cases, we need to redraw the whole scene. - 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); -// c.scale(scale, scale); - doDrawRect(c, area); - if (effect != null) { - drawFromMapPosition(c, area, effect.position, tileID); - if (effect.displayText != null) { - drawEffectText(c, area, effect, textYOffset, effect.getTextPaint()); - } - } - -// c.drawRect(new Rect( -// (area.topLeft.x - mapViewArea.topLeft.x) * tileSize, -// (area.topLeft.y - mapViewArea.topLeft.y) * tileSize, -// (area.topLeft.x - mapViewArea.topLeft.x + area.size.width) * tileSize - 1, -// (area.topLeft.y - mapViewArea.topLeft.y + area.size.height) * tileSize - 1), -// redrawHighlight); -// if (touchedTile != null) c.drawRect(new Rect( -// (touchedTile.x - mapViewArea.topLeft.x) * tileSize, -// (touchedTile.y - mapViewArea.topLeft.y) * tileSize, -// (touchedTile.x - mapViewArea.topLeft.x + 1) * tileSize - 1, -// (touchedTile.y - mapViewArea.topLeft.y + 1) * tileSize - 1), -// touchHighlight); - } } - } finally { - if (c != null) holder.unlockCanvasAndPost(c); - } - } - - private void redrawMoveArea_(CoordRect area, final SpriteMoveAnimation effect) { - if (!hasSurface) return; - if (scrolling) return; - - if (currentMap.isOutside(area)) return; - if (!mapViewArea.intersects(area)) return; - - if (shouldRedrawEverything()) { - area = mapViewArea; - } - - calculateRedrawRect(area); - Canvas c = null; - try { - c = holder.lockCanvas(redrawRect); - // lockCanvas sometimes changes redrawRect, when the double-buffer has not been - // sufficiently filled beforehand. In those cases, we need to redraw the whole scene. - if (area != mapViewArea) { - if (isRedrawRectWholeScreen(redrawRect)) { - area = mapViewArea; - } - } - synchronized (holder) { synchronized (tiles) { - c.clipRect(redrawClip); - c.translate(screenOffset.x, screenOffset.y); -// c.scale(scale, scale); - doDrawRect(c, area); - -// c.drawRect(new Rect( -// (area.topLeft.x - mapViewArea.topLeft.x) * tileSize, -// (area.topLeft.y - mapViewArea.topLeft.y) * tileSize, -// (area.topLeft.x - mapViewArea.topLeft.x + area.size.width) * tileSize - 1, -// (area.topLeft.y - mapViewArea.topLeft.y + area.size.height) * tileSize - 1), -// redrawHighlight); -// if (touchedTile != null) c.drawRect(new Rect( -// (touchedTile.x - mapViewArea.topLeft.x) * tileSize, -// (touchedTile.y - mapViewArea.topLeft.y) * tileSize, -// (touchedTile.x - mapViewArea.topLeft.x + 1) * tileSize - 1, -// (touchedTile.y - mapViewArea.topLeft.y + 1) * tileSize - 1), -// touchHighlight); - - } } - } finally { - if (c != null) holder.unlockCanvasAndPost(c); - } - } - - private boolean isRedrawRectWholeScreen(Rect redrawRect) { -// if (redrawRect.width() < mapViewArea.size.width * scaledTileSize) return false; -// if (redrawRect.height() < mapViewArea.size.height * scaledTileSize) return false; - if (redrawRect.width() < mapViewArea.size.width * tileSize) return false; - if (redrawRect.height() < mapViewArea.size.height * tileSize) return false; - return true; - } - - private boolean shouldRedrawEverything() { - if (scrolling) return true; - if (model.uiSelections.isInCombat) return true; // Discard the "optimized drawing" setting while in combat. - if (preferences.optimizedDrawing) return false; - return true; - } - private final Rect redrawRect = new Rect(); - private void redrawAreaWithEffect(final VisualEffectAnimation effect, int tileID, int textYOffset) { - CoordRect area = effect.area; -// if (shouldRedrawEverythingForVisualEffect()) area = mapViewArea; - redrawArea_(area, effect, tileID, textYOffset); - } - private void clearCanvas() { - if (!hasSurface) return; - Canvas c = null; - try { - c = holder.lockCanvas(); - synchronized (holder) { - c.drawColor(Color.BLACK); - } - } finally { - if (c != null) holder.unlockCanvasAndPost(c); - } - } - - private CoordRect adaptAreaToScrolling(final CoordRect area) { - - if (!scrolling || scrollVector == null) return area; - - int x, y, w, h; - if (scrollVector.x > 0) { - x = area.topLeft.x - scrollVector.x; - w = area.size.width + scrollVector.x; - } else { - x = area.topLeft.x; - w = area.size.width - scrollVector.x; - } - if (scrollVector.y > 0) { - y = area.topLeft.y - scrollVector.y; - h = area.size.height + scrollVector.y; - } else { - y = area.topLeft.y; - h = area.size.height - scrollVector.y; - } - CoordRect result = new CoordRect(new Coord(x, y), new Size(w, h)); - return result; - } - - private void calculateRedrawRect(final CoordRect area) { - worldCoordsToScreenCords(area, redrawRect); - } - - private void worldCoordsToScreenCords(final CoordRect worldArea, Rect destScreenRect) { -// destScreenRect.left = screenOffset.x + (worldArea.topLeft.x - mapViewArea.topLeft.x) * scaledTileSize; -// destScreenRect.top = screenOffset.y + (worldArea.topLeft.y - mapViewArea.topLeft.y) * scaledTileSize; -// destScreenRect.right = destScreenRect.left + worldArea.size.width * scaledTileSize; -// destScreenRect.bottom = destScreenRect.top + worldArea.size.height * scaledTileSize; - - destScreenRect.left = screenOffset.x + (worldArea.topLeft.x - mapViewArea.topLeft.x) * tileSize; - destScreenRect.top = screenOffset.y + (worldArea.topLeft.y - mapViewArea.topLeft.y) * tileSize; - destScreenRect.right = destScreenRect.left + worldArea.size.width * tileSize; - destScreenRect.bottom = destScreenRect.top + worldArea.size.height * tileSize; - } - -// private void worldCoordsToBitmapCoords(final CoordRect worldArea, Rect dstBitmapArea) { -// dstBitmapArea.left = worldArea.topLeft.x * tileSize; -// dstBitmapArea.top = worldArea.topLeft.y * tileSize; -// dstBitmapArea.right = dstBitmapArea.left + worldArea.size.width * tileSize; -// dstBitmapArea.bottom = dstBitmapArea.top + worldArea.size.height * tileSize; -// -// } - - private void doDrawRect(Canvas canvas, CoordRect area) { - doDrawRect_Ground(canvas, area); - doDrawRect_Objects(canvas, area); - doDrawRect_Above(canvas, area); - - } - - private void doDrawRect_Ground(Canvas canvas, CoordRect area) { - drawMapLayer(canvas, area, currentTileMap.currentLayout.layerGround); - tryDrawMapLayer(canvas, area, currentTileMap.currentLayout.layerObjects); - } - - private void doDrawRect_Objects(Canvas canvas, CoordRect area) { -// if (!tryDrawMapBitmap(canvas, area, objectsBitmap)) { -// tryDrawMapLayer(canvas, area, currentTileMap.currentLayout.layerObjects); -// } - - for (BloodSplatter splatter : currentMap.splatters) { - drawFromMapPosition(canvas, area, splatter.position, splatter.iconID); - } - - for (Loot l : currentMap.groundBags) { - if (l.isVisible) { - drawFromMapPosition(canvas, area, l.position, TileManager.iconID_groundbag); - } - } - - if (!model.player.hasVFXRunning) { - drawFromMapPosition(canvas, area, playerPosition, model.player.iconID); - } else if (area.contains(playerPosition)) { - int vfxElapsedTime = (int) (System.currentTimeMillis() - model.player.vfxStartTime); - if (vfxElapsedTime > model.player.vfxDuration) vfxElapsedTime = model.player.vfxDuration; - int x = ((model.player.position.x - mapViewArea.topLeft.x) * tileSize * vfxElapsedTime + ((model.player.lastPosition.x - mapViewArea.topLeft.x) * tileSize * (model.player.vfxDuration - vfxElapsedTime))) / model.player.vfxDuration; - int y = ((model.player.position.y - mapViewArea.topLeft.y) * tileSize * vfxElapsedTime + ((model.player.lastPosition.y - mapViewArea.topLeft.y) * tileSize * (model.player.vfxDuration - vfxElapsedTime))) / model.player.vfxDuration; - tiles.drawTile(canvas, model.player.iconID, x, y, mPaint); - } - for (MonsterSpawnArea a : currentMap.spawnAreas) { - for (Monster m : a.monsters) { - if (!m.hasVFXRunning) { - drawFromMapPosition(canvas, area, m.rectPosition, m.iconID); - } else if (area.intersects(m.rectPosition) || area.intersects(new CoordRect(m.lastPosition,m.rectPosition.size))) { - int vfxElapsedTime = (int) (System.currentTimeMillis() - m.vfxStartTime); - if (vfxElapsedTime > m.vfxDuration) vfxElapsedTime = m.vfxDuration; - int x = ((m.position.x - mapViewArea.topLeft.x) * tileSize * vfxElapsedTime + ((m.lastPosition.x - mapViewArea.topLeft.x) * tileSize * (m.vfxDuration - vfxElapsedTime))) / m.vfxDuration; - int y = ((m.position.y - mapViewArea.topLeft.y) * tileSize * vfxElapsedTime + ((m.lastPosition.y - mapViewArea.topLeft.y) * tileSize * (m.vfxDuration - vfxElapsedTime))) / m.vfxDuration; - tiles.drawTile(canvas, m.iconID, x, y, mPaint); - } - } - } - } - - private void doDrawRect_Above(Canvas canvas, CoordRect area) { - tryDrawMapLayer(canvas, area, currentTileMap.currentLayout.layerAbove); - tryDrawMapLayer(canvas, area, currentTileMap.currentLayout.layerTop); - - if (model.uiSelections.selectedPosition != null) { - if (model.uiSelections.selectedMonster != null) { - drawFromMapPosition(canvas, area, model.uiSelections.selectedPosition, TileManager.iconID_attackselect); - } else { - drawFromMapPosition(canvas, area, model.uiSelections.selectedPosition, TileManager.iconID_moveselect); - } - } - } - - private void tryDrawMapLayer(Canvas canvas, final CoordRect area, final MapLayer layer) { - if (layer != null) drawMapLayer(canvas, area, layer); - } - - private void drawMapLayer(Canvas canvas, final CoordRect area, final MapLayer layer) { - int my = area.topLeft.y; - int py = (area.topLeft.y - mapViewArea.topLeft.y) * tileSize; - int px0 = (area.topLeft.x - mapViewArea.topLeft.x) * tileSize; - for (int y = 0; y < area.size.height; ++y, ++my, py += tileSize) { - int mx = area.topLeft.x; - if (my < 0) continue; - if (my >= currentMap.size.height) break; - int px = px0; - for (int x = 0; x < area.size.width; ++x, ++mx, px += tileSize) { - if (mx < 0) continue; - if (mx >= currentMap.size.width) break; - final int tile = layer.tiles[mx][my]; - if (tile == 0) continue; - tiles.drawTile(canvas, tile, px, py, mPaint); - } - } - } - - private void drawFromMapPosition(Canvas canvas, final CoordRect area, final Coord p, final int tile) { - if (!area.contains(p)) return; - _drawFromMapPosition(canvas, area, p.x, p.y, tile); - } - private void drawFromMapPosition(Canvas canvas, final CoordRect area, final CoordRect p, final int tile) { - if (!area.intersects(p)) return; - _drawFromMapPosition(canvas, area, p.topLeft.x, p.topLeft.y, tile); - } - private void _drawFromMapPosition(Canvas canvas, final CoordRect area, int x, int y, final int tile) { - x -= mapViewArea.topLeft.x; - y -= mapViewArea.topLeft.y; -// if ( (x >= 0 && x < mapViewArea.size.width) -// && (y >= 0 && y < mapViewArea.size.height)) { - tiles.drawTile(canvas, tile, x * tileSize, y * tileSize, mPaint); -// } - } - - private void drawEffectText(Canvas canvas, final CoordRect area, final VisualEffectAnimation e, int textYOffset, Paint textPaint) { - int x = (e.position.x - mapViewArea.topLeft.x) * tileSize + tileSize/2; - int y = (e.position.y - mapViewArea.topLeft.y) * tileSize + tileSize/2 + textYOffset; - canvas.drawText(e.displayText, x, y, textPaint); - } - - @Override - public void onPlayerEnteredNewMap(PredefinedMap map, Coord p) { - movingSpritesRedrawTick.start(); - synchronized (holder) { - currentMap = map; - currentTileMap = model.currentTileMap; - tiles = world.tileManager.currentMapTiles; - movingSprites = 0; - Size visibleNumberOfTiles = new Size( - Math.min(screenSizeTileCount.width, currentMap.size.width) - ,Math.min(screenSizeTileCount.height, currentMap.size.height) - ); - mapViewArea = new CoordRect(mapTopLeft, visibleNumberOfTiles); - updateClip(); - -// screenOffset.set( -// (surfaceSize.width - scaledTileSize * visibleNumberOfTiles.width) / 2 -// ,(surfaceSize.height - scaledTileSize * visibleNumberOfTiles.height) / 2 -// ); - - - screenOffset.set( - (surfaceSize.width - tileSize * visibleNumberOfTiles.width) / 2 - ,(surfaceSize.height - tileSize * visibleNumberOfTiles.height) / 2 - ); - - currentTileMap.setColorFilter(this.mPaint); - } - -// touchedTile = null; - - clearCanvas(); - - recalculateMapTopLeft(model.player.position, false); - redrawAll(RedrawAllDebugReason.MapChanged); - } - - private void recalculateMapTopLeft(Coord playerPosition, boolean allowScrolling) { - synchronized (holder) { - int oldX = mapTopLeft.x; - int oldY = mapTopLeft.y; - this.playerPosition.set(playerPosition); - mapTopLeft.set(0, 0); - - if (currentMap.size.width > screenSizeTileCount.width) { - mapTopLeft.x = Math.max(0, playerPosition.x - mapViewArea.size.width/2); - mapTopLeft.x = Math.min(mapTopLeft.x, currentMap.size.width - mapViewArea.size.width); - } - if (currentMap.size.height > screenSizeTileCount.height) { - mapTopLeft.y = Math.max(0, playerPosition.y - mapViewArea.size.height/2); - mapTopLeft.y = Math.min(mapTopLeft.y, currentMap.size.height - mapViewArea.size.height); - } - updateClip(); - if (allowScrolling) { - if (mapTopLeft.x != oldX || mapTopLeft.y != oldY) { - scrollVector = new Coord(mapTopLeft.x - oldX, mapTopLeft.y - oldY); - new ScrollAnimationHandler(this).start(); - } - } else { - scrolling = false; - } - } - } - - private void updateClip() { - worldCoordsToScreenCords(mapViewArea, redrawClip); - } - - - public static final class ScrollAnimationHandler extends Handler implements Runnable { - - private static final int FRAME_DURATION = 40; - - private final WeakReference view; - - public ScrollAnimationHandler(MainView view) { - this.view = new WeakReference(view); - } - - @Override - public void run() { - MainView v = view.get(); - if (v == null) return; - if (System.currentTimeMillis() - v.scrollStartTime >= SCROLL_DURATION) { - onCompleted(); - } else { - postDelayed(this, FRAME_DURATION); - } - update(); - } - - private void update() { - MainView v = view.get(); - if (v == null) return; - v.redrawAll(RedrawAllDebugReason.MapScrolling); - } - - private void onCompleted() { - MainView v = view.get(); - if (v == null) return; - v.scrolling = false; - v.scrollVector = null; - } - - public void start() { - MainView v = view.get(); - if (v == null) return; - v.scrolling = true; - v.scrollStartTime = System.currentTimeMillis(); - postDelayed(this, 0); - } - } - - - public static final class SpriteMoveAnimationHandler extends Handler implements Runnable { - - private static final int FRAME_DURATION = 40; - private final WeakReference view; - private boolean stop = true; - - public SpriteMoveAnimationHandler(MainView view) { - this.view = new WeakReference(view); - } - - @Override - public void run() { - if (!stop) postDelayed(this, FRAME_DURATION); - update(); - } - - private void update() { -// L.log("stop="+stop+" - scroll="+scrolling+" - moving="+movingSprites); - if (stop) return; - MainView v = view.get(); - if (v == null) return; - if (!v.scrolling) { - if (v.movingSprites > 0) { - //TODO : limit redraw area when shouldRedrawEverything() returns false. - //Implies keeping track of the animation bounding box in a thread-safe way... :'( - v.redrawAll(RedrawAllDebugReason.SpriteMoved); - } - } - synchronized (this) { - if (v.movingSprites <= 0) stop(); - } - } - - public void start() { - if (stop) { - stop = false; - MainView v = view.get(); - if (v == null) return; - if (v.controllers.preferences.enableUiAnimations) postDelayed(this, 0); - } - } - - public void stop() { - stop = true; - } - } - - - - @Override - public void onPlayerMoved(Coord newPosition, Coord previousPosition) { - recalculateMapTopLeft(newPosition, preferences.enableUiAnimations); - redrawAll(RedrawAllDebugReason.PlayerMoved); - } - - public void subscribe() { - controllers.gameRoundController.gameRoundListeners.add(this); - controllers.effectController.visualEffectFrameListeners.add(this); - controllers.mapController.mapLayoutListeners.add(this); - controllers.movementController.playerMovementListeners.add(this); - controllers.combatController.combatSelectionListeners.add(this); - controllers.monsterSpawnController.monsterSpawnListeners.add(this); - controllers.monsterMovementController.monsterMovementListeners.add(this); - } - public void unsubscribe() { - controllers.monsterMovementController.monsterMovementListeners.remove(this); - controllers.monsterSpawnController.monsterSpawnListeners.remove(this); - controllers.combatController.combatSelectionListeners.remove(this); - controllers.movementController.playerMovementListeners.remove(this); - controllers.mapController.mapLayoutListeners.remove(this); - controllers.effectController.visualEffectFrameListeners.remove(this); - controllers.gameRoundController.gameRoundListeners.remove(this); - } - - @Override - public void onMonsterSelected(Monster m, Coord selectedPosition, Coord previousSelection) { - if (previousSelection != null) redrawTile(previousSelection, RedrawTileDebugReason.SelectionRemoved); - redrawTile(selectedPosition, RedrawTileDebugReason.SelectionAdded); - } - - @Override - public void onMovementDestinationSelected(Coord selectedPosition, Coord previousSelection) { - if (previousSelection != null) redrawTile(previousSelection, RedrawTileDebugReason.SelectionRemoved); - redrawTile(selectedPosition, RedrawTileDebugReason.SelectionAdded); - } - - @Override - public void onCombatSelectionCleared(Coord previousSelection) { - redrawTile(previousSelection, RedrawTileDebugReason.SelectionRemoved); - } - - @Override - public void onMonsterSpawned(PredefinedMap map, Monster m) { - if (map != currentMap) return; - if (!mapViewArea.intersects(m.rectPosition)) return; - redrawNextTick = true; - } - - @Override - public void onMonsterRemoved(PredefinedMap map, Monster m, CoordRect previousPosition) { - if (map != currentMap) return; - redrawArea(previousPosition, RedrawAreaDebugReason.MonsterKilled); - } - - @Override - public void onMonsterSteppedOnPlayer(Monster m) { - } - - @Override - public void onMonsterMoved(PredefinedMap map, Monster m, CoordRect previousPosition) { - if (map != currentMap) return; - if (!mapViewArea.intersects(m.rectPosition) && !mapViewArea.intersects(previousPosition)) return; - if (model.uiSelections.isInCombat) { - redrawArea(previousPosition, RedrawAreaDebugReason.MonsterMoved); - redrawArea(m.rectPosition, RedrawAreaDebugReason.MonsterMoved); - } else { - redrawNextTick = true; - } - } - - @Override - public void onSplatterAdded(PredefinedMap map, Coord p) { - if (map != currentMap) return; - if (!mapViewArea.contains(p)) return; - redrawNextTick = true; - } - - @Override - public void onSplatterChanged(PredefinedMap map, Coord p) { - if (map != currentMap) return; - if (!mapViewArea.contains(p)) return; - redrawNextTick = true; - } - - @Override - public void onSplatterRemoved(PredefinedMap map, Coord p) { - if (map != currentMap) return; - if (!mapViewArea.contains(p)) return; - redrawNextTick = true; - } - - @Override - public void onLootBagCreated(PredefinedMap map, Coord p) { - if (map != currentMap) return; - redrawTile(p, RedrawTileDebugReason.Bag); - } - - @Override - public void onLootBagRemoved(PredefinedMap map, Coord p) { - if (map != currentMap) return; - redrawTile(p, RedrawTileDebugReason.Bag); - } - - @Override - public void onMapTilesChanged(PredefinedMap map, LayeredTileMap tileMap) { - if (map != currentMap) return; - currentTileMap.setColorFilter(this.mPaint); - redrawAll(RedrawAllDebugReason.MapChanged); - } - - @Override - public void onNewAnimationFrame(VisualEffectAnimation animation, int tileID, int textYOffset) { - redrawAreaWithEffect(animation, tileID, textYOffset); - } - - @Override - public void onAnimationCompleted(VisualEffectAnimation animation) { - redrawArea(animation.area, RedrawAreaDebugReason.EffectCompleted); - } - - @Override - public void onSpriteMoveStarted(SpriteMoveAnimation animation) { - synchronized (movingSpritesRedrawTick) { - movingSprites++; - movingSpritesRedrawTick.start(); - } - } - - @Override - public void onNewSpriteMoveFrame(SpriteMoveAnimation animation) { - //redrawMoveArea_(CoordRect.getBoundingRect(animation.origin, animation.destination, animation.actor.tileSize), animation); - } - - @Override - public void onSpriteMoveCompleted(SpriteMoveAnimation animation) { - movingSprites--; - redrawArea(CoordRect.getBoundingRect(animation.origin, animation.destination, animation.actor.tileSize), RedrawAreaDebugReason.EffectCompleted); - } - - @Override - public void onAsyncAreaUpdate(CoordRect area) { - redrawArea(area, RedrawAreaDebugReason.AsyncRequest); - } - - @Override - public void onNewTick() { - if (!redrawNextTick) return; - - redrawAll(RedrawAllDebugReason.PlayerMoved); - - redrawNextTick = false; - } - - @Override - public void onNewRound() { } - - @Override - public void onNewFullRound() { } -} +package com.gpl.rpg.AndorsTrail.view; + +import java.lang.ref.WeakReference; + +import com.gpl.rpg.AndorsTrail.AndorsTrailApplication; +import com.gpl.rpg.AndorsTrail.AndorsTrailPreferences; +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.BloodSplatter; +import com.gpl.rpg.AndorsTrail.controller.VisualEffectController.SpriteMoveAnimation; +import com.gpl.rpg.AndorsTrail.controller.VisualEffectController.VisualEffectAnimation; +import com.gpl.rpg.AndorsTrail.controller.listeners.CombatSelectionListener; +import com.gpl.rpg.AndorsTrail.controller.listeners.GameRoundListener; +import com.gpl.rpg.AndorsTrail.controller.listeners.MapLayoutListener; +import com.gpl.rpg.AndorsTrail.controller.listeners.MonsterMovementListener; +import com.gpl.rpg.AndorsTrail.controller.listeners.MonsterSpawnListener; +import com.gpl.rpg.AndorsTrail.controller.listeners.PlayerMovementListener; +import com.gpl.rpg.AndorsTrail.controller.listeners.VisualEffectFrameListener; +import com.gpl.rpg.AndorsTrail.model.ModelContainer; +import com.gpl.rpg.AndorsTrail.model.actor.Monster; +import com.gpl.rpg.AndorsTrail.model.item.Loot; +import com.gpl.rpg.AndorsTrail.model.map.LayeredTileMap; +import com.gpl.rpg.AndorsTrail.model.map.MapLayer; +import com.gpl.rpg.AndorsTrail.model.map.MonsterSpawnArea; +import com.gpl.rpg.AndorsTrail.model.map.PredefinedMap; +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.Size; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Paint.Style; +import android.graphics.Rect; +import android.os.Handler; +import android.util.AttributeSet; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.SurfaceHolder; +import android.view.SurfaceView; + +public final class MainView extends SurfaceView + implements SurfaceHolder.Callback, + PlayerMovementListener, + CombatSelectionListener, + MonsterSpawnListener, + MonsterMovementListener, + MapLayoutListener, + VisualEffectFrameListener, + GameRoundListener { + + private final int tileSize; + private float scale; + private int scaledTileSize; + + private Size screenSizeTileCount = null; + private final Coord screenOffset = new Coord(); // pixel offset where the image begins + private final Coord mapTopLeft = new Coord(); // Map coords of visible map + private CoordRect mapViewArea; // Area in mapcoordinates containing the visible map. topleft == this.topleft + private Rect redrawClip = new Rect(); //Area in screen coordinates containing the visible map. + + private final ModelContainer model; + private final WorldContext world; + private final ControllerContext controllers; + private final InputController inputController; + private final AndorsTrailPreferences preferences; + + private final SurfaceHolder holder; + private final Paint mPaint = new Paint(); + private final CoordRect p1x1 = new CoordRect(new Coord(), new Size(1,1)); + private boolean hasSurface = false; + + //DEBUG +// private Coord touchedTile = null; +// private static Paint touchHighlight = new Paint(); +// private static Paint redrawHighlight = new Paint(); +// +// static { +// touchHighlight.setColor(Color.RED); +// touchHighlight.setStrokeWidth(0f); +// touchHighlight.setStyle(Style.STROKE); +// redrawHighlight.setColor(Color.CYAN); +// redrawHighlight.setStrokeWidth(0f); +// redrawHighlight.setStyle(Style.STROKE); +// } + + private PredefinedMap currentMap; + private LayeredTileMap currentTileMap; + private TileCollection tiles; + + private final Coord playerPosition = new Coord(); + private Size surfaceSize; + private boolean redrawNextTick = false; + + private boolean scrolling = false; + private Coord scrollVector; + private long scrollStartTime; + //TODO restore private final modifiers before release + public static long SCROLL_DURATION = Constants.MINIMUM_INPUT_INTERVAL; + private int movingSprites = 0; + private SpriteMoveAnimationHandler movingSpritesRedrawTick = new SpriteMoveAnimationHandler(this); + private Paint alternateColorFilterPaint = new Paint(); + + public MainView(Context context, AttributeSet attr) { + super(context, attr); + this.holder = getHolder(); + + AndorsTrailApplication app = AndorsTrailApplication.getApplicationFromActivityContext(context); + this.controllers = app.getControllerContext(); + this.world = app.getWorld(); + this.model = world.model; + this.tileSize = world.tileManager.tileSize; + this.inputController = controllers.inputController; + this.preferences = app.getPreferences(); + + alternateColorFilterPaint.setStyle(Style.FILL); + + holder.addCallback(this); + + setFocusable(true); + requestFocus(); + setOnClickListener(this.inputController); + setOnLongClickListener(this.inputController); + + + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent msg) { + if (!canAcceptInput()) return true; + + if (inputController.onKeyboardAction(keyCode)) return true; + else return super.onKeyDown(keyCode, msg); + } + + @Override + public boolean onKeyUp(int keyCode, KeyEvent msg) { + if (!canAcceptInput()) return true; + + inputController.onKeyboardCancel(); + + return super.onKeyUp(keyCode, msg); + } + + @Override + public void surfaceChanged(SurfaceHolder sh, int format, int w, int h) { + if (w <= 0 || h <= 0) return; + + + this.scale = world.tileManager.scale; + this.mPaint.setFilterBitmap(scale != 1); + this.scaledTileSize = world.tileManager.viewTileSize; +// this.surfaceSize = new Size(w, h); + this.surfaceSize = new Size((int) (getWidth() / scale), (int) (getHeight() / scale)); + this.screenSizeTileCount = new Size( + (int) Math.floor(getWidth() / scaledTileSize) + ,(int) Math.floor(getHeight() / scaledTileSize) + ); + + if (sh.getSurfaceFrame().right != surfaceSize.width || sh.getSurfaceFrame().bottom != surfaceSize.height) { + sh.setFixedSize(surfaceSize.width, surfaceSize.height); + } + + if (model.currentMap != null) { + onPlayerEnteredNewMap(model.currentMap, model.player.position); + } else { + redrawAll(RedrawAllDebugReason.SurfaceChanged); + } + } + + @Override + public void surfaceCreated(SurfaceHolder sh) { + hasSurface = true; + } + + @Override + public void surfaceDestroyed(SurfaceHolder sh) { + hasSurface = false; + movingSpritesRedrawTick.stop(); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + if (!canAcceptInput()) return true; + + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + case MotionEvent.ACTION_MOVE: + final int tile_x = (int) Math.floor(((int)event.getX() - screenOffset.x * scale) / scaledTileSize) + mapTopLeft.x; + final int tile_y = (int) Math.floor(((int)event.getY() - screenOffset.y * scale) / scaledTileSize) + mapTopLeft.y; +// touchedTile = new Coord(tile_x, tile_y); + if (inputController.onTouchedTile(tile_x, tile_y)) return true; + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_OUTSIDE: + inputController.onTouchCancel(); + break; + } + return super.onTouchEvent(event); + } + + private boolean canAcceptInput() { + if (!model.uiSelections.isMainActivityVisible) return false; + if (!hasSurface) return false; + return true; + } + + private static enum RedrawAllDebugReason { + SurfaceChanged, MapChanged, PlayerMoved, SpriteMoved, MapScrolling, FilterAnimation + } + private static enum RedrawAreaDebugReason { + MonsterMoved, MonsterKilled, EffectCompleted, AsyncRequest + } + private static enum RedrawTileDebugReason { + SelectionRemoved, SelectionAdded, Bag + } + + private void redrawAll(RedrawAllDebugReason why) { + if (preferences.enableUiAnimations) { + if (scrolling && why != RedrawAllDebugReason.MapScrolling) return; + if (!scrolling && movingSprites > 0 && why != RedrawAllDebugReason.SpriteMoved) return; + } + redrawArea_(mapViewArea, null, 0, 0); + } + private void redrawTile(final Coord p, RedrawTileDebugReason why) { + if (scrolling) return; + p1x1.topLeft.set(p); + redrawArea_(p1x1, null, 0, 0); + } + private void redrawArea(final CoordRect area, RedrawAreaDebugReason why) { + if (scrolling) return; + redrawArea_(area, null, 0, 0); + } + private void redrawArea_(CoordRect area, final VisualEffectAnimation effect, int tileID, int textYOffset) { + 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); + // lockCanvas sometimes changes redrawRect, when the double-buffer has not been + // sufficiently filled beforehand. In those cases, we need to redraw the whole scene. + 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); +// c.scale(scale, scale); + doDrawRect(c, area); + if (effect != null) { + drawFromMapPosition(c, area, effect.position, tileID); + if (effect.displayText != null) { + drawEffectText(c, area, effect, textYOffset, effect.getTextPaint()); + } + } + +// c.drawRect(new Rect( +// (area.topLeft.x - mapViewArea.topLeft.x) * tileSize, +// (area.topLeft.y - mapViewArea.topLeft.y) * tileSize, +// (area.topLeft.x - mapViewArea.topLeft.x + area.size.width) * tileSize - 1, +// (area.topLeft.y - mapViewArea.topLeft.y + area.size.height) * tileSize - 1), +// redrawHighlight); +// if (touchedTile != null) c.drawRect(new Rect( +// (touchedTile.x - mapViewArea.topLeft.x) * tileSize, +// (touchedTile.y - mapViewArea.topLeft.y) * tileSize, +// (touchedTile.x - mapViewArea.topLeft.x + 1) * tileSize - 1, +// (touchedTile.y - mapViewArea.topLeft.y + 1) * tileSize - 1), +// touchHighlight); + } } + } finally { + if (c != null) holder.unlockCanvasAndPost(c); + } + } + + private boolean isRedrawRectWholeScreen(Rect redrawRect) { +// if (redrawRect.width() < mapViewArea.size.width * scaledTileSize) return false; +// if (redrawRect.height() < mapViewArea.size.height * scaledTileSize) return false; + if (redrawRect.width() < mapViewArea.size.width * tileSize) return false; + if (redrawRect.height() < mapViewArea.size.height * tileSize) return false; + return true; + } + + private boolean shouldRedrawEverything() { + if (scrolling) return true; + if (model.uiSelections.isInCombat) return true; // Discard the "optimized drawing" setting while in combat. + if (preferences.optimizedDrawing) return false; + return true; + } + private final Rect redrawRect = new Rect(); + private void redrawAreaWithEffect(final VisualEffectAnimation effect, int tileID, int textYOffset) { + CoordRect area = effect.area; +// if (shouldRedrawEverythingForVisualEffect()) area = mapViewArea; + redrawArea_(area, effect, tileID, textYOffset); + } + private void clearCanvas() { + if (!hasSurface) return; + Canvas c = null; + try { + c = holder.lockCanvas(); + synchronized (holder) { + c.drawColor(Color.BLACK); + } + } finally { + if (c != null) holder.unlockCanvasAndPost(c); + } + } + + private CoordRect adaptAreaToScrolling(final CoordRect area) { + + if (!scrolling || scrollVector == null) return area; + + int x, y, w, h; + if (scrollVector.x > 0) { + x = area.topLeft.x - scrollVector.x; + w = area.size.width + scrollVector.x; + } else { + x = area.topLeft.x; + w = area.size.width - scrollVector.x; + } + if (scrollVector.y > 0) { + y = area.topLeft.y - scrollVector.y; + h = area.size.height + scrollVector.y; + } else { + y = area.topLeft.y; + h = area.size.height - scrollVector.y; + } + CoordRect result = new CoordRect(new Coord(x, y), new Size(w, h)); + return result; + } + + private void calculateRedrawRect(final CoordRect area) { + worldCoordsToScreenCords(area, redrawRect); + } + + private void worldCoordsToScreenCords(final CoordRect worldArea, Rect destScreenRect) { +// destScreenRect.left = screenOffset.x + (worldArea.topLeft.x - mapViewArea.topLeft.x) * scaledTileSize; +// destScreenRect.top = screenOffset.y + (worldArea.topLeft.y - mapViewArea.topLeft.y) * scaledTileSize; +// destScreenRect.right = destScreenRect.left + worldArea.size.width * scaledTileSize; +// destScreenRect.bottom = destScreenRect.top + worldArea.size.height * scaledTileSize; + + destScreenRect.left = screenOffset.x + (worldArea.topLeft.x - mapViewArea.topLeft.x) * tileSize; + destScreenRect.top = screenOffset.y + (worldArea.topLeft.y - mapViewArea.topLeft.y) * tileSize; + destScreenRect.right = destScreenRect.left + worldArea.size.width * tileSize; + destScreenRect.bottom = destScreenRect.top + worldArea.size.height * tileSize; + } + +// private void worldCoordsToBitmapCoords(final CoordRect worldArea, Rect dstBitmapArea) { +// dstBitmapArea.left = worldArea.topLeft.x * tileSize; +// dstBitmapArea.top = worldArea.topLeft.y * tileSize; +// dstBitmapArea.right = dstBitmapArea.left + worldArea.size.width * tileSize; +// dstBitmapArea.bottom = dstBitmapArea.top + worldArea.size.height * tileSize; +// +// } + + private void doDrawRect(Canvas canvas, CoordRect area) { + doDrawRect_Ground(canvas, area); + doDrawRect_Objects(canvas, area); + doDrawRect_Above(canvas, area); + if (!preferences.highQualityFilters) { + applyAlternateFilter(canvas, area); + } + } + + private void doDrawRect_Ground(Canvas canvas, CoordRect area) { + drawMapLayer(canvas, area, currentTileMap.currentLayout.layerGround); + tryDrawMapLayer(canvas, area, currentTileMap.currentLayout.layerObjects); + } + + private void doDrawRect_Objects(Canvas canvas, CoordRect area) { +// if (!tryDrawMapBitmap(canvas, area, objectsBitmap)) { +// tryDrawMapLayer(canvas, area, currentTileMap.currentLayout.layerObjects); +// } + + for (BloodSplatter splatter : currentMap.splatters) { + drawFromMapPosition(canvas, area, splatter.position, splatter.iconID); + } + + for (Loot l : currentMap.groundBags) { + if (l.isVisible) { + drawFromMapPosition(canvas, area, l.position, TileManager.iconID_groundbag); + } + } + + if (!model.player.hasVFXRunning) { + drawFromMapPosition(canvas, area, playerPosition, model.player.iconID); + } else if (area.contains(playerPosition)) { + int vfxElapsedTime = (int) (System.currentTimeMillis() - model.player.vfxStartTime); + if (vfxElapsedTime > model.player.vfxDuration) vfxElapsedTime = model.player.vfxDuration; + int x = ((model.player.position.x - mapViewArea.topLeft.x) * tileSize * vfxElapsedTime + ((model.player.lastPosition.x - mapViewArea.topLeft.x) * tileSize * (model.player.vfxDuration - vfxElapsedTime))) / model.player.vfxDuration; + int y = ((model.player.position.y - mapViewArea.topLeft.y) * tileSize * vfxElapsedTime + ((model.player.lastPosition.y - mapViewArea.topLeft.y) * tileSize * (model.player.vfxDuration - vfxElapsedTime))) / model.player.vfxDuration; + tiles.drawTile(canvas, model.player.iconID, x, y, mPaint); + } + for (MonsterSpawnArea a : currentMap.spawnAreas) { + for (Monster m : a.monsters) { + if (!m.hasVFXRunning) { + drawFromMapPosition(canvas, area, m.rectPosition, m.iconID); + } else if (area.intersects(m.rectPosition) || area.intersects(new CoordRect(m.lastPosition,m.rectPosition.size))) { + int vfxElapsedTime = (int) (System.currentTimeMillis() - m.vfxStartTime); + if (vfxElapsedTime > m.vfxDuration) vfxElapsedTime = m.vfxDuration; + int x = ((m.position.x - mapViewArea.topLeft.x) * tileSize * vfxElapsedTime + ((m.lastPosition.x - mapViewArea.topLeft.x) * tileSize * (m.vfxDuration - vfxElapsedTime))) / m.vfxDuration; + int y = ((m.position.y - mapViewArea.topLeft.y) * tileSize * vfxElapsedTime + ((m.lastPosition.y - mapViewArea.topLeft.y) * tileSize * (m.vfxDuration - vfxElapsedTime))) / m.vfxDuration; + tiles.drawTile(canvas, m.iconID, x, y, mPaint); + } + } + } + } + + private void doDrawRect_Above(Canvas canvas, CoordRect area) { + tryDrawMapLayer(canvas, area, currentTileMap.currentLayout.layerAbove); + tryDrawMapLayer(canvas, area, currentTileMap.currentLayout.layerTop); + + if (model.uiSelections.selectedPosition != null) { + if (model.uiSelections.selectedMonster != null) { + drawFromMapPosition(canvas, area, model.uiSelections.selectedPosition, TileManager.iconID_attackselect); + } else { + drawFromMapPosition(canvas, area, model.uiSelections.selectedPosition, TileManager.iconID_moveselect); + } + } + } + + private void tryDrawMapLayer(Canvas canvas, final CoordRect area, final MapLayer layer) { + if (layer != null) drawMapLayer(canvas, area, layer); + } + + private void drawMapLayer(Canvas canvas, final CoordRect area, final MapLayer layer) { + int my = area.topLeft.y; + int py = (area.topLeft.y - mapViewArea.topLeft.y) * tileSize; + int px0 = (area.topLeft.x - mapViewArea.topLeft.x) * tileSize; + for (int y = 0; y < area.size.height; ++y, ++my, py += tileSize) { + int mx = area.topLeft.x; + if (my < 0) continue; + if (my >= currentMap.size.height) break; + int px = px0; + for (int x = 0; x < area.size.width; ++x, ++mx, px += tileSize) { + if (mx < 0) continue; + if (mx >= currentMap.size.width) break; + final int tile = layer.tiles[mx][my]; + if (tile == 0) continue; + tiles.drawTile(canvas, tile, px, py, mPaint); + } + } + } + + + private void applyAlternateFilter(Canvas canvas, CoordRect area) { + canvas.drawRect(canvas.getClipBounds(), alternateColorFilterPaint); + + } + + private void drawFromMapPosition(Canvas canvas, final CoordRect area, final Coord p, final int tile) { + if (!area.contains(p)) return; + _drawFromMapPosition(canvas, area, p.x, p.y, tile); + } + private void drawFromMapPosition(Canvas canvas, final CoordRect area, final CoordRect p, final int tile) { + if (!area.intersects(p)) return; + _drawFromMapPosition(canvas, area, p.topLeft.x, p.topLeft.y, tile); + } + private void _drawFromMapPosition(Canvas canvas, final CoordRect area, int x, int y, final int tile) { + x -= mapViewArea.topLeft.x; + y -= mapViewArea.topLeft.y; +// if ( (x >= 0 && x < mapViewArea.size.width) +// && (y >= 0 && y < mapViewArea.size.height)) { + tiles.drawTile(canvas, tile, x * tileSize, y * tileSize, mPaint); +// } + } + + private void drawEffectText(Canvas canvas, final CoordRect area, final VisualEffectAnimation e, int textYOffset, Paint textPaint) { + int x = (e.position.x - mapViewArea.topLeft.x) * tileSize + tileSize/2; + int y = (e.position.y - mapViewArea.topLeft.y) * tileSize + tileSize/2 + textYOffset; + canvas.drawText(e.displayText, x, y, textPaint); + } + + @Override + public void onPlayerEnteredNewMap(PredefinedMap map, Coord p) { + movingSpritesRedrawTick.start(); + synchronized (holder) { + currentMap = map; + currentTileMap = model.currentTileMap; + tiles = world.tileManager.currentMapTiles; + movingSprites = 0; + Size visibleNumberOfTiles = new Size( + Math.min(screenSizeTileCount.width, currentMap.size.width) + ,Math.min(screenSizeTileCount.height, currentMap.size.height) + ); + mapViewArea = new CoordRect(mapTopLeft, visibleNumberOfTiles); + updateClip(); + +// screenOffset.set( +// (surfaceSize.width - scaledTileSize * visibleNumberOfTiles.width) / 2 +// ,(surfaceSize.height - scaledTileSize * visibleNumberOfTiles.height) / 2 +// ); + + + screenOffset.set( + (surfaceSize.width - tileSize * visibleNumberOfTiles.width) / 2 + ,(surfaceSize.height - tileSize * visibleNumberOfTiles.height) / 2 + ); + + currentTileMap.setColorFilter(this.mPaint, this.alternateColorFilterPaint, preferences.highQualityFilters); + } + +// touchedTile = null; + + clearCanvas(); + + recalculateMapTopLeft(model.player.position, false); + redrawAll(RedrawAllDebugReason.MapChanged); + } + + private void recalculateMapTopLeft(Coord playerPosition, boolean allowScrolling) { + synchronized (holder) { + int oldX = mapTopLeft.x; + int oldY = mapTopLeft.y; + this.playerPosition.set(playerPosition); + mapTopLeft.set(0, 0); + + if (currentMap.size.width > screenSizeTileCount.width) { + mapTopLeft.x = Math.max(0, playerPosition.x - mapViewArea.size.width/2); + mapTopLeft.x = Math.min(mapTopLeft.x, currentMap.size.width - mapViewArea.size.width); + } + if (currentMap.size.height > screenSizeTileCount.height) { + mapTopLeft.y = Math.max(0, playerPosition.y - mapViewArea.size.height/2); + mapTopLeft.y = Math.min(mapTopLeft.y, currentMap.size.height - mapViewArea.size.height); + } + updateClip(); + if (allowScrolling) { + if (mapTopLeft.x != oldX || mapTopLeft.y != oldY) { + scrollVector = new Coord(mapTopLeft.x - oldX, mapTopLeft.y - oldY); + new ScrollAnimationHandler(this).start(); + } + } else { + scrolling = false; + } + } + } + + private void updateClip() { + worldCoordsToScreenCords(mapViewArea, redrawClip); + } + + + public static final class ScrollAnimationHandler extends Handler implements Runnable { + + private static final int FRAME_DURATION = 40; + + private final WeakReference view; + + public ScrollAnimationHandler(MainView view) { + this.view = new WeakReference(view); + } + + @Override + public void run() { + MainView v = view.get(); + if (v == null) return; + if (System.currentTimeMillis() - v.scrollStartTime >= SCROLL_DURATION) { + onCompleted(); + } else { + postDelayed(this, FRAME_DURATION); + } + update(); + } + + private void update() { + MainView v = view.get(); + if (v == null) return; + v.redrawAll(RedrawAllDebugReason.MapScrolling); + } + + private void onCompleted() { + MainView v = view.get(); + if (v == null) return; + v.scrolling = false; + v.scrollVector = null; + } + + public void start() { + MainView v = view.get(); + if (v == null) return; + v.scrolling = true; + v.scrollStartTime = System.currentTimeMillis(); + postDelayed(this, 0); + } + } + + + public static final class SpriteMoveAnimationHandler extends Handler implements Runnable { + + private static final int FRAME_DURATION = 40; + private final WeakReference view; + private boolean stop = true; + + public SpriteMoveAnimationHandler(MainView view) { + this.view = new WeakReference(view); + } + + @Override + public void run() { + if (!stop) postDelayed(this, FRAME_DURATION); + update(); + } + + private void update() { +// L.log("stop="+stop+" - scroll="+scrolling+" - moving="+movingSprites); + if (stop) return; + MainView v = view.get(); + if (v == null) return; + if (!v.scrolling) { + if (v.movingSprites > 0) { + //TODO : limit redraw area when shouldRedrawEverything() returns false. + //Implies keeping track of the animation bounding box in a thread-safe way... :'( + v.redrawAll(RedrawAllDebugReason.SpriteMoved); + } + } + synchronized (this) { + if (v.movingSprites <= 0) stop(); + } + } + + public void start() { + if (stop) { + stop = false; + MainView v = view.get(); + if (v == null) return; + if (v.controllers.preferences.enableUiAnimations) postDelayed(this, 0); + } + } + + public void stop() { + stop = true; + } + } + + + + @Override + public void onPlayerMoved(Coord newPosition, Coord previousPosition) { + recalculateMapTopLeft(newPosition, preferences.enableUiAnimations); + redrawAll(RedrawAllDebugReason.PlayerMoved); + } + + public void subscribe() { + controllers.gameRoundController.gameRoundListeners.add(this); + controllers.effectController.visualEffectFrameListeners.add(this); + controllers.mapController.mapLayoutListeners.add(this); + controllers.movementController.playerMovementListeners.add(this); + controllers.combatController.combatSelectionListeners.add(this); + controllers.monsterSpawnController.monsterSpawnListeners.add(this); + controllers.monsterMovementController.monsterMovementListeners.add(this); + } + public void unsubscribe() { + controllers.monsterMovementController.monsterMovementListeners.remove(this); + controllers.monsterSpawnController.monsterSpawnListeners.remove(this); + controllers.combatController.combatSelectionListeners.remove(this); + controllers.movementController.playerMovementListeners.remove(this); + controllers.mapController.mapLayoutListeners.remove(this); + controllers.effectController.visualEffectFrameListeners.remove(this); + controllers.gameRoundController.gameRoundListeners.remove(this); + } + + @Override + public void onMonsterSelected(Monster m, Coord selectedPosition, Coord previousSelection) { + if (previousSelection != null) redrawTile(previousSelection, RedrawTileDebugReason.SelectionRemoved); + redrawTile(selectedPosition, RedrawTileDebugReason.SelectionAdded); + } + + @Override + public void onMovementDestinationSelected(Coord selectedPosition, Coord previousSelection) { + if (previousSelection != null) redrawTile(previousSelection, RedrawTileDebugReason.SelectionRemoved); + redrawTile(selectedPosition, RedrawTileDebugReason.SelectionAdded); + } + + @Override + public void onCombatSelectionCleared(Coord previousSelection) { + redrawTile(previousSelection, RedrawTileDebugReason.SelectionRemoved); + } + + @Override + public void onMonsterSpawned(PredefinedMap map, Monster m) { + if (map != currentMap) return; + if (!mapViewArea.intersects(m.rectPosition)) return; + redrawNextTick = true; + } + + @Override + public void onMonsterRemoved(PredefinedMap map, Monster m, CoordRect previousPosition) { + if (map != currentMap) return; + redrawArea(previousPosition, RedrawAreaDebugReason.MonsterKilled); + } + + @Override + public void onMonsterSteppedOnPlayer(Monster m) { + } + + @Override + public void onMonsterMoved(PredefinedMap map, Monster m, CoordRect previousPosition) { + if (map != currentMap) return; + if (!mapViewArea.intersects(m.rectPosition) && !mapViewArea.intersects(previousPosition)) return; + if (model.uiSelections.isInCombat) { + redrawArea(previousPosition, RedrawAreaDebugReason.MonsterMoved); + redrawArea(m.rectPosition, RedrawAreaDebugReason.MonsterMoved); + } else { + redrawNextTick = true; + } + } + + @Override + public void onSplatterAdded(PredefinedMap map, Coord p) { + if (map != currentMap) return; + if (!mapViewArea.contains(p)) return; + redrawNextTick = true; + } + + @Override + public void onSplatterChanged(PredefinedMap map, Coord p) { + if (map != currentMap) return; + if (!mapViewArea.contains(p)) return; + redrawNextTick = true; + } + + @Override + public void onSplatterRemoved(PredefinedMap map, Coord p) { + if (map != currentMap) return; + if (!mapViewArea.contains(p)) return; + redrawNextTick = true; + } + + @Override + public void onLootBagCreated(PredefinedMap map, Coord p) { + if (map != currentMap) return; + redrawTile(p, RedrawTileDebugReason.Bag); + } + + @Override + public void onLootBagRemoved(PredefinedMap map, Coord p) { + if (map != currentMap) return; + redrawTile(p, RedrawTileDebugReason.Bag); + } + + @Override + public void onMapTilesChanged(PredefinedMap map, LayeredTileMap tileMap) { + if (map != currentMap) return; + currentTileMap.setColorFilter(this.mPaint, this.alternateColorFilterPaint, preferences.highQualityFilters); + redrawAll(RedrawAllDebugReason.MapChanged); + } + + @Override + public void onNewAnimationFrame(VisualEffectAnimation animation, int tileID, int textYOffset) { + redrawAreaWithEffect(animation, tileID, textYOffset); + } + + @Override + public void onAnimationCompleted(VisualEffectAnimation animation) { + redrawArea(animation.area, RedrawAreaDebugReason.EffectCompleted); + } + + @Override + public void onSpriteMoveStarted(SpriteMoveAnimation animation) { + synchronized (movingSpritesRedrawTick) { + movingSprites++; + movingSpritesRedrawTick.start(); + } + } + + @Override + public void onNewSpriteMoveFrame(SpriteMoveAnimation animation) { + //redrawMoveArea_(CoordRect.getBoundingRect(animation.origin, animation.destination, animation.actor.tileSize), animation); + } + + @Override + public void onSpriteMoveCompleted(SpriteMoveAnimation animation) { + movingSprites--; + redrawArea(CoordRect.getBoundingRect(animation.origin, animation.destination, animation.actor.tileSize), RedrawAreaDebugReason.EffectCompleted); + } + + @Override + public void onAsyncAreaUpdate(CoordRect area) { + redrawArea(area, RedrawAreaDebugReason.AsyncRequest); + } + + @Override + public void onNewTick() { + if (!redrawNextTick) return; + + redrawAll(RedrawAllDebugReason.PlayerMoved); + + redrawNextTick = false; + } + + @Override + public void onNewRound() { } + + @Override + public void onNewFullRound() { } +}