mirror of
https://github.com/OMGeeky/andors-trail.git
synced 2026-02-23 15:38:29 +01:00
Hopefully fixed the issue with the loss of old worldmap tiles.
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user