Merge branch 'dynamic-layeredtilemap'

This commit is contained in:
Oskar Wiksten
2013-06-22 16:31:48 +02:00
21 changed files with 560 additions and 326 deletions

View File

@@ -1,125 +1,124 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE map SYSTEM "http://mapeditor.org/dtd/1.0/map.dtd">
<map version="1.0" orientation="orthogonal" width="30" height="30" tilewidth="32" tileheight="32">
<tileset firstgid="1" name="map_bed_1" tilewidth="32" tileheight="32">
<image source="../drawable/map_bed_1.png"/>
<image source="../drawable/map_bed_1.png" width="512" height="256"/>
</tileset>
<tileset firstgid="129" name="map_border_1" tilewidth="32" tileheight="32">
<image source="../drawable/map_border_1.png"/>
<image source="../drawable/map_border_1.png" width="512" height="256"/>
</tileset>
<tileset firstgid="257" name="map_bridge_1" tilewidth="32" tileheight="32">
<image source="../drawable/map_bridge_1.png"/>
<image source="../drawable/map_bridge_1.png" width="512" height="256"/>
</tileset>
<tileset firstgid="385" name="map_broken_1" tilewidth="32" tileheight="32">
<image source="../drawable/map_broken_1.png"/>
<image source="../drawable/map_broken_1.png" width="512" height="256"/>
</tileset>
<tileset firstgid="513" name="map_cavewall_1" tilewidth="32" tileheight="32">
<image source="../drawable/map_cavewall_1.png"/>
<image source="../drawable/map_cavewall_1.png" width="576" height="192"/>
</tileset>
<tileset firstgid="621" name="map_cavewall_2" tilewidth="32" tileheight="32">
<image source="../drawable/map_cavewall_2.png"/>
<image source="../drawable/map_cavewall_2.png" width="576" height="192"/>
</tileset>
<tileset firstgid="729" name="map_cavewall_3" tilewidth="32" tileheight="32">
<image source="../drawable/map_cavewall_3.png"/>
<image source="../drawable/map_cavewall_3.png" width="576" height="192"/>
</tileset>
<tileset firstgid="837" name="map_chair_table_1" tilewidth="32" tileheight="32">
<image source="../drawable/map_chair_table_1.png"/>
<image source="../drawable/map_chair_table_1.png" width="512" height="256"/>
</tileset>
<tileset firstgid="965" name="map_crate_1" tilewidth="32" tileheight="32">
<image source="../drawable/map_crate_1.png"/>
<image source="../drawable/map_crate_1.png" width="512" height="256"/>
</tileset>
<tileset firstgid="1093" name="map_cupboard_1" tilewidth="32" tileheight="32">
<image source="../drawable/map_cupboard_1.png"/>
<image source="../drawable/map_cupboard_1.png" width="512" height="256"/>
</tileset>
<tileset firstgid="1221" name="map_curtain_1" tilewidth="32" tileheight="32">
<image source="../drawable/map_curtain_1.png"/>
<image source="../drawable/map_curtain_1.png" width="512" height="256"/>
</tileset>
<tileset firstgid="1349" name="map_entrance_1" tilewidth="32" tileheight="32">
<image source="../drawable/map_entrance_1.png"/>
<image source="../drawable/map_entrance_1.png" width="512" height="256"/>
</tileset>
<tileset firstgid="1477" name="map_fence_1" tilewidth="32" tileheight="32">
<image source="../drawable/map_fence_1.png"/>
<image source="../drawable/map_fence_1.png" width="512" height="256"/>
</tileset>
<tileset firstgid="1605" name="map_fence_2" tilewidth="32" tileheight="32">
<image source="../drawable/map_fence_2.png"/>
<image source="../drawable/map_fence_2.png" width="512" height="256"/>
</tileset>
<tileset firstgid="1733" name="map_ground_1" tilewidth="32" tileheight="32">
<image source="../drawable/map_ground_1.png"/>
<image source="../drawable/map_ground_1.png" width="512" height="256"/>
</tileset>
<tileset firstgid="1861" name="map_ground_2" tilewidth="32" tileheight="32">
<image source="../drawable/map_ground_2.png"/>
<image source="../drawable/map_ground_2.png" width="512" height="256"/>
</tileset>
<tileset firstgid="1989" name="map_ground_3" tilewidth="32" tileheight="32">
<image source="../drawable/map_ground_3.png"/>
<image source="../drawable/map_ground_3.png" width="512" height="256"/>
</tileset>
<tileset firstgid="2117" name="map_ground_4" tilewidth="32" tileheight="32">
<image source="../drawable/map_ground_4.png"/>
<image source="../drawable/map_ground_4.png" width="512" height="256"/>
</tileset>
<tileset firstgid="2245" name="map_ground_5" tilewidth="32" tileheight="32">
<image source="../drawable/map_ground_5.png"/>
<image source="../drawable/map_ground_5.png" width="512" height="256"/>
</tileset>
<tileset firstgid="2373" name="map_ground_6" tilewidth="32" tileheight="32">
<image source="../drawable/map_ground_6.png"/>
<image source="../drawable/map_ground_6.png" width="512" height="256"/>
</tileset>
<tileset firstgid="2501" name="map_ground_7" tilewidth="32" tileheight="32">
<image source="../drawable/map_ground_7.png"/>
<image source="../drawable/map_ground_7.png" width="512" height="256"/>
</tileset>
<tileset firstgid="2629" name="map_ground_8" tilewidth="32" tileheight="32">
<image source="../drawable/map_ground_8.png"/>
<image source="../drawable/map_ground_8.png" width="512" height="256"/>
</tileset>
<tileset firstgid="2757" name="map_indoor_1" tilewidth="32" tileheight="32">
<image source="../drawable/map_indoor_1.png"/>
<image source="../drawable/map_indoor_1.png" width="512" height="256"/>
</tileset>
<tileset firstgid="2885" name="map_kitchen_1" tilewidth="32" tileheight="32">
<image source="../drawable/map_kitchen_1.png"/>
<image source="../drawable/map_kitchen_1.png" width="512" height="256"/>
</tileset>
<tileset firstgid="3013" name="map_outdoor_1" tilewidth="32" tileheight="32">
<image source="../drawable/map_outdoor_1.png"/>
<image source="../drawable/map_outdoor_1.png" width="512" height="256"/>
</tileset>
<tileset firstgid="3141" name="map_pillar_1" tilewidth="32" tileheight="32">
<image source="../drawable/map_pillar_1.png"/>
<image source="../drawable/map_pillar_1.png" width="512" height="256"/>
</tileset>
<tileset firstgid="3269" name="map_plant_1" tilewidth="32" tileheight="32">
<image source="../drawable/map_plant_1.png"/>
<image source="../drawable/map_plant_1.png" width="512" height="256"/>
</tileset>
<tileset firstgid="3397" name="map_rock_1" tilewidth="32" tileheight="32">
<image source="../drawable/map_rock_1.png"/>
<image source="../drawable/map_rock_1.png" width="512" height="256"/>
</tileset>
<tileset firstgid="3525" name="map_roof_1" tilewidth="32" tileheight="32">
<image source="../drawable/map_roof_1.png"/>
<image source="../drawable/map_roof_1.png" width="512" height="256"/>
</tileset>
<tileset firstgid="3653" name="map_roof_2" tilewidth="32" tileheight="32">
<image source="../drawable/map_roof_2.png"/>
<image source="../drawable/map_roof_2.png" width="512" height="256"/>
</tileset>
<tileset firstgid="3781" name="map_shop_1" tilewidth="32" tileheight="32">
<image source="../drawable/map_shop_1.png"/>
<image source="../drawable/map_shop_1.png" width="512" height="256"/>
</tileset>
<tileset firstgid="3909" name="map_sign_ladder_1" tilewidth="32" tileheight="32">
<image source="../drawable/map_sign_ladder_1.png"/>
<image source="../drawable/map_sign_ladder_1.png" width="512" height="256"/>
</tileset>
<tileset firstgid="4037" name="map_table_1" tilewidth="32" tileheight="32">
<image source="../drawable/map_table_1.png"/>
<image source="../drawable/map_table_1.png" width="512" height="256"/>
</tileset>
<tileset firstgid="4165" name="map_trail_1" tilewidth="32" tileheight="32">
<image source="../drawable/map_trail_1.png"/>
<image source="../drawable/map_trail_1.png" width="512" height="256"/>
</tileset>
<tileset firstgid="4293" name="map_tree_1" tilewidth="32" tileheight="32">
<image source="../drawable/map_tree_1.png"/>
<image source="../drawable/map_tree_1.png" width="512" height="256"/>
</tileset>
<tileset firstgid="4421" name="map_wall_1" tilewidth="32" tileheight="32">
<image source="../drawable/map_wall_1.png"/>
<image source="../drawable/map_wall_1.png" width="512" height="256"/>
</tileset>
<tileset firstgid="4549" name="map_wall_2" tilewidth="32" tileheight="32">
<image source="../drawable/map_wall_2.png"/>
<image source="../drawable/map_wall_2.png" width="480" height="256"/>
</tileset>
<tileset firstgid="4669" name="map_wall_3" tilewidth="32" tileheight="32">
<image source="../drawable/map_wall_3.png"/>
<image source="../drawable/map_wall_3.png" width="480" height="256"/>
</tileset>
<tileset firstgid="4789" name="map_window_1" tilewidth="32" tileheight="32">
<image source="../drawable/map_window_1.png"/>
<image source="../drawable/map_window_1.png" width="512" height="256"/>
</tileset>
<tileset firstgid="4917" name="map_window_2" tilewidth="32" tileheight="32">
<image source="../drawable/map_window_2.png"/>
<image source="../drawable/map_window_2.png" width="512" height="256"/>
</tileset>
<layer name="Ground" width="30" height="30">
<data encoding="base64" compression="zlib">
@@ -144,4 +143,5 @@
<objectgroup name="Mapevents" width="30" height="30"/>
<objectgroup name="Spawn" width="30" height="30"/>
<objectgroup name="Keys" width="30" height="30"/>
<objectgroup name="Replace" width="30" height="30"/>
</map>

