diff --git a/cache/src/main/org/apollo/cache/decoder/MapFileDecoder.java b/cache/src/main/org/apollo/cache/decoder/MapFileDecoder.java deleted file mode 100644 index 6b3d6425..00000000 --- a/cache/src/main/org/apollo/cache/decoder/MapFileDecoder.java +++ /dev/null @@ -1,135 +0,0 @@ -package org.apollo.cache.decoder; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.util.HashMap; -import java.util.Map; - -import org.apollo.cache.IndexedFileSystem; -import org.apollo.cache.archive.Archive; -import org.apollo.cache.archive.ArchiveEntry; - -/** - * Decodes {@link MapDefinition}s from the {@link IndexedFileSystem}. - * - * @author Ryley - * @author Major - */ -public final class MapFileDecoder { - - /** - * A definition for a region. - */ - public static final class MapDefinition { - - /** - * Indicates whether or not this map is members-only. - */ - private final boolean members; - - /** - * The object file id. - */ - private final int objects; - - /** - * The packed coordinates. - */ - private final int packedCoordinates; - - /** - * The terrain file id. - */ - private final int terrain; - - /** - * Creates the {@link MapDefinition}. - * - * @param packedCoordinates The packed coordinates. - * @param terrain The terrain file id. - * @param objects The object file id. - * @param members Indicates whether or not this map is members-only. - */ - public MapDefinition(int packedCoordinates, int terrain, int objects, boolean members) { - this.packedCoordinates = packedCoordinates; - this.terrain = terrain; - this.objects = objects; - this.members = members; - } - - /** - * Gets the id of the file containing the object data. - * - * @return The file id. - */ - public int getObjectFile() { - return objects; - } - - /** - * Gets the packed coordinates. - * - * @return The packed coordinates. - */ - public int getPackedCoordinates() { - return packedCoordinates; - } - - /** - * Gets the id of the file containing the terrain data. - * - * @return The file id. - */ - public int getTerrainFile() { - return terrain; - } - - /** - * Returns whether or not this MapDefinition is for a members-only area of the world. - * - * @return {@code true} if this MapDefinition is for a members-only area, {@code false} if not. - */ - public boolean isMembersOnly() { - return members; - } - - } - - /** - * The width (and length) of a map file, in tiles. - */ - public static final int MAP_FILE_WIDTH = 64; - - /** - * The file id of the versions archive. - */ - private static final int VERSIONS_ARCHIVE_FILE_ID = 5; - - /** - * Decodes {@link MapDefinition}s from the specified {@link IndexedFileSystem}. - * - * @param fs The IndexedFileSystem. - * @return A {@link Map} of packed coordinates to their MapDefinitions. - * @throws IOException If there is an error reading or decoding the Archive. - */ - public static Map decode(IndexedFileSystem fs) throws IOException { - Archive archive = fs.getArchive(0, VERSIONS_ARCHIVE_FILE_ID); - ArchiveEntry entry = archive.getEntry("map_index"); - Map definitions = new HashMap<>(); - - ByteBuffer buffer = entry.getBuffer(); - int count = buffer.capacity() / (3 * Short.BYTES + Byte.BYTES); - - for (int times = 0; times < count; times++) { - int id = buffer.getShort() & 0xFFFF; - int terrain = buffer.getShort() & 0xFFFF; - int objects = buffer.getShort() & 0xFFFF; - boolean members = buffer.get() == 1; - - definitions.put(id, new MapDefinition(id, terrain, objects, members)); - } - - return definitions; - } - -} \ No newline at end of file diff --git a/cache/src/main/org/apollo/cache/map/MapConstants.java b/cache/src/main/org/apollo/cache/map/MapConstants.java new file mode 100644 index 00000000..63492978 --- /dev/null +++ b/cache/src/main/org/apollo/cache/map/MapConstants.java @@ -0,0 +1,62 @@ +package org.apollo.cache.map; + +/** + * Contains {@link MapFile}-related constants. + * + * @author Major + */ +public final class MapConstants { + + /** + * The index containing the map files. + */ + public static final int MAP_INDEX = 4; + + /** + * The width (and length) of a {@link MapFile} in {@link Tile}s. + */ + public static final int MAP_WIDTH = 64; + + /** + * The amount of planes in a MapFile. + */ + public static final int MAP_PLANES = 4; + + /** + * The multiplicand for height values. + */ + static final int HEIGHT_MULTIPLICAND = 8; + + /** + * The lowest type value that will result in the decoding of a Tile being continued. + */ + static final int LOWEST_CONTINUED_TYPE = 2; + + /** + * The minimum type that specifies the Tile attributes. + */ + static final int MINIMUM_ATTRIBUTES_TYPE = 81; + + /** + * The minimum type that specifies the Tile underlay id. + */ + static final int MINIMUM_OVERLAY_TYPE = 49; + + /** + * The amount of possible overlay orientations. + */ + static final int ORIENTATION_COUNT = 4; + + /** + * The height difference between two planes. + */ + static final int PLANE_HEIGHT_DIFFERENCE = 240; + + /** + * Sole private constructor to prevent instantiation. + */ + private MapConstants() { + + } + +} \ No newline at end of file diff --git a/cache/src/main/org/apollo/cache/map/MapFile.java b/cache/src/main/org/apollo/cache/map/MapFile.java new file mode 100644 index 00000000..b1e6d646 --- /dev/null +++ b/cache/src/main/org/apollo/cache/map/MapFile.java @@ -0,0 +1,48 @@ +package org.apollo.cache.map; + +import com.google.common.base.Preconditions; + +/** + * A 3-dimensional 64x64 area of the map. + * + * @author Major + */ +public final class MapFile { + + /** + * The array of MapPlanes. + */ + private final MapPlane[] planes; + + /** + * Creates the MapFile. + * + * @param planes The {@link MapPlane}s. + */ + public MapFile(MapPlane[] planes) { + this.planes = planes.clone(); + } + + /** + * Gets the {@link MapPlane} with the specified level. + * + * @param plane The plane. + * @return The MapPlane. + * @throws ArrayIndexOutOfBoundsException If {@code plane} is out of bounds. + */ + public MapPlane getPlane(int plane) { + int length = planes.length; + Preconditions.checkElementIndex(plane, length, "Plane index out of bounds, must be [0, " + length + ")."); + return planes[plane]; + } + + /** + * Gets all of the {@link MapPlane}s in this MapFile. + * + * @return The MapPlanes. + */ + public MapPlane[] getPlanes() { + return planes.clone(); + } + +} \ No newline at end of file diff --git a/cache/src/main/org/apollo/cache/map/MapFileDecoder.java b/cache/src/main/org/apollo/cache/map/MapFileDecoder.java new file mode 100644 index 00000000..e85ae5e2 --- /dev/null +++ b/cache/src/main/org/apollo/cache/map/MapFileDecoder.java @@ -0,0 +1,123 @@ +package org.apollo.cache.map; + +import org.apollo.cache.IndexedFileSystem; +import org.apollo.util.CompressionUtil; + +import java.nio.ByteBuffer; +import java.io.IOException; + +/** + * A decoder for the terrain data stored in {@link MapFile}s. + * + * @author Major + */ +public class MapFileDecoder { + /** + * Creates a MapFileDecoder for the specified map file. + * + * @param fs The {@link IndexedFileSystem} to get the file from. + * @param index The {@link MapIndex} to get the file index from. + * @return The MapFileDecoder. + * @throws IOException If there is an error reading or decompressing the file. + */ + public static MapFileDecoder create(IndexedFileSystem fs, MapIndex index) throws IOException { + ByteBuffer compressed = fs.getFile(MapConstants.MAP_INDEX, index.getMapFile()); + ByteBuffer decompressed = ByteBuffer.wrap(CompressionUtil.degzip(compressed)); + + return new MapFileDecoder(decompressed); + } + + /** + * The DataBuffer containing the MapFile data. + */ + private final ByteBuffer buffer; + + /** + * Creates the MapIndexDecoder. + *

+ * This constructor expects the {@link ByteBuffer} to not be compressed. + * + * @param buffer The DataBuffer containing the MapFile data. + */ + public MapFileDecoder(ByteBuffer buffer) { + this.buffer = buffer.asReadOnlyBuffer(); + } + + /** + * Decodes the data into a {@link MapFile}. + * + * @return The MapFile. + */ + public MapFile decode() { + MapPlane[] planes = new MapPlane[MapConstants.MAP_PLANES]; + + for (int level = 0; level < MapConstants.MAP_PLANES; level++) { + planes[level] = decodePlane(planes, level); + } + + return new MapFile(planes); + } + + /** + * Decodes a {@link MapPlane} with the specified level. + * + * @param planes The previously-decoded {@link MapPlane}s, for calculating the height of the tiles. + * @param level The level. + * @return The MapPlane. + */ + private MapPlane decodePlane(MapPlane[] planes, int level) { + Tile[][] tiles = new Tile[MapConstants.MAP_WIDTH][MapConstants.MAP_WIDTH]; + + for (int x = 0; x < MapConstants.MAP_WIDTH; x++) { + for (int z = 0; z < MapConstants.MAP_WIDTH; z++) { + tiles[x][z] = decodeTile(planes, level, x, z); + } + } + + return new MapPlane(level, tiles); + } + + /** + * Decodes the data into a {@link Tile}. + * + * @param planes The previously-decoded {@link MapPlane}s, for calculating the height of the Tile. + * @param level The level the Tile is on. + * @param x The x coordinate of the Tile. + * @param z The z coordinate of the Tile. + * @return The MapFile. + */ + private Tile decodeTile(MapPlane[] planes, int level, int x, int z) { + Tile.Builder builder = Tile.builder(x, z, level); + + int type; + do { + type = buffer.get() & 0xFF; + + if (type == 0) { + if (level == 0) { + builder.setHeight(TileUtils.calculateHeight(x, z)); + } else { + Tile below = planes[level - 1].getTile(x, z); + builder.setHeight(below.getHeight() + MapConstants.PLANE_HEIGHT_DIFFERENCE); + } + } else if (type == 1) { + int height = buffer.get(); + int below = (level == 0) ? 0 : planes[level - 1].getTile(x, z).getHeight(); + + builder.setHeight((height == 1 ? 0 : height) * MapConstants.HEIGHT_MULTIPLICAND + below); + } else if (type <= MapConstants.MINIMUM_OVERLAY_TYPE) { + builder.setOverlay(buffer.get()); + builder.setOverlayType((type - MapConstants.LOWEST_CONTINUED_TYPE) + / MapConstants.ORIENTATION_COUNT); + builder.setOverlayOrientation(type - MapConstants.LOWEST_CONTINUED_TYPE + % MapConstants.ORIENTATION_COUNT); + } else if (type <= MapConstants.MINIMUM_ATTRIBUTES_TYPE) { + builder.setAttributes(type - MapConstants.MINIMUM_OVERLAY_TYPE); + } else { + builder.setUnderlay(type - MapConstants.MINIMUM_ATTRIBUTES_TYPE); + } + } while (type >= MapConstants.LOWEST_CONTINUED_TYPE); + + return builder.build(); + } +} diff --git a/cache/src/main/org/apollo/cache/map/MapIndex.java b/cache/src/main/org/apollo/cache/map/MapIndex.java new file mode 100644 index 00000000..9b707c85 --- /dev/null +++ b/cache/src/main/org/apollo/cache/map/MapIndex.java @@ -0,0 +1,125 @@ +package org.apollo.cache.map; + +import org.apollo.cache.def.ItemDefinition; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * A definition for a map. + */ +public final class MapIndex { + + /** + * Indicates whether or not this map is members-only. + */ + private final boolean members; + + /** + * The object file id. + */ + private final int objects; + + /** + * The packed coordinates. + */ + private final int packedCoordinates; + + /** + * The terrain file id. + */ + private final int terrain; + + /** + * A mapping of region ids to {@link MapIndex}es. + */ + private static Map indices; + + /** + * Initialises the class with the specified set of indices. + */ + public static void init(Map indices) { + MapIndex.indices = Collections.unmodifiableMap(indices); + } + + /** + * Gets the {@code Map} of {@link MapIndex} instances. + * + * @return The map of {@link MapIndex} instances. + */ + public static Map getIndices() { + return indices; + } + + /** + * Creates the {@link MapIndex}. + * + * @param packedCoordinates The packed coordinates. + * @param terrain The terrain file id. + * @param objects The object file id. + * @param members Indicates whether or not this map is members-only. + */ + public MapIndex(int packedCoordinates, int terrain, int objects, boolean members) { + this.packedCoordinates = packedCoordinates; + this.terrain = terrain; + this.objects = objects; + this.members = members; + } + + /** + * Gets the id of the file containing the object data. + * + * @return The file id. + */ + public int getObjectFile() { + return objects; + } + + /** + * Gets the packed coordinates. + * + * @return The packed coordinates. + */ + public int getPackedCoordinates() { + return packedCoordinates; + } + + /** + * Gets the id of the file containing the terrain data. + * + * @return The file id. + */ + public int getMapFile() { + return terrain; + } + + /** + * Gets the X coordinate of this map. + * + * @return The X coordinate of this map. + */ + public int getX() { + return (packedCoordinates >> 8 & 0xFF) * MapConstants.MAP_WIDTH; + } + + /** + * Gets the Y coordinate of this map. + * + * @return The y coordinate of this map. + */ + public int getY() { + return (packedCoordinates & 0xFF) * MapConstants.MAP_WIDTH; + + } + + /** + * Returns whether or not this MapIndex is for a members-only area of the world. + * + * @return {@code true} if this MapIndex is for a members-only area, {@code false} if not. + */ + public boolean isMembersOnly() { + return members; + } + +} diff --git a/cache/src/main/org/apollo/cache/map/MapIndexDecoder.java b/cache/src/main/org/apollo/cache/map/MapIndexDecoder.java new file mode 100644 index 00000000..7bca2add --- /dev/null +++ b/cache/src/main/org/apollo/cache/map/MapIndexDecoder.java @@ -0,0 +1,70 @@ +package org.apollo.cache.map; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.Map; + +import org.apollo.cache.IndexedFileSystem; +import org.apollo.cache.archive.Archive; +import org.apollo.cache.archive.ArchiveEntry; +import org.apollo.cache.map.MapIndex; + +/** + * Decodes {@link MapIndex}s from the {@link IndexedFileSystem}. + * + * @author Ryley + * @author Major + */ +public final class MapIndexDecoder implements Runnable { + + /** + * The file id of the versions archive. + */ + private static final int VERSIONS_ARCHIVE_FILE_ID = 5; + + /** + * The IndexedFileSystem. + */ + private final IndexedFileSystem fs; + + public MapIndexDecoder(IndexedFileSystem fs) { + this.fs = fs; + } + + /** + * Decodes {@link MapIndex}s from the specified {@link IndexedFileSystem}. + * + * @return A {@link Map} of packed coordinates to their MapDefinitions. + * @throws IOException If there is an error reading or decoding the Archive. + */ + public Map decode() throws IOException { + Archive archive = fs.getArchive(0, VERSIONS_ARCHIVE_FILE_ID); + ArchiveEntry entry = archive.getEntry("map_index"); + Map definitions = new HashMap<>(); + + ByteBuffer buffer = entry.getBuffer(); + int count = buffer.capacity() / (3 * Short.BYTES + Byte.BYTES); + + for (int times = 0; times < count; times++) { + int id = buffer.getShort() & 0xFFFF; + int terrain = buffer.getShort() & 0xFFFF; + int objects = buffer.getShort() & 0xFFFF; + boolean members = buffer.get() == 1; + + definitions.put(id, new MapIndex(id, terrain, objects, members)); + } + + return definitions; + } + + @Override + public void run() { + try { + MapIndex.init(decode()); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } +} \ No newline at end of file diff --git a/cache/src/main/org/apollo/cache/map/MapObject.java b/cache/src/main/org/apollo/cache/map/MapObject.java new file mode 100644 index 00000000..b2cf741d --- /dev/null +++ b/cache/src/main/org/apollo/cache/map/MapObject.java @@ -0,0 +1,105 @@ +package org.apollo.cache.map; + +/** + * Represents a static world object in a map file. + */ +public final class MapObject { + + /** + * The object definition id of this {@code MapObject}. + */ + private final int id; + + /** + * The packed coordinates (local XY and height) for this object. + */ + private int packedCoordinates; + + /** + * The type of this object. + */ + private final int type; + + /** + * The orientation of this object. + */ + private final int orientation; + + /** + * Creates a new {@code MapObject}. + * + * @param id The object ID of this map object. + * @param packedCoordinates A packed integer containing the coordinates of this map object. + * @param type The type of object. + * @param orientation The object facing direction. + */ + public MapObject(int id, int packedCoordinates, int type, int orientation) { + this.id = id; + this.packedCoordinates = packedCoordinates; + this.type = type; + this.orientation = orientation; + } + + /** + * Get the object ID of this map object. + * + * @return The object ID for {@link org.apollo.cache.def.ObjectDefinition} lookups. + */ + public int getId() { + return id; + } + + /** + * Get the plane this map object exists on. + * + * @return The plane this map object is on. + */ + public int getHeight() { + return packedCoordinates >> 12 & 0x3; + } + + /** + * Get the X coordinate of this object relative to the map position. + * + * @return The local X coordinate. + */ + public int getLocalX() { + return packedCoordinates >> 6 & 0x3F; + } + + /** + * Get the Y coordinate of this object relative to the map position. + * + * @return The local Y coordinate. + */ + public int getLocalY() { + return packedCoordinates & 0x3F; + } + + /** + * Get the integer representation of this objects orientation (0 indexed, starting West-North-East-South). + * + * @return The orientation of this object. + */ + public int getOrientation() { + return orientation; + } + + /** + * Get a packed integer containing the x/y coordinates and height for this object. + * + * @return The packed coordinates. + */ + public int getPackedCoordinates() { + return packedCoordinates; + } + + /** + * Get the type of this object. + * + * @return The type of this object. + */ + public int getType() { + return type; + } +} diff --git a/cache/src/main/org/apollo/cache/map/MapObjectsDecoder.java b/cache/src/main/org/apollo/cache/map/MapObjectsDecoder.java new file mode 100644 index 00000000..351ea7d7 --- /dev/null +++ b/cache/src/main/org/apollo/cache/map/MapObjectsDecoder.java @@ -0,0 +1,83 @@ +package org.apollo.cache.map; + +import org.apollo.cache.IndexedFileSystem; +import org.apollo.cache.map.MapIndex; +import org.apollo.cache.map.MapConstants; +import org.apollo.cache.map.MapObject; +import org.apollo.util.BufferUtil; +import org.apollo.util.CompressionUtil; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; + +/** + * A decoder for reading the map objects for a given map. + * + * @author Major + */ +public final class MapObjectsDecoder { + /** + * Creates a MapObjectsDecoder for the specified map file. + * + * @param fs The {@link IndexedFileSystem} to get the file from. + * @param index The map index to decode objects for. + * @return The MapObjectsDecoder. + * @throws IOException If there is an error reading or decompressing the file. + */ + public static MapObjectsDecoder create(IndexedFileSystem fs, MapIndex index) throws IOException { + ByteBuffer compressed = fs.getFile(MapConstants.MAP_INDEX, index.getObjectFile()); + ByteBuffer decompressed = ByteBuffer.wrap(CompressionUtil.degzip(compressed)); + + return new MapObjectsDecoder(decompressed); + } + + /** + * The buffer to decode {@link MapObject}s from. + */ + private final ByteBuffer buffer; + + /** + * Create a new {@link MapObjectsDecoder} from the given buffer and map coordinates. + * + * @param buffer The decompressed object file buffer. + */ + public MapObjectsDecoder(ByteBuffer buffer) { + this.buffer = buffer.asReadOnlyBuffer(); + } + + /** + * Decodes the data in the {@code buffer} to a list of {@link MapObject}s. + * + * @return A list of decoded {@link MapObject}s. + */ + public List decode() { + List objects = new ArrayList<>(); + + int id = -1; + int idOffset = BufferUtil.readSmart(buffer); + + while (idOffset != 0) { + id += idOffset; + + int packed = 0; + int positionOffset = BufferUtil.readSmart(buffer); + + while (positionOffset != 0) { + packed += positionOffset - 1; + + int attributes = buffer.get() & 0xFF; + int type = attributes >> 2; + int orientation = attributes & 0x3; + objects.add(new MapObject(id, packed, type, orientation)); + + positionOffset = BufferUtil.readSmart(buffer); + } + + idOffset = BufferUtil.readSmart(buffer); + } + + return objects; + } +} diff --git a/cache/src/main/org/apollo/cache/map/MapPlane.java b/cache/src/main/org/apollo/cache/map/MapPlane.java new file mode 100644 index 00000000..1dfc3a01 --- /dev/null +++ b/cache/src/main/org/apollo/cache/map/MapPlane.java @@ -0,0 +1,90 @@ +package org.apollo.cache.map; + +import java.util.Arrays; +import java.util.stream.Stream; + +/** + * A plane of a map, which is a distinct height level. + * + * @author Major + */ +public final class MapPlane { + + /** + * Returns a shallow copy of the specified 2-dimensional array. + * + * @param array The array to copy. Must not be {@code null}. + * @return The copy. + */ + private static T[][] clone(T[][] array) { + T[][] copy = array.clone(); + for (int index = 0; index < copy.length; index++) { + copy[index] = array[index].clone(); + } + + return copy; + } + + /** + * The level of this MapPlane. + */ + private final int level; + + /** + * The 2-dimensional array of Tiles. + */ + private final Tile[][] tiles; + + /** + * Creates the MapPlane. + * + * @param level The level of the MapPlane. + * @param tiles The 2D array of {@link Tile}s. Must not be {@code null}. Must be square. + */ + public MapPlane(int level, Tile[][] tiles) { + this.level = level; + this.tiles = clone(tiles); + } + + /** + * Gets the level of this MapPlane. + * + * @return The level. + */ + public int getLevel() { + return level; + } + + /** + * Gets the amount of tiles in this MapPlane. + * + * @return The amount of tiles. + */ + public int getSize() { + return tiles.length * tiles[0].length; + } + + /** + * Gets the {@link Tile} at the specified (x, z) coordinate. + * + * @param x The x coordinate. + * @param z The z coordinate. + * @return The Tile. + */ + public Tile getTile(int x, int z) { + return tiles[x][z]; + } + + /** + * Gets the {@link Tile}s in this MapPlane. + *

+ * This method returns the Tiles according on a column-based ordering: for a 2x2 tile set, the order will be + * {@code (0, 0), (0, 1), (1, 0), (1, 1)}. + * + * @return The Tiles. + */ + public Stream getTiles() { + return Arrays.stream(tiles).flatMap(Arrays::stream); + } + +} \ No newline at end of file diff --git a/cache/src/main/org/apollo/cache/map/Tile.java b/cache/src/main/org/apollo/cache/map/Tile.java new file mode 100644 index 00000000..e19d8133 --- /dev/null +++ b/cache/src/main/org/apollo/cache/map/Tile.java @@ -0,0 +1,295 @@ +package org.apollo.cache.map; + +/** + * A single tile on the map. + * + * @author Major + */ +public final class Tile { + + /** + * A builder class for a Tile. + */ + public static final class Builder { + + /** + * The attributes of the Tile. + */ + private int attributes; + + /** + * The height of the Tile. + */ + private int height; + + /** + * The overlay id of the Tile. + */ + private int overlay; + + /** + * The overlay orientation of the Tile. + */ + private int overlayOrientation; + + /** + * The overlay type of the Tile. + */ + private int overlayType; + + /** + * The x coordinate of the Tile. + */ + private int x; + + /** + * The y coordinate of the Tile. + */ + private int y; + + /** + * The underlay id of the Tile. + */ + private int underlay; + + /** + * Creates the Builder. + * + * @param x The x position of the Tile. + * @param y The y position of the Tile. + * @param height The height level of the Tile. + */ + public Builder(int x, int y, int height) { + this.x = x; + this.y = y; + this.height = height; + } + + /** + * Builds the contents of this Builder into a Tile. + * + * @return The Tile. + */ + public Tile build() { + return new Tile(x, y, attributes, height, overlay, overlayType, overlayOrientation, underlay); + } + + /** + * Sets the attributes of the Tile. + * + * @param attributes The attributes. + */ + public void setAttributes(int attributes) { + this.attributes = attributes; + } + + /** + * Sets the height of the Tile. + * + * @param height The height. + */ + public void setHeight(int height) { + this.height = height; + } + + /** + * Sets the overlay id of the Tile. + * + * @param overlay The overlay id. + */ + public void setOverlay(int overlay) { + this.overlay = overlay; + } + + /** + * Sets the overlay orientation of the Tile. + * + * @param orientation The overlay orientation. + */ + public void setOverlayOrientation(int orientation) { + this.overlayOrientation = orientation; + } + + /** + * Sets the overlay type of the Tile. + * + * @param type The overlay type. + */ + public void setOverlayType(int type) { + this.overlayType = type; + } + + /** + * Sets the position of the Tile. + * + * @param x The x coordinate of the Tile. + * @param y the y coordinate of the Tile + * @param height The height level of the Tile. + */ + public void setPosition(int x, int y, int height) { + this.x = x; + this.y = y; + this.height = height; + } + + /** + * Sets the underlay id of the Tile. + * + * @param underlay The underlay. + */ + public void setUnderlay(int underlay) { + this.underlay = underlay; + } + + } + + /** + * Creates a {@link Builder} for a Tile. + * + * @param x The x coordinate of the Tile. + * @param y the y coordinate of the Tile. + * @param height The height level of the Tile. + * @return The Builder. + */ + public static Builder builder(int x, int y, int height) { + return new Builder(x, y, height); + } + + /** + * The attributes of this Tile. + */ + private final int attributes; + + /** + * The height of this Tile. + */ + private final int height; + + /** + * The overlay id of this Tile. + */ + private final int overlay; + + /** + * The overlay orientation of this Tile. + */ + private final int overlayOrientation; + + /** + * The overlay type of this Tile. + */ + private final int overlayType; + + /** + * The x coordinate of this Tile. + */ + private final int x; + + /** + * The y coordinate of this Tile. + */ + private final int y; + + /** + * The underlay id of this Tile. + */ + private final int underlay; + + /** + * Creates the Tile. + * + * @param x The x coordinate of the Tile. + * @param y The y coordinate of the Tile. + * @param attributes The attributes. + * @param height The height. + * @param overlay The overlay id. + * @param overlayType The overlay type. + * @param overlayOrientation The overlay orientation. + * @param underlay The underlay id. + */ + public Tile(int x, int y, int attributes, int height, int overlay, int overlayType, int overlayOrientation, + int underlay) { + this.x = x; + this.y = y; + this.attributes = attributes; + this.height = height; + this.overlay = overlay; + this.overlayType = overlayType; + this.overlayOrientation = overlayOrientation; + this.underlay = underlay; + } + + /** + * Gets the attributes of this Tile. + * + * @return The attributes. + */ + public int getAttributes() { + return attributes; + } + + /** + * Gets the height of this Tile. + * + * @return The height. + */ + public int getHeight() { + return height; + } + + /** + * Gets the overlay id of this Tile. + * + * @return The overlay id. + */ + public int getOverlay() { + return overlay; + } + + /** + * Gets the overlay orientation of this Tile. + * + * @return The overlay orientation. + */ + public int getOverlayOrientation() { + return overlayOrientation; + } + + /** + * Gets the overlay type of this Tile. + * + * @return The overlay types. + */ + public int getOverlayType() { + return overlayType; + } + + /** + * Gets the underlay id of this Tile. + * + * @return The underlay id. + */ + public int getUnderlay() { + return underlay; + } + + + /** + * Gets the x coordinate of this Tile. + * + * @return The x coordinate. + */ + public int getX() { + return x; + } + + /** + * Gets the y coordinate of this Tile. + * + * @return The y coordinate. + */ + public int getY() { + return y; + } + +} diff --git a/cache/src/main/org/apollo/cache/map/TileUtils.java b/cache/src/main/org/apollo/cache/map/TileUtils.java new file mode 100644 index 00000000..977e9562 --- /dev/null +++ b/cache/src/main/org/apollo/cache/map/TileUtils.java @@ -0,0 +1,161 @@ +package org.apollo.cache.map; + +/* + * Copyright (c) 2012-2013 Jonathan Edgecombe + * Copyright (c) 2015 Major + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * Contains tile-related utility methods. + * + * @author Johnny + * @author Major + */ +public final class TileUtils { + + /** + * The x coordinate offset, used for computing the Tile height. + */ + static final int TILE_HEIGHT_X_OFFSET = 0xe3b7b; + + /** + * The z coordinate offset, used for computing the Tile height. + */ + static final int TILE_HEIGHT_Z_OFFSET = 0x87cce; + + /** + * The cosine table used for interpolation. + */ + private static final int[] COSINE = new int[2048]; + + static { + for (int index = 0; index < COSINE.length; index++) { + COSINE[index] = (int) (65536 * Math.cos(2 * Math.PI * index / COSINE.length)); + } + } + + /** + * Calculates the height offset for the specified coordinate pair. + * + * @param x The x coordinate of the Tile. + * @param z The z coordinate of the Tile. + * @return The height offset. + */ + public static int calculateHeight(int x, int z) { + int regionSize = 8; + int regionOffset = 6; + int offset = regionOffset * regionSize; + + int baseX = x - offset; + int baseZ = z - offset; + + return computeHeight(x + TILE_HEIGHT_X_OFFSET - baseX, z + TILE_HEIGHT_Z_OFFSET - baseZ) + * MapConstants.HEIGHT_MULTIPLICAND; + } + + /** + * Gets the height offset for the specified coordinate pair. + * + * @param x The offset-x coordinate of the tile. + * @param z The offset-z coordinate of the tile. + * @return The tile height offset. + */ + private static int computeHeight(int x, int z) { + int total = interpolatedNoise(x + 45365, z + 91923, 4) - 128; + + total += (interpolatedNoise(x + 10294, z + 37821, 2) - 128) / 2; + total += (interpolatedNoise(x, z, 1) - 128) / 4; + + total = (int) Math.max(total * 0.3 + 35, 10); + return Math.min(total, 60); + } + + /** + * Interpolates two smooth noise values. + * + * @param a The first smooth noise value. + * @param b The second smooth noise value. + * @param theta The angle. + * @param reciprocal The frequency reciprocal. + * @return The interpolated value. + */ + private static int interpolate(int a, int b, int theta, int reciprocal) { + int cosine = 65536 - COSINE[theta * COSINE.length / (2 * reciprocal)] / 2; + return (a * (65536 - cosine)) / 65536 + (b * cosine) / 65536; + } + + /** + * Gets interpolated noise for the specified coordinate pair, using the specified frequency reciprocal. + * + * @param x The x coordinate. + * @param z The z coordinate. + * @param reciprocal The frequency reciprocal. + * @return The interpolated noise. + */ + private static int interpolatedNoise(int x, int z, int reciprocal) { + int xt = x % reciprocal; + int zt = z % reciprocal; + + x /= reciprocal; + z /= reciprocal; + + int c = smoothNoise(x, z); + int e = smoothNoise(x + 1, z); + int ce = interpolate(c, e, xt, reciprocal); + + int n = smoothNoise(x, z + 1); + int ne = smoothNoise(x + 1, z + 1); + int u = interpolate(n, ne, xt, reciprocal); + + return interpolate(ce, u, zt, reciprocal); + } + + /** + * Computes noise for the specified coordinate pair. + * + * @param x The x coordinate. + * @param z The z coordinate. + * @return The noise. + */ + private static int noise(int x, int z) { + int n = x + z * 57; + n = (n << 13) ^ n; + n = (n * (n * n * 15731 + 789221) + 1376312589) & Integer.MAX_VALUE; + return (n >> 19) & 0xff; + } + + /** + * Computes smooth noise for the specified coordinate pair. + * + * @param x The x coordinate. + * @param z The z coordinate. + * @return The smooth noise. + */ + private static int smoothNoise(int x, int z) { + int corners = noise(x - 1, z - 1) + noise(x + 1, z - 1) + noise(x - 1, z + 1) + noise(x + 1, z + 1); + int sides = noise(x - 1, z) + noise(x + 1, z) + noise(x, z - 1) + noise(x, z + 1); + int center = noise(x, z); + + return corners / 16 + sides / 8 + center / 4; + } + + /** + * Sole private constructor to prevent instantiation. + */ + private TileUtils() { + + } + +} \ No newline at end of file diff --git a/game/src/main/org/apollo/game/fs/decoder/GameObjectDecoder.java b/game/src/main/org/apollo/game/fs/decoder/GameObjectDecoder.java index 68b7af03..1998f12f 100644 --- a/game/src/main/org/apollo/game/fs/decoder/GameObjectDecoder.java +++ b/game/src/main/org/apollo/game/fs/decoder/GameObjectDecoder.java @@ -8,10 +8,10 @@ import java.util.List; import java.util.Map; import org.apollo.cache.IndexedFileSystem; -import org.apollo.cache.decoder.MapFileDecoder; -import org.apollo.cache.decoder.MapFileDecoder.MapDefinition; import org.apollo.cache.decoder.ObjectDefinitionDecoder; import org.apollo.cache.def.ObjectDefinition; +import org.apollo.cache.map.MapIndex; +import org.apollo.cache.map.MapIndexDecoder; import org.apollo.game.io.player.PlayerSerializer; import org.apollo.game.model.Position; import org.apollo.game.model.World; @@ -85,10 +85,13 @@ public final class GameObjectDecoder implements Runnable { ObjectDefinitionDecoder decoder = new ObjectDefinitionDecoder(fs); decoder.run(); - try { - Map definitions = MapFileDecoder.decode(fs); + MapIndexDecoder mapIndexDecoder = new MapIndexDecoder(fs); + mapIndexDecoder.run(); - for (MapDefinition definition : definitions.values()) { + try { + Map indices = MapIndex.getIndices(); + + for (MapIndex definition : indices.values()) { int packed = definition.getPackedCoordinates(); int x = (packed >> 8 & 0xFF) * (Region.SIZE * Region.SIZE); int y = (packed & 0xFF) * (Region.SIZE * Region.SIZE); @@ -97,7 +100,7 @@ public final class GameObjectDecoder implements Runnable { ByteBuffer decompressed = ByteBuffer.wrap(CompressionUtil.degzip(objects)); decodeObjects(decompressed, x, y); - ByteBuffer terrain = fs.getFile(4, definition.getTerrainFile()); + ByteBuffer terrain = fs.getFile(4, definition.getMapFile()); decompressed = ByteBuffer.wrap(CompressionUtil.degzip(terrain)); decodeTerrain(decompressed, x, y); }