mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-08 12:51:51 +09:00
Former-commit-id: 1647fa32ef6894bd7db44f741f07c2f4dcdf9054 Former-commit-id: 0e5810dcfbe1fd59b13e7cabe9f1e93c5542da2d
293 lines
7.4 KiB
Java
293 lines
7.4 KiB
Java
package org.newdawn.slick.tiled;
|
|
|
|
import java.io.ByteArrayInputStream;
|
|
import java.io.IOException;
|
|
import java.util.Properties;
|
|
import java.util.zip.GZIPInputStream;
|
|
|
|
import org.newdawn.slick.SlickException;
|
|
import org.newdawn.slick.util.Log;
|
|
import org.w3c.dom.Element;
|
|
import org.w3c.dom.Node;
|
|
import org.w3c.dom.NodeList;
|
|
|
|
/**
|
|
* A layer of tiles on the map
|
|
*
|
|
* @author kevin
|
|
*/
|
|
public class Layer {
|
|
/** The code used to decode Base64 encoding */
|
|
private static byte[] baseCodes = new byte[256];
|
|
|
|
/**
|
|
* Static initialiser for the codes created against Base64
|
|
*/
|
|
static {
|
|
for (int i = 0; i < 256; i++)
|
|
baseCodes[i] = -1;
|
|
for (int i = 'A'; i <= 'Z'; i++)
|
|
baseCodes[i] = (byte) (i - 'A');
|
|
for (int i = 'a'; i <= 'z'; i++)
|
|
baseCodes[i] = (byte) (26 + i - 'a');
|
|
for (int i = '0'; i <= '9'; i++)
|
|
baseCodes[i] = (byte) (52 + i - '0');
|
|
baseCodes['+'] = 62;
|
|
baseCodes['/'] = 63;
|
|
}
|
|
|
|
/** The map this layer belongs to */
|
|
private final TiledMap map;
|
|
/** The index of this layer */
|
|
public int index;
|
|
/** The name of this layer - read from the XML */
|
|
public String name;
|
|
/**
|
|
* The tile data representing this data, index 0 = tileset, index 1 = tile
|
|
* id
|
|
*/
|
|
public int[][][] data;
|
|
/** The width of this layer */
|
|
public int width;
|
|
/** The height of this layer */
|
|
public int height;
|
|
|
|
/** the properties of this layer */
|
|
public Properties props;
|
|
|
|
/**
|
|
* Create a new layer based on the XML definition
|
|
*
|
|
* @param element
|
|
* The XML element describing the layer
|
|
* @param map
|
|
* The map this layer is part of
|
|
* @throws SlickException
|
|
* Indicates a failure to parse the XML layer
|
|
*/
|
|
public Layer(TiledMap map, Element element) throws SlickException {
|
|
this.map = map;
|
|
name = element.getAttribute("name");
|
|
width = Integer.parseInt(element.getAttribute("width"));
|
|
height = Integer.parseInt(element.getAttribute("height"));
|
|
data = new int[width][height][3];
|
|
|
|
// now read the layer properties
|
|
Element propsElement = (Element) element.getElementsByTagName(
|
|
"properties").item(0);
|
|
if (propsElement != null) {
|
|
NodeList properties = propsElement.getElementsByTagName("property");
|
|
if (properties != null) {
|
|
props = new Properties();
|
|
for (int p = 0; p < properties.getLength(); p++) {
|
|
Element propElement = (Element) properties.item(p);
|
|
|
|
String name = propElement.getAttribute("name");
|
|
String value = propElement.getAttribute("value");
|
|
props.setProperty(name, value);
|
|
}
|
|
}
|
|
}
|
|
|
|
Element dataNode = (Element) element.getElementsByTagName("data").item(
|
|
0);
|
|
String encoding = dataNode.getAttribute("encoding");
|
|
String compression = dataNode.getAttribute("compression");
|
|
|
|
if (encoding.equals("base64") && compression.equals("gzip")) {
|
|
try {
|
|
Node cdata = dataNode.getFirstChild();
|
|
char[] enc = cdata.getNodeValue().trim().toCharArray();
|
|
byte[] dec = decodeBase64(enc);
|
|
GZIPInputStream is = new GZIPInputStream(
|
|
new ByteArrayInputStream(dec));
|
|
|
|
for (int y = 0; y < height; y++) {
|
|
for (int x = 0; x < width; x++) {
|
|
int tileId = 0;
|
|
tileId |= is.read();
|
|
tileId |= is.read() << 8;
|
|
tileId |= is.read() << 16;
|
|
tileId |= is.read() << 24;
|
|
|
|
if (tileId == 0) {
|
|
data[x][y][0] = -1;
|
|
data[x][y][1] = 0;
|
|
data[x][y][2] = 0;
|
|
} else {
|
|
TileSet set = map.findTileSet(tileId);
|
|
|
|
if (set != null) {
|
|
data[x][y][0] = set.index;
|
|
data[x][y][1] = tileId - set.firstGID;
|
|
}
|
|
data[x][y][2] = tileId;
|
|
}
|
|
}
|
|
}
|
|
} catch (IOException e) {
|
|
Log.error(e);
|
|
throw new SlickException("Unable to decode base 64 block");
|
|
}
|
|
} else {
|
|
throw new SlickException("Unsupport tiled map type: " + encoding
|
|
+ "," + compression + " (only gzip base64 supported)");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the gloal ID of the tile at the specified location in this layer
|
|
*
|
|
* @param x
|
|
* The x coorindate of the tile
|
|
* @param y
|
|
* The y coorindate of the tile
|
|
* @return The global ID of the tile
|
|
*/
|
|
public int getTileID(int x, int y) {
|
|
return data[x][y][2];
|
|
}
|
|
|
|
/**
|
|
* Set the global tile ID at a specified location
|
|
*
|
|
* @param x
|
|
* The x location to set
|
|
* @param y
|
|
* The y location to set
|
|
* @param tile
|
|
* The tile value to set
|
|
*/
|
|
public void setTileID(int x, int y, int tile) {
|
|
if (tile == 0) {
|
|
data[x][y][0] = -1;
|
|
data[x][y][1] = 0;
|
|
data[x][y][2] = 0;
|
|
} else {
|
|
TileSet set = map.findTileSet(tile);
|
|
|
|
data[x][y][0] = set.index;
|
|
data[x][y][1] = tile - set.firstGID;
|
|
data[x][y][2] = tile;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Render a section of this layer
|
|
*
|
|
* @param x
|
|
* The x location to render at
|
|
* @param y
|
|
* The y location to render at
|
|
* @param sx
|
|
* The x tile location to start rendering
|
|
* @param sy
|
|
* The y tile location to start rendering
|
|
* @param width
|
|
* The number of tiles across to render
|
|
* @param ty
|
|
* The line of tiles to render
|
|
* @param lineByLine
|
|
* True if we should render line by line, i.e. giving us a chance
|
|
* to render something else between lines
|
|
* @param mapTileWidth
|
|
* the tile width specified in the map file
|
|
* @param mapTileHeight
|
|
* the tile height specified in the map file
|
|
*/
|
|
public void render(int x, int y, int sx, int sy, int width, int ty,
|
|
boolean lineByLine, int mapTileWidth, int mapTileHeight) {
|
|
for (int tileset = 0; tileset < map.getTileSetCount(); tileset++) {
|
|
TileSet set = null;
|
|
|
|
for (int tx = 0; tx < width; tx++) {
|
|
if ((sx + tx < 0) || (sy + ty < 0)) {
|
|
continue;
|
|
}
|
|
if ((sx + tx >= this.width) || (sy + ty >= this.height)) {
|
|
continue;
|
|
}
|
|
|
|
if (data[sx + tx][sy + ty][0] == tileset) {
|
|
if (set == null) {
|
|
set = map.getTileSet(tileset);
|
|
set.tiles.startUse();
|
|
}
|
|
|
|
int sheetX = set.getTileX(data[sx + tx][sy + ty][1]);
|
|
int sheetY = set.getTileY(data[sx + tx][sy + ty][1]);
|
|
|
|
int tileOffsetY = set.tileHeight - mapTileHeight;
|
|
|
|
// set.tiles.renderInUse(x+(tx*set.tileWidth),
|
|
// y+(ty*set.tileHeight), sheetX, sheetY);
|
|
set.tiles.renderInUse(x + (tx * mapTileWidth), y
|
|
+ (ty * mapTileHeight) - tileOffsetY, sheetX,
|
|
sheetY);
|
|
}
|
|
}
|
|
|
|
if (lineByLine) {
|
|
if (set != null) {
|
|
set.tiles.endUse();
|
|
set = null;
|
|
}
|
|
map.renderedLine(ty, ty + sy, index);
|
|
}
|
|
|
|
if (set != null) {
|
|
set.tiles.endUse();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Decode a Base64 string as encoded by TilED
|
|
*
|
|
* @param data
|
|
* The string of character to decode
|
|
* @return The byte array represented by character encoding
|
|
*/
|
|
private byte[] decodeBase64(char[] data) {
|
|
int temp = data.length;
|
|
for (int ix = 0; ix < data.length; ix++) {
|
|
if ((data[ix] > 255) || baseCodes[data[ix]] < 0) {
|
|
--temp;
|
|
}
|
|
}
|
|
|
|
int len = (temp / 4) * 3;
|
|
if ((temp % 4) == 3)
|
|
len += 2;
|
|
if ((temp % 4) == 2)
|
|
len += 1;
|
|
|
|
byte[] out = new byte[len];
|
|
|
|
int shift = 0;
|
|
int accum = 0;
|
|
int index = 0;
|
|
|
|
for (int ix = 0; ix < data.length; ix++) {
|
|
int value = (data[ix] > 255) ? -1 : baseCodes[data[ix]];
|
|
|
|
if (value >= 0) {
|
|
accum <<= 6;
|
|
shift += 6;
|
|
accum |= value;
|
|
if (shift >= 8) {
|
|
shift -= 8;
|
|
out[index++] = (byte) ((accum >> shift) & 0xff);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (index != out.length) {
|
|
throw new RuntimeException(
|
|
"Data length appears to be wrong (wrote " + index
|
|
+ " should be " + out.length + ")");
|
|
}
|
|
|
|
return out;
|
|
}
|
|
} |