package net.scapeemulator.game.cache;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.LinkedList;
import java.util.List;
import java.util.zip.ZipException;
import net.scapeemulator.cache.Cache;
import net.scapeemulator.cache.Container;
import net.scapeemulator.cache.ReferenceTable;
import net.scapeemulator.cache.util.ByteBufferUtils;
import net.scapeemulator.game.model.Position;
import net.scapeemulator.game.model.World;
import net.scapeemulator.game.model.npc.NPC;
import net.scapeemulator.game.model.npc.stateful.impl.NormalNPC;
import net.scapeemulator.game.model.object.ObjectType;
import net.scapeemulator.game.util.LandscapeKeyTable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class MapLoader {
/**
* Flags that the tile is below a roof, for removing the roof during rendering.
*/
public static final int ROOF_FLAG = 0x4;
public static final int FLAG_CLIP = 0x1;
public static final int BRIDGE_FLAG = 0x2;
public static final boolean LOAD_NPCS = false;
private static final Logger logger = LoggerFactory.getLogger(MapLoader.class);
private final List<MapListener> listeners = new LinkedList<MapListener>();
private final Cache cache;
private final LandscapeKeyTable keyTable;
private final boolean[][] loaded = new boolean[256][256];
private ReferenceTable rt;
public MapLoader(Cache cache, LandscapeKeyTable keyTable) {
this.cache = cache;
this.keyTable = keyTable;
}
public void addListener(MapListener listener) {
listeners.add(listener);
}
public void load(boolean loadAll) throws IOException {
rt = ReferenceTable.decode(Container.decode(cache.getStore().read(255, 5)).getData());
if (loadAll) {
logger.info("Reading all map and landscape files...");
for (int x = 0; x < loaded.length; x++) {
for (int y = 0; y < loaded[x].length; y++) {
load(x, y);
}
}
}
}
public void load(int mX, int mY) {
for (int x = mX - 4; x <= mX + 4 && x < loaded.length; x++) {
if (x < 0) {
continue;
}
for (int y = mY - 4; y <= mY + 4 && y < loaded[x].length; y++) {
if (y < 0) {
continue;
}
if (loaded[x][y]) {
continue;
}
try {
int landscapeId = rt.getEntryId("l" + x + "_" + y);
if (landscapeId != -1) {
readLandscape(x, y, landscapeId);
}
int mapId = rt.getEntryId("m" + x + "_" + y);
if (mapId != -1) {
readMap(x, y, mapId);
}
int npcId = rt.getEntryId("n" + x + "_" + y);
if (LOAD_NPCS && npcId != -1) {
readNPC(x, y, npcId);
}
loaded[x][y] = true;
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
private void readLandscape(int x, int y, int fileId) throws IOException {
ByteBuffer buffer = cache.getStore().read(5, fileId);
int[] keys = keyTable.getKeys(x, y);
try {
buffer = Container.decode(buffer, keys).getData();
} catch (ZipException ze) {
System.err.println(ze.getMessage() + " readLandscape(" + x + ", " + y + ", " + fileId + ")");
}
int id = -1;
while (true) {
int deltaId = ByteBufferUtils.getSmart(buffer);
if (deltaId == 0) {
break;
}
id += deltaId;
int pos = 0;
while (true) {
int deltaPos = ByteBufferUtils.getSmart(buffer);
if (deltaPos == 0) {
break;
}
pos += deltaPos - 1;
int localX = (pos >> 6) & 0x3F;
int localY = pos & 0x3F;
int height = (pos >> 12) & 0x3;
int temp = buffer.get() & 0xFF;
int type = temp >> 2;
int rotation = temp & 0x3;
Position position = new Position(x * 64 + localX, y * 64 + localY, height);
for (MapListener listener : listeners) {
listener.objectDecoded(id, rotation, ObjectType.forId(type), position);
}
}
}
}
@SuppressWarnings("unused")
private void readMap(int x, int y, int id) throws IOException {
ByteBuffer buffer = cache.read(5, id).getData();
for (int plane = 0; plane < 4; plane++) {
for (int localX = 0; localX < 64; localX++) {
for (int localY = 0; localY < 64; localY++) {
Position position = new Position(x * 64 + localX, y * 64 + localY, plane);
/* The tile variables */
int flags = 0;
for (;;) {
int config = buffer.get() & 0xFF;
if (config == 0) {
for (MapListener listener : listeners) {
listener.tileDecoded(flags, position);
}
break;
} else if (config == 1) {
int i = buffer.get() & 0xFF;
for (MapListener listener : listeners) {
listener.tileDecoded(flags, position);
}
break;
} else if (config <= 49) {
int i = buffer.get() & 0xFF;
} else if (config <= 81) {
flags = config - 49;
}
}
}
}
}
}
private void readNPC(int x, int y, int fileId) throws IOException {
ByteBuffer buffer = cache.read(5, fileId).getData();
while (buffer.hasRemaining()) {
int compressedData = buffer.getShort() & 0xFFFFF;
int height = compressedData >> 14;
int localX = 63 & compressedData >> 7;
int localY = compressedData & 63;
int npcId = buffer.getShort();
NPC npc = new NormalNPC(npcId);
npc.setPosition(new Position((x * 64) + localX, (y * 64) + localY, height));
World.getWorld().addNpc(npc);
}
}
}