View File

@@ -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();

View File

@@ -121,6 +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(), true);
break;
case INTENTREQUEST_SAVEGAME:
if (resultCode != Activity.RESULT_OK) break;
@@ -138,15 +139,26 @@ public final class MainActivity extends Activity implements PlayerMovementListen
final Player player = world.model.player;
return Savegames.saveWorld(world, this, slot, getString(R.string.savegame_currenthero_displayinfo, player.getLevel(), player.getTotalExperience(), player.getGold()));
}
@Override
protected void onStart() {
super.onStart();
if (!AndorsTrailApplication.getApplicationFromActivity(this).getWorldSetup().isSceneReady) return;
subscribeToModelChanges();
}
@Override
protected void onStop() {
super.onStop();
unsubscribeFromModel();
}
@Override
protected void onPause() {
super.onPause();
controllers.gameRoundController.pause();
controllers.movementController.stopMovement();
unsubscribeFromModel();
save(Savegames.SLOT_QUICKSAVE);
}
@@ -155,8 +167,6 @@ public final class MainActivity extends Activity implements PlayerMovementListen
super.onResume();
if (!AndorsTrailApplication.getApplicationFromActivity(this).getWorldSetup().isSceneReady) return;
subscribeToModelChanges();
controllers.gameRoundController.resume();
if (world.model.uiSelections.isInCombat) {

View File

@@ -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;

View File

@@ -6,7 +6,6 @@ import java.util.Collection;
import com.gpl.rpg.AndorsTrail.AndorsTrailPreferences;
import com.gpl.rpg.AndorsTrail.context.ControllerContext;
import com.gpl.rpg.AndorsTrail.context.WorldContext;
import com.gpl.rpg.AndorsTrail.controller.listeners.LootBagListeners;
import com.gpl.rpg.AndorsTrail.controller.listeners.QuickSlotListeners;
import com.gpl.rpg.AndorsTrail.model.ModelContainer;
import com.gpl.rpg.AndorsTrail.model.ability.SkillCollection;
@@ -23,8 +22,7 @@ public final class ItemController {
private final ControllerContext controllers;
private final WorldContext world;
public final QuickSlotListeners quickSlotListeners = new QuickSlotListeners();
public final LootBagListeners lootBagListeners = new LootBagListeners();
public final QuickSlotListeners quickSlotListeners = new QuickSlotListeners();
public ItemController(ControllerContext controllers, WorldContext world) {
this.controllers = controllers;
@@ -213,7 +211,7 @@ public final class ItemController {
if (loot.hasItems()) return false;
world.model.currentMap.removeGroundLoot(loot);
lootBagListeners.onLootBagRemoved(world.model.currentMap, loot.position);
controllers.mapController.mapLayoutListeners.onLootBagRemoved(world.model.currentMap, loot.position);
return true; // The bag was removed.
}

View File

@@ -1,13 +1,17 @@
package com.gpl.rpg.AndorsTrail.controller;
import android.content.res.Resources;
import com.gpl.rpg.AndorsTrail.context.ControllerContext;
import com.gpl.rpg.AndorsTrail.context.WorldContext;
import com.gpl.rpg.AndorsTrail.controller.listeners.MapLayoutListeners;
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.PredefinedMap;
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;
public final class MapController {
@@ -15,6 +19,7 @@ public final class MapController {
private final ControllerContext controllers;
private final WorldContext world;
public final WorldEventListeners worldEventListeners = new WorldEventListeners();
public final MapLayoutListeners mapLayoutListeners = new MapLayoutListeners();
public MapController(ControllerContext controllers, WorldContext world) {
this.controllers = controllers;
@@ -106,4 +111,31 @@ public final class MapController {
m.resetTemporaryData();
}
}
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(PredefinedMap map, LayeredTileMap tileMap) {
boolean hasUpdated = false;
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;
}
public boolean satisfiesCondition(ReplaceableMapSection replacement) {
return world.model.player.hasExactQuestProgress(replacement.requireQuestStage);
}
}

View File

@@ -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,10 +100,12 @@ public final class MovementController implements TimedMessageTask.Callback {
controllers.monsterSpawnController.spawnAll(newMap, model.currentTileMap);
}
}
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() {
@@ -294,8 +297,6 @@ public final class MovementController implements TimedMessageTask.Callback {
world.model.currentTileMap = mapTiles;
world.tileManager.currentMapTiles = cachedTiles;
world.tileManager.cacheAdjacentMaps(res, world, nextMap);
WorldMapController.updateWorldMap(world, nextMap, mapTiles, cachedTiles, res);
}

View File

@@ -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;
@@ -38,14 +34,22 @@ 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 PredefinedMap map, final LayeredTileMap mapTiles, final TileCollection cachedTiles, 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);
}
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)) return;
if (!shouldUpdateWorldMap(map, worldMapSegmentName, world.maps.worldMapRequiresUpdate)) return;
(new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... arg0) {
@@ -53,24 +57,27 @@ 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);
}
} catch (IOException e) {
L.log("Error creating worldmap file for map " + map.name + " : " + e.toString());
}
return null;
return null;
}
}).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;
}
@@ -78,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);
@@ -88,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 {
@@ -114,15 +130,15 @@ public final class WorldMapController {
canvas.scale(scale, scale);
synchronized (cachedTiles) {
drawMapLayer(canvas, mapTiles.layers[LayeredTileMap.LAYER_GROUND]);
tryDrawMapLayer(canvas, LayeredTileMap.LAYER_OBJECTS);
tryDrawMapLayer(canvas, LayeredTileMap.LAYER_ABOVE);
drawMapLayer(canvas, mapTiles.currentLayout.layerGround);
tryDrawMapLayer(canvas, mapTiles.currentLayout.layerObjects);
tryDrawMapLayer(canvas, mapTiles.currentLayout.layerAbove);
}
return image;
}
private void tryDrawMapLayer(Canvas canvas, final int layerIndex) {
if (mapTiles.layers.length > layerIndex) drawMapLayer(canvas, mapTiles.layers[layerIndex]);
private void tryDrawMapLayer(Canvas canvas, final MapLayer layer) {
if (layer != null) drawMapLayer(canvas, layer);
}
private void drawMapLayer(Canvas canvas, final MapLayer layer) {
@@ -144,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");
}
@@ -161,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);
}
@@ -182,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
@@ -212,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 ")
@@ -244,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;

