mirror of
https://github.com/OMGeeky/andors-trail.git
synced 2026-02-23 15:38:29 +01:00
Worldmap: PNG file names now contain the hash of the tiles that the map contains.
* This way, when a map has some replacements applied, the hash will change, thus making it possible to have several different PNG files for each map, each with its own combination of replacements. * Only display maps on worldmap that are visited by the current player. * Update worldmap after replacements have been applied.
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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<Void, Void, Void>() {
|
||||
@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<String> displayedMapNames = new HashSet<String>();
|
||||
Map<String, File> displayedMapFilenamesPerMapName = new HashMap<String, File>(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("<div class=\"namedarea ")
|
||||
@@ -255,7 +263,7 @@ public final class WorldMapController {
|
||||
return world.maps.findPredefinedMap(map.mapName).size;
|
||||
}
|
||||
|
||||
private static CoordRect determineNamedAreaBoundary(NamedWorldMapArea area, WorldMapSegment segment, WorldContext world, Collection<String> displayedMapNames) {
|
||||
private static CoordRect determineNamedAreaBoundary(NamedWorldMapArea area, WorldMapSegment segment, WorldContext world, Set<String> displayedMapNames) {
|
||||
Coord topLeft = null;
|
||||
Coord bottomRight = null;
|
||||
|
||||
|
||||
@@ -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<Integer> 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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import java.util.List;
|
||||
public final class MapCollection {
|
||||
private final HashMap<String, PredefinedMap> predefinedMaps = new HashMap<String, PredefinedMap>();
|
||||
public final HashMap<String, WorldMapSegment> worldMapSegments = new HashMap<String, WorldMapSegment>();
|
||||
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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ public final class PredefinedMap {
|
||||
public final ArrayList<Loot> groundBags = new ArrayList<Loot>();
|
||||
public boolean visited = false;
|
||||
public long lastVisitTime = VISIT_RESET;
|
||||
public int lastVisitVersion = 0;
|
||||
public String lastSeenLayoutHash = "";
|
||||
private final boolean isOutdoors;
|
||||
|
||||
public final ArrayList<BloodSplatter> splatters = new ArrayList<BloodSplatter>();
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<Integer> 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<String, TMXLayer> layersPerLayerName, String layerName, String mapName) {
|
||||
@@ -264,12 +267,14 @@ public final class TMXMapTranslator {
|
||||
}
|
||||
|
||||
private static MapLayer transformMapLayer(
|
||||
TMXLayer srcLayer,
|
||||
HashMap<String, TMXLayer> layersPerLayerName,
|
||||
String layerName,
|
||||
TMXLayerMap srcMap,
|
||||
TileCache tileCache,
|
||||
CoordRect area,
|
||||
HashSet<Integer> 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<String, TMXLayer> 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<String, TMXLayer> 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];
|
||||
|
||||
15
AndorsTrail/src/com/gpl/rpg/AndorsTrail/util/ByteUtils.java
Normal file
15
AndorsTrail/src/com/gpl/rpg/AndorsTrail/util/ByteUtils.java
Normal file
@@ -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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user