Hopefully fixed the issue with the loss of old worldmap tiles.

This commit is contained in:
Zukero
2018-09-07 18:01:20 +02:00
parent 84ef943864
commit 897a2e0939
4 changed files with 738 additions and 737 deletions

View File

@@ -22,7 +22,7 @@ public final class AndorsTrailApplication extends Application {
public static final boolean DEVELOPMENT_DEBUGRESOURCES = false;
public static final boolean DEVELOPMENT_FORCE_STARTNEWGAME = false;
public static final boolean DEVELOPMENT_FORCE_CONTINUEGAME = false;
public static final boolean DEVELOPMENT_DEBUGBUTTONS = true;
public static final boolean DEVELOPMENT_DEBUGBUTTONS = false;
public static final boolean DEVELOPMENT_FASTSPEED = false;
public static final boolean DEVELOPMENT_VALIDATEDATA = true;
public static final boolean DEVELOPMENT_DEBUGMESSAGES = true;

View File

@@ -1,203 +1,203 @@
package com.gpl.rpg.AndorsTrail.model.map;
import java.util.Collection;
import android.graphics.ColorFilter;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
import com.gpl.rpg.AndorsTrail.util.Coord;
import com.gpl.rpg.AndorsTrail.util.CoordRect;
import com.gpl.rpg.AndorsTrail.util.Size;
public final class LayeredTileMap {
private static final ColorFilter colorFilterBlack20 = createGrayScaleColorFilter(0.8f);
private static final ColorFilter colorFilterBlack40 = createGrayScaleColorFilter(0.6f);
private static final ColorFilter colorFilterBlack60 = createGrayScaleColorFilter(0.4f);
private static final ColorFilter colorFilterBlack80 = createGrayScaleColorFilter(0.2f);
private static final ColorFilter colorFilterInvert = createInvertColorFilter();
private static final ColorFilter colorFilterBW = createBWColorFilter();
private static final ColorFilter colorFilterRedTint = createRedTintColorFilter();
private static final ColorFilter colorFilterGreenTint = createGreenTintColorFilter();
private static final ColorFilter colorFilterBlueTint = createBlueTintColorFilter();
public enum ColorFilterId {
none,
black20,
black40,
black60,
black80,
invert,
bw,
redtint,
greentint,
bluetint
}
private final Size size;
public final MapSection currentLayout;
private String currentLayoutHash;
public final ReplaceableMapSection[] replacements;
public final ColorFilterId originalColorFilter;
public ColorFilterId colorFilter;
public final Collection<Integer> usedTileIDs;
public LayeredTileMap(
Size size
, MapSection layout
, ReplaceableMapSection[] replacements
, ColorFilterId colorFilter
, Collection<Integer> usedTileIDs
) {
this.size = size;
this.currentLayout = layout;
this.replacements = replacements;
this.originalColorFilter = colorFilter;
colorFilter = originalColorFilter;
this.usedTileIDs = usedTileIDs;
this.currentLayoutHash = currentLayout.calculateHash(colorFilter.name());
}
public final boolean isWalkable(final Coord p) {
if (isOutside(p.x, p.y)) return false;
return currentLayout.isWalkable[p.x][p.y];
}
public final boolean isWalkable(final int x, final int y) {
if (isOutside(x, y)) return false;
return currentLayout.isWalkable[x][y];
}
public final boolean isWalkable(final CoordRect p) {
for (int y = 0; y < p.size.height; ++y) {
for (int x = 0; x < p.size.width; ++x) {
if (!isWalkable(p.topLeft.x + x, p.topLeft.y + y)) return false;
}
}
return true;
}
public final boolean isOutside(final Coord p) { return isOutside(p.x, p.y); }
public final boolean isOutside(final int x, final int y) {
if (x < 0) return true;
if (y < 0) return true;
if (x >= size.width) return true;
if (y >= size.height) return true;
return false;
}
public final boolean isOutside(final CoordRect area) {
if (isOutside(area.topLeft)) return true;
if (area.topLeft.x + area.size.width > size.width) return true;
if (area.topLeft.y + area.size.height > size.height) return true;
return false;
}
public void setColorFilter(Paint mPaint) {
mPaint.setColorFilter(getColorFilter());
}
public ColorFilter getColorFilter() {
if (colorFilter == null) return null;
switch (colorFilter) {
case black20:
return colorFilterBlack20;
case black40:
return colorFilterBlack40;
case black60:
return colorFilterBlack60;
case black80:
return colorFilterBlack80;
case invert:
return colorFilterInvert;
case bw:
return colorFilterBW;
case redtint:
return colorFilterRedTint;
case greentint:
return colorFilterGreenTint;
case bluetint:
return colorFilterBlueTint;
default:
return null;
}
}
private static ColorMatrixColorFilter createGrayScaleColorFilter(float blackOpacity) {
final float f = blackOpacity;
return new ColorMatrixColorFilter(new float[] {
f, 0.00f, 0.00f, 0.0f, 0.0f,
0.00f, f, 0.00f, 0.0f, 0.0f,
0.00f, 0.00f, f, 0.0f, 0.0f,
0.00f, 0.00f, 0.00f, 1.0f, 0.0f
});
}
private static ColorMatrixColorFilter createInvertColorFilter() {
return new ColorMatrixColorFilter(new float[] {
-1.00f, 0.00f, 0.00f, 0.0f, 255.0f,
0.00f, -1.00f, 0.00f, 0.0f, 255.0f,
0.00f, 0.00f, -1.00f, 0.0f, 255.0f,
0.00f, 0.00f, 0.00f, 1.0f, 0.0f
});
}
private static ColorMatrixColorFilter createBWColorFilter() {
return new ColorMatrixColorFilter(new float[] {
0.33f, 0.59f, 0.11f, 0.0f, 0.0f,
0.33f, 0.59f, 0.11f, 0.0f, 0.0f,
0.33f, 0.59f, 0.11f, 0.0f, 0.0f,
0.00f, 0.00f, 0.00f, 1.0f, 0.0f
});
}
private static ColorMatrixColorFilter createRedTintColorFilter() {
return new ColorMatrixColorFilter(new float[] {
1.20f, 0.20f, 0.20f, 0.0f, 25.0f,
0.00f, 0.80f, 0.00f, 0.0f, 0.0f,
0.00f, 0.00f, 0.80f, 0.0f, 0.0f,
0.00f, 0.00f, 0.00f, 1.0f, 0.0f
});
}
private static ColorMatrixColorFilter createGreenTintColorFilter() {
return new ColorMatrixColorFilter(new float[] {
0.85f, 0.00f, 0.00f, 0.0f, 0.0f,
0.15f, 1.15f, 0.15f, 0.0f, 15.0f,
0.00f, 0.00f, 0.85f, 0.0f, 0.0f,
0.00f, 0.00f, 0.00f, 1.0f, 0.0f
});
}
private static ColorMatrixColorFilter createBlueTintColorFilter() {
return new ColorMatrixColorFilter(new float[] {
0.70f, 0.00f, 0.00f, 0.0f, 0.0f,
0.00f, 0.70f, 0.00f, 0.0f, 0.0f,
0.30f, 0.30f, 1.30f, 0.0f, 40.0f,
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(colorFilter.name());
}
public void changeColorFilter(ColorFilterId id) {
if (colorFilter == id) return;
colorFilter = id;
currentLayoutHash = currentLayout.calculateHash(colorFilter.name());
}
public void changeColorFilter(String idString) {
ColorFilterId id;
if (idString == null) id = originalColorFilter;
else id = ColorFilterId.valueOf(idString);
if (id != null) {
changeColorFilter(id);
}
}
}
package com.gpl.rpg.AndorsTrail.model.map;
import java.util.Collection;
import android.graphics.ColorFilter;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
import com.gpl.rpg.AndorsTrail.util.Coord;
import com.gpl.rpg.AndorsTrail.util.CoordRect;
import com.gpl.rpg.AndorsTrail.util.Size;
public final class LayeredTileMap {
private static final ColorFilter colorFilterBlack20 = createGrayScaleColorFilter(0.8f);
private static final ColorFilter colorFilterBlack40 = createGrayScaleColorFilter(0.6f);
private static final ColorFilter colorFilterBlack60 = createGrayScaleColorFilter(0.4f);
private static final ColorFilter colorFilterBlack80 = createGrayScaleColorFilter(0.2f);
private static final ColorFilter colorFilterInvert = createInvertColorFilter();
private static final ColorFilter colorFilterBW = createBWColorFilter();
private static final ColorFilter colorFilterRedTint = createRedTintColorFilter();
private static final ColorFilter colorFilterGreenTint = createGreenTintColorFilter();
private static final ColorFilter colorFilterBlueTint = createBlueTintColorFilter();
public enum ColorFilterId {
none,
black20,
black40,
black60,
black80,
invert,
bw,
redtint,
greentint,
bluetint
}
private final Size size;
public final MapSection currentLayout;
private String currentLayoutHash;
public final ReplaceableMapSection[] replacements;
public final ColorFilterId originalColorFilter;
public ColorFilterId colorFilter;
public final Collection<Integer> usedTileIDs;
public LayeredTileMap(
Size size
, MapSection layout
, ReplaceableMapSection[] replacements
, ColorFilterId colorFilter
, Collection<Integer> usedTileIDs
) {
this.size = size;
this.currentLayout = layout;
this.replacements = replacements;
this.originalColorFilter = colorFilter;
colorFilter = originalColorFilter;
this.usedTileIDs = usedTileIDs;
this.currentLayoutHash = currentLayout.calculateHash(colorFilter.name());
}
public final boolean isWalkable(final Coord p) {
if (isOutside(p.x, p.y)) return false;
return currentLayout.isWalkable[p.x][p.y];
}
public final boolean isWalkable(final int x, final int y) {
if (isOutside(x, y)) return false;
return currentLayout.isWalkable[x][y];
}
public final boolean isWalkable(final CoordRect p) {
for (int y = 0; y < p.size.height; ++y) {
for (int x = 0; x < p.size.width; ++x) {
if (!isWalkable(p.topLeft.x + x, p.topLeft.y + y)) return false;
}
}
return true;
}
public final boolean isOutside(final Coord p) { return isOutside(p.x, p.y); }
public final boolean isOutside(final int x, final int y) {
if (x < 0) return true;
if (y < 0) return true;
if (x >= size.width) return true;
if (y >= size.height) return true;
return false;
}
public final boolean isOutside(final CoordRect area) {
if (isOutside(area.topLeft)) return true;
if (area.topLeft.x + area.size.width > size.width) return true;
if (area.topLeft.y + area.size.height > size.height) return true;
return false;
}
public void setColorFilter(Paint mPaint) {
mPaint.setColorFilter(getColorFilter());
}
public ColorFilter getColorFilter() {
if (colorFilter == null) return null;
switch (colorFilter) {
case black20:
return colorFilterBlack20;
case black40:
return colorFilterBlack40;
case black60:
return colorFilterBlack60;
case black80:
return colorFilterBlack80;
case invert:
return colorFilterInvert;
case bw:
return colorFilterBW;
case redtint:
return colorFilterRedTint;
case greentint:
return colorFilterGreenTint;
case bluetint:
return colorFilterBlueTint;
default:
return null;
}
}
private static ColorMatrixColorFilter createGrayScaleColorFilter(float blackOpacity) {
final float f = blackOpacity;
return new ColorMatrixColorFilter(new float[] {
f, 0.00f, 0.00f, 0.0f, 0.0f,
0.00f, f, 0.00f, 0.0f, 0.0f,
0.00f, 0.00f, f, 0.0f, 0.0f,
0.00f, 0.00f, 0.00f, 1.0f, 0.0f
});
}
private static ColorMatrixColorFilter createInvertColorFilter() {
return new ColorMatrixColorFilter(new float[] {
-1.00f, 0.00f, 0.00f, 0.0f, 255.0f,
0.00f, -1.00f, 0.00f, 0.0f, 255.0f,
0.00f, 0.00f, -1.00f, 0.0f, 255.0f,
0.00f, 0.00f, 0.00f, 1.0f, 0.0f
});
}
private static ColorMatrixColorFilter createBWColorFilter() {
return new ColorMatrixColorFilter(new float[] {
0.33f, 0.59f, 0.11f, 0.0f, 0.0f,
0.33f, 0.59f, 0.11f, 0.0f, 0.0f,
0.33f, 0.59f, 0.11f, 0.0f, 0.0f,
0.00f, 0.00f, 0.00f, 1.0f, 0.0f
});
}
private static ColorMatrixColorFilter createRedTintColorFilter() {
return new ColorMatrixColorFilter(new float[] {
1.20f, 0.20f, 0.20f, 0.0f, 25.0f,
0.00f, 0.80f, 0.00f, 0.0f, 0.0f,
0.00f, 0.00f, 0.80f, 0.0f, 0.0f,
0.00f, 0.00f, 0.00f, 1.0f, 0.0f
});
}
private static ColorMatrixColorFilter createGreenTintColorFilter() {
return new ColorMatrixColorFilter(new float[] {
0.85f, 0.00f, 0.00f, 0.0f, 0.0f,
0.15f, 1.15f, 0.15f, 0.0f, 15.0f,
0.00f, 0.00f, 0.85f, 0.0f, 0.0f,
0.00f, 0.00f, 0.00f, 1.0f, 0.0f
});
}
private static ColorMatrixColorFilter createBlueTintColorFilter() {
return new ColorMatrixColorFilter(new float[] {
0.70f, 0.00f, 0.00f, 0.0f, 0.0f,
0.00f, 0.70f, 0.00f, 0.0f, 0.0f,
0.30f, 0.30f, 1.30f, 0.0f, 40.0f,
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(colorFilter == ColorFilterId.none ? null : colorFilter.name());
}
public void changeColorFilter(ColorFilterId id) {
if (colorFilter == id) return;
colorFilter = id;
currentLayoutHash = currentLayout.calculateHash(colorFilter == ColorFilterId.none ? null : colorFilter.name());
}
public void changeColorFilter(String idString) {
ColorFilterId id;
if (idString == null) id = originalColorFilter;
else id = ColorFilterId.valueOf(idString);
if (id != null) {
changeColorFilter(id);
}
}
}

View File

@@ -1,59 +1,59 @@
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 MapLayer layerTop;
public final boolean[][] isWalkable;
private final byte[] layoutHash;
public MapSection(
MapLayer layerGround
, MapLayer layerObjects
, MapLayer layerAbove
, MapLayer layerTop
, boolean[][] isWalkable
, byte[] layoutHash
) {
this.layerGround = layerGround;
this.layerObjects = layerObjects;
this.layerAbove = layerAbove;
this.layerTop = layerTop;
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);
replaceTileLayerSection(layerTop, replaceLayersWith.layerTop, 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);
}
}
ByteUtils.xorArray(layoutHash, replaceLayersWith.layoutHash);
}
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(String filter) {
byte[] hash = layoutHash.clone();
ByteUtils.xorArray(hash, filter.getBytes());
return ByteUtils.toHexString(hash, 4);
}
}
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 MapLayer layerTop;
public final boolean[][] isWalkable;
private final byte[] layoutHash;
public MapSection(
MapLayer layerGround
, MapLayer layerObjects
, MapLayer layerAbove
, MapLayer layerTop
, boolean[][] isWalkable
, byte[] layoutHash
) {
this.layerGround = layerGround;
this.layerObjects = layerObjects;
this.layerAbove = layerAbove;
this.layerTop = layerTop;
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);
replaceTileLayerSection(layerTop, replaceLayersWith.layerTop, 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);
}
}
ByteUtils.xorArray(layoutHash, replaceLayersWith.layoutHash);
}
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(String filter) {
byte[] hash = layoutHash.clone();
if (filter != null) ByteUtils.xorArray(hash, filter.getBytes());
return ByteUtils.toHexString(hash, 4);
}
}

