public void CreateTileEntity(int x, int y, int z) { BlockInfoEx info = BlockInfo.BlockTable[_blocks[x, y, z]] as BlockInfoEx; if (info == null) { throw new InvalidOperationException("The given block is of a type that does not support TileEntities."); } TileEntity te = TileEntityFactory.Create(info.TileEntityName); if (te == null) { throw new UnknownTileEntityException("The TileEntity type '" + info.TileEntityName + "' has not been registered with the TileEntityFactory."); } BlockKey key = (TranslateCoordinates != null) ? TranslateCoordinates(x, y, z) : new BlockKey(x, y, z); TagNodeCompound oldte; if (_tileEntityTable.TryGetValue(key, out oldte)) { _tileEntities.Remove(oldte); } te.X = key.x; te.Y = key.y; te.Z = key.z; TagNodeCompound tree = te.BuildTree() as TagNodeCompound; _tileEntities.Add(tree); _tileEntityTable[key] = tree; }
private void DoLava(int x, int y, int z) { Queue <BlockKey> flowQueue = new Queue <BlockKey>(); BlockKey prikey = new BlockKey(x, y, z); flowQueue.Enqueue(prikey); List <BlockKey> outflow = TileOutflow(prikey); foreach (BlockKey outkey in outflow) { flowQueue.Enqueue(outkey); } while (flowQueue.Count > 0) { BlockKey key = flowQueue.Dequeue(); int curflow = 16; int inflow = TileInflow(key); BlockCoord tile = TranslateCoord(key.x, key.y, key.z); BlockInfo tileInfo = tile.chunk.GetInfo(tile.lx, tile.ly, tile.lz); if (tileInfo.ID == BlockType.STATIONARY_LAVA || tileInfo.ID == BlockType.LAVA) { curflow = tile.chunk.GetData(tile.lx, tile.ly, tile.lz); } else if (tileInfo.BlocksFluid) { continue; } bool curFall = (curflow & (int)LiquidState.FALLING) != 0; bool inFall = (inflow & (int)LiquidState.FALLING) != 0; // We won't update from the following states if (curflow == 0 || curflow == inflow || curFall) { continue; } int newflow = curflow; // Update from inflow if necessary if (inFall) { newflow = inflow; } else if (inflow >= 6) { newflow = 16; } else { newflow = inflow + 2; } // If we haven't changed the flow, don't propagate if (newflow == curflow) { continue; } // Update flow, add or remove lava tile as necessary if (newflow < 16 && curflow == 16) { // If we're overwriting water, replace with appropriate stone type and abort propagation if (tileInfo.ID == BlockType.STATIONARY_WATER || tileInfo.ID == BlockType.WATER) { if ((newflow & (int)LiquidState.FALLING) == 0) { tile.chunk.SetID(tile.lx, tile.ly, tile.lz, BlockType.COBBLESTONE); tile.chunk.SetData(tile.lx, tile.ly, tile.lz, 0); continue; } } tile.chunk.SetID(tile.lx, tile.ly, tile.lz, BlockType.STATIONARY_LAVA); tile.chunk.SetData(tile.lx, tile.ly, tile.lz, newflow); } else if (newflow == 16) { tile.chunk.SetID(tile.lx, tile.ly, tile.lz, BlockType.AIR); tile.chunk.SetData(tile.lx, tile.ly, tile.lz, 0); } else { tile.chunk.SetData(tile.lx, tile.ly, tile.lz, newflow); } // Process outflows outflow = TileOutflow(key); foreach (BlockKey nkey in outflow) { flowQueue.Enqueue(nkey); } } }
private int TileInflow(BlockKey key) { // Check if water is falling on us if (key.y < _ydim - 1) { BlockCoord up = TranslateCoord(key.x, key.y + 1, key.z); BlockInfo upInfo = up.chunk.GetInfo(up.lx, up.ly, up.lz); if (upInfo.State == BlockState.FLUID) { return(up.chunk.GetData(up.lx, up.ly, up.lz) | (int)LiquidState.FALLING); } } // Otherwise return the min inflow of our neighbors + step BlockKey[] keys = { new BlockKey(key.x - 1, key.y, key.z), new BlockKey(key.x + 1, key.y, key.z), new BlockKey(key.x, key.y, key.z - 1), new BlockKey(key.x, key.y, key.z + 1), }; int minFlow = 16; // XXX: Might have different neighboring fluids for (int i = 0; i < 4; i++) { BlockCoord neighbor = TranslateCoord(keys[i].x, keys[i].y, keys[i].z); if (neighbor.chunk == null) { continue; } BlockInfo neighborInfo = neighbor.chunk.GetInfo(neighbor.lx, neighbor.ly, neighbor.lz); if (neighborInfo.State == BlockState.FLUID) { int flow = neighbor.chunk.GetData(neighbor.lx, neighbor.ly, neighbor.lz); bool flowFall = (flow & (int)LiquidState.FALLING) != 0; if (flowFall) { if (keys[i].y == 0) { continue; } BlockCoord low = TranslateCoord(keys[i].x, keys[i].y - 1, keys[i].z); BlockInfo lowinfo = low.chunk.GetInfo(low.lx, low.ly, low.lz); if (lowinfo.BlocksFluid) { return(0); } continue; } if (flow < minFlow) { minFlow = flow; } } } return(minFlow); }
// ----- private List <BlockKey> TileOutflow(BlockKey key, int reach = 5) { Queue <BlockKey> searchQueue = new Queue <BlockKey>(); Queue <KeyValuePair <BlockKey, int> > traceQueue = new Queue <KeyValuePair <BlockKey, int> >(); Dictionary <BlockKey, int> markTable = new Dictionary <BlockKey, int>(); searchQueue.Enqueue(key); markTable.Add(key, 0); // Identify sinks while (searchQueue.Count > 0) { BlockKey branch = searchQueue.Dequeue(); int distance = markTable[branch]; // Ignore blocks out of range if (distance > reach) { continue; } // Ignore invalid blocks BlockCoord branchHigh = TranslateCoord(branch.x, branch.y, branch.z); if (branchHigh.chunk == null || branch.y == 0) { markTable.Remove(branch); continue; } // If we're not the magical source block... if (distance > 0) { // Ignore blocks that block fluid (and thus could not become a fluid) BlockInfo branchHighInfo = branchHigh.chunk.GetInfo(branchHigh.lx, branchHigh.ly, branchHigh.lz); if (branchHighInfo.BlocksFluid) { markTable.Remove(branch); continue; } } // If we found a hole, add as a sink, mark the sink distance. BlockCoord branchLow = TranslateCoord(branch.x, branch.y - 1, branch.z); BlockInfo branchLowInfo = branchLow.chunk.GetInfo(branchLow.lx, branchLow.ly, branchLow.lz); if (!branchLowInfo.BlocksFluid) { // If we are our own sink, return the only legal outflow if (key == branch) { List <BlockKey> ret = new List <BlockKey>(); ret.Add(new BlockKey(branch.x, branch.y - 1, branch.z)); return(ret); } reach = distance; traceQueue.Enqueue(new KeyValuePair <BlockKey, int>(branch, distance)); continue; } // Expand to neighbors if (distance < reach) { BlockKey[] keys = { new BlockKey(branch.x - 1, branch.y, branch.z), new BlockKey(branch.x + 1, branch.y, branch.z), new BlockKey(branch.x, branch.y, branch.z - 1), new BlockKey(branch.x, branch.y, branch.z + 1), }; for (int i = 0; i < 4; i++) { if (!markTable.ContainsKey(keys[i])) { searchQueue.Enqueue(keys[i]); markTable.Add(keys[i], distance + 1); } } } } // Candidate outflows are marked BlockKey[] neighbors = { new BlockKey(key.x - 1, key.y, key.z), new BlockKey(key.x + 1, key.y, key.z), new BlockKey(key.x, key.y, key.z - 1), new BlockKey(key.x, key.y, key.z + 1), }; List <BlockKey> outflow = new List <BlockKey>(); foreach (BlockKey n in neighbors) { if (markTable.ContainsKey(n)) { outflow.Add(n); } } // If there's no sinks, all neighbors are valid outflows if (traceQueue.Count == 0) { return(outflow); } // Trace back from each sink eliminating shortest path marks while (traceQueue.Count > 0) { KeyValuePair <BlockKey, int> tilekv = traceQueue.Dequeue(); BlockKey tile = tilekv.Key; int distance = tilekv.Value; markTable.Remove(tile); BlockKey[] keys = { new BlockKey(tile.x - 1, tile.y, tile.z), new BlockKey(tile.x + 1, tile.y, tile.z), new BlockKey(tile.x, tile.y, tile.z - 1), new BlockKey(tile.x, tile.y, tile.z + 1), }; for (int i = 0; i < 4; i++) { int nval; if (!markTable.TryGetValue(keys[i], out nval)) { continue; } if (nval < distance) { markTable.Remove(keys[i]); traceQueue.Enqueue(new KeyValuePair <BlockKey, int>(keys[i], nval)); } } } // Remove any candidates that are still marked foreach (BlockKey n in neighbors) { if (markTable.ContainsKey(n)) { outflow.Remove(n); } } return(outflow); }
private void DoWater(int x, int y, int z) { Queue <BlockKey> flowQueue = new Queue <BlockKey>(); BlockKey prikey = new BlockKey(x, y, z); flowQueue.Enqueue(prikey); List <BlockKey> outflow = TileOutflow(prikey); foreach (BlockKey outkey in outflow) { flowQueue.Enqueue(outkey); } while (flowQueue.Count > 0) { BlockKey key = flowQueue.Dequeue(); int curflow = 16; int inflow = TileInflow(key); BlockCoord tile = TranslateCoord(key.x, key.y, key.z); BlockInfo tileInfo = tile.chunk.GetInfo(tile.lx, tile.ly, tile.lz); if (tileInfo.ID == BlockInfo.StationaryWater.ID || tileInfo.ID == BlockInfo.Water.ID) { curflow = tile.chunk.GetData(tile.lx, tile.ly, tile.lz); } else if (tileInfo.BlocksFluid) { continue; } bool curFall = (curflow & (int)LiquidState.FALLING) != 0; bool inFall = (inflow & (int)LiquidState.FALLING) != 0; // We won't update from the following states if (curflow == 0 || curflow == inflow || curFall) { continue; } int newflow = curflow; // Update from inflow if necessary if (inFall) { newflow = inflow; } else if (inflow >= 7) { newflow = 16; } else { newflow = inflow + 1; } // If we haven't changed the flow, don't propagate if (newflow == curflow) { continue; } // Update flow, add or remove water tile as necessary if (newflow < 16 && curflow == 16) { // If we're overwriting lava, replace with appropriate stone type and abort propagation if (tileInfo.ID == BlockInfo.StationaryLava.ID || tileInfo.ID == BlockInfo.Lava.ID) { if ((newflow & (int)LiquidState.FALLING) != 0) { int odata = tile.chunk.GetData(tile.lx, tile.ly, tile.lz); if (odata == 0) { tile.chunk.SetID(tile.lx, tile.ly, tile.lz, BlockInfo.Obsidian.ID); } else { tile.chunk.SetID(tile.lx, tile.ly, tile.lz, BlockInfo.Cobblestone.ID); } tile.chunk.SetData(tile.lx, tile.ly, tile.lz, 0); continue; } } // Otherwise replace the tile with our water flow tile.chunk.SetID(tile.lx, tile.ly, tile.lz, BlockInfo.StationaryWater.ID); tile.chunk.SetData(tile.lx, tile.ly, tile.lz, newflow); } else if (newflow == 16) { tile.chunk.SetID(tile.lx, tile.ly, tile.lz, BlockInfo.Air.ID); tile.chunk.SetData(tile.lx, tile.ly, tile.lz, 0); } else { tile.chunk.SetData(tile.lx, tile.ly, tile.lz, newflow); } // Process outflows outflow = TileOutflow(key); foreach (BlockKey nkey in outflow) { flowQueue.Enqueue(nkey); } } }
private void UpdateBlockSkyLight() { IBoundedLitBlockCollection[,] chunkMap = LocalBlockLightMap(); int xdim = _xdim; int ydim = _ydim; int zdim = _zdim; while (_update.Count > 0) { BlockKey k = _update.Dequeue(); int index = LightBitmapIndex(k); _lightbit[index] = false; int xi = k.x + xdim; int zi = k.z + zdim; IBoundedLitBlockCollection cc = chunkMap[xi / xdim, zi / zdim]; if (cc == null) { continue; } int x = xi % xdim; int y = k.y; int z = zi % zdim; int lightval = cc.GetSkyLight(x, y, z); BlockInfo info = cc.GetInfo(x, y, z); int light = BlockInfo.MIN_LUMINANCE; if (cc.GetHeight(x, z) <= y) { light = BlockInfo.MAX_LUMINANCE; } else { int lle = NeighborSkyLight(chunkMap, k.x, k.y, k.z - 1); int lln = NeighborSkyLight(chunkMap, k.x - 1, k.y, k.z); int lls = NeighborSkyLight(chunkMap, k.x, k.y, k.z + 1); int llw = NeighborSkyLight(chunkMap, k.x + 1, k.y, k.z); int lld = NeighborSkyLight(chunkMap, k.x, k.y - 1, k.z); int llu = NeighborSkyLight(chunkMap, k.x, k.y + 1, k.z); light = Math.Max(light, lle); light = Math.Max(light, lln); light = Math.Max(light, lls); light = Math.Max(light, llw); light = Math.Max(light, lld); light = Math.Max(light, llu); } light = Math.Max(light - info.Opacity, 0); if (light != lightval) { //Console.WriteLine("Block SkyLight: ({0},{1},{2}) " + lightval + " -> " + light, k.x, k.y, k.z); cc.SetSkyLight(x, y, z, light); if (info.TransmitsLight) { if (k.y > 0) { QueueRelight(new BlockKey(k.x, k.y - 1, k.z)); } if (k.y < ydim - 1) { QueueRelight(new BlockKey(k.x, k.y + 1, k.z)); } QueueRelight(new BlockKey(k.x - 1, k.y, k.z)); QueueRelight(new BlockKey(k.x + 1, k.y, k.z)); QueueRelight(new BlockKey(k.x, k.y, k.z - 1)); QueueRelight(new BlockKey(k.x, k.y, k.z + 1)); } } } }