using System;
using System.IO;
using System.Collections.Generic;
namespace Substrate
{
///
/// Provides a wrapper around a physical Chunk stored in a chunk container. Modifying data in a ChunkRef will signal to the chunk
/// container that the physical chunk needs to be saved.
///
public class ChunkRef : IChunk
{
private IChunkContainer _container;
private Chunk _chunk;
private AlphaBlockCollection _blocks;
private EntityCollection _entities;
private int _cx;
private int _cz;
private bool _dirty;
///
/// Gets the global X-coordinate of the chunk.
///
public int X
{
get { return _container.ChunkGlobalX(_cx); }
}
///
/// Gets the global Z-coordinate of the chunk.
///
public int Z
{
get { return _container.ChunkGlobalZ(_cz); }
}
///
/// Gets the local X-coordinate of the chunk within container.
///
public int LocalX
{
get { return _container.ChunkLocalX(_cx); }
}
///
/// Gets the local Z-coordinate of the chunk within container.
///
public int LocalZ
{
get { return _container.ChunkLocalZ(_cz); }
}
///
/// Gets the collection of all blocks and their data stored in the chunk.
///
public AlphaBlockCollection Blocks
{
get
{
if (_blocks == null) {
GetChunk();
}
return _blocks;
}
}
///
/// Gets the collection of all entities stored in the chunk.
///
public EntityCollection Entities
{
get
{
if (_entities == null) {
GetChunk();
}
return _entities;
}
}
///
/// Gets or sets the value indicating that the chunk has been modified, but not saved.
///
public bool IsDirty
{
get
{
return _dirty
|| (_blocks != null && _blocks.IsDirty)
|| (_entities != null && _entities.IsDirty);
}
set
{
_dirty = value;
if (_blocks != null)
_blocks.IsDirty = false;
if (_entities != null)
_entities.IsDirty = false;
}
}
///
/// Forbid direct instantiation of ChunkRef objects
///
private ChunkRef ()
{
}
///
/// Create a reference to a chunk stored in a chunk container.
///
/// Chunk container
/// Local X-coordinate of chunk within container.
/// Local Z-coordinate of chunk within container.
/// ChunkRef representing a reference to a physical chunk at the specified location within the container.
public static ChunkRef Create (IChunkContainer container, int cx, int cz)
{
if (!container.ChunkExists(cx, cz)) {
return null;
}
ChunkRef c = new ChunkRef();
c._container = container;
c._cx = cx;
c._cz = cz;
return c;
}
///
/// Gets or sets the chunk's TerrainPopulated status.
///
public bool IsTerrainPopulated
{
get { return GetChunk().IsTerrainPopulated; }
set
{
if (GetChunk().IsTerrainPopulated != value) {
GetChunk().IsTerrainPopulated = value;
_dirty = true;
}
}
}
///
/// Saves the underlying physical chunk to the specified output stream.
///
/// An open output stream.
/// A value indicating whether the chunk is no longer considered dirty.
public bool Save (Stream outStream)
{
if (IsDirty) {
if (GetChunk().Save(outStream)) {
IsDirty = false;
return true;
}
return false;
}
return true;
}
///
/// Gets a ChunkRef to the chunk positioned immediately north (X - 1).
///
/// ChunkRef to the northern neighboring chunk.
public ChunkRef GetNorthNeighbor ()
{
return _container.GetChunkRef(_cx - 1, _cz);
}
///
/// Gets a ChunkRef to the chunk positioned immediately south (X + 1).
///
/// ChunkRef to the southern neighboring chunk.
public ChunkRef GetSouthNeighbor ()
{
return _container.GetChunkRef(_cx + 1, _cz);
}
///
/// Gets a ChunkRef to the chunk positioned immediatly east (Z - 1).
///
/// ChunkRef to the eastern neighboring chunk.
public ChunkRef GetEastNeighbor ()
{
return _container.GetChunkRef(_cx, _cz - 1);
}
///
/// Gets a ChunkRef to the chunk positioned immedately west (Z + 1).
///
/// ChunkRef to the western neighboring chunk.
public ChunkRef GetWestNeighbor ()
{
return _container.GetChunkRef(_cx, _cz + 1);
}
///
/// Returns a deep copy of the physical chunk underlying the ChunkRef.
///
/// A copy of the physical Chunk object.
public Chunk GetChunkCopy ()
{
return GetChunk().Copy();
}
///
/// Returns the reference of the physical chunk underlying the ChunkRef, and releases the reference from itself.
///
///
/// This function returns the reference to the chunk stored in the chunk container. Because the ChunkRef simultaneously gives up
/// its "ownership" of the Chunk, the container will not consider the Chunk dirty even if it is modified. Attempting to use the ChunkRef after
/// releasing its internal reference will query the container for a new reference. If the chunk is still cached, it will get the same reference
/// back, otherwise it will get an independent copy. Chunks should only be taken from ChunkRefs to transfer them to another ChunkRef, or
/// to modify them without intending to permanently store the changes.
///
/// The physical Chunk object underlying the ChunkRef
public Chunk GetChunkRef ()
{
Chunk chunk = GetChunk();
_chunk = null;
_dirty = false;
return chunk;
}
///
/// Replaces the underlying physical chunk with a different one, updating its physical location to reflect the ChunkRef.
///
///
/// Use this function to save chunks that have been created or manipulated independently of a container, or to
/// move a physical chunk between locations within a container (by taking the reference from another ChunkRef).
///
/// Physical Chunk to store into the location represented by this ChunkRef.
public void SetChunkRef (Chunk chunk)
{
_chunk = chunk;
_chunk.SetLocation(X, Z);
_dirty = true;
}
///
/// Gets an internal Chunk reference from cache or queries the container for it.
///
/// The ChunkRef's underlying Chunk.
private Chunk GetChunk ()
{
if (_chunk == null) {
_chunk = _container.GetChunk(_cx, _cz);
if (_chunk != null) {
_blocks = _chunk.Blocks;
_entities = _chunk.Entities;
// Set callback functions in the underlying block collection
_blocks.ResolveNeighbor += ResolveNeighborHandler;
_blocks.TranslateCoordinates += TranslateCoordinatesHandler;
}
}
return _chunk;
}
///
/// Callback function to return the block collection of a ChunkRef at a relative offset to this one.
///
/// Relative offset from the X-coordinate.
/// Relative offset from the Y-coordinate.
/// Relative offset from the Z-coordinate.
/// Another ChunkRef's underlying block collection, or null if the ChunkRef cannot be found.
private AlphaBlockCollection ResolveNeighborHandler (int relx, int rely, int relz)
{
ChunkRef cr = _container.GetChunkRef(_cx + relx, _cz + relz);
if (cr != null) {
return cr.Blocks;
}
return null;
}
///
/// Translates chunk-local block coordinates to corresponding global coordinates.
///
/// Chunk-local X-coordinate.
/// Chunk-local Y-coordinate.
/// Chunk-local Z-coordinate.
/// BlockKey containing the global block coordinates.
private BlockKey TranslateCoordinatesHandler (int lx, int ly, int lz)
{
int x = X * _blocks.XDim + lx;
int z = Z * _blocks.ZDim + lz;
return new BlockKey(x, ly, z);
}
}
}