// ClassicalSharp copyright 2014-2016 UnknownShadow200 | Licensed under MIT using System; using ClassicalSharp.Map; namespace ClassicalSharp.Singleplayer { public class FoilagePhysics { Game game; World map; Random rnd = new Random(); public FoilagePhysics( Game game, Physics physics ) { this.game = game; map = game.World; physics.OnPlace[Block.Sapling] = HandleSapling; physics.OnRandomTick[Block.Sapling] = HandleSapling; physics.OnRandomTick[Block.Dirt] = HandleDirt; physics.OnRandomTick[Block.Grass] = HandleGrass; physics.OnRandomTick[Block.Dandelion] = HandleFlower; physics.OnRandomTick[Block.Rose] = HandleFlower; physics.OnRandomTick[Block.RedMushroom] = HandleFlower; physics.OnRandomTick[Block.BrownMushroom] = HandleFlower; } void HandleSapling( int index, byte block ) { int x = index % map.Width; int y = (index / map.Width) / map.Length; int z = (index / map.Width) % map.Length; GrowTree( x, y, z ); } void HandleDirt( int index, byte block ) { int x = index % map.Width; int y = (index / map.Width) / map.Length; int z = (index / map.Width) % map.Length; if( y > map.GetLightHeight( x, z ) ) game.UpdateBlock( x, y, z, Block.Grass ); } void HandleGrass( int index, byte block ) { int x = index % map.Width; int y = (index / map.Width) / map.Length; int z = (index / map.Width) % map.Length; if( y <= map.GetLightHeight( x, z ) ) game.UpdateBlock( x, y, z, Block.Dirt ); } void HandleFlower( int index, byte block ) { int x = index % map.Width; int y = (index / map.Width) / map.Length; int z = (index / map.Width) % map.Length; if( y <= map.GetLightHeight( x, z ) ) game.UpdateBlock( x, y, z, Block.Air ); } // Algorithm source: Looking at the trees generated by the default classic server. // Hence, the random thresholds may be slightly off. public void GrowTree( int x, int y, int z ) { int trunkH = rnd.Next( 1, 4 ); game.UpdateBlock( x, y, z, 0 ); // Can the new tree grow? if( !CheckBounds( x, x, y, y + trunkH - 1, z, z ) || !CheckBounds( x - 2, x + 2, y + trunkH, y + trunkH + 1, z - 2, z + 2 ) || !CheckBounds( x - 1, x + 1, y + trunkH + 2, y + trunkH + 3, z - 1, z + 1 ) ) { game.UpdateBlock( x, y, z, 0 ); return; } // Leaves bottom layer y += trunkH; for( int zz = -2; zz <= 2; zz++ ) { for( int xx = -2; xx <= 2; xx++ ) { if( Math.Abs( xx ) == 2 && Math.Abs( zz ) == 2 ) { if( rnd.Next( 0, 5 ) < 4 ) game.UpdateBlock( x + xx, y, z + zz, Block.Leaves ); if( rnd.Next( 0, 5 ) < 2 ) game.UpdateBlock( x + xx, y + 1, z + zz, Block.Leaves ); } else { game.UpdateBlock( x + xx, y, z + zz, Block.Leaves ); game.UpdateBlock( x + xx, y + 1, z + zz, Block.Leaves ); } } } // Leaves top layer y += 2; for( int zz = -1; zz <= 1; zz++ ) { for( int xx = -1; xx <= 1; xx++ ) { if( xx == 0 || zz == 0 ) { game.UpdateBlock( x + xx, y, z + zz, Block.Leaves ); game.UpdateBlock( x + xx, y + 1, z + zz, Block.Leaves ); } else if( rnd.Next( 0, 5 ) == 0 ) { game.UpdateBlock( x + xx, y, z + zz, Block.Leaves ); } } } // Base trunk y -= 2 + trunkH; for( int yy = 0; yy < trunkH + 3; yy++ ) game.UpdateBlock( x, y + yy, z, Block.Log ); } bool CheckBounds( int x1, int x2, int y1, int y2, int z1, int z2 ) { for( int y = y1; y <= y2; y++ ) for( int z = z1; z <= z2; z++ ) for( int x = x1; x <= x2; x++ ) { if( !map.IsValidPos( x, y, z ) ) return false; byte block = map.GetBlock( x, y, z ); if( !(block == 0 || block == Block.Leaves ) ) return false; } return true; } } }