View File

@@ -1,474 +1,475 @@
package com.gpl.rpg.AndorsTrail.model.map;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import android.content.res.Resources;
import com.gpl.rpg.AndorsTrail.AndorsTrailApplication;
import com.gpl.rpg.AndorsTrail.model.actor.MonsterType;
import com.gpl.rpg.AndorsTrail.model.actor.MonsterTypeCollection;
import com.gpl.rpg.AndorsTrail.model.item.DropList;
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.TMXObject;
import com.gpl.rpg.AndorsTrail.model.map.TMXMapFileParser.TMXObjectGroup;
import com.gpl.rpg.AndorsTrail.model.map.TMXMapFileParser.TMXObjectMap;
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.model.script.Requirement;
import com.gpl.rpg.AndorsTrail.resource.tiles.TileCache;
import com.gpl.rpg.AndorsTrail.util.Coord;
import com.gpl.rpg.AndorsTrail.util.CoordRect;
import com.gpl.rpg.AndorsTrail.util.L;
import com.gpl.rpg.AndorsTrail.util.Range;
import com.gpl.rpg.AndorsTrail.util.Size;
public final class TMXMapTranslator {
private final ArrayList<TMXObjectMap> maps = new ArrayList<TMXObjectMap>();
public void read(Resources r, int xmlResourceId, String name) {
maps.add(TMXMapFileParser.readObjectMap(r, xmlResourceId, name));
}
public static LayeredTileMap readLayeredTileMap(Resources res, TileCache tileCache, PredefinedMap map) {
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<TMXObjectMap> maps, MonsterTypeCollection monsterTypes, DropListCollection dropLists) {
ArrayList<PredefinedMap> result = new ArrayList<PredefinedMap>();
for (TMXObjectMap m : maps) {
assert(m.name != null);
assert(m.name.length() > 0);
assert(m.width > 0);
assert(m.height > 0);
boolean isOutdoors = false;
for (TMXProperty p : m.properties) {
if(p.name.equalsIgnoreCase("outdoors")) isOutdoors = (Integer.parseInt(p.value) != 0);
else if(AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA) L.log("OPTIMIZE: Map " + m.name + " has unrecognized property \"" + p.name + "\".");
}
final Size mapSize = new Size(m.width, m.height);
List<MapObject> mapObjects = new LinkedList<MapObject>();
List<MonsterSpawnArea> spawnAreas = new LinkedList<MonsterSpawnArea>();
List<String> activeGroups = new LinkedList<String>();
for (TMXObjectGroup group : m.objectGroups) {
boolean active = true;
for (TMXProperty p : group.properties) {
if (p.name.equalsIgnoreCase("active")) {
active = Boolean.parseBoolean(p.value);
} else if (AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA) {
L.log("OPTIMIZE: Map " + m.name + ", group " + group.name + " has unrecognized property \"" + p.name + "\".");
}
}
if (active) {
activeGroups.add(group.name);
}
for (TMXObject object : group.objects) {
final CoordRect position = getTMXObjectPosition(object, m);
final Coord topLeft = position.topLeft;
if (object.type == null) {
if (AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA)
L.log("WARNING: Map " + m.name + ", object \"" + object.name + "\"@" + topLeft.toString() + " has null type.");
} else if (object.type.equalsIgnoreCase("sign")) {
String phraseID = object.name;
for (TMXProperty p : object.properties) {
if (AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA) L.log("OPTIMIZE: Map " + m.name + ", sign " + object.name + "@" + topLeft.toString() + " has unrecognized property \"" + p.name + "\".");
}
mapObjects.add(MapObject.createMapSignEvent(position, phraseID, group.name));
} else if (object.type.equalsIgnoreCase("mapchange")) {
String map = null;
String place = null;
for (TMXProperty p : object.properties) {
if (p.name.equalsIgnoreCase("map")) {
map = p.value;
} else if (p.name.equalsIgnoreCase("place")) {
place = p.value;
} else if (AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA) {
L.log("OPTIMIZE: Map " + m.name + ", mapchange " + object.name + "@" + topLeft.toString() + " has unrecognized property \"" + p.name + "\".");
}
}
mapObjects.add(MapObject.createMapChangeArea(position, object.name, map, place, group.name));
} else if (object.type.equalsIgnoreCase("spawn")) {
boolean isActiveForNewGame = true;
boolean ignoreAreas = false;
int maxQuantity = 1;
int spawnChance = 10;
String spawnGroup = object.name;
for (TMXProperty p : object.properties) {
if (AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA) {
if (p.value.equals("")) {
L.log("OPTIMIZE: Map " + m.name + ", spawn " + object.name + "@" + topLeft.toString() + " has property \"" + p.name + "\" without value.");
continue;
}
}
if (p.name.equalsIgnoreCase("quantity")) {
maxQuantity = Integer.parseInt(p.value);
} else if (p.name.equalsIgnoreCase("spawnchance")) {
spawnChance = Integer.parseInt(p.value);
} else if (p.name.equalsIgnoreCase("active")) {
isActiveForNewGame = Boolean.parseBoolean(p.value);
} else if (p.name.equalsIgnoreCase("ignoreAreas")) {
ignoreAreas = Boolean.parseBoolean(p.value);
} else if (p.name.equalsIgnoreCase("spawngroup")) {
spawnGroup = p.value;
} else if (AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA) {
L.log("OPTIMIZE: Map " + m.name + ", spawn " + object.name + "@" + topLeft.toString() + " has unrecognized property \"" + p.name + "\".");
}
}
ArrayList<MonsterType> types = monsterTypes.getMonsterTypesFromSpawnGroup(spawnGroup);
if (types.isEmpty()) {
if (AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA) {
L.log("OPTIMIZE: Map " + m.name + " contains spawn \"" + object.name + "\"@" + topLeft.toString() + " that does not correspond to any monsters. The spawn will be removed.");
}
continue;
}
String[] monsterTypeIDs = new String[types.size()];
boolean isUnique = types.get(0).isUnique;
for (int i = 0; i < monsterTypeIDs.length; ++i) {
monsterTypeIDs[i] = types.get(i).id;
}
MonsterSpawnArea area = new MonsterSpawnArea(
position
,new Range(maxQuantity, 0)
,new Range(1000, spawnChance)
,object.name
,monsterTypeIDs
,isUnique
,ignoreAreas
,group.name
,isActiveForNewGame
);
spawnAreas.add(area);
} else if (object.type.equalsIgnoreCase("key")) {
String phraseID = "";
for (TMXProperty p : object.properties) {
if (p.name.equalsIgnoreCase("phrase")) {
phraseID = p.value;
} else if (AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA) {
if (!requirementPropertiesNames.contains(p.name.toLowerCase())) {
L.log("OPTIMIZE: Map " + m.name + ", key " + object.name + "@" + topLeft.toString() + " has unrecognized property \"" + p.name + "\".");
}
}
}
Requirement req = parseRequirement(object);
mapObjects.add(MapObject.createKeyArea(position, phraseID, req, group.name));
} else if (object.type.equals("rest")) {
mapObjects.add(MapObject.createRestArea(position, object.name, group.name));
} else if (object.type.equals("container")) {
DropList dropList = dropLists.getDropList(object.name);
if (dropList == null) continue;
mapObjects.add(MapObject.createContainerArea(position, dropList, group.name));
} else if (object.type.equals("replace")) {
// Do nothing. Will be handled when reading map layers instead.
} else if (object.type.equalsIgnoreCase("script")) {
String phraseID = object.name;
MapObject.MapObjectEvaluationType evaluateWhen = MapObject.MapObjectEvaluationType.whenEntering;
for (TMXProperty p : object.properties) {
if (p.name.equalsIgnoreCase("when")) {
if (p.value.equalsIgnoreCase("enter")) {
evaluateWhen = MapObject.MapObjectEvaluationType.whenEntering;
} else if (p.value.equalsIgnoreCase("step")) {
evaluateWhen = MapObject.MapObjectEvaluationType.onEveryStep;
} else if (p.value.equalsIgnoreCase("round")) {
evaluateWhen = MapObject.MapObjectEvaluationType.afterEveryRound;
} else if (p.value.equalsIgnoreCase("always")) {
evaluateWhen = MapObject.MapObjectEvaluationType.continuously;
} else if (AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA) {
L.log("OPTIMIZE: Map " + m.name + ", script " + object.name + "@" + topLeft.toString() + " has unrecognized value for \"when\" property: \"" + p.value + "\".");
}
} else if (AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA) {
L.log("OPTIMIZE: Map " + m.name + ", script " + object.name + "@" + topLeft.toString() + " has unrecognized property \"" + p.name + "\".");
}
}
mapObjects.add(MapObject.createScriptArea(position, phraseID, evaluateWhen, group.name));
} else if (AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA) {
L.log("OPTIMIZE: Map " + m.name + ", has unrecognized object type \"" + object.type + "\" for name \"" + object.name + "\".");
}
}
}
MapObject[] _eventObjects = new MapObject[mapObjects.size()];
_eventObjects = mapObjects.toArray(_eventObjects);
MonsterSpawnArea[] _spawnAreas = new MonsterSpawnArea[spawnAreas.size()];
_spawnAreas = spawnAreas.toArray(_spawnAreas);
result.add(new PredefinedMap(m.xmlResourceId, m.name, mapSize, _eventObjects, _spawnAreas, activeGroups, isOutdoors));
}
return result;
}
private static final List<String> requirementPropertiesNames = Arrays.asList(new String[]{"requireType".toLowerCase(), "requireId".toLowerCase(), "requireValue".toLowerCase(), "requireNegation".toLowerCase()});
private static Requirement parseRequirement(TMXObject object) {
Requirement.RequirementType requireType = Requirement.RequirementType.questProgress;
String requireId = null;
int requireValue = 0;
boolean requireNegation = false;
for (TMXProperty p : object.properties) {
if (p.name.equalsIgnoreCase("requireType")) {
try {
requireType = Requirement.RequirementType.valueOf(p.value);
} catch (IllegalArgumentException e) {
requireType = null;
if (AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA) {
L.log("OPTIMIZE: Unrecognized requirement type: "+p.value);
}
}
} else if (p.name.equalsIgnoreCase("requireId")) {
requireId = p.value;
} else if (p.name.equalsIgnoreCase("requireValue")) {
requireValue = Integer.parseInt(p.value);
} else if (p.name.equalsIgnoreCase("requireNegation")) {
requireNegation = Boolean.parseBoolean(p.value);
}
}
if (requireType == null) return null;
return new Requirement(requireType, requireId, requireValue, requireNegation);
}
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_TOP = "top";
private static final String LAYERNAME_WALKABLE = "walkable";
private static final String PROPNAME_FILTER = "colorfilter";
private static final SetOfLayerNames defaultLayerNames = new SetOfLayerNames(LAYERNAME_GROUND, LAYERNAME_OBJECTS, LAYERNAME_ABOVE, LAYERNAME_TOP, LAYERNAME_WALKABLE);
private static LayeredTileMap transformMap(TMXLayerMap map, TileCache tileCache) {
final Size mapSize = new Size(map.width, map.height);
LayeredTileMap.ColorFilterId colorFilter = LayeredTileMap.ColorFilterId.none;
for (TMXProperty prop : map.properties) {
if (prop.name.equalsIgnoreCase(PROPNAME_FILTER)) {
String filterId = prop.value;
if (filterId != null) {
colorFilter = LayeredTileMap.ColorFilterId.valueOf(filterId);
}
}
}
HashSet<Integer> usedTileIDs = new HashSet<Integer>();
HashMap<String, TMXLayer> layersPerLayerName = new HashMap<String, TMXLayer>();
for (TMXLayer layer : map.layers) {
String layerName = layer.name;
assert(layerName != null);
assert(layerName.length() > 0);
layerName = layerName.toLowerCase();
if (AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA) {
if (layersPerLayerName.containsKey(layerName)) {
L.log("WARNING: Map \"" + map.name + "\" contains multiple layers with name \"" + layerName + "\".");
}
}
layersPerLayerName.put(layerName, layer);
}
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_TOP)) layerNames.topLayersName = prop.value;
else if (prop.name.equalsIgnoreCase(LAYERNAME_WALKABLE)) layerNames.walkableLayersName = prop.value;
else if (AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA) {
if (!requirementPropertiesNames.contains(prop.name))
L.log("OPTIMIZE: Map " + map.name + " contains replace area with unknown property \"" + prop.name + "\".");
}
}
MapSection replacementSection = transformMapSection(map, tileCache, position, layersPerLayerName, usedTileIDs, layerNames);
Requirement req = parseRequirement(obj);
if (req == null || !req.isValid()) {
QuestProgress qp = QuestProgress.parseQuestProgress(obj.name);
if (qp != null) req = new Requirement(qp);
}
if (req == null || !req.isValid()) {
if (AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA) {
L.log("WARNING: Map " + map.name + " contains replace area "+obj.name+" with unparsable requirement");
}
continue;
}
replaceableSections.add(new ReplaceableMapSection(position, replacementSection, req, objectGroup.name));
}
}
}
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);
final MapLayer layerTop = transformMapLayer(layersPerLayerName, layerNames.topLayersName, 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, layerTop, 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.toLowerCase());
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];
if (ts.firstgid <= gid) {
dest.tilesetName = ts.name;
dest.localId = (gid - ts.firstgid);
return true;
}
}
L.log("WARNING: Cannot find tile for gid " + gid);
return false;
}
private static final class Tile {
public String tilesetName;
public int localId;
}
private static final class SetOfLayerNames {
public String groundLayerName;
public String objectsLayerName;
public String aboveLayersName;
public String topLayersName;
public String walkableLayersName;
public SetOfLayerNames() {
this.groundLayerName = null;
this.objectsLayerName = null;
this.aboveLayersName = null;
this.topLayersName = null;
this.walkableLayersName = null;
}
public SetOfLayerNames(String groundLayerName, String objectsLayerName, String aboveLayersName, String topLayersName, String walkableLayersName) {
this.groundLayerName = groundLayerName;
this.objectsLayerName = objectsLayerName;
this.aboveLayersName = aboveLayersName;
this.topLayersName = topLayersName;
this.walkableLayersName = walkableLayersName;
}
}
}
package com.gpl.rpg.AndorsTrail.model.map;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import android.content.res.Resources;
import com.gpl.rpg.AndorsTrail.AndorsTrailApplication;
import com.gpl.rpg.AndorsTrail.model.actor.MonsterType;
import com.gpl.rpg.AndorsTrail.model.actor.MonsterTypeCollection;
import com.gpl.rpg.AndorsTrail.model.item.DropList;
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.TMXObject;
import com.gpl.rpg.AndorsTrail.model.map.TMXMapFileParser.TMXObjectGroup;
import com.gpl.rpg.AndorsTrail.model.map.TMXMapFileParser.TMXObjectMap;
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.model.script.Requirement;
import com.gpl.rpg.AndorsTrail.resource.tiles.TileCache;
import com.gpl.rpg.AndorsTrail.util.Coord;
import com.gpl.rpg.AndorsTrail.util.CoordRect;
import com.gpl.rpg.AndorsTrail.util.L;
import com.gpl.rpg.AndorsTrail.util.Range;
import com.gpl.rpg.AndorsTrail.util.Size;
public final class TMXMapTranslator {
private final ArrayList<TMXObjectMap> maps = new ArrayList<TMXObjectMap>();
public void read(Resources r, int xmlResourceId, String name) {
maps.add(TMXMapFileParser.readObjectMap(r, xmlResourceId, name));
}
public static LayeredTileMap readLayeredTileMap(Resources res, TileCache tileCache, PredefinedMap map) {
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<TMXObjectMap> maps, MonsterTypeCollection monsterTypes, DropListCollection dropLists) {
ArrayList<PredefinedMap> result = new ArrayList<PredefinedMap>();
for (TMXObjectMap m : maps) {
assert(m.name != null);
assert(m.name.length() > 0);
assert(m.width > 0);
assert(m.height > 0);
boolean isOutdoors = false;
for (TMXProperty p : m.properties) {
if(p.name.equalsIgnoreCase("outdoors")) isOutdoors = (Integer.parseInt(p.value) != 0);
else if(AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA) L.log("OPTIMIZE: Map " + m.name + " has unrecognized property \"" + p.name + "\".");
}
final Size mapSize = new Size(m.width, m.height);
List<MapObject> mapObjects = new LinkedList<MapObject>();
List<MonsterSpawnArea> spawnAreas = new LinkedList<MonsterSpawnArea>();
List<String> activeGroups = new LinkedList<String>();
for (TMXObjectGroup group : m.objectGroups) {
boolean active = true;
for (TMXProperty p : group.properties) {
if (p.name.equalsIgnoreCase("active")) {
active = Boolean.parseBoolean(p.value);
} else if (AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA) {
L.log("OPTIMIZE: Map " + m.name + ", group " + group.name + " has unrecognized property \"" + p.name + "\".");
}
}
if (active) {
activeGroups.add(group.name);
}
for (TMXObject object : group.objects) {
final CoordRect position = getTMXObjectPosition(object, m);
final Coord topLeft = position.topLeft;
if (object.type == null) {
if (AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA)
L.log("WARNING: Map " + m.name + ", object \"" + object.name + "\"@" + topLeft.toString() + " has null type.");
} else if (object.type.equalsIgnoreCase("sign")) {
String phraseID = object.name;
for (TMXProperty p : object.properties) {
if (AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA) L.log("OPTIMIZE: Map " + m.name + ", sign " + object.name + "@" + topLeft.toString() + " has unrecognized property \"" + p.name + "\".");
}
mapObjects.add(MapObject.createMapSignEvent(position, phraseID, group.name));
} else if (object.type.equalsIgnoreCase("mapchange")) {
String map = null;
String place = null;
for (TMXProperty p : object.properties) {
if (p.name.equalsIgnoreCase("map")) {
map = p.value;
} else if (p.name.equalsIgnoreCase("place")) {
place = p.value;
} else if (AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA) {
L.log("OPTIMIZE: Map " + m.name + ", mapchange " + object.name + "@" + topLeft.toString() + " has unrecognized property \"" + p.name + "\".");
}
}
mapObjects.add(MapObject.createMapChangeArea(position, object.name, map, place, group.name));
} else if (object.type.equalsIgnoreCase("spawn")) {
boolean isActiveForNewGame = true;
boolean ignoreAreas = false;
int maxQuantity = 1;
int spawnChance = 10;
String spawnGroup = object.name;
for (TMXProperty p : object.properties) {
if (AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA) {
if (p.value.equals("")) {
L.log("OPTIMIZE: Map " + m.name + ", spawn " + object.name + "@" + topLeft.toString() + " has property \"" + p.name + "\" without value.");
continue;
}
}
if (p.name.equalsIgnoreCase("quantity")) {
maxQuantity = Integer.parseInt(p.value);
} else if (p.name.equalsIgnoreCase("spawnchance")) {
spawnChance = Integer.parseInt(p.value);
} else if (p.name.equalsIgnoreCase("active")) {
isActiveForNewGame = Boolean.parseBoolean(p.value);
} else if (p.name.equalsIgnoreCase("ignoreAreas")) {
ignoreAreas = Boolean.parseBoolean(p.value);
} else if (p.name.equalsIgnoreCase("spawngroup")) {
spawnGroup = p.value;
} else if (AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA) {
L.log("OPTIMIZE: Map " + m.name + ", spawn " + object.name + "@" + topLeft.toString() + " has unrecognized property \"" + p.name + "\".");
}
}
ArrayList<MonsterType> types = monsterTypes.getMonsterTypesFromSpawnGroup(spawnGroup);
if (types.isEmpty()) {
if (AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA) {
L.log("OPTIMIZE: Map " + m.name + " contains spawn \"" + object.name + "\"@" + topLeft.toString() + " that does not correspond to any monsters. The spawn will be removed.");
}
continue;
}
String[] monsterTypeIDs = new String[types.size()];
boolean isUnique = types.get(0).isUnique;
for (int i = 0; i < monsterTypeIDs.length; ++i) {
monsterTypeIDs[i] = types.get(i).id;
}
MonsterSpawnArea area = new MonsterSpawnArea(
position
,new Range(maxQuantity, 0)
,new Range(1000, spawnChance)
,object.name
,monsterTypeIDs
,isUnique
,ignoreAreas
,group.name
,isActiveForNewGame
);
spawnAreas.add(area);
} else if (object.type.equalsIgnoreCase("key")) {
String phraseID = "";
for (TMXProperty p : object.properties) {
if (p.name.equalsIgnoreCase("phrase")) {
phraseID = p.value;
} else if (AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA) {
if (!requirementPropertiesNames.contains(p.name.toLowerCase())) {
L.log("OPTIMIZE: Map " + m.name + ", key " + object.name + "@" + topLeft.toString() + " has unrecognized property \"" + p.name + "\".");
}
}
}
Requirement req = parseRequirement(object);
mapObjects.add(MapObject.createKeyArea(position, phraseID, req, group.name));
} else if (object.type.equals("rest")) {
mapObjects.add(MapObject.createRestArea(position, object.name, group.name));
} else if (object.type.equals("container")) {
DropList dropList = dropLists.getDropList(object.name);
if (dropList == null) continue;
mapObjects.add(MapObject.createContainerArea(position, dropList, group.name));
} else if (object.type.equals("replace")) {
// Do nothing. Will be handled when reading map layers instead.
} else if (object.type.equalsIgnoreCase("script")) {
String phraseID = object.name;
MapObject.MapObjectEvaluationType evaluateWhen = MapObject.MapObjectEvaluationType.whenEntering;
for (TMXProperty p : object.properties) {
if (p.name.equalsIgnoreCase("when")) {
if (p.value.equalsIgnoreCase("enter")) {
evaluateWhen = MapObject.MapObjectEvaluationType.whenEntering;
} else if (p.value.equalsIgnoreCase("step")) {
evaluateWhen = MapObject.MapObjectEvaluationType.onEveryStep;
} else if (p.value.equalsIgnoreCase("round")) {
evaluateWhen = MapObject.MapObjectEvaluationType.afterEveryRound;
} else if (p.value.equalsIgnoreCase("always")) {
evaluateWhen = MapObject.MapObjectEvaluationType.continuously;
} else if (AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA) {
L.log("OPTIMIZE: Map " + m.name + ", script " + object.name + "@" + topLeft.toString() + " has unrecognized value for \"when\" property: \"" + p.value + "\".");
}
} else if (AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA) {
L.log("OPTIMIZE: Map " + m.name + ", script " + object.name + "@" + topLeft.toString() + " has unrecognized property \"" + p.name + "\".");
}
}
mapObjects.add(MapObject.createScriptArea(position, phraseID, evaluateWhen, group.name));
} else if (AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA) {
L.log("OPTIMIZE: Map " + m.name + ", has unrecognized object type \"" + object.type + "\" for name \"" + object.name + "\".");
}
}
}
MapObject[] _eventObjects = new MapObject[mapObjects.size()];
_eventObjects = mapObjects.toArray(_eventObjects);
MonsterSpawnArea[] _spawnAreas = new MonsterSpawnArea[spawnAreas.size()];
_spawnAreas = spawnAreas.toArray(_spawnAreas);
result.add(new PredefinedMap(m.xmlResourceId, m.name, mapSize, _eventObjects, _spawnAreas, activeGroups, isOutdoors));
}
return result;
}
private static final List<String> requirementPropertiesNames = Arrays.asList(new String[]{"requireType".toLowerCase(), "requireId".toLowerCase(), "requireValue".toLowerCase(), "requireNegation".toLowerCase()});
private static Requirement parseRequirement(TMXObject object) {
Requirement.RequirementType requireType = Requirement.RequirementType.questProgress;
String requireId = null;
int requireValue = 0;
boolean requireNegation = false;
for (TMXProperty p : object.properties) {
if (p.name.equalsIgnoreCase("requireType")) {
try {
requireType = Requirement.RequirementType.valueOf(p.value);
} catch (IllegalArgumentException e) {
requireType = null;
if (AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA) {
L.log("OPTIMIZE: Unrecognized requirement type: "+p.value);
}
}
} else if (p.name.equalsIgnoreCase("requireId")) {
requireId = p.value;
} else if (p.name.equalsIgnoreCase("requireValue")) {
requireValue = Integer.parseInt(p.value);
} else if (p.name.equalsIgnoreCase("requireNegation")) {
requireNegation = Boolean.parseBoolean(p.value);
}
}
if (requireType == null) return null;
return new Requirement(requireType, requireId, requireValue, requireNegation);
}
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_TOP = "top";
private static final String LAYERNAME_WALKABLE = "walkable";
private static final String PROPNAME_FILTER = "colorfilter";
private static final SetOfLayerNames defaultLayerNames = new SetOfLayerNames(LAYERNAME_GROUND, LAYERNAME_OBJECTS, LAYERNAME_ABOVE, LAYERNAME_TOP, LAYERNAME_WALKABLE);
private static LayeredTileMap transformMap(TMXLayerMap map, TileCache tileCache) {
final Size mapSize = new Size(map.width, map.height);
LayeredTileMap.ColorFilterId colorFilter = LayeredTileMap.ColorFilterId.none;
for (TMXProperty prop : map.properties) {
if (prop.name.equalsIgnoreCase(PROPNAME_FILTER)) {
String filterId = prop.value;
if (filterId != null) {
colorFilter = LayeredTileMap.ColorFilterId.valueOf(filterId);
}
}
}
HashSet<Integer> usedTileIDs = new HashSet<Integer>();
HashMap<String, TMXLayer> layersPerLayerName = new HashMap<String, TMXLayer>();
for (TMXLayer layer : map.layers) {
String layerName = layer.name;
assert(layerName != null);
assert(layerName.length() > 0);
layerName = layerName.toLowerCase();
if (AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA) {
if (layersPerLayerName.containsKey(layerName)) {
L.log("WARNING: Map \"" + map.name + "\" contains multiple layers with name \"" + layerName + "\".");
}
}
layersPerLayerName.put(layerName, layer);
}
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_TOP)) layerNames.topLayersName = prop.value;
else if (prop.name.equalsIgnoreCase(LAYERNAME_WALKABLE)) layerNames.walkableLayersName = prop.value;
else if (AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA) {
if (!requirementPropertiesNames.contains(prop.name))
L.log("OPTIMIZE: Map " + map.name + " contains replace area with unknown property \"" + prop.name + "\".");
}
}
MapSection replacementSection = transformMapSection(map, tileCache, position, layersPerLayerName, usedTileIDs, layerNames);
Requirement req = parseRequirement(obj);
if (req == null || !req.isValid()) {
QuestProgress qp = QuestProgress.parseQuestProgress(obj.name);
if (qp != null) req = new Requirement(qp);
}
if (req == null || !req.isValid()) {
if (AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA) {
L.log("WARNING: Map " + map.name + " contains replace area "+obj.name+" with unparsable requirement");
}
continue;
}
replaceableSections.add(new ReplaceableMapSection(position, replacementSection, req, objectGroup.name));
}
}
}
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);
final MapLayer layerTop = transformMapLayer(layersPerLayerName, layerNames.topLayersName, 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, layerTop, 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.toLowerCase());
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);
digestLayer(layersPerLayerName, layerNames.topLayersName, 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];
if (ts.firstgid <= gid) {
dest.tilesetName = ts.name;
dest.localId = (gid - ts.firstgid);
return true;
}
}
L.log("WARNING: Cannot find tile for gid " + gid);
return false;
}
private static final class Tile {
public String tilesetName;
public int localId;
}
private static final class SetOfLayerNames {
public String groundLayerName;
public String objectsLayerName;
public String aboveLayersName;
public String topLayersName;
public String walkableLayersName;
public SetOfLayerNames() {
this.groundLayerName = null;
this.objectsLayerName = null;
this.aboveLayersName = null;
this.topLayersName = null;
this.walkableLayersName = null;
}
public SetOfLayerNames(String groundLayerName, String objectsLayerName, String aboveLayersName, String topLayersName, String walkableLayersName) {
this.groundLayerName = groundLayerName;
this.objectsLayerName = objectsLayerName;
this.aboveLayersName = aboveLayersName;
this.topLayersName = topLayersName;
this.walkableLayersName = walkableLayersName;
}
}
}