View File

@@ -1,26 +0,0 @@
package com.gpl.rpg.AndorsTrail.controller.listeners;
import com.gpl.rpg.AndorsTrail.util.ListOfListeners;
import com.gpl.rpg.AndorsTrail.model.map.PredefinedMap;
import com.gpl.rpg.AndorsTrail.util.Coord;
public final class LootBagListeners extends ListOfListeners<LootBagListener> implements LootBagListener {
private final Function2<LootBagListener, PredefinedMap, Coord> onLootBagCreated = new Function2<LootBagListener, PredefinedMap, Coord>() {
@Override public void call(LootBagListener listener, PredefinedMap map, Coord p) { listener.onLootBagCreated(map, p); }
};
private final Function2<LootBagListener, PredefinedMap, Coord> onLootBagRemoved = new Function2<LootBagListener, PredefinedMap, Coord>() {
@Override public void call(LootBagListener listener, PredefinedMap map, Coord p) { listener.onLootBagRemoved(map, p); }
};
@Override
public void onLootBagCreated(PredefinedMap map, Coord p) {
callAllListeners(this.onLootBagCreated, map, p);
}
@Override
public void onLootBagRemoved(PredefinedMap map, Coord p) {
callAllListeners(this.onLootBagRemoved, map, p);
}
}

View File

