using System; using System.Collections.Generic; using Substrate.Core; using Substrate.Nbt; namespace Substrate { /// /// Functions for reading and modifying a bounded-size collection of Alpha-compatible block data. /// /// An is a wrapper around existing pieces of data. Although it /// holds references to data, it does not "own" the data in the same way that a does. An /// simply overlays a higher-level interface on top of existing data. public class AlphaBlockCollection : IBoundedAlphaBlockCollection { private readonly int _xdim; private readonly int _ydim; private readonly int _zdim; private XZYByteArray _blocks; private XZYNibbleArray _data; private XZYNibbleArray _blockLight; private XZYNibbleArray _skyLight; private ZXByteArray _heightMap; private TagNodeList _tileEntities; private BlockLight _lightManager; private BlockFluid _fluidManager; private BlockTileEntities _tileEntityManager; private bool _dirty = false; private bool _autoLight = true; private bool _autoFluid = false; public delegate AlphaBlockCollection NeighborLookupHandler (int relx, int rely, int relz); public event NeighborLookupHandler ResolveNeighbor { add { _lightManager.ResolveNeighbor += delegate(int relx, int rely, int relz) { return value(relx, rely, relz); }; _fluidManager.ResolveNeighbor += delegate(int relx, int rely, int relz) { return value(relx, rely, relz); }; } remove { _lightManager = new BlockLight(this); _fluidManager = new BlockFluid(this); } } public event BlockCoordinateHandler TranslateCoordinates { add { _tileEntityManager.TranslateCoordinates += value; } remove { _tileEntityManager.TranslateCoordinates -= value; } } /// /// Gets or sets a value indicating whether changes to blocks will trigger automatic lighting updates. /// /// Automatic updates to lighting may spill into neighboring objects, if they can /// be resolved. public bool AutoLight { get { return _autoLight; } set { _autoLight = value; } } /// /// Gets or sets a value indicating whether changes to blocks will trigger automatic fluid updates. /// /// Automatic updates to fluid may cascade through neighboring objects and beyond, /// if they can be resolved. public bool AutoFluid { get { return _autoFluid; } set { _autoFluid = value; } } /// /// Gets or sets a value indicating whether this needs to be saved. /// /// If this is backed by a reference conainer type, set this property to false /// to prevent any modifications from being saved. The next update will set this property true again, however. public bool IsDirty { get { return _dirty; } set { _dirty = value; } } /// /// Creates a new overlay on top of Alpha-specific units of data. /// /// An array of Block IDs. /// An array of data nibbles. /// An array of block light nibbles. /// An array of sky light nibbles. /// An array of height map values. /// A list of tile entities corresponding to blocks in this collection. public AlphaBlockCollection ( XZYByteArray blocks, XZYNibbleArray data, XZYNibbleArray blockLight, XZYNibbleArray skyLight, ZXByteArray heightMap, TagNodeList tileEntities) { _blocks = blocks; _data = data; _blockLight = blockLight; _skyLight = skyLight; _heightMap = heightMap; _tileEntities = tileEntities; _xdim = _blocks.XDim; _ydim = _blocks.YDim; _zdim = _blocks.ZDim; _lightManager = new BlockLight(this); _fluidManager = new BlockFluid(this); _tileEntityManager = new BlockTileEntities(_blocks, _tileEntities); } /// /// Returns a new object from local coordinates relative to this collection. /// /// Local X-coordinate of block. /// Local Y-coordinate of block. /// Local Z-coordiante of block. /// A new object representing context-independent data of a single block. /// Context-independent data excludes data such as lighting. object actually contain a copy /// of the data they represent, so changes to the will not affect this container, and vice-versa. public AlphaBlock GetBlock (int x, int y, int z) { return new AlphaBlock(this, x, y, z); } /// /// Returns a new object from local coordaintes relative to this collection. /// /// Local X-coordinate of block. /// Local Y-coordinate of block. /// Local Z-coordinate of block. /// A new object representing context-dependent data of a single block. /// Context-depdendent data includes all data associated with this block. Since a represents /// a view of a block within this container, any updates to data in the container will be reflected in the , /// and vice-versa for updates to the . public AlphaBlockRef GetBlockRef (int x, int y, int z) { return new AlphaBlockRef(this, _blocks.GetIndex(x, y, z)); } /// /// Updates a block in this collection with values from a object. /// /// Local X-coordinate of a block. /// Local Y-coordinate of a block. /// Local Z-coordinate of a block. /// A object to copy block data from. public void SetBlock (int x, int y, int z, AlphaBlock block) { SetID(x, y, z, block.ID); SetData(x, y, z, block.Data); SetTileEntity(x, y, z, block.GetTileEntity().Copy()); } #region IBlockCollection Members /// /// Gets the length of this collection's X-dimension. /// public int XDim { get { return _xdim; } } /// /// Gets the length of this collection's Y-dimension. /// public int YDim { get { return _ydim; } } /// /// Gets the length of this collection's Z-dimension. /// public int ZDim { get { return _zdim; } } /// /// Returns an object compatible with the interface from local coordinates relative to this collection. /// /// Local X-coordinate of a block. /// Local Y-coordinate of a block. /// Local Z-coordinate of a block. /// An -compatible object. /// IBlock IBlockCollection.GetBlock (int x, int y, int z) { return GetBlock(x, y, z); } /// /// Returns a reference object compatible with the interface from local coordinates relative to this collection. /// /// Local X-coordinate of a block. /// Local Y-coordinate of a block. /// Local Z-coordinate of a block. /// An -compatible reference object. /// IBlock IBlockCollection.GetBlockRef (int x, int y, int z) { return GetBlockRef(x, y, z); } /// /// Updates a block in this collection with values from an object. /// /// Local X-coordinate of a block. /// Local Y-coordinate of a block. /// Local Z-coordinate of a block. /// An object to copy block data from. public void SetBlock (int x, int y, int z, IBlock block) { SetID(x, y, z, block.ID); } /// /// Gets information on the type of a block at the given local coordinates. /// /// Local X-coordinate of a block. /// Local Y-coordinate of a block. /// Local Z-coordinate of a block. /// A object containing information of a block's type. public BlockInfo GetInfo (int x, int y, int z) { return BlockInfo.BlockTable[_blocks[x, y, z]]; } internal BlockInfo GetInfo (int index) { return BlockInfo.BlockTable[_blocks[index]]; } /// /// Gets the ID (type) of a block at the given local coordinates. /// /// Local X-coordinate of a block. /// Local Y-coordinate of a block. /// Local Z-coordinate of a block. /// The ID (type) of the specified block. public int GetID (int x, int y, int z) { return _blocks[x, y, z]; } internal int GetID (int index) { return _blocks[index]; } /// /// Sets the ID (type) of a block at the given local coordinates, maintaining consistency of data. /// /// Local X-coordinate of a block. /// Local Y-coordinate of a block. /// Local Z-coordinate of a block. /// The new ID of the block. /// Depending on the options set for this , this method can be very /// heavy-handed in the amount of work it does to maintain consistency of tile entities, lighting, fluid, etc. /// for the affected block and possibly many other indirectly-affected blocks in the collection or neighboring /// collections. If many SetID calls are expected to be made, some of this auto-reconciliation behavior should /// be disabled, and the data should be rebuilt at the -level at the end. public void SetID (int x, int y, int z, int id) { int oldid = _blocks[x, y, z]; if (oldid == id) { return; } // Update value _blocks[x, y, z] = (byte)id; // Update tile entities BlockInfo info1 = BlockInfo.BlockTable[oldid]; BlockInfo info2 = BlockInfo.BlockTable[id]; BlockInfoEx einfo1 = info1 as BlockInfoEx; BlockInfoEx einfo2 = info2 as BlockInfoEx; if (einfo1 != einfo2) { if (einfo1 != null) { ClearTileEntity(x, y, z); } if (einfo2 != null) { CreateTileEntity(x, y, z); } } // Light consistency if (_autoLight) { if (info1.ObscuresLight != info2.ObscuresLight) { _lightManager.UpdateHeightMap(x, y, z); } if (info1.Luminance != info2.Luminance || info1.Opacity != info2.Opacity || info1.TransmitsLight != info2.TransmitsLight) { UpdateBlockLight(x, y, z); } if (info1.Opacity != info2.Opacity || info1.TransmitsLight != info2.TransmitsLight) { UpdateSkyLight(x, y, z); } } // Fluid consistency if (_autoFluid) { if (info1.State == BlockState.FLUID || info2.State == BlockState.FLUID) { UpdateFluid(x, y, z); } } _dirty = true; } internal void SetID (int index, int id) { int x, y, z; _blocks.GetMultiIndex(index, out x, out y, out z); SetID(x, y, z, id); } /// /// Returns a count of all blocks in this with the given ID (type). /// /// The ID of blocks to count. /// A count of all matching blocks. public int CountByID (int id) { int c = 0; for (int i = 0; i < _blocks.Length; i++) { if (_blocks[i] == id) { c++; } } return c; } #endregion #region IDataBlockContainer Members /// /// Returns an object compatible with the interface from local coordinates relative to this collection. /// /// Local X-coordinate of a block. /// Local Y-coordinate of a block. /// Local Z-coordinate of a block. /// An -compatible object. /// IDataBlock IDataBlockCollection.GetBlock (int x, int y, int z) { return GetBlock(x, y, z); } /// /// Returns a reference object compatible with the interface from local coordinates relative to this collection. /// /// Local X-coordinate of a block. /// Local Y-coordinate of a block. /// Local Z-coordinate of a block. /// An -compatible reference object. /// IDataBlock IDataBlockCollection.GetBlockRef (int x, int y, int z) { return GetBlockRef(x, y, z); } /// /// Updates a block in this collection with values from an object. /// /// Local X-coordinate of a block. /// Local Y-coordinate of a block. /// Local Z-coordinate of a block. /// An object to copy block data from. public void SetBlock (int x, int y, int z, IDataBlock block) { SetID(x, y, z, block.ID); SetData(x, y, z, block.Data); } /// /// Gets the data value of a block at the given local coordinates. /// /// Local X-coordinate of a block. /// Local Y-coordinate of a block. /// Local Z-coordinate of a block. /// The data value of the specified block. public int GetData (int x, int y, int z) { return _data[x, y, z]; } internal int GetData (int index) { return _data[index]; } /// /// Sets the data value of a block at the given local coordinates. /// /// Local X-coordinate of a block. /// Local Y-coordinate of a block. /// Local Z-coordinate of a block. /// The new data value of the block. public void SetData (int x, int y, int z, int data) { if (_data[x, y, z] != data) { _data[x, y, z] = (byte)data; _dirty = true; } /*if (BlockManager.EnforceDataLimits && BlockInfo.BlockTable[_blocks[index]] != null) { if (!BlockInfo.BlockTable[_blocks[index]].TestData(data)) { return false; } }*/ } internal void SetData (int index, int data) { if (_data[index] != data) { _data[index] = (byte)data; _dirty = true; } } /// /// Returns a count of all blocks in this matching the given ID (type) and data value. /// /// The ID of blocks to count. /// The data value of blocks to count. /// A count of all matching blocks. public int CountByData (int id, int data) { int c = 0; for (int i = 0; i < _blocks.Length; i++) { if (_blocks[i] == id && _data[i] == data) { c++; } } return c; } #endregion #region ILitBlockCollection Members /// /// Not Implemented. /// /// Local X-coordinate of a block. /// Local Y-coordinate of a block. /// Local Z-coordinate of a block. /// An -compatible object. /// ILitBlock ILitBlockCollection.GetBlock (int x, int y, int z) { throw new NotImplementedException(); } /// /// Returns a reference object compatible with the interface from local coordinates relative to this collection. /// /// Local X-coordinate of a block. /// Local Y-coordinate of a block. /// Local Z-coordinate of a block. /// An -compatible reference object. /// ILitBlock ILitBlockCollection.GetBlockRef (int x, int y, int z) { return GetBlockRef(x, y, z); } /// /// Updates a block in this collection with values from an object. /// /// Local X-coordinate of a block. /// Local Y-coordinate of a block. /// Local Z-coordinate of a block. /// An object to copy block data from. public void SetBlock (int x, int y, int z, ILitBlock block) { SetID(x, y, z, block.ID); SetBlockLight(x, y, z, block.BlockLight); SetSkyLight(x, y, z, block.SkyLight); } /// /// Gets the block-source light value of a block at the given local coordinates. /// /// Local X-coordinate of a block. /// Local Y-coordinate of a block. /// Local Z-coordinate of a block. /// The block-source light value of the specified block. public int GetBlockLight (int x, int y, int z) { return _blockLight[x, y, z]; } internal int GetBlockLight (int index) { return _blockLight[index]; } /// /// Gets the sky-source light value of a block at the given local coordinates. /// /// Local X-coordinate of a block. /// Local Y-coordinate of a block. /// Local Z-coordinate of a block. /// The sky-source light value of the specified block. public int GetSkyLight (int x, int y, int z) { return _skyLight[x, y, z]; } internal int GetSkyLight (int index) { return _skyLight[index]; } /// /// Sets the blocks-source light value of a block at the given local coordinates. /// /// Local X-coordinate of a block. /// Local Y-coordinate of a block. /// Local Z-coordinate of a block. /// The new block-source light value of the block. public void SetBlockLight (int x, int y, int z, int light) { if (_blockLight[x, y, z] != light) { _blockLight[x, y, z] = (byte)light; _dirty = true; } } internal void SetBlockLight (int index, int light) { if (_blockLight[index] != light) { _blockLight[index] = (byte)light; _dirty = true; } } /// /// Sets the sky-source light value of a block at the given local coordinates. /// /// Local X-coordinate of a block. /// Local Y-coordinate of a block. /// Local Z-coordinate of a block. /// The new sky-source light value of the block. public void SetSkyLight (int x, int y, int z, int light) { if (_skyLight[x, y, z] != light) { _skyLight[x, y, z] = (byte)light; _dirty = true; } } internal void SetSkyLight (int index, int light) { if (_skyLight[index] != light) { _skyLight[index] = (byte)light; _dirty = true; } } /// /// Gets the lowest Y-coordinate of a block at which sky-source light remains unfiltered. /// /// Local X-coordinate of a map location. /// Local Z-coordinate of a map location. /// The height-map value of a map location for calculating sky-source lighting. public int GetHeight (int x, int z) { return _heightMap[x, z]; } /// /// Sets the lowest Y-coordinate of a block at which sky-source light remains unfiltered. /// /// Local X-coordinate of a map location. /// Local Z-coordinate of a map location. /// The new height-map value of the given map location. public void SetHeight (int x, int z, int height) { _heightMap[x, z] = (byte)height; } /// /// Updates the block-source lighting of a block at the given local coordinates to maintain light consistency. /// /// Local X-coordinate of a block. /// Local Y-coordinate of a block. /// Local Z-coordinate of a block. /// The lighting of the block will be updated to be consistent with the lighting in neighboring blocks. /// If the block is itself a light source, many nearby blocks may be updated to maintain consistent lighting. These /// updates may also touch neighboring objects, if they can be resolved. /// This function assumes that the entire and neighboring s /// already have consistent lighting, with the exception of the block being updated. If this assumption is violated, /// lighting may fail to converge correctly. public void UpdateBlockLight (int x, int y, int z) { _lightManager.UpdateBlockLight(x, y, z); _dirty = true; } /// /// Updates the sky-source lighting of a block at the given local coordinates to maintain light consistency. /// /// Local X-coordinate of a block. /// Local Y-coordinate of a block. /// Local Z-coordinate of a block. /// The lighting of the block will be updated to be consistent with the lighting in neighboring blocks. /// If the block is itself a light source, many nearby blocks may be updated to maintain consistent lighting. These /// updates may also touch neighboring objects, if they can be resolved. /// This function assumes that the entire and neighboring s /// already have consistent lighting, with the exception of the block being updated. If this assumption is violated, /// lighting may fail to converge correctly. public void UpdateSkyLight (int x, int y, int z) { _lightManager.UpdateBlockSkyLight(x, y, z); _dirty = true; } /// /// Resets the block-source light value to 0 for all blocks in this . /// public void ResetBlockLight () { _blockLight.Clear(); _dirty = true; } /// /// Resets the sky-source light value to 0 for all blocks in this . /// public void ResetSkyLight () { _skyLight.Clear(); _dirty = true; } /// /// Reconstructs the block-source lighting for all blocks in this . /// /// This function should only be called after the lighting has been reset in this /// and all neighboring s, or lighting may fail to converge correctly. /// This function cannot reset the lighting on its own, due to interactions between s. /// If many light source or block opacity values will be modified in this , it may /// be preferable to avoid explicit or implicit calls to and call this function once when /// modifications are complete. /// / public void RebuildBlockLight () { _lightManager.RebuildBlockLight(); _dirty = true; } /// /// Reconstructs the sky-source lighting for all blocks in this . /// /// This function should only be called after the lighting has been reset in this /// and all neighboring s, or lighting may fail to converge correctly. /// This function cannot reset the lighting on its own, due to interactions between s. /// If many light source or block opacity values will be modified in this , it may /// be preferable to avoid explicit or implicit calls to and call this function once when /// modifications are complete. /// public void RebuildSkyLight () { _lightManager.RebuildBlockSkyLight(); _dirty = true; } /// /// Reconstructs the height-map for this . /// public void RebuildHeightMap () { _lightManager.RebuildHeightMap(); _dirty = true; } /// /// Reconciles any block-source lighting inconsistencies between this and any of its neighbors. /// /// It will be necessary to call this function if an is reset and rebuilt, but /// some of its neighbors are not. A rebuilt will spill lighting updates into its neighbors, /// but will not see lighting that should be propagated back from its neighbors. /// public void StitchBlockLight () { _lightManager.StitchBlockLight(); _dirty = true; } /// /// Reconciles any sky-source lighting inconsistencies between this and any of its neighbors. /// /// It will be necessary to call this function if an is reset and rebuilt, but /// some of its neighbors are not. A rebuilt will spill lighting updates into its neighbors, /// but will not see lighting that should be propagated back from its neighbors. /// public void StitchSkyLight () { _lightManager.StitchBlockSkyLight(); _dirty = true; } /// /// Reconciles any block-source lighting inconsistencies between this and another on a given edge. /// /// An -compatible object with the same dimensions as this . /// The edge that is a neighbor on. /// It will be necessary to call this function if an is reset and rebuilt, but /// some of its neighbors are not. A rebuilt will spill lighting updates into its neighbors, /// but will not see lighting that should be propagated back from its neighbors. /// public void StitchBlockLight (IBoundedLitBlockCollection blockset, BlockCollectionEdge edge) { _lightManager.StitchBlockLight(blockset, edge); _dirty = true; } /// /// Reconciles any sky-source lighting inconsistencies between this and another on a given edge. /// /// An -compatible object with the same dimensions as this . /// The edge that is a neighbor on. /// It will be necessary to call this function if an is reset and rebuilt, but /// some of its neighbors are not. A rebuilt will spill lighting updates into its neighbors, /// but will not see lighting that should be propagated back from its neighbors. /// public void StitchSkyLight (IBoundedLitBlockCollection blockset, BlockCollectionEdge edge) { _lightManager.StitchBlockSkyLight(blockset, edge); _dirty = true; } #endregion #region IPropertyBlockCollection Members /// /// Returns an object compatible with the interface from local coordinates relative to this collection. /// /// Local X-coordinate of a block. /// Local Y-coordinate of a block. /// Local Z-coordinate of a block. /// An -compatible object. /// IPropertyBlock IPropertyBlockCollection.GetBlock (int x, int y, int z) { return GetBlock(x, y, z); } /// /// Returns a reference object compatible with the interface from local coordinates relative to this collection. /// /// Local X-coordinate of a block. /// Local Y-coordinate of a block. /// Local Z-coordinate of a block. /// An -compatible reference object. /// IPropertyBlock IPropertyBlockCollection.GetBlockRef (int x, int y, int z) { return GetBlockRef(x, y, z); } /// /// Updates a block in this collection with values from an object. /// /// Local X-coordinate of a block. /// Local Y-coordinate of a block. /// Local Z-coordinate of a block. /// An object to copy block data from. public void SetBlock (int x, int y, int z, IPropertyBlock block) { SetID(x, y, z, block.ID); SetTileEntity(x, y, z, block.GetTileEntity().Copy()); } /// /// Gets a record for a block at the given local coordinates, if one exists. /// /// Local X-coordinate of a block. /// Local Y-coordinate of a block. /// Local Z-coordinate of a block. /// A for the given block, or null if the is missing or the block type does not use a . public TileEntity GetTileEntity (int x, int y, int z) { return _tileEntityManager.GetTileEntity(x, y, z); } internal TileEntity GetTileEntity (int index) { int x, y, z; _blocks.GetMultiIndex(index, out x, out y, out z); return _tileEntityManager.GetTileEntity(x, y, z); } /// /// Sets a record for a block at the given local coordinates. /// /// Local X-coordinate of a block. /// Local Y-coordinate of a block. /// Local Z-coordinate of a block. /// The to add to the given block. /// Thrown when the being passed is of the wrong type for the given block. /// Thrown when the given block is of a type that does not support a record. public void SetTileEntity (int x, int y, int z, TileEntity te) { _tileEntityManager.SetTileEntity(x, y, z, te); _dirty = true; } internal void SetTileEntity (int index, TileEntity te) { int x, y, z; _blocks.GetMultiIndex(index, out x, out y, out z); _tileEntityManager.SetTileEntity(x, y, z, te); _dirty = true; } /// /// Creates a default record suitable for the block at the given local coordinates. /// /// Local X-coordinate of a block. /// Local Y-coordinate of a block. /// Local Z-coordinate of a block. /// Thrown when the given block is of a type that does not support a record. /// Thrown when the block type requests a that has not been registered with the . public void CreateTileEntity (int x, int y, int z) { _tileEntityManager.CreateTileEntity(x, y, z); _dirty = true; } internal void CreateTileEntity (int index) { int x, y, z; _blocks.GetMultiIndex(index, out x, out y, out z); _tileEntityManager.CreateTileEntity(x, y, z); _dirty = true; } /// /// Clears any record set for a block at the givne local coordinates, if one exists. /// /// Local X-coordinate of a block. /// Local Y-coordinate of a block. /// Local Z-coordinate of a block. public void ClearTileEntity (int x, int y, int z) { _tileEntityManager.ClearTileEntity(x, y, z); _dirty = true; } internal void ClearTileEntity (int index) { int x, y, z; _blocks.GetMultiIndex(index, out x, out y, out z); _tileEntityManager.ClearTileEntity(x, y, z); _dirty = true; } #endregion public void ResetFluid () { _fluidManager.ResetWater(_blocks, _data); _fluidManager.ResetLava(_blocks, _data); _dirty = true; } public void RebuildFluid () { _fluidManager.RebuildWater(); _fluidManager.RebuildLava(); _dirty = true; } public void UpdateFluid (int x, int y, int z) { bool autofluid = _autoFluid; _autoFluid = false; int blocktype = _blocks[x, y, z]; if (blocktype == BlockType.WATER || blocktype == BlockType.STATIONARY_WATER) { _fluidManager.UpdateWater(x, y, z); _dirty = true; } else if (blocktype == BlockType.LAVA || blocktype == BlockType.STATIONARY_LAVA) { _fluidManager.UpdateLava(x, y, z); _dirty = true; } _autoFluid = autofluid; } /*#region IEnumerable Members public IEnumerator GetEnumerator () { return new AlphaBlockEnumerator(this); } #endregion #region IEnumerable Members System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator () { return new AlphaBlockEnumerator(this); } #endregion public class AlphaBlockEnumerator : IEnumerator { private AlphaBlockCollection _collection; private int _index; private int _size; public AlphaBlockEnumerator (AlphaBlockCollection collection) { _collection = collection; _index = -1; _size = collection.XDim * collection.YDim * collection.ZDim; } #region IEnumerator Members public AlphaBlockRef Current { get { if (_index == -1 || _index == _size) { throw new InvalidOperationException(); } return new AlphaBlockRef(_collection, _index); } } #endregion #region IDisposable Members public void Dispose () { } #endregion #region IEnumerator Members object System.Collections.IEnumerator.Current { get { return Current; } } public bool MoveNext () { if (++_index == _size) { return false; } return true; } public void Reset () { _index = -1; } #endregion }*/ } }