First attempt at dynamic map tiles that change based on quest progress.

This commit is contained in:
Oskar Wiksten
2013-06-22 12:38:49 +02:00
parent 0a9da53930
commit c34614f09b
15 changed files with 390 additions and 239 deletions

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

@@ -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,20 +1,23 @@
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.MapObject;
import com.gpl.rpg.AndorsTrail.model.map.*;
import com.gpl.rpg.AndorsTrail.util.Coord;
import com.gpl.rpg.AndorsTrail.util.CoordRect;
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 +109,30 @@ public final class MapController {
m.resetTemporaryData();
}
}
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;
mapLayoutListeners.onMapTilesChanged(world.model.currentMap, world.model.currentTileMap);
}
private boolean applyReplacements(LayeredTileMap map) {
if (map.replacements == null) return false;
boolean hasUpdated = false;
for(ReplaceableMapSection replacement : map.replacements) {
if (replacement.isApplied) continue;
if (!satisfiesCondition(replacement)) continue;
map.applyReplacement(replacement);
hasUpdated = true;
}
return hasUpdated;
}
public boolean satisfiesCondition(ReplaceableMapSection replacement) {
return world.model.player.hasExactQuestProgress(replacement.requireQuestStage);
}
}

View File

@@ -99,6 +99,8 @@ public final class MovementController implements TimedMessageTask.Callback {
controllers.monsterSpawnController.spawnAll(newMap, model.currentTileMap);
}
}
controllers.mapController.applyCurrentMapReplacements(res);
WorldMapController.updateWorldMapForCurrentMap(world, res);
newMap.visited = true;
moveBlockedActors(newMap, model.currentTileMap);
refreshMonsterAggressiveness(newMap, model.player);
@@ -294,8 +296,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

@@ -38,14 +38,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 updateWorldMapForCurrentMap(final WorldContext world, final Resources res) {
updateWorldMap(world, world.model.currentMap, world.model.currentTileMap, world.tileManager.currentMapTiles, res);
}
public 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;
(new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... arg0) {
@@ -53,10 +61,13 @@ public final class WorldMapController {
try {
updateCachedBitmap(map, renderer);
updateWorldMapSegment(res, world, worldMapSegmentName);
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();
}
@@ -114,15 +125,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) {

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,36 @@ 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;
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;
}
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 +88,8 @@ public final class LayeredTileMap {
0.00f, 0.00f, 0.00f, 1.0f, 0.0f
});
}
public void applyReplacement(ReplaceableMapSection replacement) {
replacement.apply(currentLayout);
}
}

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,43 @@
package com.gpl.rpg.AndorsTrail.model.map;
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;
public MapSection(
MapLayer layerGround,
MapLayer layerObjects,
MapLayer layerAbove,
boolean[][] isWalkable) {
this.layerGround = layerGround;
this.layerObjects = layerObjects;
this.layerAbove = layerAbove;
this.isWalkable = isWalkable;
}
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);
}
}
}
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);
}
}
}

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

@@ -16,16 +16,16 @@ 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));
}
}
});
@@ -241,19 +245,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 {

View File

@@ -1,8 +1,6 @@
package com.gpl.rpg.AndorsTrail.model.map;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.*;
import com.gpl.rpg.AndorsTrail.AndorsTrailApplication;
import com.gpl.rpg.AndorsTrail.model.actor.MonsterType;
@@ -12,12 +10,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 +26,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 +61,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 +139,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 +156,155 @@ 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(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);
boolean[][] isWalkable = transformWalkableMapLayer(findLayer(layersPerLayerName, layerNames.walkableLayersName, srcMap.name), area);
return new MapSection(layerGround, layerObjects, layerAbove, isWalkable);
}
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(
TMXLayer srcLayer,
TMXLayerMap srcMap,
TileCache tileCache,
CoordRect area,
HashSet<Integer> usedTileIDs
) {
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 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 +314,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 +322,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

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