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, IBoundedActiveBlockCollection { private readonly int _xdim; private readonly int _ydim; private readonly int _zdim; private IDataArray3 _blocks; private IDataArray3 _data; private IDataArray3 _blockLight; private IDataArray3 _skyLight; private IDataArray2 _heightMap; private TagNodeList _tileEntities; private TagNodeList _tileTicks; private BlockLight _lightManager; private BlockFluid _fluidManager; private BlockTileEntities _tileEntityManager; private BlockTileTicks _tileTickManager; private bool _dirty = false; private bool _autoLight = true; private bool _autoFluid = false; private bool _autoTick = false; public delegate AlphaBlockCollection NeighborLookupHandler (int relx, int rely, int relz); /// /// Creates a new of a given dimension. /// /// The length of the X-dimension of the collection. /// The length of the Y-dimension of the collection. /// The length of the Z-dimension of the collection. [Obsolete] public AlphaBlockCollection (int xdim, int ydim, int zdim) { _blocks = new XZYByteArray(xdim, ydim, zdim); _data = new XZYNibbleArray(xdim, ydim, zdim); _blockLight = new XZYNibbleArray(xdim, ydim, zdim); _skyLight = new XZYNibbleArray(xdim, ydim, zdim); _heightMap = new ZXByteArray(xdim, zdim); _tileEntities = new TagNodeList(TagType.TAG_COMPOUND); _tileTicks = new TagNodeList(TagType.TAG_COMPOUND); _xdim = xdim; _ydim = ydim; _zdim = zdim; Refresh(); } /// /// 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 ( IDataArray3 blocks, IDataArray3 data, IDataArray3 blockLight, IDataArray3 skyLight, IDataArray2 heightMap, TagNodeList tileEntities) : this(blocks, data, blockLight, skyLight, heightMap, tileEntities, null) { } /// /// 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. /// A list of tile ticks corresponding to blocks in this collection. public AlphaBlockCollection ( IDataArray3 blocks, IDataArray3 data, IDataArray3 blockLight, IDataArray3 skyLight, IDataArray2 heightMap, TagNodeList tileEntities, TagNodeList tileTicks) { _blocks = blocks; _data = data; _blockLight = blockLight; _skyLight = skyLight; _heightMap = heightMap; _tileEntities = tileEntities; _tileTicks = tileTicks; if (_tileTicks == null) _tileTicks = new TagNodeList(TagType.TAG_COMPOUND); _xdim = _blocks.XDim; _ydim = _blocks.YDim; _zdim = _blocks.ZDim; Refresh(); } /// /// Updates internal managers if underlying data, such as TileEntities, have been modified outside of the container. /// public void Refresh () { _lightManager = new BlockLight(this); _fluidManager = new BlockFluid(this); _tileEntityManager = new BlockTileEntities(_blocks, _tileEntities); _tileTickManager = new BlockTileTicks(_blocks, _tileTicks); } internal TagNodeList TileTicks { get { return _tileTicks; } } #region Events 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; } } #endregion /// /// 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 changes to blocks will create tile tick entries. /// public bool AutoTileTick { get { return _autoTick; } set { _autoTick = 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; } } /// /// 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); TileEntity te = block.GetTileEntity(); if (te != null) { SetTileEntity(x, y, z, te.Copy()); } TileTick tt = block.GetTileTick(); if (tt != null) { SetTileTick(x, y, z, tt.Copy()); } } #region IBoundedBlockCollection Members /// public int XDim { get { return _xdim; } } /// public int YDim { get { return _ydim; } } /// public int ZDim { get { return _zdim; } } IBlock IBoundedBlockCollection.GetBlock (int x, int y, int z) { return GetBlock(x, y, z); } IBlock IBoundedBlockCollection.GetBlockRef (int x, int y, int z) { return GetBlockRef(x, y, z); } /// public void SetBlock (int x, int y, int z, IBlock block) { SetID(x, y, z, block.ID); } /// 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]]; } /// public int GetID (int x, int y, int z) { return _blocks[x, y, z]; } internal int GetID (int index) { return _blocks[index]; } /// /// 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] = 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 || !info1.Registered) { 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); } } // TileTick consistency if (_autoTick) { if (info1.ID != info2.ID) { ClearTileTick(x, y, z); if (info2.Tick > 0) { SetTileTickValue(x, y, z, info2.Tick); } } } _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); } /// 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 IBoundedDataBlockContainer Members IDataBlock IBoundedDataBlockCollection.GetBlock (int x, int y, int z) { return GetBlock(x, y, z); } IDataBlock IBoundedDataBlockCollection.GetBlockRef (int x, int y, int z) { return GetBlockRef(x, y, z); } /// public void SetBlock (int x, int y, int z, IDataBlock block) { SetID(x, y, z, block.ID); SetData(x, y, z, block.Data); } /// public int GetData (int x, int y, int z) { return _data[x, y, z]; } internal int GetData (int index) { return _data[index]; } /// 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; } } /// 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 IBoundedLitBlockCollection Members ILitBlock IBoundedLitBlockCollection.GetBlock (int x, int y, int z) { throw new NotImplementedException(); } ILitBlock IBoundedLitBlockCollection.GetBlockRef (int x, int y, int z) { return GetBlockRef(x, y, z); } /// 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); } /// public int GetBlockLight (int x, int y, int z) { return _blockLight[x, y, z]; } internal int GetBlockLight (int index) { return _blockLight[index]; } /// public int GetSkyLight (int x, int y, int z) { return _skyLight[x, y, z]; } internal int GetSkyLight (int index) { return _skyLight[index]; } /// 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; } } /// 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; } } /// public int GetHeight (int x, int z) { return _heightMap[x, z]; } /// public void SetHeight (int x, int z, int height) { _heightMap[x, z] = (byte)height; } /// public void UpdateBlockLight (int x, int y, int z) { _lightManager.UpdateBlockLight(x, y, z); _dirty = true; } /// public void UpdateSkyLight (int x, int y, int z) { _lightManager.UpdateBlockSkyLight(x, y, z); _dirty = true; } /// public void ResetBlockLight () { _blockLight.Clear(); _dirty = true; } /// public void ResetSkyLight () { _skyLight.Clear(); _dirty = true; } /// public void RebuildBlockLight () { _lightManager.RebuildBlockLight(); _dirty = true; } /// public void RebuildSkyLight () { _lightManager.RebuildBlockSkyLight(); _dirty = true; } /// public void RebuildHeightMap () { _lightManager.RebuildHeightMap(); _dirty = true; } /// public void StitchBlockLight () { _lightManager.StitchBlockLight(); _dirty = true; } /// public void StitchSkyLight () { _lightManager.StitchBlockSkyLight(); _dirty = true; } /// public void StitchBlockLight (IBoundedLitBlockCollection blockset, BlockCollectionEdge edge) { _lightManager.StitchBlockLight(blockset, edge); _dirty = true; } /// public void StitchSkyLight (IBoundedLitBlockCollection blockset, BlockCollectionEdge edge) { _lightManager.StitchBlockSkyLight(blockset, edge); _dirty = true; } #endregion #region IBoundedPropertyBlockCollection Members IPropertyBlock IBoundedPropertyBlockCollection.GetBlock (int x, int y, int z) { return GetBlock(x, y, z); } IPropertyBlock IBoundedPropertyBlockCollection.GetBlockRef (int x, int y, int z) { return GetBlockRef(x, y, z); } /// public void SetBlock (int x, int y, int z, IPropertyBlock block) { SetID(x, y, z, block.ID); SetTileEntity(x, y, z, block.GetTileEntity().Copy()); } /// 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); } /// 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; } /// 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; } /// 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 #region IBoundedActiveBlockCollection Members IActiveBlock IBoundedActiveBlockCollection.GetBlock (int x, int y, int z) { return GetBlock(x, y, z); } IActiveBlock IBoundedActiveBlockCollection.GetBlockRef (int x, int y, int z) { return GetBlockRef(x, y, z); } /// public void SetBlock (int x, int y, int z, IActiveBlock block) { SetID(x, y, z, block.ID); SetTileTick(x, y, z, block.GetTileTick().Copy()); } /// public int GetTileTickValue (int x, int y, int z) { return _tileTickManager.GetTileTickValue(x, y, z); } internal int GetTileTickValue (int index) { int x, y, z; _blocks.GetMultiIndex(index, out x, out y, out z); return _tileTickManager.GetTileTickValue(x, y, z); } /// public void SetTileTickValue (int x, int y, int z, int tickValue) { _tileTickManager.SetTileTickValue(x, y, z, tickValue); _dirty = true; } internal void SetTileTickValue (int index, int tickValue) { int x, y, z; _blocks.GetMultiIndex(index, out x, out y, out z); _tileTickManager.SetTileTickValue(x, y, z, tickValue); _dirty = true; } /// public TileTick GetTileTick (int x, int y, int z) { return _tileTickManager.GetTileTick(x, y, z); } internal TileTick GetTileTick (int index) { int x, y, z; _blocks.GetMultiIndex(index, out x, out y, out z); return _tileTickManager.GetTileTick(x, y, z); } /// public void SetTileTick (int x, int y, int z, TileTick tt) { _tileTickManager.SetTileTick(x, y, z, tt); _dirty = true; } internal void SetTileTick (int index, TileTick tt) { int x, y, z; _blocks.GetMultiIndex(index, out x, out y, out z); _tileTickManager.SetTileTick(x, y, z, tt); _dirty = true; } /// public void CreateTileTick (int x, int y, int z) { _tileTickManager.CreateTileTick(x, y, z); _dirty = true; } internal void CreateTileTick (int index) { int x, y, z; _blocks.GetMultiIndex(index, out x, out y, out z); _tileTickManager.CreateTileTick(x, y, z); _dirty = true; } /// public void ClearTileTick (int x, int y, int z) { _tileTickManager.ClearTileTick(x, y, z); _dirty = true; } internal void ClearTileTick (int index) { int x, y, z; _blocks.GetMultiIndex(index, out x, out y, out z); _tileTickManager.ClearTileTick(x, y, z); _dirty = true; } #endregion /// /// Resets all fluid blocks in the collection to their inactive type. /// public void ResetFluid () { _fluidManager.ResetWater(_blocks, _data); _fluidManager.ResetLava(_blocks, _data); _dirty = true; } /// /// Performs fluid simulation on all fluid blocks on this container. /// /// Simulation will cause inactive fluid blocks to convert into and spread active fluid blocks according /// to the fluid calculation rules in Minecraft. Fluid calculation may spill into neighboring block collections /// (and beyond). public void RebuildFluid () { _fluidManager.RebuildWater(); _fluidManager.RebuildLava(); _dirty = true; } /// /// Recalculates fluid starting from a given fluid block in this collection. /// /// Local X-coordinate of block. /// Local Y-coordinate of block. /// Local Z-coordiante of block. public void UpdateFluid (int x, int y, int z) { bool autofluid = _autoFluid; _autoFluid = false; int blocktype = _blocks[x, y, z]; if (blocktype == BlockInfo.Water.ID || blocktype == BlockInfo.StationaryWater.ID) { _fluidManager.UpdateWater(x, y, z); _dirty = true; } else if (blocktype == BlockInfo.Lava.ID || blocktype == BlockInfo.StationaryLava.ID) { _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 }*/ } }