@@ -1,9 +1,11 @@
package com.gpl.rpg.AndorsTrail.controller.listeners;
import com.gpl.rpg.AndorsTrail.model.map.LayeredTileMap;
import com.gpl.rpg.AndorsTrail.model.map.PredefinedMap;
import com.gpl.rpg.AndorsTrail.util.Coord;
public interface LootBagListener {
public interface MapLayoutListener {
void onLootBagCreated(PredefinedMap map, Coord p);
void onLootBagRemoved(PredefinedMap map, Coord p);
void onMapTilesChanged(PredefinedMap map, LayeredTileMap tileMap);
}

View File

@@ -0,0 +1,36 @@
package com.gpl.rpg.AndorsTrail.controller.listeners;
import com.gpl.rpg.AndorsTrail.model.map.LayeredTileMap;
import com.gpl.rpg.AndorsTrail.util.ListOfListeners;
import com.gpl.rpg.AndorsTrail.model.map.PredefinedMap;
import com.gpl.rpg.AndorsTrail.util.Coord;
public final class MapLayoutListeners extends ListOfListeners<MapLayoutListener> implements MapLayoutListener {
private final Function2<MapLayoutListener, PredefinedMap, Coord> onLootBagCreated = new Function2<MapLayoutListener, PredefinedMap, Coord>() {
@Override public void call(MapLayoutListener listener, PredefinedMap map, Coord p) { listener.onLootBagCreated(map, p); }
};
private final Function2<MapLayoutListener, PredefinedMap, Coord> onLootBagRemoved = new Function2<MapLayoutListener, PredefinedMap, Coord>() {
@Override public void call(MapLayoutListener listener, PredefinedMap map, Coord p) { listener.onLootBagRemoved(map, p); }
};
private final Function2<MapLayoutListener, PredefinedMap, LayeredTileMap> onMapTilesChanged = new Function2<MapLayoutListener, PredefinedMap, LayeredTileMap>() {
@Override public void call(MapLayoutListener listener, PredefinedMap map, LayeredTileMap tileMap) { listener.onMapTilesChanged(map, tileMap); }
};
@Override
public void onLootBagCreated(PredefinedMap map, Coord p) {
callAllListeners(this.onLootBagCreated, map, p);
}
@Override
public void onLootBagRemoved(PredefinedMap map, Coord p) {
callAllListeners(this.onLootBagRemoved, map, p);
}
@Override
public void onMapTilesChanged(PredefinedMap map, LayeredTileMap tileMap) {
callAllListeners(this.onMapTilesChanged, map, tileMap);
}
}

View File

@@ -11,46 +11,38 @@ import com.gpl.rpg.AndorsTrail.util.CoordRect;
import com.gpl.rpg.AndorsTrail.util.Size;
public final class LayeredTileMap {
public static final int LAYER_GROUND = 0;
public static final int LAYER_OBJECTS = 1;
public static final int LAYER_ABOVE = 2;
private static final ColorFilter colorFilterBlack20 = createGrayScaleColorFilter(0.2f);
private static final ColorFilter colorFilterBlack40 = createGrayScaleColorFilter(0.4f);
private static final ColorFilter colorFilterBlack60 = createGrayScaleColorFilter(0.6f);
private static final ColorFilter colorFilterBlack80 = createGrayScaleColorFilter(0.8f);
private final Size size;
public final MapLayer[] layers;
public final Collection<Integer> usedTileIDs;
private final boolean[][] isWalkable;
public final MapSection currentLayout;
private String currentLayoutHash;
public final ReplaceableMapSection[] replacements;
public final String colorFilter;
public final Collection<Integer> usedTileIDs;
public LayeredTileMap(
Size size,
MapLayer[] layers,
boolean[][] isWalkable,
Collection<Integer> usedTileIDs,
String colorFilter) {
assert(size.width > 0);
assert(size.height > 0);
assert(layers.length == 3);
assert(isWalkable.length == size.width);
assert(isWalkable[0].length == size.height);
MapSection layout,
ReplaceableMapSection[] replacements,
String colorFilter,
Collection<Integer> usedTileIDs) {
this.size = size;
this.layers = layers;
this.usedTileIDs = usedTileIDs;
this.isWalkable = isWalkable;
this.currentLayout = layout;
this.replacements = replacements;
this.colorFilter = colorFilter;
this.usedTileIDs = usedTileIDs;
this.currentLayoutHash = currentLayout.calculateHash();
}
public final boolean isWalkable(final Coord p) {
if (isOutside(p.x, p.y)) return false;
return isWalkable[p.x][p.y];
return currentLayout.isWalkable[p.x][p.y];
}
public final boolean isWalkable(final int x, final int y) {
if (isOutside(x, y)) return false;
return isWalkable[x][y];
return currentLayout.isWalkable[x][y];
}
public final boolean isWalkable(final CoordRect p) {
for (int y = 0; y < p.size.height; ++y) {
@@ -98,4 +90,13 @@ public final class LayeredTileMap {
0.00f, 0.00f, 0.00f, 1.0f, 0.0f
});
}
public String getCurrentLayoutHash() {
return currentLayoutHash;
}
public void applyReplacement(ReplaceableMapSection replacement) {
replacement.apply(currentLayout);
currentLayoutHash = currentLayout.calculateHash();
}
}

View File

@@ -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) {

View File

@@ -4,27 +4,12 @@ import com.gpl.rpg.AndorsTrail.util.Coord;
import com.gpl.rpg.AndorsTrail.util.Size;
public final class MapLayer {
private final Size size;
public final int[][] tiles;
public MapLayer(Size size) {
this.size = size;
tiles = new int[size.width][size.height];
}
public void setTile(int type, int x, int y) {
tiles[x][y] = type;
}
public void setTile(int type, Coord p) {
setTile(type, p.x, p.y);
}
public final boolean isOutside(int x, 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(Coord p) {
return isOutside(p.x, p.y);
}
}

View File

@@ -0,0 +1,54 @@
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 {
public final MapLayer layerGround;
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,
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) {
replaceTileLayerSection(layerGround, replaceLayersWith.layerGround, replacementArea);
replaceTileLayerSection(layerObjects, replaceLayersWith.layerObjects, replacementArea);
replaceTileLayerSection(layerAbove, replaceLayersWith.layerAbove, replacementArea);
if (replaceLayersWith.isWalkable != null) {
final int dy = replacementArea.topLeft.y;
final int height = replacementArea.size.height;
for (int sx = 0, dx = replacementArea.topLeft.x; sx < replacementArea.size.width; ++sx, ++dx) {
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) {
if (src == null) return;
final int dy = area.topLeft.y;
final int height = area.size.height;
for (int sx = 0, dx = area.topLeft.x; sx < area.size.width; ++sx, ++dx) {
System.arraycopy(src.tiles[sx], 0, dest.tiles[dx], dy, height);
}
}
public String calculateHash() {
return ByteUtils.toHexString(layoutHash, 4);
}
}

View File

@@ -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);
}
}
}

