diff --git a/AndorsTrail/res/raw/droplists_graveyard1.json b/AndorsTrail/res/raw/droplists_graveyard1.json index 02a64a6cb..11008e65a 100644 --- a/AndorsTrail/res/raw/droplists_graveyard1.json +++ b/AndorsTrail/res/raw/droplists_graveyard1.json @@ -26,14 +26,30 @@ { "itemID":"bone", "chance":"30", + "quantity":{ + "min":1, + "max":2 + } + }, + { + "itemID":"gold", + "chance":"70", + "quantity":{ + "min":1, + "max":6 + } + }, + { + "itemID":"club1", + "chance":"10", "quantity":{ "min":1, "max":1 } }, { - "itemID":"marrowtaint", - "chance":"1/1000", + "itemID":"shield_iron1", + "chance":"5", "quantity":{ "min":1, "max":1 diff --git a/AndorsTrail/res/raw/itemlist_guynmart.json b/AndorsTrail/res/raw/itemlist_guynmart.json index fb1b7a1ea..fa097f78f 100644 --- a/AndorsTrail/res/raw/itemlist_guynmart.json +++ b/AndorsTrail/res/raw/itemlist_guynmart.json @@ -118,11 +118,15 @@ "increaseAttackCost":0, "increaseAttackChance":-6, "increaseBlockChance":10, - "increaseDamageResistance":1, - "addedConditions":[ + "increaseDamageResistance":1 + }, + "hitReceivedEffect":{ + "conditionsSource":[ { "condition":"barkskin", - "magnitude":3 + "magnitude":2, + "duration":2, + "chance":"20" } ] } diff --git a/AndorsTrail/res/xml/graveyard1.tmx b/AndorsTrail/res/xml/graveyard1.tmx index 34197808a..be0628e81 100644 --- a/AndorsTrail/res/xml/graveyard1.tmx +++ b/AndorsTrail/res/xml/graveyard1.tmx @@ -1,188 +1,187 @@ - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -192,7 +191,7 @@ - eJytVU1rGzEQFb0JfLAWEnCPPfTcOP0DLvkDCfkzbWnvJST3fP6C5vPWYxOo7XOgDmu70B6CIR89tddWD81Ds7J2QyAPxGhXo9n3ZkbaSceYH37cN1hguGjMP9fseye+tw/43IoduLwvffhtYNIQ76sf5+KLmOSg49An5an50I5dGB9axvxph5h/vf3YimvpHvCbdkKO3nq/dw3jsh2+D1vn874VuSPuJ//8S3i/fB7szEW9BN71rDFdP5b9uPB7lrz96e1GK+bxTmICB4UxpYtz4MoF7cBvb1dsXBu6MMe7jQzPvgv9Am7QiHyBR084AeC0ZoMfgfmqxDxVuSVP4JuP9V1pXrfVuhPQc63eISZ7YJpoR26QW+oj+IzYx0X8Bt/n8jlQ2qkJeG2rz3XzXD41Zi5yWpU6d23UgPyyv5ZkrY7nfkYTsVeE+Gu2uvbC1fM88X6H4jt2VX3siTLJS6o95dlNeoZ631iTBWo+cqFPkIe6fELTjatqWpfcIS/sWd0Dew11r9NEHBbVNZ6Nz8LjobproJfSfidH2oFrPu8E5j2pM5+hHbXcT/yovY4nexBYkVyOpO68kzTwnjUqXfW8d+38AEfkqq/q9Ur6VPcIeabnHd+ijhupp87Zsao170TOdT6nmfNOoJ5Xsq90jzvvZ7qXhNdM1fmyHfnqs3/RmdfOup8rngeJPgD/qi0/NuWfw1qX6myMG/qTvazzwLXOs7APa0fF/J2Y68+R0ttXc6wtqLuH3wTSez7libzl7u9tv7YjY1d6kD1TSo5wDlKe3PdlMe4fqnnT4J7dhOfkCS14/gfUNXdi + eJytVU1rFEEQbbw17GF7wMB69OBZR//ASv5AQv6Mit5D0Lvm4xcYo948moC7e15ww+yuoAdZSKInvWo/uh5d09s9QbCg6a/qmvdeVfcsBsZ88e1HRw+bbBjzx3X7Xonv5TU+l9KPXd6XPvw2bNER76Nvp+KLmMSg49AnxanxsJ/7GE97xvzqx5i//fhZL+zN3foZ4FsOgkaPvN/jQkut5PekF7Ej7q6ffxPcd26FfuUiXxrWhtaY2rf7vp35M/d8/9X3e72o45XE/Ol5HVXGNBIHY9i5i/zhs2nj3sSFMdb2MjhHLtQLsE37QS/gGAomGDBt2+BHw3hLYr5T2u4q3T75WJ8V5x3bzjsNfL6rNcRkDSxVTPCANtCW/GicI/ZJFb/B9ZyeY8WdnGAPbHteGuf01LZyEdOW5Lm2kQP0nfajxrUt4zzMcKIdVCH+tm3v3XZlnG+937H4zl2bH2uiSXRJuac466RmyPehNVlDzmcu1Al0KOkJTheuzWlHtIMurFldAwcdeS9xoh1X7T3ejdeC47q8a0MtpfVOjOzHroxT5xPjoeSZc3BHLg8TP3Iv4WQNwjZFy5nknW+SNqwzR41r3/farjdghFYjla+7Uqe6Rogzve/TfuRxIfnUmp2oXPNN5FjruczcdxryeS7nGvdv9/29riXBtVJ5Bn7i1Xf/bLDOnXk/VTiPEn4w/Kte+PZc/jnMdaPuxryjPlnLWgfuDW6Ec9h7U62/ibn6nCm+IzXG3k319vCbsPSdT3FCt9z7/dLvvZK2LzXImmlEI9yDFCfPfdiI5ydq3NV4Zj/BufiPPXD+BVf1dSk= @@ -202,33 +201,33 @@ - eJzFlssNwjAMhi0kDkgcCheUOZiAigVgPC4wAtBJ4ByQQGrnoBaN5KZO4iRFfJeUYP/xI64KAPBQAFr1V4r5rVV/z+fj4tLaXS1brdw2Ul2kLr5rUwz/M3G6zgyxmoRtnp3mrV3v3XMVcU7F+BteAZ2ayZlSzgDewljsmE3ukhqMAcaK2PcE4Wpjc1wO97adpt33nJyMZiy+HFI1sWYbh++e2T8xNaKM1esqcC99uPI5M7FL54yrb26uqT3znRujKa0x1ZTMUaymhNJhT9/5O4EmN+OhM1LBPqX2GKH9ofc0R5POwJrocLP+D6aLdF9fbyn2t4R0DsZ6tx3m/LPU55d8ADDDO+s= + eJzFlksOwiAQhicmLkxcVDeGc3gCGy+gx3OjR1A5ia7RRJP2HJYE4pQOMECN36aIMz/zYJoCADwEgBL9J8b+VqK/F/Lxcensro6tEn4brm5TfddtNfzfxuk7M8ZqErd5Gs1b97ybtUw4RxL+lldEpyFyxtQzgDczFjdmmzunBmOgY9W490RD1cbluBzubY2m2/eSnKxmKqEccjV1zTYe3z2xfyJqhBmr1zJyL0P48jkTsXPnjKpvaa65PQudm6LJrTHW5MxRqiaH2mOP3/k7hiY147EzctF9yu2xBvcH39MSTTwDa6RDzfo/mC7yfUO9xbjfEtw5GOvddpjTa67PL/kANJc76w== - + - eJzVk1EOwCAIQz3F7n8Fb7gvE7MAfUV/RkJmXK0t4HzGmCJXKNzCEhzFundTvIP5fm9kl2vXksUJX7R2+6G07PtUe+WVBO1Bxq3qQnV2eudg6XxV/qKzxHvFR8L10+XLvFNPTo+UvkorOaf4iXdSY+K9et/VG3MyqgOdeTWj2T+qxw2Xs9JEdGb9VHunOh3/CtvhpLMc3fPXfAGBeSqQ + eJzVk8ENwCAMA5mi+6/Ahn0h9UHsM1EfICFVYMw5pPMZY5q5htMtLdFRbXo31aeeNFPC2PFVo+O3+07r4Vi+65RdZSWD9kDl7eryNyfV0v5W+XZnSXbld5qdvlXiV2WnmdL/UfEpVnLO+ZPspMYkO91zjK4PKibqV9Wx2qM86Ug9FRPhrN7TrXU5k/xOe+JJe3l3z63zBeTIKpA= - eJzNUlsKwDAI6yl2/yv0hvsqDIkmcTImyB6kqUnc11qb9CmGO1gFp2Ldu1W8g4nPie5yPWfJ6g0fenfzYH51OTOtSqkZZNzMF+Znx2tFe3e/qlkqXxh3PB85Xe1qVo4/7v0KrtKH5kL4L3ceYdAZNLOza4zT7cxHZYcyTehbnRPxTHAqXGoGbOfZvwlOpMPVrvqt5l55yLj+3jfjIT3/ + eJzNklEKwCAMQz3F7n8Fb7gvfyRtXlDGhMIc8dnEzmeMaWotp1taoqPa9G6qT5nUU9LjCbdbJzz1neZRna2yp8zKK1l0Biq2y0V537UnTOqJznfXS5eLY+/nd2bqnb5Vkk96P9F1/lRfSv/lzCuNOqN6TmbNMdOqciQzVHlSe9qn4txgEhZ9Azfz7t8NpvKReqd503fvMnSsv9cLhdk9/w== - + - + - + @@ -239,121 +238,121 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -362,7 +361,7 @@ - + @@ -372,169 +371,169 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/AndorsTrailApplication.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/AndorsTrailApplication.java index d6203d460..2cf7f9ce0 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/AndorsTrailApplication.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/AndorsTrailApplication.java @@ -3,12 +3,13 @@ package com.gpl.rpg.AndorsTrail; import java.io.File; import java.io.IOException; import java.util.Locale; - +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; @@ -16,6 +17,7 @@ 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 { @@ -32,9 +34,9 @@ public final class AndorsTrailApplication extends Application { public static final boolean IS_RELEASE_VERSION = !CURRENT_VERSION_DISPLAY.matches(".*[a-d].*"); private final AndorsTrailPreferences preferences = new AndorsTrailPreferences(); - private final WorldContext world = new WorldContext(); - private final ControllerContext controllers = new ControllerContext(this, world); - private final WorldSetup setup = new WorldSetup(world, controllers, this); + private WorldContext world = new WorldContext(); + private ControllerContext controllers = new ControllerContext(this, world); + private WorldSetup setup = new WorldSetup(world, controllers, this); public WorldContext getWorld() { return world; } public WorldSetup getWorldSetup() { return setup; } public AndorsTrailPreferences getPreferences() { return preferences; } @@ -62,15 +64,23 @@ public final class AndorsTrailApplication extends Application { setLocale(activity); } + //Get default locale at startup, as somehow it seems that changing the app's + //configured locale impacts the value returned by Locale.getDefault() nowadays. + private final Locale defaultLocale = Locale.getDefault(); + + @SuppressLint("NewApi") public boolean setLocale(Activity context) { Resources res = context.getResources(); Configuration conf = res.getConfiguration(); - final Locale targetLocale = preferences.useLocalizedResources ? Locale.getDefault() : Locale.US; - if (targetLocale.equals(conf.locale)) return false; + final Locale targetLocale = preferences.useLocalizedResources ? defaultLocale : Locale.US; + if (targetLocale.equals(conf.locale)) { + return false; + } conf.locale = targetLocale; res.updateConfiguration(conf, res.getDisplayMetrics()); this.getResources().updateConfiguration(conf, res.getDisplayMetrics()); + return true; } @@ -127,4 +137,9 @@ public final class AndorsTrailApplication extends Application { } return false; } + public void discardWorld() { + world = new WorldContext(); + controllers = new ControllerContext(this, world); + setup = new WorldSetup(world, controllers, getApplicationContext()); + } } diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/activity/fragment/StartScreenActivity_MainMenu.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/activity/fragment/StartScreenActivity_MainMenu.java index 56bfeeadf..954301187 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/activity/fragment/StartScreenActivity_MainMenu.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/activity/fragment/StartScreenActivity_MainMenu.java @@ -1,10 +1,12 @@ package com.gpl.rpg.AndorsTrail.activity.fragment; +import android.annotation.SuppressLint; import android.app.Activity; import android.app.Dialog; import android.content.Intent; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; +import android.os.Build; import android.os.Bundle; import android.support.v4.app.Fragment; import android.view.LayoutInflater; @@ -28,6 +30,7 @@ import com.gpl.rpg.AndorsTrail.controller.Constants; import com.gpl.rpg.AndorsTrail.resource.tiles.TileManager; import com.gpl.rpg.AndorsTrail.savegames.Savegames; import com.gpl.rpg.AndorsTrail.savegames.Savegames.FileHeader; +import com.gpl.rpg.AndorsTrail.util.L; import com.gpl.rpg.AndorsTrail.util.ThemeHelper; import com.gpl.rpg.AndorsTrail.view.CustomDialogFactory; @@ -259,18 +262,25 @@ public class StartScreenActivity_MainMenu extends Fragment { // Changing the locale after having loaded the game requires resources to // be re-loaded. Therefore, we just exit here. Toast.makeText(getActivity(), R.string.change_locale_requires_restart, Toast.LENGTH_LONG).show(); - getActivity().finish(); + doFinish(); return; } } if (ThemeHelper.changeTheme(preferences.selectedTheme)) { // Changing the theme requires a restart to re-create all activities. Toast.makeText(getActivity(), R.string.change_theme_requires_restart, Toast.LENGTH_LONG).show(); - getActivity().finish(); + doFinish(); return; } app.getWorld().tileManager.updatePreferences(preferences); } + + @SuppressLint("NewApi") + private void doFinish() { + //For Lollipop and above + ((AndorsTrailApplication)getActivity().getApplication()).discardWorld(); + getActivity().finish(); + } public interface OnNewGameRequestedListener { diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/WorldMapController.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/WorldMapController.java index 0712c12b9..32626c5c7 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/WorldMapController.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/WorldMapController.java @@ -1,317 +1,318 @@ -package com.gpl.rpg.AndorsTrail.controller; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.PrintWriter; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -import android.content.Context; -import android.content.Intent; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.Bitmap.Config; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.os.AsyncTask; -import android.os.Environment; -import android.widget.Toast; - -import com.gpl.rpg.AndorsTrail.AndorsTrailApplication; -import com.gpl.rpg.AndorsTrail.R; -import com.gpl.rpg.AndorsTrail.activity.DisplayWorldMapActivity; -import com.gpl.rpg.AndorsTrail.context.WorldContext; -import com.gpl.rpg.AndorsTrail.model.map.LayeredTileMap; -import com.gpl.rpg.AndorsTrail.model.map.MapLayer; -import com.gpl.rpg.AndorsTrail.model.map.PredefinedMap; -import com.gpl.rpg.AndorsTrail.model.map.WorldMapSegment; -import com.gpl.rpg.AndorsTrail.model.map.WorldMapSegment.NamedWorldMapArea; -import com.gpl.rpg.AndorsTrail.model.map.WorldMapSegment.WorldMapSegmentMap; -import com.gpl.rpg.AndorsTrail.resource.tiles.TileCollection; -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 WorldMapController { - - private static final int WORLDMAP_SCREENSHOT_TILESIZE = 8; - public static final int WORLDMAP_DISPLAY_TILESIZE = WORLDMAP_SCREENSHOT_TILESIZE; - - public static void updateWorldMap(final WorldContext world, final Resources res) { - updateWorldMap(world, world.model.currentMap, world.model.currentTileMap, world.tileManager.currentMapTiles, res); - } - - private static void updateWorldMap( - final WorldContext world, - final PredefinedMap map, - final LayeredTileMap mapTiles, - final TileCollection cachedTiles, - final Resources res) { - final String worldMapSegmentName = world.maps.getWorldMapSegmentNameForMap(map.name); - if (worldMapSegmentName == null) return; - - if (!shouldUpdateWorldMap(map, worldMapSegmentName, world.maps.worldMapRequiresUpdate)) return; - - (new AsyncTask() { - @Override - protected Void doInBackground(Void... arg0) { - final MapRenderer renderer = new MapRenderer(world, map, mapTiles, cachedTiles); - try { - updateCachedBitmap(map, renderer); - updateWorldMapSegment(res, world, worldMapSegmentName); - world.maps.worldMapRequiresUpdate = false; - if (AndorsTrailApplication.DEVELOPMENT_DEBUGMESSAGES) { - L.log("WorldMapController: Updated worldmap segment " + worldMapSegmentName + " for map " + map.name); - } - } catch (IOException e) { - L.log("Error creating worldmap file for map " + map.name + " : " + e.toString()); - } - return null; - } - }).execute(); - } - - private static boolean shouldUpdateWorldMap(PredefinedMap map, String worldMapSegmentName, boolean forceUpdate) { - if (forceUpdate) return true; - if (!map.visited) return true; - File file = getFileForMap(map, false); - if (!file.exists()) return true; - - file = getCombinedWorldMapFile(worldMapSegmentName); - if (!file.exists()) return true; - - return false; - } - - private static void updateCachedBitmap(PredefinedMap map, MapRenderer renderer) throws IOException { - ensureWorldmapDirectoryExists(); - - File file = getFileForMap(map, false); - if (file.exists()) return; - - Bitmap image = renderer.drawMap(); - FileOutputStream fos = new FileOutputStream(file); - image.compress(Bitmap.CompressFormat.PNG, 70, fos); - fos.flush(); - fos.close(); - image.recycle(); - L.log("WorldMapController: Wrote " + file.getAbsolutePath()); - } - - private static final class MapRenderer { - private final PredefinedMap map; - private final LayeredTileMap mapTiles; - private final TileCollection cachedTiles; - private final int tileSize; - private final float scale; - private final Paint mPaint = new Paint(); - - public MapRenderer(final WorldContext world, final PredefinedMap map, final LayeredTileMap mapTiles, final TileCollection cachedTiles) { - this.map = map; - this.mapTiles = mapTiles; - this.cachedTiles = cachedTiles; - this.tileSize = world.tileManager.tileSize; - this.scale = (float) WORLDMAP_SCREENSHOT_TILESIZE / world.tileManager.tileSize; - mapTiles.setColorFilter(mPaint); - } - - public Bitmap drawMap() { - Bitmap image = Bitmap.createBitmap(map.size.width * WORLDMAP_SCREENSHOT_TILESIZE, map.size.height * WORLDMAP_SCREENSHOT_TILESIZE, Config.RGB_565); - image.setDensity(Bitmap.DENSITY_NONE); - Canvas canvas = new Canvas(image); - canvas.scale(scale, scale); - - synchronized (cachedTiles) { - drawMapLayer(canvas, mapTiles.currentLayout.layerGround); - tryDrawMapLayer(canvas, mapTiles.currentLayout.layerObjects); - tryDrawMapLayer(canvas, mapTiles.currentLayout.layerAbove); - } - return image; - } - - private void tryDrawMapLayer(Canvas canvas, final MapLayer layer) { - if (layer != null) drawMapLayer(canvas, layer); - } - - private void drawMapLayer(Canvas canvas, final MapLayer layer) { - int py = 0; - for (int y = 0; y < map.size.height; ++y, py += tileSize) { - int px = 0; - for (int x = 0; x < map.size.width; ++x, px += tileSize) { - final int tile = layer.tiles[x][y]; - if (tile == 0) continue; - cachedTiles.drawTile(canvas, tile, px, py, mPaint); - } - } - } - } - - private static void ensureWorldmapDirectoryExists() throws IOException { - File root = Environment.getExternalStorageDirectory(); - File dir = new File(root, Constants.FILENAME_SAVEGAME_DIRECTORY); - if (!dir.exists()) dir.mkdir(); - dir = new File(dir, Constants.FILENAME_WORLDMAP_DIRECTORY); - if (!dir.exists()) dir.mkdir(); - - File noMediaFile = new File(dir, ".nomedia"); - if (!noMediaFile.exists()) noMediaFile.createNewFile(); - } - public static boolean fileForMapExists(PredefinedMap map) { - if (map.lastSeenLayoutHash.length() > 0) { - return getPngFile(map.name + '.' + map.lastSeenLayoutHash).exists(); - } - return getPngFile(map.name).exists(); - } - private static File getFileForMap(PredefinedMap map, boolean verifyFileExists) { - if (map.lastSeenLayoutHash.length() > 0) { - File fileWithHash = getPngFile(map.name + '.' + map.lastSeenLayoutHash); - if (!verifyFileExists) return fileWithHash; - else if (fileWithHash.exists()) return fileWithHash; - } - return getPngFile(map.name); - } - private static File getPngFile(String fileName) { - return new File(getWorldmapDirectory(), fileName + ".png"); - } - private static File getWorldmapDirectory() { - File dir = Environment.getExternalStorageDirectory(); - dir = new File(dir, Constants.FILENAME_SAVEGAME_DIRECTORY); - return new File(dir, Constants.FILENAME_WORLDMAP_DIRECTORY); - } - public static File getCombinedWorldMapFile(String segmentName) { - return new File(getWorldmapDirectory(), Constants.FILENAME_WORLDMAP_HTMLFILE_PREFIX + segmentName + Constants.FILENAME_WORLDMAP_HTMLFILE_SUFFIX); - } - - private static String getWorldMapSegmentAsHtml(Resources res, WorldContext world, String segmentName) { - WorldMapSegment segment = world.maps.worldMapSegments.get(segmentName); - - Map displayedMapFilenamesPerMapName = new HashMap(segment.maps.size()); - Coord offsetWorldmapTo = new Coord(999999, 999999); - for (WorldMapSegmentMap map : segment.maps.values()) { - PredefinedMap predefinedMap = world.maps.findPredefinedMap(map.mapName); - if (predefinedMap == null) continue; - if (!predefinedMap.visited) continue; - File f = WorldMapController.getFileForMap(predefinedMap, true); - if (!f.exists()) continue; - displayedMapFilenamesPerMapName.put(map.mapName, f); - - offsetWorldmapTo.x = Math.min(offsetWorldmapTo.x, map.worldPosition.x); - offsetWorldmapTo.y = Math.min(offsetWorldmapTo.y, map.worldPosition.y); - } - - Coord bottomRight = new Coord(0, 0); - - StringBuilder mapsAsHtml = new StringBuilder(1000); - for (WorldMapSegmentMap segmentMap : segment.maps.values()) { - File f = displayedMapFilenamesPerMapName.get(segmentMap.mapName); - if (f == null) continue; - - Size size = getMapSize(segmentMap, world); - mapsAsHtml - .append(""); - if (AndorsTrailApplication.DEVELOPMENT_DEBUGMESSAGES) mapsAsHtml.append('\n'); - - bottomRight.x = Math.max(bottomRight.x, segmentMap.worldPosition.x + size.width); - bottomRight.y = Math.max(bottomRight.y, segmentMap.worldPosition.y + size.height); - } - Size worldmapSegmentSize = new Size( - (bottomRight.x - offsetWorldmapTo.x) * WorldMapController.WORLDMAP_DISPLAY_TILESIZE - ,(bottomRight.y - offsetWorldmapTo.y) * WorldMapController.WORLDMAP_DISPLAY_TILESIZE - ); - - StringBuilder namedAreasAsHtml = new StringBuilder(500); - for (NamedWorldMapArea area : segment.namedAreas.values()) { - CoordRect r = determineNamedAreaBoundary(area, segment, world, displayedMapFilenamesPerMapName.keySet()); - if (r == null) continue; - namedAreasAsHtml - .append("
") - .append(area.name) - .append("
"); - if (AndorsTrailApplication.DEVELOPMENT_DEBUGMESSAGES) namedAreasAsHtml.append('\n'); - } - - return res.getString(R.string.worldmap_template) - .replace("{{maps}}", mapsAsHtml.toString()) - .replace("{{areas}}", namedAreasAsHtml.toString()) - .replace("{{sizex}}", Integer.toString(worldmapSegmentSize.width)) - .replace("{{sizey}}", Integer.toString(worldmapSegmentSize.height)) - .replace("{{offsetx}}", Integer.toString(offsetWorldmapTo.x * WorldMapController.WORLDMAP_DISPLAY_TILESIZE)) - .replace("{{offsety}}", Integer.toString(offsetWorldmapTo.y * WorldMapController.WORLDMAP_DISPLAY_TILESIZE)); - } - - private static Size getMapSize(WorldMapSegmentMap map, WorldContext world) { - return world.maps.findPredefinedMap(map.mapName).size; - } - - private static CoordRect determineNamedAreaBoundary(NamedWorldMapArea area, WorldMapSegment segment, WorldContext world, Set displayedMapNames) { - Coord topLeft = null; - Coord bottomRight = null; - - for (String mapName : area.mapNames) { - if (!displayedMapNames.contains(mapName)) continue; - WorldMapSegmentMap map = segment.maps.get(mapName); - Size size = getMapSize(map, world); - if (topLeft == null) { - topLeft = new Coord(map.worldPosition); - } else { - topLeft.x = Math.min(topLeft.x, map.worldPosition.x); - topLeft.y = Math.min(topLeft.y, map.worldPosition.y); - } - if (bottomRight == null) { - bottomRight = new Coord(map.worldPosition.x + size.width, map.worldPosition.y + size.height); - } else { - bottomRight.x = Math.max(bottomRight.x, map.worldPosition.x + size.width); - bottomRight.y = Math.max(bottomRight.y, map.worldPosition.y + size.height); - } - } - if (topLeft == null) return null; - return new CoordRect(topLeft, new Size(bottomRight.x - topLeft.x, bottomRight.y - topLeft.y)); - } - - private static void updateWorldMapSegment(Resources res, WorldContext world, String segmentName) throws IOException { - String mapAsHtml = getWorldMapSegmentAsHtml(res, world, segmentName); - File outputFile = getCombinedWorldMapFile(segmentName); - PrintWriter pw = new PrintWriter(outputFile); - pw.write(mapAsHtml); - pw.close(); - } - - public static boolean displayWorldMap(Context context, WorldContext world) { - String worldMapSegmentName = world.maps.getWorldMapSegmentNameForMap(world.model.currentMap.name); - if (worldMapSegmentName == null) { - Toast.makeText(context, context.getResources().getString(R.string.display_worldmap_not_available), Toast.LENGTH_LONG).show(); - return false; - } - - Intent intent = new Intent(context, DisplayWorldMapActivity.class); - intent.putExtra("worldMapSegmentName", worldMapSegmentName); - context.startActivity(intent); - - return true; - } -} +package com.gpl.rpg.AndorsTrail.controller; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.os.AsyncTask; +import android.os.Environment; +import android.widget.Toast; + +import com.gpl.rpg.AndorsTrail.AndorsTrailApplication; +import com.gpl.rpg.AndorsTrail.R; +import com.gpl.rpg.AndorsTrail.activity.DisplayWorldMapActivity; +import com.gpl.rpg.AndorsTrail.context.WorldContext; +import com.gpl.rpg.AndorsTrail.model.map.LayeredTileMap; +import com.gpl.rpg.AndorsTrail.model.map.MapLayer; +import com.gpl.rpg.AndorsTrail.model.map.PredefinedMap; +import com.gpl.rpg.AndorsTrail.model.map.WorldMapSegment; +import com.gpl.rpg.AndorsTrail.model.map.WorldMapSegment.NamedWorldMapArea; +import com.gpl.rpg.AndorsTrail.model.map.WorldMapSegment.WorldMapSegmentMap; +import com.gpl.rpg.AndorsTrail.resource.tiles.TileCollection; +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 WorldMapController { + + private static final int WORLDMAP_SCREENSHOT_TILESIZE = 8; + public static final int WORLDMAP_DISPLAY_TILESIZE = WORLDMAP_SCREENSHOT_TILESIZE; + + public static void updateWorldMap(final WorldContext world, final Resources res) { + updateWorldMap(world, world.model.currentMap, world.model.currentTileMap, world.tileManager.currentMapTiles, res); + } + + private static void updateWorldMap( + final WorldContext world, + final PredefinedMap map, + final LayeredTileMap mapTiles, + final TileCollection cachedTiles, + final Resources res) { + final String worldMapSegmentName = world.maps.getWorldMapSegmentNameForMap(map.name); + if (worldMapSegmentName == null) return; + + if (!shouldUpdateWorldMap(map, worldMapSegmentName, world.maps.worldMapRequiresUpdate)) return; + + (new AsyncTask() { + @Override + protected Void doInBackground(Void... arg0) { + final MapRenderer renderer = new MapRenderer(world, map, mapTiles, cachedTiles); + try { + updateCachedBitmap(map, renderer); + updateWorldMapSegment(res, world, worldMapSegmentName); + world.maps.worldMapRequiresUpdate = false; + if (AndorsTrailApplication.DEVELOPMENT_DEBUGMESSAGES) { + L.log("WorldMapController: Updated worldmap segment " + worldMapSegmentName + " for map " + map.name); + } + } catch (IOException e) { + L.log("Error creating worldmap file for map " + map.name + " : " + e.toString()); + } + return null; + } + }).execute(); + } + + private static boolean shouldUpdateWorldMap(PredefinedMap map, String worldMapSegmentName, boolean forceUpdate) { + if (forceUpdate) return true; + if (!map.visited) return true; + File file = getFileForMap(map, false); + if (!file.exists()) return true; + + file = getCombinedWorldMapFile(worldMapSegmentName); + if (!file.exists()) return true; + + return false; + } + + private static void updateCachedBitmap(PredefinedMap map, MapRenderer renderer) throws IOException { + ensureWorldmapDirectoryExists(); + + File file = getFileForMap(map, false); + if (file.exists()) return; + + Bitmap image = renderer.drawMap(); + FileOutputStream fos = new FileOutputStream(file); + image.compress(Bitmap.CompressFormat.PNG, 70, fos); + fos.flush(); + fos.close(); + image.recycle(); + L.log("WorldMapController: Wrote " + file.getAbsolutePath()); + } + + private static final class MapRenderer { + private final PredefinedMap map; + private final LayeredTileMap mapTiles; + private final TileCollection cachedTiles; + private final int tileSize; + private final float scale; + private final Paint mPaint = new Paint(); + + public MapRenderer(final WorldContext world, final PredefinedMap map, final LayeredTileMap mapTiles, final TileCollection cachedTiles) { + this.map = map; + this.mapTiles = mapTiles; + this.cachedTiles = cachedTiles; + this.tileSize = world.tileManager.tileSize; + this.scale = (float) WORLDMAP_SCREENSHOT_TILESIZE / world.tileManager.tileSize; + mapTiles.setColorFilter(mPaint); + } + + public Bitmap drawMap() { + Bitmap image = Bitmap.createBitmap(map.size.width * WORLDMAP_SCREENSHOT_TILESIZE, map.size.height * WORLDMAP_SCREENSHOT_TILESIZE, Config.RGB_565); + image.setDensity(Bitmap.DENSITY_NONE); + Canvas canvas = new Canvas(image); + canvas.scale(scale, scale); + + synchronized (cachedTiles) { + drawMapLayer(canvas, mapTiles.currentLayout.layerGround); + tryDrawMapLayer(canvas, mapTiles.currentLayout.layerObjects); + tryDrawMapLayer(canvas, mapTiles.currentLayout.layerAbove); + tryDrawMapLayer(canvas, mapTiles.currentLayout.layerTop); + } + return image; + } + + private void tryDrawMapLayer(Canvas canvas, final MapLayer layer) { + if (layer != null) drawMapLayer(canvas, layer); + } + + private void drawMapLayer(Canvas canvas, final MapLayer layer) { + int py = 0; + for (int y = 0; y < map.size.height; ++y, py += tileSize) { + int px = 0; + for (int x = 0; x < map.size.width; ++x, px += tileSize) { + final int tile = layer.tiles[x][y]; + if (tile == 0) continue; + cachedTiles.drawTile(canvas, tile, px, py, mPaint); + } + } + } + } + + private static void ensureWorldmapDirectoryExists() throws IOException { + File root = Environment.getExternalStorageDirectory(); + File dir = new File(root, Constants.FILENAME_SAVEGAME_DIRECTORY); + if (!dir.exists()) dir.mkdir(); + dir = new File(dir, Constants.FILENAME_WORLDMAP_DIRECTORY); + if (!dir.exists()) dir.mkdir(); + + File noMediaFile = new File(dir, ".nomedia"); + if (!noMediaFile.exists()) noMediaFile.createNewFile(); + } + public static boolean fileForMapExists(PredefinedMap map) { + if (map.lastSeenLayoutHash.length() > 0) { + return getPngFile(map.name + '.' + map.lastSeenLayoutHash).exists(); + } + return getPngFile(map.name).exists(); + } + private static File getFileForMap(PredefinedMap map, boolean verifyFileExists) { + if (map.lastSeenLayoutHash.length() > 0) { + File fileWithHash = getPngFile(map.name + '.' + map.lastSeenLayoutHash); + if (!verifyFileExists) return fileWithHash; + else if (fileWithHash.exists()) return fileWithHash; + } + return getPngFile(map.name); + } + private static File getPngFile(String fileName) { + return new File(getWorldmapDirectory(), fileName + ".png"); + } + private static File getWorldmapDirectory() { + File dir = Environment.getExternalStorageDirectory(); + dir = new File(dir, Constants.FILENAME_SAVEGAME_DIRECTORY); + return new File(dir, Constants.FILENAME_WORLDMAP_DIRECTORY); + } + public static File getCombinedWorldMapFile(String segmentName) { + return new File(getWorldmapDirectory(), Constants.FILENAME_WORLDMAP_HTMLFILE_PREFIX + segmentName + Constants.FILENAME_WORLDMAP_HTMLFILE_SUFFIX); + } + + private static String getWorldMapSegmentAsHtml(Resources res, WorldContext world, String segmentName) { + WorldMapSegment segment = world.maps.worldMapSegments.get(segmentName); + + Map displayedMapFilenamesPerMapName = new HashMap(segment.maps.size()); + Coord offsetWorldmapTo = new Coord(999999, 999999); + for (WorldMapSegmentMap map : segment.maps.values()) { + PredefinedMap predefinedMap = world.maps.findPredefinedMap(map.mapName); + if (predefinedMap == null) continue; + if (!predefinedMap.visited) continue; + File f = WorldMapController.getFileForMap(predefinedMap, true); + if (!f.exists()) continue; + displayedMapFilenamesPerMapName.put(map.mapName, f); + + offsetWorldmapTo.x = Math.min(offsetWorldmapTo.x, map.worldPosition.x); + offsetWorldmapTo.y = Math.min(offsetWorldmapTo.y, map.worldPosition.y); + } + + Coord bottomRight = new Coord(0, 0); + + StringBuilder mapsAsHtml = new StringBuilder(1000); + for (WorldMapSegmentMap segmentMap : segment.maps.values()) { + File f = displayedMapFilenamesPerMapName.get(segmentMap.mapName); + if (f == null) continue; + + Size size = getMapSize(segmentMap, world); + mapsAsHtml + .append(""); + if (AndorsTrailApplication.DEVELOPMENT_DEBUGMESSAGES) mapsAsHtml.append('\n'); + + bottomRight.x = Math.max(bottomRight.x, segmentMap.worldPosition.x + size.width); + bottomRight.y = Math.max(bottomRight.y, segmentMap.worldPosition.y + size.height); + } + Size worldmapSegmentSize = new Size( + (bottomRight.x - offsetWorldmapTo.x) * WorldMapController.WORLDMAP_DISPLAY_TILESIZE + ,(bottomRight.y - offsetWorldmapTo.y) * WorldMapController.WORLDMAP_DISPLAY_TILESIZE + ); + + StringBuilder namedAreasAsHtml = new StringBuilder(500); + for (NamedWorldMapArea area : segment.namedAreas.values()) { + CoordRect r = determineNamedAreaBoundary(area, segment, world, displayedMapFilenamesPerMapName.keySet()); + if (r == null) continue; + namedAreasAsHtml + .append("
") + .append(area.name) + .append("
"); + if (AndorsTrailApplication.DEVELOPMENT_DEBUGMESSAGES) namedAreasAsHtml.append('\n'); + } + + return res.getString(R.string.worldmap_template) + .replace("{{maps}}", mapsAsHtml.toString()) + .replace("{{areas}}", namedAreasAsHtml.toString()) + .replace("{{sizex}}", Integer.toString(worldmapSegmentSize.width)) + .replace("{{sizey}}", Integer.toString(worldmapSegmentSize.height)) + .replace("{{offsetx}}", Integer.toString(offsetWorldmapTo.x * WorldMapController.WORLDMAP_DISPLAY_TILESIZE)) + .replace("{{offsety}}", Integer.toString(offsetWorldmapTo.y * WorldMapController.WORLDMAP_DISPLAY_TILESIZE)); + } + + private static Size getMapSize(WorldMapSegmentMap map, WorldContext world) { + return world.maps.findPredefinedMap(map.mapName).size; + } + + private static CoordRect determineNamedAreaBoundary(NamedWorldMapArea area, WorldMapSegment segment, WorldContext world, Set displayedMapNames) { + Coord topLeft = null; + Coord bottomRight = null; + + for (String mapName : area.mapNames) { + if (!displayedMapNames.contains(mapName)) continue; + WorldMapSegmentMap map = segment.maps.get(mapName); + Size size = getMapSize(map, world); + if (topLeft == null) { + topLeft = new Coord(map.worldPosition); + } else { + topLeft.x = Math.min(topLeft.x, map.worldPosition.x); + topLeft.y = Math.min(topLeft.y, map.worldPosition.y); + } + if (bottomRight == null) { + bottomRight = new Coord(map.worldPosition.x + size.width, map.worldPosition.y + size.height); + } else { + bottomRight.x = Math.max(bottomRight.x, map.worldPosition.x + size.width); + bottomRight.y = Math.max(bottomRight.y, map.worldPosition.y + size.height); + } + } + if (topLeft == null) return null; + return new CoordRect(topLeft, new Size(bottomRight.x - topLeft.x, bottomRight.y - topLeft.y)); + } + + private static void updateWorldMapSegment(Resources res, WorldContext world, String segmentName) throws IOException { + String mapAsHtml = getWorldMapSegmentAsHtml(res, world, segmentName); + File outputFile = getCombinedWorldMapFile(segmentName); + PrintWriter pw = new PrintWriter(outputFile); + pw.write(mapAsHtml); + pw.close(); + } + + public static boolean displayWorldMap(Context context, WorldContext world) { + String worldMapSegmentName = world.maps.getWorldMapSegmentNameForMap(world.model.currentMap.name); + if (worldMapSegmentName == null) { + Toast.makeText(context, context.getResources().getString(R.string.display_worldmap_not_available), Toast.LENGTH_LONG).show(); + return false; + } + + Intent intent = new Intent(context, DisplayWorldMapActivity.class); + intent.putExtra("worldMapSegmentName", worldMapSegmentName); + context.startActivity(intent); + + return true; + } +} diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/PredefinedMap.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/PredefinedMap.java index 4502a2999..b13269174 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/PredefinedMap.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/PredefinedMap.java @@ -1,397 +1,391 @@ -package com.gpl.rpg.AndorsTrail.model.map; - -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; - -import com.gpl.rpg.AndorsTrail.AndorsTrailApplication; -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.VisualEffectController.BloodSplatter; -import com.gpl.rpg.AndorsTrail.model.actor.Monster; -import com.gpl.rpg.AndorsTrail.model.item.ItemType; -import com.gpl.rpg.AndorsTrail.model.item.Loot; -import com.gpl.rpg.AndorsTrail.model.map.MapObject.MapObjectType; -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 PredefinedMap { - private static final long VISIT_RESET = 0; - - public final int xmlResourceId; - public final String name; - public final Size size; - public final MapObject[] eventObjects; - public final MonsterSpawnArea[] spawnAreas; - public final List initiallyActiveMapObjectGroups; - public final List activeMapObjectGroups; - public final ArrayList groundBags = new ArrayList(); - public boolean visited = false; - public long lastVisitTime = VISIT_RESET; - public String lastSeenLayoutHash = ""; - public final boolean isOutdoors; - public String currentColorFilter = null; - - public final ArrayList splatters = new ArrayList(); - - public PredefinedMap( - int xmlResourceId - , String name - , Size size - , MapObject[] eventObjects - , MonsterSpawnArea[] spawnAreas - , List initiallyActiveMapObjectGroups - , boolean isOutdoors - ) { - this.xmlResourceId = xmlResourceId; - this.name = name; - this.size = size; - this.eventObjects = eventObjects; - this.spawnAreas = spawnAreas; - this.initiallyActiveMapObjectGroups = initiallyActiveMapObjectGroups; - this.activeMapObjectGroups = new LinkedList(); - this.activeMapObjectGroups.addAll(this.initiallyActiveMapObjectGroups); - activateMapObjects(); - assert(size.width > 0); - assert(size.height > 0); - this.isOutdoors = isOutdoors; - } - - public final boolean isOutside(final Coord p) { return isOutside(p.x, p.y); } - public final boolean isOutside(final int x, final int y) { - if (x < 0) return true; - if (y < 0) return true; - if (x >= size.width) return true; - if (y >= size.height) return true; - return false; - } - public final boolean isOutside(final CoordRect area) { - if (isOutside(area.topLeft)) return true; - if (area.topLeft.x + area.size.width > size.width) return true; - if (area.topLeft.y + area.size.height > size.height) return true; - return false; - } - public boolean intersects(CoordRect area) { - return new CoordRect(new Coord(0,0), size).intersects(area); - } - - public MapObject findEventObject(MapObject.MapObjectType objectType, String name) { - for (MapObject o : eventObjects) { - if (o.type != objectType) continue; - if (!name.equals(o.id)) continue; - return o; - } - return null; - } - public List getActiveEventObjectsAt(final Coord p) { - List result = null; - for (MapObject o : eventObjects) { - if (!o.isActive) continue; - if (!o.position.contains(p)) continue; - //if (!activeMapObjectGroups.contains(o.group)) continue; - if (result == null) result = new ArrayList(); - result.add(o); - } - return result; - } - public boolean hasContainerAt(final Coord p) { - for (MapObject o : eventObjects) { - if (!o.isActive) continue; - if (o.type != MapObject.MapObjectType.container) continue; - if (!o.position.contains(p)) continue; - return true; - } - return false; - } - public Monster getMonsterAt(final CoordRect p) { - return getMonsterAt(p, null); - } - public Monster getMonsterAt(final CoordRect p, Monster exceptMe) { - for (MonsterSpawnArea a : spawnAreas) { - Monster m = a.getMonsterAt(p); - if (m != null && (exceptMe == null || exceptMe != m)) return m; - } - return null; - } - public Monster getMonsterAt(final Coord p) { return getMonsterAt(p.x, p.y); } - public Monster getMonsterAt(final int x, final int y) { - for (MonsterSpawnArea a : spawnAreas) { - Monster m = a.getMonsterAt(x, y); - if (m != null) return m; - } - return null; - } - - public Monster findSpawnedMonster(final String monsterTypeID) { - for (MonsterSpawnArea a : spawnAreas) { - Monster m = a.findSpawnedMonster(monsterTypeID); - if (m != null) return m; - } - return null; - } - - public Loot getBagAt(final Coord p) { - for (Loot l : groundBags) { - if (l.position.equals(p)) return l; - } - return null; - } - public Loot getBagOrCreateAt(final Coord position) { - Loot b = getBagAt(position); - if (b != null) return b; - boolean isContainer = hasContainerAt(position); - b = new Loot(!isContainer); - b.position.set(position); - if (isOutside(position)) { - if (AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA) { - L.log("WARNING: trying to place bag outside map. Map is " + size.toString() + ", bag tried to place at " + position.toString()); - } - return b; - } - groundBags.add(b); - return b; - } - public void itemDropped(ItemType itemType, int quantity, Coord position) { - Loot l = getBagOrCreateAt(position); - l.items.addItem(itemType, quantity); - } - public void removeGroundLoot(Loot loot) { - groundBags.remove(loot); - } - public void resetForNewGame() { - for (MonsterSpawnArea a : spawnAreas) { - a.resetForNewGame(); - } - activeMapObjectGroups.clear(); - activeMapObjectGroups.addAll(initiallyActiveMapObjectGroups); - activateMapObjects(); - resetTemporaryData(); - groundBags.clear(); - visited = false; - currentColorFilter = null; - lastSeenLayoutHash = ""; - } - - public boolean isRecentlyVisited() { - if (lastVisitTime == VISIT_RESET) return false; - return (System.currentTimeMillis() - lastVisitTime) < Constants.MAP_UNVISITED_RESPAWN_DURATION_MS; - } - public void updateLastVisitTime() { - lastVisitTime = System.currentTimeMillis(); - } - public void resetTemporaryData() { - for(MonsterSpawnArea a : spawnAreas) { - if (a.isUnique) a.resetShops(); - else a.removeAllMonsters(); - } - splatters.clear(); - lastVisitTime = VISIT_RESET; - } - public boolean hasResetTemporaryData() { - return lastVisitTime == VISIT_RESET; - } - - public void createAllContainerLoot() { - for (MapObject o : eventObjects) { - if (!o.isActive) continue; - if (o.type != MapObject.MapObjectType.container) continue; - createContainerLoot(o); - } - } - - public void createContainerLoot(MapObject container) { - Loot bag = getBagOrCreateAt(container.position.topLeft); - container.dropList.createRandomLoot(bag, null); - } - - - private void activateMapObjects() { - if (AndorsTrailApplication.DEVELOPMENT_DEBUGMESSAGES) { - L.log("Applying active status to all map objects in map "+name); - } - for (MapObject o : eventObjects) { - o.isActive = activeMapObjectGroups.contains(o.group); - } - } - - public void activateMapObjectGroup(String group) { - if (AndorsTrailApplication.DEVELOPMENT_DEBUGMESSAGES) { - L.log("Applying active status to object group "+group+" in map "+name); - } - if (!activeMapObjectGroups.contains(group)) { - activeMapObjectGroups.add(group); - for (MapObject o : eventObjects) { - if (o.group.equals(group)) { - o.isActive = true; - if (o.type == MapObjectType.container) createContainerLoot(o); - } - } - } - } - - public void deactivateMapObjectGroup(String group) { - if (AndorsTrailApplication.DEVELOPMENT_DEBUGMESSAGES) { - L.log("Removing active status to object group "+group+" in map"+name); - } - if (activeMapObjectGroups.contains(group)) { - activeMapObjectGroups.remove(group); - for (MapObject o : eventObjects) { - if (o.group.equals(group)) { - o.isActive = false; - } - } - } - } - - - // ====== PARCELABLE =================================================================== - - public void readFromParcel(DataInputStream src, WorldContext world, ControllerContext controllers, int fileversion) throws IOException { - boolean shouldLoadMapData = true; - if (fileversion >= 37) shouldLoadMapData = src.readBoolean(); - - int loadedSpawnAreas = 0; - if (shouldLoadMapData) { - loadedSpawnAreas = src.readInt(); - for(int i = 0; i < loadedSpawnAreas; ++i) { - if (AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA) { - if (i >= this.spawnAreas.length) { - L.log("WARNING: Trying to load monsters from savegame in map " + this.name + " for spawn #" + i + ". This will totally fail."); - } - } - if(fileversion >= 43) { - //Spawn areas now have unique IDs. Need to check as maps can change. - String id = src.readUTF(); - int j = i; - boolean found = false; - do { - if (this.spawnAreas[j].areaID.equals(id)) { - this.spawnAreas[j].readFromParcel(src, world, fileversion); - found = true; - break; - } - j = (j+1)%spawnAreas.length; - } while (j != i); - if (AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA) { - if (!found) { - L.log("WARNING: Trying to load monsters from savegame in map " + this.name + " for spawn #" + id + " but this area cannot be found. This will totally fail."); - } - } - } else { - this.spawnAreas[i].readFromParcel(src, world, fileversion); - } - } - - activeMapObjectGroups.clear(); - if(fileversion >= 43) { - int activeListLength = src.readInt(); - for (int i = 0; i < activeListLength; i++) { - String activeGroupID = src.readUTF(); - activeMapObjectGroups.add(activeGroupID); - } - } else { - activeMapObjectGroups.addAll(initiallyActiveMapObjectGroups); - } - activateMapObjects(); - - groundBags.clear(); - if (fileversion <= 5) return; - - final int size2 = src.readInt(); - for(int i = 0; i < size2; ++i) { - groundBags.add(new Loot(src, world, fileversion)); - } - - if (fileversion <= 11) return; - - if (fileversion < 37) visited = src.readBoolean(); - - if (fileversion <= 15) { - if (visited) { - lastVisitTime = System.currentTimeMillis(); - createAllContainerLoot(); - } - return; - } - - if (fileversion >= 43) { - if (src.readBoolean()) { - currentColorFilter = src.readUTF(); - } else { - currentColorFilter = null; - } - } - - lastVisitTime = src.readLong(); - - if (visited) { - if (fileversion > 30 && fileversion < 36) { - /*int lastVisitVersion = */src.readInt(); - } - } - } else { - activeMapObjectGroups.clear(); - activeMapObjectGroups.addAll(initiallyActiveMapObjectGroups); - activateMapObjects(); - } - if (fileversion >= 37) { - if (fileversion < 41) visited = true; - else visited = src.readBoolean(); - } - - if (fileversion < 36) lastSeenLayoutHash = ""; - else lastSeenLayoutHash = src.readUTF(); - - for(int i = loadedSpawnAreas; i < spawnAreas.length; ++i) { - MonsterSpawnArea area = this.spawnAreas[i]; - if (area.isUnique && visited) controllers.monsterSpawnController.spawnAllInArea(this, null, area, true); - else area.resetForNewGame(); - } - } - - public boolean shouldSaveMapData(WorldContext world) { - if (!hasResetTemporaryData()) return true; - if (this == world.model.currentMap) return true; - if (!groundBags.isEmpty()) return true; - for (MonsterSpawnArea a : spawnAreas) { - if (this.visited && a.isUnique) return true; - if (a.isSpawning != a.isSpawningForNewGame) return true; - } - if (!activeMapObjectGroups.containsAll(initiallyActiveMapObjectGroups) - || !initiallyActiveMapObjectGroups.containsAll(activeMapObjectGroups)) return true; - if (currentColorFilter != null) return true; - return false; - } - - public void writeToParcel(DataOutputStream dest, WorldContext world) throws IOException { - if (shouldSaveMapData(world)) { - dest.writeBoolean(true); - dest.writeInt(spawnAreas.length); - for(MonsterSpawnArea a : spawnAreas) { - dest.writeUTF(a.areaID); - a.writeToParcel(dest); - } - dest.writeInt(activeMapObjectGroups.size()); - for(String s : activeMapObjectGroups) { - dest.writeUTF(s); - } - dest.writeInt(groundBags.size()); - for(Loot l : groundBags) { - l.writeToParcel(dest); - } - dest.writeBoolean(currentColorFilter != null); - if (currentColorFilter != null) dest.writeUTF(currentColorFilter); - dest.writeLong(lastVisitTime); - } else { - dest.writeBoolean(false); - } - dest.writeBoolean(visited); - dest.writeUTF(lastSeenLayoutHash); - } -} +package com.gpl.rpg.AndorsTrail.model.map; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +import com.gpl.rpg.AndorsTrail.AndorsTrailApplication; +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.VisualEffectController.BloodSplatter; +import com.gpl.rpg.AndorsTrail.model.actor.Monster; +import com.gpl.rpg.AndorsTrail.model.item.ItemType; +import com.gpl.rpg.AndorsTrail.model.item.Loot; +import com.gpl.rpg.AndorsTrail.model.map.MapObject.MapObjectType; +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 PredefinedMap { + private static final long VISIT_RESET = 0; + + public final int xmlResourceId; + public final String name; + public final Size size; + public final MapObject[] eventObjects; + public final MonsterSpawnArea[] spawnAreas; + public final List initiallyActiveMapObjectGroups; + public final List activeMapObjectGroups; + public final ArrayList groundBags = new ArrayList(); + public final String initialColorFilter; + public boolean visited = false; + public long lastVisitTime = VISIT_RESET; + public String lastSeenLayoutHash = ""; + public final boolean isOutdoors; + public String currentColorFilter = null; + + public final ArrayList splatters = new ArrayList(); + + public PredefinedMap( + int xmlResourceId + , String name + , Size size + , MapObject[] eventObjects + , MonsterSpawnArea[] spawnAreas + , List initiallyActiveMapObjectGroups + , boolean isOutdoors + , String colorFilter + ) { + this.xmlResourceId = xmlResourceId; + this.name = name; + this.size = size; + this.eventObjects = eventObjects; + this.spawnAreas = spawnAreas; + this.initiallyActiveMapObjectGroups = initiallyActiveMapObjectGroups; + this.activeMapObjectGroups = new LinkedList(); + this.activeMapObjectGroups.addAll(this.initiallyActiveMapObjectGroups); + activateMapObjects(); + assert(size.width > 0); + assert(size.height > 0); + this.isOutdoors = isOutdoors; + this.initialColorFilter = colorFilter; + } + + public final boolean isOutside(final Coord p) { return isOutside(p.x, p.y); } + public final boolean isOutside(final int x, final int y) { + if (x < 0) return true; + if (y < 0) return true; + if (x >= size.width) return true; + if (y >= size.height) return true; + return false; + } + public final boolean isOutside(final CoordRect area) { + if (isOutside(area.topLeft)) return true; + if (area.topLeft.x + area.size.width > size.width) return true; + if (area.topLeft.y + area.size.height > size.height) return true; + return false; + } + public boolean intersects(CoordRect area) { + return new CoordRect(new Coord(0,0), size).intersects(area); + } + + public MapObject findEventObject(MapObject.MapObjectType objectType, String name) { + for (MapObject o : eventObjects) { + if (o.type != objectType) continue; + if (!name.equals(o.id)) continue; + return o; + } + return null; + } + public List getActiveEventObjectsAt(final Coord p) { + List result = null; + for (MapObject o : eventObjects) { + if (!o.isActive) continue; + if (!o.position.contains(p)) continue; + //if (!activeMapObjectGroups.contains(o.group)) continue; + if (result == null) result = new ArrayList(); + result.add(o); + } + return result; + } + public boolean hasContainerAt(final Coord p) { + for (MapObject o : eventObjects) { + if (!o.isActive) continue; + if (o.type != MapObject.MapObjectType.container) continue; + if (!o.position.contains(p)) continue; + return true; + } + return false; + } + public Monster getMonsterAt(final CoordRect p) { + return getMonsterAt(p, null); + } + public Monster getMonsterAt(final CoordRect p, Monster exceptMe) { + for (MonsterSpawnArea a : spawnAreas) { + Monster m = a.getMonsterAt(p); + if (m != null && (exceptMe == null || exceptMe != m)) return m; + } + return null; + } + public Monster getMonsterAt(final Coord p) { return getMonsterAt(p.x, p.y); } + public Monster getMonsterAt(final int x, final int y) { + for (MonsterSpawnArea a : spawnAreas) { + Monster m = a.getMonsterAt(x, y); + if (m != null) return m; + } + return null; + } + + public Monster findSpawnedMonster(final String monsterTypeID) { + for (MonsterSpawnArea a : spawnAreas) { + Monster m = a.findSpawnedMonster(monsterTypeID); + if (m != null) return m; + } + return null; + } + + public Loot getBagAt(final Coord p) { + for (Loot l : groundBags) { + if (l.position.equals(p)) return l; + } + return null; + } + public Loot getBagOrCreateAt(final Coord position) { + Loot b = getBagAt(position); + if (b != null) return b; + boolean isContainer = hasContainerAt(position); + b = new Loot(!isContainer); + b.position.set(position); + if (isOutside(position)) { + if (AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA) { + L.log("WARNING: trying to place bag outside map. Map is " + size.toString() + ", bag tried to place at " + position.toString()); + } + return b; + } + groundBags.add(b); + return b; + } + public void itemDropped(ItemType itemType, int quantity, Coord position) { + Loot l = getBagOrCreateAt(position); + l.items.addItem(itemType, quantity); + } + public void removeGroundLoot(Loot loot) { + groundBags.remove(loot); + } + public void resetForNewGame() { + for (MonsterSpawnArea a : spawnAreas) { + a.resetForNewGame(); + } + activeMapObjectGroups.clear(); + activeMapObjectGroups.addAll(initiallyActiveMapObjectGroups); + activateMapObjects(); + resetTemporaryData(); + groundBags.clear(); + visited = false; + currentColorFilter = initialColorFilter; + lastSeenLayoutHash = ""; + } + + public boolean isRecentlyVisited() { + if (lastVisitTime == VISIT_RESET) return false; + return (System.currentTimeMillis() - lastVisitTime) < Constants.MAP_UNVISITED_RESPAWN_DURATION_MS; + } + public void updateLastVisitTime() { + lastVisitTime = System.currentTimeMillis(); + } + public void resetTemporaryData() { + for(MonsterSpawnArea a : spawnAreas) { + if (a.isUnique) a.resetShops(); + else a.removeAllMonsters(); + } + splatters.clear(); + lastVisitTime = VISIT_RESET; + } + public boolean hasResetTemporaryData() { + return lastVisitTime == VISIT_RESET; + } + + public void createAllContainerLoot() { + for (MapObject o : eventObjects) { + if (!o.isActive) continue; + if (o.type != MapObject.MapObjectType.container) continue; + createContainerLoot(o); + } + } + + public void createContainerLoot(MapObject container) { + Loot bag = getBagOrCreateAt(container.position.topLeft); + container.dropList.createRandomLoot(bag, null); + } + + + private void activateMapObjects() { + for (MapObject o : eventObjects) { + o.isActive = activeMapObjectGroups.contains(o.group); + } + } + + public void activateMapObjectGroup(String group) { + if (!activeMapObjectGroups.contains(group)) { + activeMapObjectGroups.add(group); + for (MapObject o : eventObjects) { + if (o.group.equals(group)) { + o.isActive = true; + if (o.type == MapObjectType.container) createContainerLoot(o); + } + } + } + } + + public void deactivateMapObjectGroup(String group) { + if (activeMapObjectGroups.contains(group)) { + activeMapObjectGroups.remove(group); + for (MapObject o : eventObjects) { + if (o.group.equals(group)) { + o.isActive = false; + } + } + } + } + + + // ====== PARCELABLE =================================================================== + + public void readFromParcel(DataInputStream src, WorldContext world, ControllerContext controllers, int fileversion) throws IOException { + boolean shouldLoadMapData = true; + if (fileversion >= 37) shouldLoadMapData = src.readBoolean(); + + int loadedSpawnAreas = 0; + if (shouldLoadMapData) { + loadedSpawnAreas = src.readInt(); + for(int i = 0; i < loadedSpawnAreas; ++i) { + if (AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA) { + if (i >= this.spawnAreas.length) { + L.log("WARNING: Trying to load monsters from savegame in map " + this.name + " for spawn #" + i + ". This will totally fail."); + } + } + if(fileversion >= 43) { + //Spawn areas now have unique IDs. Need to check as maps can change. + String id = src.readUTF(); + int j = i; + boolean found = false; + do { + if (this.spawnAreas[j].areaID.equals(id)) { + this.spawnAreas[j].readFromParcel(src, world, fileversion); + found = true; + break; + } + j = (j+1)%spawnAreas.length; + } while (j != i); + if (AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA) { + if (!found) { + L.log("WARNING: Trying to load monsters from savegame in map " + this.name + " for spawn #" + id + " but this area cannot be found. This will totally fail."); + } + } + } else { + this.spawnAreas[i].readFromParcel(src, world, fileversion); + } + } + + activeMapObjectGroups.clear(); + if(fileversion >= 43) { + int activeListLength = src.readInt(); + for (int i = 0; i < activeListLength; i++) { + String activeGroupID = src.readUTF(); + activeMapObjectGroups.add(activeGroupID); + } + } else { + activeMapObjectGroups.addAll(initiallyActiveMapObjectGroups); + } + activateMapObjects(); + + groundBags.clear(); + if (fileversion <= 5) return; + + final int size2 = src.readInt(); + for(int i = 0; i < size2; ++i) { + groundBags.add(new Loot(src, world, fileversion)); + } + + if (fileversion <= 11) return; + + if (fileversion < 37) visited = src.readBoolean(); + + if (fileversion <= 15) { + if (visited) { + lastVisitTime = System.currentTimeMillis(); + createAllContainerLoot(); + } + return; + } + + if (fileversion >= 43) { + if (src.readBoolean()) { + currentColorFilter = src.readUTF(); + } else { + currentColorFilter = null; + } + } + + lastVisitTime = src.readLong(); + + if (visited) { + if (fileversion > 30 && fileversion < 36) { + /*int lastVisitVersion = */src.readInt(); + } + } + } else { + activeMapObjectGroups.clear(); + activeMapObjectGroups.addAll(initiallyActiveMapObjectGroups); + activateMapObjects(); + } + if (fileversion >= 37) { + if (fileversion < 41) visited = true; + else visited = src.readBoolean(); + } + + if (fileversion < 36) lastSeenLayoutHash = ""; + else lastSeenLayoutHash = src.readUTF(); + + for(int i = loadedSpawnAreas; i < spawnAreas.length; ++i) { + MonsterSpawnArea area = this.spawnAreas[i]; + if (area.isUnique && visited) controllers.monsterSpawnController.spawnAllInArea(this, null, area, true); + else area.resetForNewGame(); + } + } + + public boolean shouldSaveMapData(WorldContext world) { + if (!hasResetTemporaryData()) return true; + if (this == world.model.currentMap) return true; + if (!groundBags.isEmpty()) return true; + for (MonsterSpawnArea a : spawnAreas) { + if (this.visited && a.isUnique) return true; + if (a.isSpawning != a.isSpawningForNewGame) return true; + } + if (!activeMapObjectGroups.containsAll(initiallyActiveMapObjectGroups) + || !initiallyActiveMapObjectGroups.containsAll(activeMapObjectGroups)) return true; + if (currentColorFilter != null) return true; + return false; + } + + public void writeToParcel(DataOutputStream dest, WorldContext world) throws IOException { + if (shouldSaveMapData(world)) { + dest.writeBoolean(true); + dest.writeInt(spawnAreas.length); + for(MonsterSpawnArea a : spawnAreas) { + dest.writeUTF(a.areaID); + a.writeToParcel(dest); + } + dest.writeInt(activeMapObjectGroups.size()); + for(String s : activeMapObjectGroups) { + dest.writeUTF(s); + } + dest.writeInt(groundBags.size()); + for(Loot l : groundBags) { + l.writeToParcel(dest); + } + dest.writeBoolean(currentColorFilter != null); + if (currentColorFilter != null) dest.writeUTF(currentColorFilter); + dest.writeLong(lastVisitTime); + } else { + dest.writeBoolean(false); + } + dest.writeBoolean(visited); + dest.writeUTF(lastSeenLayoutHash); + } +} diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/TMXMapTranslator.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/TMXMapTranslator.java index 23c676dab..20aeb424f 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/TMXMapTranslator.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/TMXMapTranslator.java @@ -59,8 +59,10 @@ public final class TMXMapTranslator { assert(m.height > 0); boolean isOutdoors = false; + String colorFilter = null; for (TMXProperty p : m.properties) { if(p.name.equalsIgnoreCase("outdoors")) isOutdoors = (Integer.parseInt(p.value) != 0); + else if(p.name.equalsIgnoreCase("colorfilter")) colorFilter = p.value; else if(AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA) L.log("OPTIMIZE: Map " + m.name + " has unrecognized property \"" + p.name + "\"."); } @@ -212,7 +214,7 @@ public final class TMXMapTranslator { MonsterSpawnArea[] _spawnAreas = new MonsterSpawnArea[spawnAreas.size()]; _spawnAreas = spawnAreas.toArray(_spawnAreas); - result.add(new PredefinedMap(m.xmlResourceId, m.name, mapSize, _eventObjects, _spawnAreas, activeGroups, isOutdoors)); + result.add(new PredefinedMap(m.xmlResourceId, m.name, mapSize, _eventObjects, _spawnAreas, activeGroups, isOutdoors, colorFilter)); } return result; @@ -347,6 +349,7 @@ public final class TMXMapTranslator { HashSet usedTileIDs, SetOfLayerNames layerNames ) { + final MapLayer layerGround = transformMapLayer(layersPerLayerName, layerNames.groundLayerName, srcMap, tileCache, area, usedTileIDs); final MapLayer layerObjects = transformMapLayer(layersPerLayerName, layerNames.objectsLayerName, srcMap, tileCache, area, usedTileIDs); final MapLayer layerAbove = transformMapLayer(layersPerLayerName, layerNames.aboveLayersName, srcMap, tileCache, area, usedTileIDs); diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/resource/tiles/TileCache.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/resource/tiles/TileCache.java index 0fde8963f..dec2c32df 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/resource/tiles/TileCache.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/resource/tiles/TileCache.java @@ -1,123 +1,125 @@ -package com.gpl.rpg.AndorsTrail.resource.tiles; - -import java.lang.ref.Reference; -import java.lang.ref.ReferenceQueue; -import java.lang.ref.WeakReference; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map.Entry; - -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.util.SparseArray; -import android.util.SparseIntArray; - -import com.gpl.rpg.AndorsTrail.util.LruCache; - -public final class TileCache { - - private final ReferenceQueue gcQueue = new ReferenceQueue(); - private ResourceFileTile[] resourceTiles = new ResourceFileTile[1]; - private final HashMap tileIDsPerTilesetAndLocalID = new HashMap(); - private final LruCache cache = new LruCache(1000); - - public int getMaxTileID() { return resourceTiles.length-1; } - public void allocateMaxTileID(int maxTileID) { - if (maxTileID <= 0) return; - - ResourceFileTile[] oldArray = resourceTiles; - resourceTiles = new ResourceFileTile[maxTileID+1]; - System.arraycopy(oldArray, 0, resourceTiles, 0, oldArray.length); - } - public void setTile(int tileID, ResourceFileTileset tileset, int localID) { - if (resourceTiles[tileID] == null) resourceTiles[tileID] = new ResourceFileTile(tileset, localID); - SparseIntArray tileIDsPerLocalID = tileIDsPerTilesetAndLocalID.get(tileset.tilesetName); - if (tileIDsPerLocalID == null) { - tileIDsPerLocalID = new SparseIntArray(); - tileIDsPerTilesetAndLocalID.put(tileset.tilesetName, tileIDsPerLocalID); - } - tileIDsPerLocalID.put(localID, tileID); - } - public int getTileID(String tileSetName, int localID) { - return tileIDsPerTilesetAndLocalID.get(tileSetName).get(localID); - } - - private static final class ResourceFileTile { - public final ResourceFileTileset tileset; - public final int localID; - //public WeakReference bitmap; - public ResourceFileTile(ResourceFileTileset tileset, int localID) { - this.tileset = tileset; - this.localID = localID; - } - } - - private void cleanQueue() { - System.gc(); - Reference ref; - while ((ref = gcQueue.poll()) != null) { - Bitmap b = ref.get(); - if (b != null) b.recycle(); - } - } - - public TileCollection loadTilesFor(Collection iconIDs, Resources r) { return loadTilesFor(iconIDs, r, null); } - public TileCollection loadTilesFor(Collection iconIDs, Resources r, TileCollection result) { - int maxTileID = 0; - HashMap> tilesToLoadPerSourceFile = new HashMap>(); - for(int tileID : iconIDs) { - ResourceFileTile tile = resourceTiles[tileID]; - SparseArray tiles = tilesToLoadPerSourceFile.get(tile.tileset); - if (tiles == null) { - tiles = new SparseArray(); - tilesToLoadPerSourceFile.put(tile.tileset, tiles); - } - tiles.put(tileID, tile); - maxTileID = Math.max(maxTileID, tileID); - } - - boolean hasLoadedTiles = false; - if (result == null) result = new TileCollection(maxTileID); - for(Entry> e : tilesToLoadPerSourceFile.entrySet()) { - TileCutter cutter = null; - - SparseArray tilesToLoad = e.getValue(); - for (int i = 0; i < tilesToLoad.size(); ++i) { - int tileID = tilesToLoad.keyAt(i); - ResourceFileTile tile = tilesToLoad.valueAt(i); - - Bitmap bitmap = cache.get(tileID); - - if (bitmap == null) { - if (cutter == null) { - if (!hasLoadedTiles) cleanQueue(); - cutter = new TileCutter(e.getKey(), r); - hasLoadedTiles = true; - } - - bitmap = cutter.createTile(tile.localID); - cache.put(tileID, bitmap); - new WeakReference(bitmap, gcQueue); - } - result.setBitmap(tileID, bitmap); - } - - if (cutter != null) cutter.recycle(); - } - if (hasLoadedTiles) cleanQueue(); - return result; - } - - public Bitmap loadSingleTile(int tileID, Resources r) { - cleanQueue(); - ResourceFileTile tile = resourceTiles[tileID]; - Bitmap bitmap = cache.get(tileID); - if (bitmap != null) return bitmap; - - TileCutter cutter = new TileCutter(tile.tileset, r); - Bitmap result = cutter.createTile(tile.localID); - cutter.recycle(); - cache.put(tileID, result); - return result; - } -} +package com.gpl.rpg.AndorsTrail.resource.tiles; + +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map.Entry; + +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.util.SparseArray; +import android.util.SparseIntArray; + +import com.gpl.rpg.AndorsTrail.AndorsTrailApplication; +import com.gpl.rpg.AndorsTrail.util.L; +import com.gpl.rpg.AndorsTrail.util.LruCache; + +public final class TileCache { + + private final ReferenceQueue gcQueue = new ReferenceQueue(); + private ResourceFileTile[] resourceTiles = new ResourceFileTile[1]; + private final HashMap tileIDsPerTilesetAndLocalID = new HashMap(); + private final LruCache cache = new LruCache(1000); + + public int getMaxTileID() { return resourceTiles.length-1; } + public void allocateMaxTileID(int maxTileID) { + if (maxTileID <= 0) return; + + ResourceFileTile[] oldArray = resourceTiles; + resourceTiles = new ResourceFileTile[maxTileID+1]; + System.arraycopy(oldArray, 0, resourceTiles, 0, oldArray.length); + } + public void setTile(int tileID, ResourceFileTileset tileset, int localID) { + if (resourceTiles[tileID] == null) resourceTiles[tileID] = new ResourceFileTile(tileset, localID); + SparseIntArray tileIDsPerLocalID = tileIDsPerTilesetAndLocalID.get(tileset.tilesetName); + if (tileIDsPerLocalID == null) { + tileIDsPerLocalID = new SparseIntArray(); + tileIDsPerTilesetAndLocalID.put(tileset.tilesetName, tileIDsPerLocalID); + } + tileIDsPerLocalID.put(localID, tileID); + } + public int getTileID(String tileSetName, int localID) { + return tileIDsPerTilesetAndLocalID.get(tileSetName).get(localID); + } + + private static final class ResourceFileTile { + public final ResourceFileTileset tileset; + public final int localID; + //public WeakReference bitmap; + public ResourceFileTile(ResourceFileTileset tileset, int localID) { + this.tileset = tileset; + this.localID = localID; + } + } + + private void cleanQueue() { + System.gc(); + Reference ref; + while ((ref = gcQueue.poll()) != null) { + Bitmap b = ref.get(); + if (b != null) b.recycle(); + } + } + + public TileCollection loadTilesFor(Collection iconIDs, Resources r) { return loadTilesFor(iconIDs, r, null); } + public TileCollection loadTilesFor(Collection iconIDs, Resources r, TileCollection result) { + int maxTileID = 0; + HashMap> tilesToLoadPerSourceFile = new HashMap>(); + for(int tileID : iconIDs) { + ResourceFileTile tile = resourceTiles[tileID]; + SparseArray tiles = tilesToLoadPerSourceFile.get(tile.tileset); + if (tiles == null) { + tiles = new SparseArray(); + tilesToLoadPerSourceFile.put(tile.tileset, tiles); + } + tiles.put(tileID, tile); + maxTileID = Math.max(maxTileID, tileID); + } + + boolean hasLoadedTiles = false; + if (result == null) result = new TileCollection(maxTileID); + for(Entry> e : tilesToLoadPerSourceFile.entrySet()) { + TileCutter cutter = null; + + SparseArray tilesToLoad = e.getValue(); + for (int i = 0; i < tilesToLoad.size(); ++i) { + int tileID = tilesToLoad.keyAt(i); + ResourceFileTile tile = tilesToLoad.valueAt(i); + + Bitmap bitmap = cache.get(tileID); + + if (bitmap == null) { + if (cutter == null) { + if (!hasLoadedTiles) cleanQueue(); + cutter = new TileCutter(e.getKey(), r); + hasLoadedTiles = true; + } + + bitmap = cutter.createTile(tile.localID); + cache.put(tileID, bitmap); + new WeakReference(bitmap, gcQueue); + } + result.setBitmap(tileID, bitmap); + } + + if (cutter != null) cutter.recycle(); + } + if (hasLoadedTiles) cleanQueue(); + return result; + } + + public Bitmap loadSingleTile(int tileID, Resources r) { + cleanQueue(); + ResourceFileTile tile = resourceTiles[tileID]; + Bitmap bitmap = cache.get(tileID); + if (bitmap != null) return bitmap; + + TileCutter cutter = new TileCutter(tile.tileset, r); + Bitmap result = cutter.createTile(tile.localID); + cutter.recycle(); + cache.put(tileID, result); + return result; + } +} diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/util/L.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/util/L.java index 71ee062f7..854e82478 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/util/L.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/util/L.java @@ -1,15 +1,15 @@ -package com.gpl.rpg.AndorsTrail.util; - -import android.util.Log; - -import com.gpl.rpg.AndorsTrail.AndorsTrailApplication; - -public final class L { - private static final String TAG = "AndorsTrail"; - - public static void log(String s) { - if (AndorsTrailApplication.DEVELOPMENT_DEBUGMESSAGES) { - Log.d(TAG, s); - } - } -} +package com.gpl.rpg.AndorsTrail.util; + +import android.util.Log; + +import com.gpl.rpg.AndorsTrail.AndorsTrailApplication; + +public final class L { + private static final String TAG = "AndorsTrail"; + + public static void log(String s) { + if (AndorsTrailApplication.DEVELOPMENT_DEBUGMESSAGES) { + Log.w(TAG, s); + } + } +}