diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/AndorsTrailApplication.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/AndorsTrailApplication.java index 2bb46e9c3..00a9ee139 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/AndorsTrailApplication.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/AndorsTrailApplication.java @@ -18,7 +18,7 @@ public final class AndorsTrailApplication extends Application { public static final boolean DEVELOPMENT_VALIDATEDATA = true; public static final boolean DEVELOPMENT_DEBUGMESSAGES = true; public static final boolean DEVELOPMENT_INCOMPATIBLE_SAVEGAMES = DEVELOPMENT_DEBUGRESOURCES; - public static final int CURRENT_VERSION = DEVELOPMENT_INCOMPATIBLE_SAVEGAMES ? 999 : 35; + public static final int CURRENT_VERSION = DEVELOPMENT_INCOMPATIBLE_SAVEGAMES ? 999 : 36; public static final String CURRENT_VERSION_DISPLAY = "0.7.0dev"; private final AndorsTrailPreferences preferences = new AndorsTrailPreferences(); diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/activity/MainActivity.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/activity/MainActivity.java index 3b4c086a4..44e615078 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/activity/MainActivity.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/activity/MainActivity.java @@ -121,7 +121,7 @@ public final class MainActivity extends Activity implements PlayerMovementListen break; case INTENTREQUEST_CONVERSATION: MovementController.refreshMonsterAggressiveness(world.model.currentMap, world.model.player); - controllers.mapController.applyCurrentMapReplacements(getResources()); + controllers.mapController.applyCurrentMapReplacements(getResources(), true); break; case INTENTREQUEST_SAVEGAME: if (resultCode != Activity.RESULT_OK) break; diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/ConversationController.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/ConversationController.java index 047703952..390b31479 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/ConversationController.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/ConversationController.java @@ -20,7 +20,6 @@ import com.gpl.rpg.AndorsTrail.model.item.Loot; import com.gpl.rpg.AndorsTrail.model.quest.QuestLogEntry; import com.gpl.rpg.AndorsTrail.model.quest.QuestProgress; import com.gpl.rpg.AndorsTrail.util.ConstRange; -import com.gpl.rpg.AndorsTrail.util.Coord; import java.util.ArrayList; diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/MapController.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/MapController.java index 79f667811..c2aa9e4ba 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/MapController.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/MapController.java @@ -8,9 +8,11 @@ import com.gpl.rpg.AndorsTrail.controller.listeners.WorldEventListeners; import com.gpl.rpg.AndorsTrail.model.ability.SkillCollection; import com.gpl.rpg.AndorsTrail.model.actor.Monster; import com.gpl.rpg.AndorsTrail.model.actor.Player; -import com.gpl.rpg.AndorsTrail.model.map.*; +import com.gpl.rpg.AndorsTrail.model.map.LayeredTileMap; +import com.gpl.rpg.AndorsTrail.model.map.MapObject; +import com.gpl.rpg.AndorsTrail.model.map.PredefinedMap; +import com.gpl.rpg.AndorsTrail.model.map.ReplaceableMapSection; import com.gpl.rpg.AndorsTrail.util.Coord; -import com.gpl.rpg.AndorsTrail.util.CoordRect; public final class MapController { @@ -110,25 +112,26 @@ public final class MapController { } } - public void applyCurrentMapReplacements(final Resources res) { - if (!applyReplacements(world.model.currentTileMap)) return; - - world.model.currentMap.visited = false; // Force worldmap png to be updated. - WorldMapController.updateWorldMapForCurrentMap(world, res); - world.model.currentMap.visited = true; + public void applyCurrentMapReplacements(final Resources res, boolean updateWorldmap) { + if (!applyReplacements(world.model.currentMap, world.model.currentTileMap)) return; + world.maps.worldMapRequiresUpdate = true; + if (!updateWorldmap) return; + WorldMapController.updateWorldMap(world, res); mapLayoutListeners.onMapTilesChanged(world.model.currentMap, world.model.currentTileMap); } - private boolean applyReplacements(LayeredTileMap map) { - if (map.replacements == null) return false; + private boolean applyReplacements(PredefinedMap map, LayeredTileMap tileMap) { boolean hasUpdated = false; - for(ReplaceableMapSection replacement : map.replacements) { - if (replacement.isApplied) continue; - if (!satisfiesCondition(replacement)) continue; - map.applyReplacement(replacement); - hasUpdated = true; + if (tileMap.replacements != null) { + for(ReplaceableMapSection replacement : tileMap.replacements) { + if (replacement.isApplied) continue; + if (!satisfiesCondition(replacement)) continue; + tileMap.applyReplacement(replacement); + hasUpdated = true; + } } + map.lastSeenLayoutHash = tileMap.getCurrentLayoutHash(); return hasUpdated; } diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/MovementController.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/MovementController.java index afd05e734..c335cce29 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/MovementController.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/MovementController.java @@ -88,6 +88,7 @@ public final class MovementController implements TimedMessageTask.Callback { private void playerVisitsMapFirstTime(PredefinedMap m) { m.reset(); m.createAllContainerLoot(); + world.maps.worldMapRequiresUpdate = true; } public void prepareMapAsCurrentMap(PredefinedMap newMap, Resources res, boolean spawnMonsters) { @@ -99,12 +100,12 @@ public final class MovementController implements TimedMessageTask.Callback { controllers.monsterSpawnController.spawnAll(newMap, model.currentTileMap); } } - controllers.mapController.applyCurrentMapReplacements(res); - WorldMapController.updateWorldMapForCurrentMap(world, res); + controllers.mapController.applyCurrentMapReplacements(res, false); newMap.visited = true; moveBlockedActors(newMap, model.currentTileMap); refreshMonsterAggressiveness(newMap, model.player); controllers.effectController.updateSplatters(newMap); + WorldMapController.updateWorldMap(world, res); } private boolean mayMovePlayer() { diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/WorldMapController.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/WorldMapController.java index 163b8bfa5..c9d82cc2d 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/WorldMapController.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/WorldMapController.java @@ -4,8 +4,7 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; -import java.util.Collection; -import java.util.HashSet; +import java.util.*; import android.content.Context; import android.content.Intent; @@ -22,10 +21,7 @@ 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.*; 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; @@ -39,11 +35,11 @@ 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 updateWorldMapForCurrentMap(final WorldContext world, final Resources res) { + public static void updateWorldMap(final WorldContext world, final Resources res) { updateWorldMap(world, world.model.currentMap, world.model.currentTileMap, world.tileManager.currentMapTiles, res); } - public static void updateWorldMap( + private static void updateWorldMap( final WorldContext world, final PredefinedMap map, final LayeredTileMap mapTiles, @@ -52,7 +48,7 @@ public final class WorldMapController { final String worldMapSegmentName = world.maps.getWorldMapSegmentNameForMap(map.name); if (worldMapSegmentName == null) return; - if (!shouldUpdateWorldMap(map, worldMapSegmentName)) return; + if (!shouldUpdateWorldMap(map, worldMapSegmentName, world.maps.worldMapRequiresUpdate)) return; (new AsyncTask() { @Override @@ -61,6 +57,7 @@ public final class WorldMapController { 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); } @@ -72,16 +69,15 @@ public final class WorldMapController { }).execute(); } - private static boolean shouldUpdateWorldMap(PredefinedMap map, String worldMapSegmentName) { + private static boolean shouldUpdateWorldMap(PredefinedMap map, String worldMapSegmentName, boolean forceUpdate) { + if (forceUpdate) return true; if (!map.visited) return true; File file = getFileForMap(map); if (!file.exists()) return true; - if (map.lastVisitVersion < AndorsTrailApplication.CURRENT_VERSION) return true; - file = getCombinedWorldMapFile(worldMapSegmentName); if (!file.exists()) return true; - + return false; } @@ -89,9 +85,7 @@ public final class WorldMapController { ensureWorldmapDirectoryExists(); File file = getFileForMap(map); - if (file.exists()) { - if (map.lastVisitVersion == AndorsTrailApplication.CURRENT_VERSION) return; - } + if (file.exists()) return; Bitmap image = renderer.drawMap(); FileOutputStream fos = new FileOutputStream(file); @@ -99,6 +93,17 @@ public final class WorldMapController { fos.flush(); fos.close(); image.recycle(); + L.log("WorldMapController: Wrote " + file.getAbsolutePath()); + + // Before we had the hash as part of the filename, the png files were just named [mapname].png + // let's just remove those old files since the new hash-based filenames contain the same data. + if (map.lastSeenLayoutHash.length() > 0) { + File oldFile = getFileForMap(map.name); + if (!oldFile.getName().equalsIgnoreCase(file.getName())) { + oldFile.delete(); + L.log("WorldMapController: Deleted " + oldFile.getAbsolutePath()); + } + } } private static final class MapRenderer { @@ -155,11 +160,14 @@ public final class WorldMapController { 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(); } - private static File getFileForMap(PredefinedMap map) { return getFileForMap(map.name); } + private static File getFileForMap(PredefinedMap map) { + if (map.lastSeenLayoutHash.length() <= 0) return getFileForMap(map.name); + else return getFileForMap(map.name + "." + map.lastSeenLayoutHash); + } private static File getFileForMap(String mapName) { return new File(getWorldmapDirectory(), mapName + ".png"); } @@ -172,19 +180,19 @@ public final class WorldMapController { return new File(getWorldmapDirectory(), Constants.FILENAME_WORLDMAP_HTMLFILE_PREFIX + segmentName + Constants.FILENAME_WORLDMAP_HTMLFILE_SUFFIX); } - private static boolean shouldDisplayMapOnWorldmap(String mapName) { - File f = WorldMapController.getFileForMap(mapName); - return f.exists(); - } private static String getWorldMapSegmentAsHtml(Resources res, WorldContext world, String segmentName) { WorldMapSegment segment = world.maps.worldMapSegments.get(segmentName); - Collection displayedMapNames = new HashSet(); + Map displayedMapFilenamesPerMapName = new HashMap(segment.maps.size()); Coord offsetWorldmapTo = new Coord(999999, 999999); for (WorldMapSegmentMap map : segment.maps.values()) { - if (!shouldDisplayMapOnWorldmap(map.mapName)) continue; + PredefinedMap predefinedMap = world.maps.findPredefinedMap(map.mapName); + if (predefinedMap == null) continue; + if (!predefinedMap.visited) continue; + File f = WorldMapController.getFileForMap(predefinedMap); + if (!f.exists()) continue; + displayedMapFilenamesPerMapName.put(map.mapName, f); - displayedMapNames.add(map.mapName); offsetWorldmapTo.x = Math.min(offsetWorldmapTo.x, map.worldPosition.x); offsetWorldmapTo.y = Math.min(offsetWorldmapTo.y, map.worldPosition.y); } @@ -193,8 +201,8 @@ public final class WorldMapController { StringBuilder mapsAsHtml = new StringBuilder(1000); for (WorldMapSegmentMap segmentMap : segment.maps.values()) { - File f = WorldMapController.getFileForMap(segmentMap.mapName); - if (!f.exists()) continue; + File f = displayedMapFilenamesPerMapName.get(segmentMap.mapName); + if (f == null) continue; Size size = getMapSize(segmentMap, world); mapsAsHtml @@ -223,7 +231,7 @@ public final class WorldMapController { StringBuilder namedAreasAsHtml = new StringBuilder(500); for (NamedWorldMapArea area : segment.namedAreas.values()) { - CoordRect r = determineNamedAreaBoundary(area, segment, world, displayedMapNames); + CoordRect r = determineNamedAreaBoundary(area, segment, world, displayedMapFilenamesPerMapName.keySet()); if (r == null) continue; namedAreasAsHtml .append("
displayedMapNames) { + private static CoordRect determineNamedAreaBoundary(NamedWorldMapArea area, WorldMapSegment segment, WorldContext world, Set displayedMapNames) { Coord topLeft = null; Coord bottomRight = null; 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 658c5afe8..86ee8d55c 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/LayeredTileMap.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/LayeredTileMap.java @@ -18,6 +18,7 @@ public final class LayeredTileMap { private final Size size; public final MapSection currentLayout; + private String currentLayoutHash; public final ReplaceableMapSection[] replacements; public final String colorFilter; public final Collection usedTileIDs; @@ -32,6 +33,7 @@ public final class LayeredTileMap { this.replacements = replacements; this.colorFilter = colorFilter; this.usedTileIDs = usedTileIDs; + this.currentLayoutHash = currentLayout.calculateHash(); } public final boolean isWalkable(final Coord p) { @@ -89,7 +91,12 @@ public final class LayeredTileMap { }); } + public String getCurrentLayoutHash() { + return currentLayoutHash; + } + public void applyReplacement(ReplaceableMapSection replacement) { replacement.apply(currentLayout); + currentLayoutHash = currentLayout.calculateHash(); } } diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/MapCollection.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/MapCollection.java index d56649a8a..ded0dcba7 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/MapCollection.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/MapCollection.java @@ -17,6 +17,7 @@ import java.util.List; public final class MapCollection { private final HashMap predefinedMaps = new HashMap(); public final HashMap worldMapSegments = new HashMap(); + public boolean worldMapRequiresUpdate = true; public MapCollection() {} @@ -43,6 +44,7 @@ public final class MapCollection { for (PredefinedMap m : getAllMaps()) { m.reset(); } + worldMapRequiresUpdate = true; } public String getWorldMapSegmentNameForMap(String mapName) { diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/MapSection.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/MapSection.java index 77600cd56..91ae3e433 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/MapSection.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/MapSection.java @@ -1,5 +1,6 @@ package com.gpl.rpg.AndorsTrail.model.map; +import com.gpl.rpg.AndorsTrail.util.ByteUtils; import com.gpl.rpg.AndorsTrail.util.CoordRect; public final class MapSection { @@ -7,16 +8,19 @@ public final class MapSection { public final MapLayer layerObjects; public final MapLayer layerAbove; public final boolean[][] isWalkable; + private final byte[] layoutHash; public MapSection( MapLayer layerGround, MapLayer layerObjects, MapLayer layerAbove, - boolean[][] isWalkable) { + boolean[][] isWalkable, + byte[] layoutHash) { this.layerGround = layerGround; this.layerObjects = layerObjects; this.layerAbove = layerAbove; this.isWalkable = isWalkable; + this.layoutHash = layoutHash; } public void replaceLayerContentsWith(final MapSection replaceLayersWith, final CoordRect replacementArea) { @@ -30,6 +34,9 @@ public final class MapSection { System.arraycopy(replaceLayersWith.isWalkable[sx], 0, isWalkable[dx], dy, height); } } + for(int i = 0; i < layoutHash.length; ++i) { + layoutHash[i] ^= replaceLayersWith.layoutHash[i]; + } } private static void replaceTileLayerSection(MapLayer dest, MapLayer src, CoordRect area) { @@ -40,4 +47,8 @@ public final class MapSection { System.arraycopy(src.tiles[sx], 0, dest.tiles[dx], dy, height); } } + + public String calculateHash() { + return ByteUtils.toHexString(layoutHash); + } } 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 fb5568da8..d3e322d6f 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/PredefinedMap.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/PredefinedMap.java @@ -29,7 +29,7 @@ public final class PredefinedMap { public final ArrayList groundBags = new ArrayList(); public boolean visited = false; public long lastVisitTime = VISIT_RESET; - public int lastVisitVersion = 0; + public String lastSeenLayoutHash = ""; private final boolean isOutdoors; public final ArrayList splatters = new ArrayList(); @@ -136,7 +136,7 @@ public final class PredefinedMap { } groundBags.clear(); visited = false; - lastVisitVersion = 0; + lastSeenLayoutHash = ""; } public boolean isRecentlyVisited() { @@ -145,7 +145,6 @@ public final class PredefinedMap { } public void updateLastVisitTime() { lastVisitTime = System.currentTimeMillis(); - lastVisitVersion = AndorsTrailApplication.CURRENT_VERSION; } public void resetTemporaryData() { for(MonsterSpawnArea a : spawnAreas) { @@ -213,10 +212,13 @@ public final class PredefinedMap { lastVisitTime = src.readLong(); if (visited) { - if (fileversion <= 30) lastVisitVersion = 30; - else lastVisitVersion = src.readInt(); + if (fileversion > 30 && fileversion < 36) { + /*int lastVisitVersion = */src.readInt(); + } + 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); @@ -235,6 +237,8 @@ public final class PredefinedMap { } dest.writeBoolean(visited); dest.writeLong(lastVisitTime); - if (visited) dest.writeInt(lastVisitVersion); + if (visited) { + dest.writeUTF(lastSeenLayoutHash); + } } } diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/TMXMapFileParser.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/TMXMapFileParser.java index c60614eee..ae545414b 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/TMXMapFileParser.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/TMXMapFileParser.java @@ -1,19 +1,19 @@ package com.gpl.rpg.AndorsTrail.model.map; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.zip.GZIPInputStream; -import java.util.zip.InflaterInputStream; - -import org.xmlpull.v1.XmlPullParserException; - -import com.gpl.rpg.AndorsTrail.util.Base64; -import com.gpl.rpg.AndorsTrail.util.L; -import com.gpl.rpg.AndorsTrail.util.XmlResourceParserUtils; - import android.content.res.Resources; import android.content.res.XmlResourceParser; +import com.gpl.rpg.AndorsTrail.util.Base64; +import com.gpl.rpg.AndorsTrail.util.L; +import com.gpl.rpg.AndorsTrail.util.XmlResourceParserUtils; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.zip.GZIPInputStream; +import java.util.zip.InflaterInputStream; public final class TMXMapFileParser { public static TMXObjectMap readObjectMap(Resources r, int xmlResourceId, String name) { @@ -200,11 +200,17 @@ public final class TMXMapFileParser { for(int y = 0; y < layer.height; ++y) { for(int x = 0; x < layer.width; ++x, i += 4) { int gid = readIntLittleEndian(buffer, i); - //if (gid != 0) L.log(getHexString(buffer, i) + " -> " + gid); layer.gids[x][y] = gid; - //L.log("(" + x + "," + y + ") : " + layer.gids[x][y]); } } + + try { + MessageDigest digest = MessageDigest.getInstance("MD5"); + digest.update(buffer); + layer.layoutHash = digest.digest(); + } catch (NoSuchAlgorithmException e) { + throw new IOException("Failed to create layout hash for map layer " + layer.name); + } } private static TMXProperty readTMXProperty(XmlResourceParser xrp) { @@ -275,6 +281,7 @@ public final class TMXMapFileParser { public int width; public int height; public int[][] gids; + public byte[] layoutHash; } public static final class TMXObjectGroup { public String name; 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 c0f4e6dbb..e962e6db4 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/TMXMapTranslator.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/TMXMapTranslator.java @@ -1,5 +1,7 @@ package com.gpl.rpg.AndorsTrail.model.map; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.util.*; import com.gpl.rpg.AndorsTrail.AndorsTrailApplication; @@ -244,11 +246,12 @@ public final class TMXMapTranslator { HashSet usedTileIDs, SetOfLayerNames layerNames ) { - final MapLayer layerGround = transformMapLayer(findLayer(layersPerLayerName, layerNames.groundLayerName, srcMap.name), srcMap, tileCache, area, usedTileIDs); - final MapLayer layerObjects = transformMapLayer(findLayer(layersPerLayerName, layerNames.objectsLayerName, srcMap.name), srcMap, tileCache, area, usedTileIDs); - final MapLayer layerAbove = transformMapLayer(findLayer(layersPerLayerName, layerNames.aboveLayersName, srcMap.name), srcMap, tileCache, area, usedTileIDs); + 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); boolean[][] isWalkable = transformWalkableMapLayer(findLayer(layersPerLayerName, layerNames.walkableLayersName, srcMap.name), area); - return new MapSection(layerGround, layerObjects, layerAbove, isWalkable); + byte[] layoutHash = calculateLayoutHash(srcMap, layersPerLayerName, layerNames); + return new MapSection(layerGround, layerObjects, layerAbove, isWalkable, layoutHash); } private static TMXLayer findLayer(HashMap layersPerLayerName, String layerName, String mapName) { @@ -264,12 +267,14 @@ public final class TMXMapTranslator { } private static MapLayer transformMapLayer( - TMXLayer srcLayer, + HashMap layersPerLayerName, + String layerName, TMXLayerMap srcMap, TileCache tileCache, CoordRect area, HashSet usedTileIDs ) { + TMXLayer srcLayer = findLayer(layersPerLayerName, layerName, srcMap.name); if (srcLayer == null) return null; final MapLayer result = new MapLayer(area.size); Tile tile = new Tile(); @@ -305,6 +310,26 @@ public final class TMXMapTranslator { return isWalkable; } + private static byte[] calculateLayoutHash(TMXLayerMap map, HashMap layersPerLayerName, SetOfLayerNames layerNames) { + try { + MessageDigest digest = MessageDigest.getInstance("MD5"); + digestLayer(layersPerLayerName, layerNames.groundLayerName, map, digest); + digestLayer(layersPerLayerName, layerNames.objectsLayerName, map, digest); + digestLayer(layersPerLayerName, layerNames.aboveLayersName, map, digest); + return digest.digest(); + } catch (NoSuchAlgorithmException e) { + L.log("ERROR: Failed to create layout hash for map " + map.name + " : " + e.toString()); + } + return new byte[0]; + } + + private static void digestLayer(HashMap layersPerLayerName, String layerName, TMXLayerMap map, MessageDigest digest) { + TMXLayer srcLayer = findLayer(layersPerLayerName, layerName, map.name); + if (srcLayer == null) return; + if (srcLayer.layoutHash == null) return; + digest.update(srcLayer.layoutHash); + } + private static boolean getTile(final TMXLayerMap map, final int gid, final Tile dest) { for(int i = map.tileSets.length - 1; i >= 0; --i) { TMXTileSet ts = map.tileSets[i]; diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/util/ByteUtils.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/util/ByteUtils.java new file mode 100644 index 000000000..724a3c276 --- /dev/null +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/util/ByteUtils.java @@ -0,0 +1,15 @@ +package com.gpl.rpg.AndorsTrail.util; + +public final class ByteUtils { + public static String toHexString(byte[] bytes) { + if (bytes == null) return ""; + if (bytes.length == 0) return ""; + StringBuffer result = new StringBuffer(); + for (int i = 0; i < bytes.length; i++) { + String h = Integer.toHexString(0xFF & bytes[i]); + if (h.length() < 2) result.append('0'); + result.append(h); + } + return result.toString(); + } +}