private void GenerateHeightMap(IChunk chunk) { Coordinates3D coords; var map = new byte[Chunk.Width, Chunk.Depth]; for (byte x = 0; x < Chunk.Width; x++) { for (byte z = 0; z < Chunk.Depth; z++) { for (byte y = (byte)(chunk.GetHeight(x, z) + 2); y > 0; y--) { if (y >= Chunk.Height) { continue; } coords.X = x; coords.Y = y - 1; coords.Z = z; var id = chunk.GetBlockID(coords); if (id == 0) { continue; } var provider = BlockRepository.GetBlockProvider(id); if (provider.LightOpacity != 0) { map[x, z] = y; break; } } } } HeightMaps[chunk.Coordinates] = map; }
private void UpdateHeightMap(Coordinates3D coords) { IChunk chunk; var adjusted = World.FindBlockPosition(coords, out chunk, generate: false); if (!HeightMaps.ContainsKey(chunk.Coordinates)) { return; } var map = HeightMaps[chunk.Coordinates]; byte x = (byte)adjusted.X; byte z = (byte)adjusted.Z; Coordinates3D _; for (byte y = (byte)(chunk.GetHeight(x, z) + 2); y > 0; y--) { if (y >= Chunk.Height) { continue; } _.X = x; _.Y = y - 1; _.Z = z; var id = chunk.GetBlockID(_); if (id == 0) { continue; } var provider = BlockRepository.GetBlockProvider(id); if (provider.LightOpacity != 0) { map[x, z] = y; break; } } }
public void DoSpread(IMultiplayerServer server, IWorld world, BlockDescriptor descriptor) { foreach (var coord in SpreadableBlocks) { var check = descriptor.Coordinates + coord; if (world.GetBlockID(check) == AirBlock.BlockID) { // Check if this is adjacent to a flammable block foreach (var adj in AdjacentBlocks) { var provider = BlockRepository.GetBlockProvider( world.GetBlockID(check + adj)); if (provider.Flammable) { if (provider.Hardness == 0) { check = check + adj; } // Spread to this block world.SetBlockID(check, FireBlock.BlockID); ScheduleUpdate(server, world, world.GetBlockData(check)); break; } } } } }
public void DoUpdate(IMultiPlayerServer server, IWorld world, BlockDescriptor descriptor) { var down = descriptor.Coordinates + Coordinates3D.Down; var current = world.GetBlockId(descriptor.Coordinates); if (current != BlockId && current != LavaBlock.BlockId && current != StationaryLavaBlock.BlockId) { return; } // Decay var meta = world.GetMetadata(descriptor.Coordinates); meta++; if (meta == 0xE) { if (!world.IsValidPosition(down) || world.GetBlockId(down) != NetherrackBlock.BlockId) { world.SetBlockId(descriptor.Coordinates, AirBlock.BlockId); return; } } world.SetMetadata(descriptor.Coordinates, meta); if (meta > 9) { var pick = AdjacentBlocks[meta % AdjacentBlocks.Length]; var provider = BlockRepository .GetBlockProvider(world.GetBlockId(pick + descriptor.Coordinates)); if (provider.Flammable) { world.SetBlockId(pick + descriptor.Coordinates, AirBlock.BlockId); } } // Spread DoSpread(server, world, descriptor); // Schedule next event ScheduleUpdate(server, world, descriptor); }
private void FlowOutward(IWorld world, LiquidFlow target, IMultiplayerServer server) { // For each block we can flow into, generate an item entity if appropriate var provider = world.BlockRepository.GetBlockProvider(world.GetBlockID(target.TargetBlock)); provider.GenerateDropEntity(new BlockDescriptor { Coordinates = target.TargetBlock, ID = provider.ID }, world, server, ItemStack.EmptyStack); // And overwrite the block with a new fluid block. world.SetBlockID(target.TargetBlock, FlowingID); world.SetMetadata(target.TargetBlock, target.Level); var chunk = world.FindChunk(target.TargetBlock); server.Scheduler.ScheduleEvent("fluid", chunk, TimeSpan.FromSeconds(SecondsBetweenUpdates), s => AutomataUpdate(s, world, target.TargetBlock)); if (FlowingID == LavaBlock.BlockID) { (BlockRepository.GetBlockProvider(FireBlock.BlockID) as FireBlock).ScheduleUpdate( server, world, world.GetBlockData(target.TargetBlock)); } }
/// <summary> /// Propegates a lighting change to an adjacent voxel (if neccesary) /// </summary> private void PropegateLightEvent(int x, int y, int z, byte value, LightingOperation op) { var coords = new Coordinates3D(x, y, z); if (!World.IsValidPosition(coords)) { return; } IChunk chunk; var adjustedCoords = World.FindBlockPosition(coords, out chunk, false); if (chunk == null || !chunk.TerrainPopulated) { return; } var current = op.SkyLight ? World.GetSkyLight(coords) : World.GetBlockLight(coords); if (value == current) { return; } var provider = BlockRepository.GetBlockProvider(World.GetBlockId(coords)); if (op.Initial) { var emissiveness = provider.Luminance; if (chunk.GetHeight((byte)adjustedCoords.X, (byte)adjustedCoords.Z) <= y) { emissiveness = 15; } if (emissiveness >= current) { return; } } EnqueueOperation(new BoundingBox(new Vector3(x, y, z), new Vector3(x + 1, y + 1, z + 1)), op.SkyLight, op.Initial); }
/// <summary> /// Computes the correct lighting value for a given voxel. /// </summary> private void LightVoxel(int x, int y, int z, LightingOperation op) { var coords = new Coordinates3D(x, y, z); IChunk chunk; var adjustedCoords = World.FindBlockPosition(coords, out chunk, generate: false); if (chunk == null || !chunk.TerrainPopulated) // Move on if this chunk is empty { return; } var id = World.GetBlockID(coords); var provider = BlockRepository.GetBlockProvider(id); // The opacity of the block determines the amount of light it receives from // neighboring blocks. This is subtracted from the max of the neighboring // block values. We must subtract at least 1. byte opacity = Math.Max(provider.LightOpacity, (byte)1); byte current = op.SkyLight ? World.GetSkyLight(coords) : World.GetBlockLight(coords); byte final = 0; // Calculate emissiveness byte emissiveness = provider.Luminance; if (op.SkyLight) { var height = HeightMaps[chunk.Coordinates][adjustedCoords.X, adjustedCoords.Z]; // For skylight, the emissiveness is 15 if y >= height if (y >= height) { emissiveness = 15; } else { if (provider.LightOpacity >= 15) { emissiveness = 0; } } } if (opacity < 15 || emissiveness != 0) { // Compute the light based on the max of the neighbors byte max = 0; for (int i = 0; i < Neighbors.Length; i++) { if (World.IsValidPosition(coords + Neighbors[i])) { IChunk c; var adjusted = World.FindBlockPosition(coords + Neighbors[i], out c, generate: false); if (c != null) // We don't want to generate new chunks just to light this voxel { byte val; if (op.SkyLight) { val = c.GetSkyLight(adjusted); } else { val = c.GetBlockLight(adjusted); } max = Math.Max(max, val); } } } // final = MAX(max - opacity, emissiveness, 0) final = (byte)Math.Max(max - opacity, emissiveness); if (final < 0) { final = 0; } } if (final != current) { // Apply changes if (op.SkyLight) { chunk.SetSkyLight(adjustedCoords, final); } else { chunk.SetBlockLight(adjustedCoords, final); } byte propegated = (byte)Math.Max(final - 1, 0); // Propegate lighting change to neighboring blocks PropegateLightEvent(x - 1, y, z, propegated, op); PropegateLightEvent(x, y - 1, z, propegated, op); PropegateLightEvent(x, y, z - 1, propegated, op); if (x + 1 >= op.Box.Max.X) { PropegateLightEvent(x + 1, y, z, propegated, op); } if (y + 1 >= op.Box.Max.Y) { PropegateLightEvent(x, y + 1, z, propegated, op); } if (z + 1 >= op.Box.Max.Z) { PropegateLightEvent(x, y, z + 1, propegated, op); } } }
public override void BlockMined(BlockDescriptor descriptor, BlockFace face, IWorld world, IRemoteClient user) { world.SetBlockId(descriptor.Coordinates, WaterBlock.BlockId); BlockRepository.GetBlockProvider(WaterBlock.BlockId).BlockPlaced(descriptor, face, world, user); }