package Hexel.chunk;
import java.util.concurrent.ExecutionException;
import Hexel.blocks.types.Block;
import Hexel.blocks.types.BlockTransparent;
import Hexel.generation.ChunkGenerator;
import Hexel.math.HexGeometry;
import Hexel.math.Vector2i;
import Hexel.math.Vector3i;
import Hexel.util.Cleanup;
import Hexel.util.Container;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.cache.RemovalListener;
import com.google.common.cache.RemovalNotification;
public class Chunks {
public ChunkGenerator chunkGenerator;
private LoadingCache<Vector3i, Chunk> chunkCache;
private LoadingCache<Vector2i, HighestBlockChunk> highestBlockCache;
private Cleanup cleanup;
public Chunks(Cleanup cleanup, int numToBuffer) {
this.cleanup = cleanup;
this.chunkCache = CacheBuilder.newBuilder().maximumSize(numToBuffer)
.recordStats()
.removalListener(new RemovalListener<Vector3i, Chunk>() {
@Override
public void onRemoval(
RemovalNotification<Vector3i, Chunk> notification) {
Chunk c = notification.getValue();
if (c != null && c.modified) {
ChunkFile.save(c);
}
}
}).build(new CacheLoader<Vector3i, Chunk>() {
@Override
public Chunk load(Vector3i p) {
Chunk chunk;
if (ChunkFile.has(p)) {
chunk = ChunkFile.load(p);
} else {
chunk = Chunks.this.chunkGenerator.genChunk(p);
}
return chunk;
}
});
this.highestBlockCache = CacheBuilder.newBuilder().maximumSize(numToBuffer)
.removalListener(new RemovalListener<Vector2i, HighestBlockChunk>() {
@Override
public void onRemoval(
RemovalNotification<Vector2i, HighestBlockChunk> notification) {
HighestBlockChunk map = notification.getValue();
Vector2i loc = notification.getKey();
if (map != null) {
HighestBlockFile.save(loc, map);
}
}
}).build(new CacheLoader<Vector2i, HighestBlockChunk>() {
@Override
public HighestBlockChunk load(Vector2i p) {
HighestBlockChunk map;
if (HighestBlockFile.has(p)) {
map = HighestBlockFile.load(p);
} else {
map = new HighestBlockChunk();
map.x = p.x;
map.y = p.y;
map.highestBlocks = new int[Chunk.WIDTH][Chunk.HEIGHT];
for (int x = 0; x < Chunk.WIDTH; x++){
for (int y = 0; y < Chunk.HEIGHT; y++){
map.highestBlocks[x][y] = Integer.MIN_VALUE;
}
}
}
return map;
}
});
cleanup.add(new Runnable() {
@Override
public void run() {
unloadAllChunks();
}
});
}
public void setSeed(int seed){
this.chunkGenerator = new ChunkGenerator(seed, this, cleanup);
}
public Chunk getChunk(Vector3i cpos) {
try {
Chunk c = this.chunkCache.getIfPresent(cpos);
if (c != null)
return c;
else
return this.chunkCache.get(new Vector3i(cpos));
} catch (Exception e) {
System.out.println("Caught Exception retrieving chunk from cache!");
e.printStackTrace();
System.exit(1);
return null;
}
}
public HighestBlockChunk getHighestBlockChunk(Vector2i pos){
try {
HighestBlockChunk hbm = this.highestBlockCache.getIfPresent(pos);
if (hbm != null)
return hbm;
else
return this.highestBlockCache.get(new Vector2i(pos));
} catch (Exception e) {
System.out.println("Caught Exception retrieving highest block map from cache!");
e.printStackTrace();
System.exit(1);
return null;
}
}
public void unloadAllChunks() {
this.chunkCache.invalidateAll();
this.highestBlockCache.invalidateAll();
}
public HighestBlockChunk getHighestBlockChunkAtXY(int x, int y, Vector2i pos){
int cx = (int) Math.floor(x / 32.0);
int cy = (int) Math.floor(y / 16.0);
int bx = x - cx * 32;
int by = y - cy * 16;
pos.x = cx;
pos.y = cy;
HighestBlockChunk heights = this.getHighestBlockChunk(pos);
return heights;
}
public int getHighestBlockAtXY(int x, int y, Vector2i pos){
int cx = (int) Math.floor(x / 32.0);
int cy = (int) Math.floor(y / 16.0);
int bx = x - cx * 32;
int by = y - cy * 16;
pos.x = cx;
pos.y = cy;
HighestBlockChunk heights = this.getHighestBlockChunk(pos);
return heights.highestBlocks[bx][by];
}
public int getHighestBlockAtXY(int x, int y, Vector2i pos, Container<HighestBlockChunk> hbc){
int cx = (int) Math.floor(x / 32.0);
int cy = (int) Math.floor(y / 16.0);
int bx = x - cx * 32;
int by = y - cy * 16;
if (hbc.contents == null || !hbc.contents.containsPoint(x, y)){
pos.x = cx;
pos.y = cy;
hbc.contents = this.getHighestBlockChunk(pos);
}
return hbc.contents.highestBlocks[bx][by];
}
public void updateHighestBlockAtXY(int x, int y, int z, Vector2i pos){
int cx = (int) Math.floor(x / 32.0);
int cy = (int) Math.floor(y / 16.0);
int bx = x - cx * 32;
int by = y - cy * 16;
pos.x = cx;
pos.y = cy;
HighestBlockChunk heights;
try {
heights = this.highestBlockCache.get(pos);
if (z > heights.highestBlocks[bx][by])
heights.highestBlocks[bx][by] = z;
} catch (ExecutionException e) {
e.printStackTrace();
System.exit(1);
}
}
public Block getBlock(int x, int y, int z, Vector3i p, Chunk chunk) {
int cx = (int) Math.floor(x / 32.0);
int cy = (int) Math.floor(y / 16.0);
int cz = (int) Math.floor(z / 32.0);
int bx = x - cx * 32;
int by = y - cy * 16;
int bz = z - cz * 32;
if (chunk == null || !chunk.containsPoint(x, y, z)){
p.x = cx;
p.y = cy;
p.z = cz;
chunk = getChunk(p);
}
return chunk.get(bx, by, bz);
}
public Block getBlock(int x, int y, int z, Vector3i p, Container<Chunk> chunk) {
int cx = (int) Math.floor(x / 32.0);
int cy = (int) Math.floor(y / 16.0);
int cz = (int) Math.floor(z / 32.0);
int bx = x - cx * 32;
int by = y - cy * 16;
int bz = z - cz * 32;
if (chunk.contents == null || !chunk.contents.containsPoint(x, y, z)){
p.x = cx;
p.y = cy;
p.z = cz;
chunk.contents = getChunk(p);
}
return chunk.contents.get(bx, by, bz);
}
public Block getBlock(int cx, int cy, int cz, int x, int y, int z, Vector3i p, Chunk c) {
int gx = cx * 32 + x;
int gy = cy * 16 + y;
int gz = cz * 32 + z;
return this.getBlock(gx, gy, gz, p, c);
}
public void setStepsToSim(int x, int y, int z, int stepsToSim, Vector3i p){
int cx = (int) Math.floor(x / 32.0);
int cy = (int) Math.floor(y / 16.0);
int cz = (int) Math.floor(z / 32.0);
int bx = x - cx * 32;
int by = y - cy * 16;
int bz = z - cz * 32;
p.x = cx;
p.y = cy;
p.z = cz;
Chunk chunk = getChunk(p);
chunk.stepsToSim[bx][by][bz] = stepsToSim;
if (stepsToSim > 0)
chunk.nextSimStep = -1;
}
public int getStepsToSim(int x, int y, int z, Vector3i p, Chunk chunk){
int cx = (int) Math.floor(x / 32.0);
int cy = (int) Math.floor(y / 16.0);
int cz = (int) Math.floor(z / 32.0);
int bx = x - cx * 32;
int by = y - cy * 16;
int bz = z - cz * 32;
p.x = cx;
p.y = cy;
p.z = cz;
if (chunk == null || !chunk.containsPoint(x, y, z)){
p.x = cx;
p.y = cy;
p.z = cz;
chunk = getChunk(p);
}
return chunk.stepsToSim[bx][by][bz];
}
public int getStepsToSim(int x, int y, int z, Vector3i p, Container<Chunk> chunk) {
int cx = (int) Math.floor(x / 32.0);
int cy = (int) Math.floor(y / 16.0);
int cz = (int) Math.floor(z / 32.0);
int bx = x - cx * 32;
int by = y - cy * 16;
int bz = z - cz * 32;
if (chunk.contents == null || !chunk.contents.containsPoint(x, y, z)){
p.x = cx;
p.y = cy;
p.z = cz;
chunk.contents = getChunk(p);
}
return chunk.contents.stepsToSim[bx][by][bz];
}
public Chunk simBlock(int x, int y, int z, Vector3i p, Chunk chunk){
int cx = (int) Math.floor(x / 32.0);
int cy = (int) Math.floor(y / 16.0);
int cz = (int) Math.floor(z / 32.0);
int bx = x - cx * 32;
int by = y - cy * 16;
int bz = z - cz * 32;
if (chunk == null || !chunk.containsPoint(x, y, z)){
p.x = cx;
p.y = cy;
p.z = cz;
chunk = getChunk(p);
}
chunk.nextSimSteps[bx][by][bz] = -1;
chunk.nextSimStep = -1;
return chunk;
}
public void setBlock(int x, int y, int z, Block b, Vector3i p, Vector2i tmp2, Chunk chunk) {
int cx = (int) Math.floor(x / 32.0);
int cy = (int) Math.floor(y / 16.0);
int cz = (int) Math.floor(z / 32.0);
int bx = x - cx * 32;
int by = y - cy * 16;
int bz = z - cz * 32;
if (chunk == null || !chunk.containsPoint(x, y, z)){
p.x = cx;
p.y = cy;
p.z = cz;
chunk = getChunk(p);
}
chunk.set(bx, by, bz, b);
chunk.setDirty(true);
this.simBlock(x, y, z, p, chunk);
if (! (b instanceof BlockTransparent)){
updateHighestBlockAtXY(x, y, z, tmp2);
}
chunk.modified = true;
Vector3i[] neighbors;
if (x % 2 == 0) {
neighbors = HexGeometry.evenAllNeighbors;
} else {
neighbors = HexGeometry.oddAllNeighbors;
}
for (Vector3i neighbor : neighbors){
int x2 = x + neighbor.x;
int y2 = y + neighbor.y;
int z2 = z + neighbor.z;
Chunk chunk2 = this.simBlock(x2, y2, z2, p, chunk);
chunk2.setDirty(true);
}
}
}