View File

@@ -0,0 +1,25 @@
package com.gpl.rpg.AndorsTrail.model.map;
import com.gpl.rpg.AndorsTrail.model.quest.QuestProgress;
import com.gpl.rpg.AndorsTrail.util.CoordRect;
public final class ReplaceableMapSection {
public boolean isApplied = false;
public final CoordRect replacementArea;
public final MapSection replaceLayersWith;
public final QuestProgress requireQuestStage;
public ReplaceableMapSection(
CoordRect replacementArea,
MapSection replaceLayersWith,
QuestProgress requireQuestStage) {
this.replacementArea = replacementArea;
this.replaceLayersWith = replaceLayersWith;
this.requireQuestStage = requireQuestStage;
}
public void apply(MapSection dest) {
dest.replaceLayerContentsWith(replaceLayersWith, replacementArea);
isApplied = true;
}
}

View File

@@ -1,31 +1,31 @@
package com.gpl.rpg.AndorsTrail.model.map;
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;
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;
public final class TMXMapFileParser {
public static TMXMap read(Resources r, int xmlResourceId, String name) {
return read(r.getXml(xmlResourceId), xmlResourceId, name);
public static TMXObjectMap readObjectMap(Resources r, int xmlResourceId, String name) {
return readObjectMap(r.getXml(xmlResourceId), xmlResourceId, name);
}
public static TMXLayerMap readLayeredTileMap(Resources r, int xmlResourceId, String name) {
public static TMXLayerMap readLayerMap(Resources r, int xmlResourceId, String name) {
return readLayerMap(r.getXml(xmlResourceId), name);
}
private static TMXMap read(XmlResourceParser xrp, int xmlResourceId, String name) {
final TMXMap map = new TMXMap();
private static TMXObjectMap readObjectMap(XmlResourceParser xrp, int xmlResourceId, String name) {
final TMXObjectMap map = new TMXObjectMap();
map.xmlResourceId = xmlResourceId;
try {
// Map format: http://sourceforge.net/apps/mediawiki/tiled/index.php?title=Examining_the_map_format
@@ -35,7 +35,6 @@ public final class TMXMapFileParser {
String s = xrp.getName();
if (s.equals("map")) {
map.name = name;
map.orientation = xrp.getAttributeValue(null, "orientation");
map.width = xrp.getAttributeIntValue(null, "width", -1);
map.height = xrp.getAttributeIntValue(null, "height", -1);
map.tilewidth = xrp.getAttributeIntValue(null, "tilewidth", -1);
@@ -72,8 +71,11 @@ public final class TMXMapFileParser {
if (eventType == XmlResourceParser.START_TAG) {
String s = xrp.getName();
if (s.equals("map")) {
map.name = name;
map.width = xrp.getAttributeIntValue(null, "width", -1);
map.height = xrp.getAttributeIntValue(null, "height", -1);
map.tilewidth = xrp.getAttributeIntValue(null, "tilewidth", -1);
map.tileheight = xrp.getAttributeIntValue(null, "tileheight", -1);
XmlResourceParserUtils.readCurrentTagUntilEnd(xrp, new XmlResourceParserUtils.TagHandler() {
public void handleTag(XmlResourceParser xrp, String tagName) throws XmlPullParserException, IOException {
if (tagName.equals("tileset")) {
@@ -82,6 +84,8 @@ public final class TMXMapFileParser {
layers.add(readTMXMapLayer(xrp));
} else if (tagName.equals("property")) {
map.properties.add(readTMXProperty(xrp));
} else if (tagName.equals("objectgroup")) {
map.objectGroups.add(readTMXObjectGroup(xrp));
}
}
});
@@ -196,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) {
@@ -241,19 +251,21 @@ public final class TMXMapFileParser {
(buffer[offset + 3] << 24) & 0xff000000;
}
public static final class TMXMap extends TMXLayerMap {
public static final class TMXObjectMap extends TMXMap {
public int xmlResourceId;
public String name;
public String orientation;
public int tilewidth;
public int tileheight;
public final ArrayList<TMXObjectGroup> objectGroups = new ArrayList<TMXObjectGroup>();
}
public static class TMXLayerMap {
public int width;
public int height;
public static final class TMXLayerMap extends TMXMap {
public TMXTileSet[] tileSets;
public TMXLayer[] layers;
public final ArrayList<TMXObjectGroup> objectGroups = new ArrayList<TMXObjectGroup>();
}
public static class TMXMap {
public String name;
public int width;
public int height;
public int tilewidth;
public int tileheight;
public final ArrayList<TMXProperty> properties = new ArrayList<TMXProperty>();
}
public static final class TMXTileSet {
@@ -269,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;

View File

@@ -1,8 +1,8 @@
package com.gpl.rpg.AndorsTrail.model.map;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import com.gpl.rpg.AndorsTrail.AndorsTrailApplication;
import com.gpl.rpg.AndorsTrail.model.actor.MonsterType;
@@ -12,12 +12,12 @@ import com.gpl.rpg.AndorsTrail.model.item.DropListCollection;
import com.gpl.rpg.AndorsTrail.model.map.TMXMapFileParser.TMXLayer;
import com.gpl.rpg.AndorsTrail.model.map.TMXMapFileParser.TMXLayerMap;
import com.gpl.rpg.AndorsTrail.model.map.TMXMapFileParser.TMXMap;
import com.gpl.rpg.AndorsTrail.model.map.TMXMapFileParser.TMXObjectMap;
import com.gpl.rpg.AndorsTrail.model.map.TMXMapFileParser.TMXObject;
import com.gpl.rpg.AndorsTrail.model.map.TMXMapFileParser.TMXObjectGroup;
import com.gpl.rpg.AndorsTrail.model.map.TMXMapFileParser.TMXProperty;
import com.gpl.rpg.AndorsTrail.model.map.TMXMapFileParser.TMXTileSet;
import com.gpl.rpg.AndorsTrail.model.quest.QuestProgress;
import com.gpl.rpg.AndorsTrail.resource.DynamicTileLoader;
import com.gpl.rpg.AndorsTrail.resource.tiles.TileCache;
import com.gpl.rpg.AndorsTrail.util.Coord;
import com.gpl.rpg.AndorsTrail.util.CoordRect;
@@ -28,25 +28,24 @@ import com.gpl.rpg.AndorsTrail.util.Size;
import android.content.res.Resources;
public final class TMXMapTranslator {
private final ArrayList<TMXMap> maps = new ArrayList<TMXMap>();
private final ArrayList<TMXObjectMap> maps = new ArrayList<TMXObjectMap>();
public void read(Resources r, int xmlResourceId, String name) {
maps.add(TMXMapFileParser.read(r, xmlResourceId, name));
maps.add(TMXMapFileParser.readObjectMap(r, xmlResourceId, name));
}
public static LayeredTileMap readLayeredTileMap(Resources res, TileCache tileCache, PredefinedMap map) {
TMXLayerMap resultMap = TMXMapFileParser.readLayeredTileMap(res, map.xmlResourceId, map.name);
return transformMap(resultMap, tileCache, map.name);
TMXLayerMap resultMap = TMXMapFileParser.readLayerMap(res, map.xmlResourceId, map.name);
return transformMap(resultMap, tileCache);
}
public ArrayList<PredefinedMap> transformMaps(MonsterTypeCollection monsterTypes, DropListCollection dropLists) {
return transformMaps(maps, monsterTypes, dropLists);
}
public ArrayList<PredefinedMap> transformMaps(Collection<TMXMap> maps, MonsterTypeCollection monsterTypes, DropListCollection dropLists) {
public ArrayList<PredefinedMap> transformMaps(Collection<TMXObjectMap> maps, MonsterTypeCollection monsterTypes, DropListCollection dropLists) {
ArrayList<PredefinedMap> result = new ArrayList<PredefinedMap>();
Tile tile = new Tile();
for (TMXMap m : maps) {
for (TMXObjectMap m : maps) {
assert(m.name != null);
assert(m.name.length() > 0);
assert(m.width > 0);
@@ -64,13 +63,7 @@ public final class TMXMapTranslator {
for (TMXObjectGroup group : m.objectGroups) {
for (TMXObject object : group.objects) {
final Coord topLeft = new Coord(
Math.round(((float)object.x) / m.tilewidth)
,Math.round(((float)object.y) / m.tileheight)
);
final int width = Math.round(((float)object.width) / m.tilewidth);
final int height = Math.round(((float)object.height) / m.tileheight);
final CoordRect position = new CoordRect(topLeft, new Size(width, height));
final CoordRect position = getTMXObjectPosition(object, m);
if (object.type == null) {
if (AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA)
@@ -148,6 +141,8 @@ public final class TMXMapTranslator {
DropList dropList = dropLists.getDropList(object.name);
if (dropList == null) continue;
mapObjects.add(MapObject.createNewContainerArea(position, dropList));
} else if (object.type.equals("replace")) {
// Do nothing. Will be handled when reading map layers instead.
} else if (AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA) {
L.log("OPTIMIZE: Map " + m.name + ", has unrecognized object type \"" + object.type + "\" for name \"" + object.name + "\".");
}
@@ -163,66 +158,178 @@ public final class TMXMapTranslator {
return result;
}
private static LayeredTileMap transformMap(TMXLayerMap map, TileCache tileCache, String mapName) {
private static CoordRect getTMXObjectPosition(TMXObject object, TMXMap m) {
final Coord topLeft = new Coord(
Math.round(((float)object.x) / m.tilewidth)
,Math.round(((float)object.y) / m.tileheight)
);
final int width = Math.round(((float)object.width) / m.tilewidth);
final int height = Math.round(((float)object.height) / m.tileheight);
return new CoordRect(topLeft, new Size(width, height));
}
private static final String LAYERNAME_GROUND = "ground";
private static final String LAYERNAME_OBJECTS = "objects";
private static final String LAYERNAME_ABOVE = "above";
private static final String LAYERNAME_WALKABLE = "walkable";
private static final SetOfLayerNames defaultLayerNames = new SetOfLayerNames(LAYERNAME_GROUND, LAYERNAME_OBJECTS, LAYERNAME_ABOVE, LAYERNAME_WALKABLE);
private static LayeredTileMap transformMap(TMXLayerMap map, TileCache tileCache) {
final Size mapSize = new Size(map.width, map.height);
final MapLayer[] layers = new MapLayer[] {
new MapLayer(mapSize)
,new MapLayer(mapSize)
,new MapLayer(mapSize)
};
boolean[][] isWalkable = new boolean[map.width][map.height];
for (int y = 0; y < map.height; ++y) {
for (int x = 0; x < map.width; ++x) {
isWalkable[x][y] = true;
}
}
Tile tile = new Tile();
String colorFilter = null;
for (TMXProperty prop : map.properties) {
if (prop.name.equalsIgnoreCase("colorfilter")) colorFilter = prop.value;
}
HashSet<Integer> usedTileIDs = new HashSet<Integer>();
HashMap<String, TMXLayer> layersPerLayerName = new HashMap<String, TMXLayer>();
for (TMXLayer layer : map.layers) {
int ixMapLayer = 0;
String layerName = layer.name;
assert(layerName != null);
assert(layerName.length() > 0);
layerName = layerName.toLowerCase();
boolean isWalkableLayer = false;
if (layerName.startsWith("object")) {
ixMapLayer = LayeredTileMap.LAYER_OBJECTS;
} else if (layerName.startsWith("ground")) {
ixMapLayer = LayeredTileMap.LAYER_GROUND;
} else if (layerName.startsWith("above")) {
ixMapLayer = LayeredTileMap.LAYER_ABOVE;
} else if (layerName.startsWith("walk")) {
isWalkableLayer = true;
} else {
continue;
if (AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA) {
if (layersPerLayerName.containsKey(layerName)) {
L.log("WARNING: Map \"" + map.name + "\" contains multiple layers with name \"" + layerName + "\".");
}
}
for (int y = 0; y < layer.height; ++y) {
for (int x = 0; x < layer.width; ++x) {
int gid = layer.gids[x][y];
if (gid <= 0) continue;
if (!getTile(map, gid, tile)) continue;
layersPerLayerName.put(layerName, layer);
}
if (isWalkableLayer) {
isWalkable[x][y] = false;
} else {
int tileID = tileCache.getTileID(tile.tilesetName, tile.localId);
layers[ixMapLayer].tiles[x][y] = tileID;
usedTileIDs.add(tileID);
MapSection defaultLayout = transformMapSection(map,
tileCache,
new CoordRect(new Coord(0,0), mapSize),
layersPerLayerName,
usedTileIDs,
defaultLayerNames);
ArrayList<ReplaceableMapSection> replaceableSections = new ArrayList<ReplaceableMapSection>();
for (TMXObjectGroup objectGroup : map.objectGroups) {
for(TMXObject obj : objectGroup.objects) {
if ("replace".equals(obj.type)) {
final CoordRect position = getTMXObjectPosition(obj, map);
SetOfLayerNames layerNames = new SetOfLayerNames();
for (TMXProperty prop : obj.properties) {
if (prop.name.equalsIgnoreCase(LAYERNAME_GROUND)) layerNames.groundLayerName = prop.value;
else if (prop.name.equalsIgnoreCase(LAYERNAME_OBJECTS)) layerNames.objectsLayerName = prop.value;
else if (prop.name.equalsIgnoreCase(LAYERNAME_ABOVE)) layerNames.aboveLayersName = prop.value;
else if (prop.name.equalsIgnoreCase(LAYERNAME_WALKABLE)) layerNames.walkableLayersName = prop.value;
else if (AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA) {
L.log("OPTIMIZE: Map " + map.name + " contains replace area with unknown property \"" + prop.name + "\".");
}
}
MapSection replacementSection = transformMapSection(map, tileCache, position, layersPerLayerName, usedTileIDs, layerNames);
QuestProgress requireQuestStage = QuestProgress.parseQuestProgress(obj.name);
if (requireQuestStage == null) {
if (AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA) {
L.log("OPTIMIZE: Map " + map.name + " contains replace area that cannot be parsed as a quest stage.");
}
continue;
}
replaceableSections.add(new ReplaceableMapSection(position, replacementSection, requireQuestStage));
}
}
}
return new LayeredTileMap(mapSize, layers, isWalkable, usedTileIDs, colorFilter);
ReplaceableMapSection[] replaceableSections_ = null;
if (!replaceableSections.isEmpty()) {
replaceableSections_ = replaceableSections.toArray(new ReplaceableMapSection[replaceableSections.size()]);
}
return new LayeredTileMap(mapSize, defaultLayout, replaceableSections_, colorFilter, usedTileIDs);
}
private static MapSection transformMapSection(
TMXLayerMap srcMap,
TileCache tileCache,
CoordRect area,
HashMap<String, TMXLayer> layersPerLayerName,
HashSet<Integer> 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);
boolean[][] isWalkable = transformWalkableMapLayer(findLayer(layersPerLayerName, layerNames.walkableLayersName, srcMap.name), area);
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) {
if (layerName == null) return null;
if (layerName.length() == 0) return null;
TMXLayer result = layersPerLayerName.get(layerName);
if (AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA) {
if (result == null) {
L.log("WARNING: Cannot find maplayer \"" + layerName + "\" requested by map \"" + mapName + "\".");
}
}
return result;
}
private static MapLayer transformMapLayer(
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();
for (int dy = 0, sy = area.topLeft.y; dy < area.size.height; ++dy, ++sy) {
for (int dx = 0, sx = area.topLeft.x; dx < area.size.width; ++dx, ++sx) {
int gid = srcLayer.gids[sx][sy];
if (gid <= 0) continue;
if (!getTile(srcMap, gid, tile)) continue;
int tileID = tileCache.getTileID(tile.tilesetName, tile.localId);
result.tiles[dx][dy] = tileID;
usedTileIDs.add(tileID);
}
}
return result;
}
private static boolean[][] transformWalkableMapLayer(TMXLayer srcLayer, CoordRect area) {
if (srcLayer == null) return null;
final boolean[][] isWalkable = new boolean[area.size.width][area.size.height];
for (int x = 0; x < area.size.width; ++x) {
Arrays.fill(isWalkable[x], true);
}
for (int dy = 0, sy = area.topLeft.y; dy < area.size.height; ++dy, ++sy) {
for (int dx = 0, sx = area.topLeft.x; dx < area.size.width; ++dx, ++sx) {
int gid = srcLayer.gids[sx][sy];
if (gid > 0) {
isWalkable[dx][dy] = false;
}
}
}
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];
@@ -232,7 +339,7 @@ public final class TMXMapTranslator {
return true;
}
}
L.log("WARNING: Cannot find tile for gid " + gid); //(" + x + ", " + y + "), ")
L.log("WARNING: Cannot find tile for gid " + gid);
return false;
}
@@ -240,73 +347,23 @@ public final class TMXMapTranslator {
public String tilesetName;
public int localId;
}
/*
public static final class TMXMap extends TMXLayerMap {
public int xmlResourceId;
public String name;
public String orientation;
public int tilewidth;
public int tileheight;
public ArrayList<TMXObjectGroup> objectGroups = new ArrayList<TMXObjectGroup>();
}
public static class TMXLayerMap {
public int width;
public int height;
public TMXTileSet[] tileSets;
public TMXLayer[] layers;
}
public static final class TMXTileSet {
public int firstgid;
public String name;
public int tilewidth;
public int tileheight;
public String imageSource;
public String imageName;
}
public static final class TMXLayer {
public String name;
public int width;
public int height;
public int[][] gids;
}
public static final class TMXObjectGroup {
public String name;
public int width;
public int height;
public ArrayList<TMXObject> objects = new ArrayList<TMXObject>();
}
public static final class TMXObject {
public String name;
public String type;
public int x;
public int y;
public int width;
public int height;
public ArrayList<TMXProperty> properties = new ArrayList<TMXProperty>();
}
public static final class TMXProperty {
public String name;
public String value;
}
*/
/*
<map version="1.0" orientation="orthogonal" width="10" height="10" tilewidth="32" tileheight="32">
<tileset firstgid="1" name="tiles" tilewidth="32" tileheight="32">
<image source="tilesets/tiles.png"/>
</tileset>
<layer name="Tile Layer 1" width="10" height="10">
<data encoding="base64" compression="gzip">
H4sIAAAAAAAAA/NgYGDwIBK7AbEnHkyOOmwYXR02MwZSHQyTah4xGADnAt2SkAEAAA==
</data>
</layer>
<layer name="Tile Layer 2" width="10" height="10">
<data encoding="base64" compression="gzip">
H4sIAAAAAAAAA2NgoA1gYUHlP2HGro6NBbt4MysqXw2oLhEqlgSlU4H0YjR12EAbUE0KFnXPgG5iRLJ/GQ6zHuNwOy7gxE6aemQAAJRT7VKQAQAA
</data>
</layer>
</map>
*/
private static final class SetOfLayerNames {
public String groundLayerName;
public String objectsLayerName;
public String aboveLayersName;
public String walkableLayersName;
public SetOfLayerNames() {
this.groundLayerName = null;
this.objectsLayerName = null;
this.aboveLayersName = null;
this.walkableLayersName = null;
}
public SetOfLayerNames(String groundLayerName, String objectsLayerName, String aboveLayersName, String walkableLayersName) {
this.groundLayerName = groundLayerName;
this.objectsLayerName = objectsLayerName;
this.aboveLayersName = aboveLayersName;
this.walkableLayersName = walkableLayersName;
}
}
}

View File

@@ -0,0 +1,16 @@
package com.gpl.rpg.AndorsTrail.util;
public final class ByteUtils {
public static String toHexString(byte[] bytes) { return toHexString(bytes, bytes.length); }
public static String toHexString(byte[] bytes, int numBytes) {
if (bytes == null) return "";
if (bytes.length == 0) return "";
StringBuffer result = new StringBuffer();
for (int i = 0; i < Math.min(numBytes, bytes.length); i++) {
String h = Integer.toHexString(0xFF & bytes[i]);
if (h.length() < 2) result.append('0');
result.append(h);
}
return result.toString();
}
}

View File

@@ -38,7 +38,7 @@ public final class MainView extends SurfaceView
CombatSelectionListener,
MonsterSpawnListener,
MonsterMovementListener,
LootBagListener,
MapLayoutListener,
VisualEffectFrameListener,
GameRoundListener {
@@ -276,8 +276,8 @@ public final class MainView extends SurfaceView
private void doDrawRect(Canvas canvas, CoordRect area) {
drawMapLayer(canvas, area, currentTileMap.layers[LayeredTileMap.LAYER_GROUND]);
tryDrawMapLayer(canvas, area, LayeredTileMap.LAYER_OBJECTS);
drawMapLayer(canvas, area, currentTileMap.currentLayout.layerGround);
tryDrawMapLayer(canvas, area, currentTileMap.currentLayout.layerObjects);
for (BloodSplatter splatter : currentMap.splatters) {
drawFromMapPosition(canvas, area, splatter.position, splatter.iconID);
@@ -296,7 +296,7 @@ public final class MainView extends SurfaceView
}
}
tryDrawMapLayer(canvas, area, LayeredTileMap.LAYER_ABOVE);
tryDrawMapLayer(canvas, area, currentTileMap.currentLayout.layerAbove);
if (model.uiSelections.selectedPosition != null) {
if (model.uiSelections.selectedMonster != null) {
@@ -307,8 +307,8 @@ public final class MainView extends SurfaceView
}
}
private void tryDrawMapLayer(Canvas canvas, final CoordRect area, final int layerIndex) {
if (currentTileMap.layers.length > layerIndex) drawMapLayer(canvas, area, currentTileMap.layers[layerIndex]);
private void tryDrawMapLayer(Canvas canvas, final CoordRect area, final MapLayer layer) {
if (layer != null) drawMapLayer(canvas, area, layer);
}
private void drawMapLayer(Canvas canvas, final CoordRect area, final MapLayer layer) {
@@ -401,7 +401,7 @@ public final class MainView extends SurfaceView
public void subscribe() {
controllers.gameRoundController.gameRoundListeners.add(this);
controllers.effectController.visualEffectFrameListeners.add(this);
controllers.itemController.lootBagListeners.add(this);
controllers.mapController.mapLayoutListeners.add(this);
controllers.movementController.playerMovementListeners.add(this);
controllers.combatController.combatSelectionListeners.add(this);
controllers.monsterSpawnController.monsterSpawnListeners.add(this);
@@ -412,7 +412,7 @@ public final class MainView extends SurfaceView
controllers.monsterSpawnController.monsterSpawnListeners.remove(this);
controllers.combatController.combatSelectionListeners.remove(this);
controllers.movementController.playerMovementListeners.remove(this);
controllers.itemController.lootBagListeners.remove(this);
controllers.mapController.mapLayoutListeners.remove(this);
controllers.effectController.visualEffectFrameListeners.remove(this);
controllers.gameRoundController.gameRoundListeners.remove(this);
}
@@ -494,6 +494,12 @@ public final class MainView extends SurfaceView
redrawTile(p, REDRAW_TILE_BAG);
}
@Override
public void onMapTilesChanged(PredefinedMap map, LayeredTileMap tileMap) {
if (map != currentMap) return;
redrawAll(REDRAW_ALL_MAP_CHANGED);
}
@Override
public void onNewAnimationFrame(VisualEffectAnimation animation, int tileID, int textYOffset) {
redrawAreaWithEffect(animation, tileID, textYOffset);