diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/AndorsTrailApplication.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/AndorsTrailApplication.java index 6931ab527..d6203d460 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/AndorsTrailApplication.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/AndorsTrailApplication.java @@ -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; diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/LayeredTileMap.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/LayeredTileMap.java index d807fc78c..c35be2452 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/LayeredTileMap.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/LayeredTileMap.java @@ -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 usedTileIDs; - public LayeredTileMap( - Size size - , MapSection layout - , ReplaceableMapSection[] replacements - , ColorFilterId colorFilter - , Collection 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 usedTileIDs; + public LayeredTileMap( + Size size + , MapSection layout + , ReplaceableMapSection[] replacements + , ColorFilterId colorFilter + , Collection 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); + } + } +} diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/MapSection.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/MapSection.java index 3fa84fd2d..e82dbda58 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/MapSection.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/MapSection.java @@ -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); + } +} diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/TMXMapTranslator.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/TMXMapTranslator.java index 098bb72c6..23c676dab 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/TMXMapTranslator.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/map/TMXMapTranslator.java @@ -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 maps = new ArrayList(); - - 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 transformMaps(MonsterTypeCollection monsterTypes, DropListCollection dropLists) { - return transformMaps(maps, monsterTypes, dropLists); - } - public ArrayList transformMaps(Collection maps, MonsterTypeCollection monsterTypes, DropListCollection dropLists) { - ArrayList result = new ArrayList(); - - 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 mapObjects = new LinkedList(); - List spawnAreas = new LinkedList(); - List activeGroups = new LinkedList(); - - 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 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 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 usedTileIDs = new HashSet(); - HashMap layersPerLayerName = new HashMap(); - 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 replaceableSections = new ArrayList(); - 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 layersPerLayerName, - HashSet 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 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 layersPerLayerName, - String layerName, - TMXLayerMap srcMap, - TileCache tileCache, - CoordRect area, - HashSet usedTileIDs - ) { - TMXLayer srcLayer = findLayer(layersPerLayerName, layerName, srcMap.name); - if (srcLayer == null) return null; - final MapLayer result = new MapLayer(area.size); - Tile tile = new Tile(); - 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 layersPerLayerName, SetOfLayerNames layerNames) { - try { - MessageDigest digest = MessageDigest.getInstance("MD5"); - digestLayer(layersPerLayerName, layerNames.groundLayerName, map, digest); - digestLayer(layersPerLayerName, layerNames.objectsLayerName, map, digest); - digestLayer(layersPerLayerName, layerNames.aboveLayersName, map, digest); - return digest.digest(); - } catch (NoSuchAlgorithmException e) { - L.log("ERROR: Failed to create layout hash for map " + map.name + " : " + e.toString()); - } - return new byte[0]; - } - - private static void digestLayer(HashMap layersPerLayerName, String layerName, TMXLayerMap map, MessageDigest digest) { - TMXLayer srcLayer = findLayer(layersPerLayerName, layerName, map.name); - if (srcLayer == null) return; - if (srcLayer.layoutHash == null) return; - digest.update(srcLayer.layoutHash); - } - - private static boolean getTile(final TMXLayerMap map, final int gid, final Tile dest) { - for(int i = map.tileSets.length - 1; i >= 0; --i) { - TMXTileSet ts = map.tileSets[i]; - 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 maps = new ArrayList(); + + 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 transformMaps(MonsterTypeCollection monsterTypes, DropListCollection dropLists) { + return transformMaps(maps, monsterTypes, dropLists); + } + public ArrayList transformMaps(Collection maps, MonsterTypeCollection monsterTypes, DropListCollection dropLists) { + ArrayList result = new ArrayList(); + + 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 mapObjects = new LinkedList(); + List spawnAreas = new LinkedList(); + List activeGroups = new LinkedList(); + + 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 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 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 usedTileIDs = new HashSet(); + HashMap layersPerLayerName = new HashMap(); + 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 replaceableSections = new ArrayList(); + 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 layersPerLayerName, + HashSet 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 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 layersPerLayerName, + String layerName, + TMXLayerMap srcMap, + TileCache tileCache, + CoordRect area, + HashSet usedTileIDs + ) { + TMXLayer srcLayer = findLayer(layersPerLayerName, layerName, srcMap.name); + if (srcLayer == null) return null; + final MapLayer result = new MapLayer(area.size); + Tile tile = new Tile(); + 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 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 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; + } + } +}