@@ -3740,6 +3740,159 @@ protected BiomeType getBiome(int x, int y, int z, BiomeType defaultBiomeType) {
37403740 return changed ;
37413741 }
37423742
3743+ public int morph (BlockVector3 position , double brushSize , int minErodeFaces , int numErodeIterations , int minDilateFaces , int numDilateIterations ) throws MaxChangedBlocksException {
3744+ int ceilBrushSize = (int ) Math .ceil (brushSize );
3745+ int bufferSize = ceilBrushSize * 2 + 3 ; // + 1 due to checking the adjacent blocks, plus the 0th block
3746+ // Store block states in a 3d array so we can do multiple mutations then commit.
3747+ // Two are required as for each iteration, one is "current" and the other is "new"
3748+ BlockState [][][] currentBuffer = new BlockState [bufferSize ][bufferSize ][bufferSize ];
3749+ BlockState [][][] nextBuffer = new BlockState [bufferSize ][bufferSize ][bufferSize ];
3750+
3751+ // Simply used for swapping the two
3752+ BlockState [][][] tmp ;
3753+
3754+ // Load into buffer
3755+ for (int x = 0 ; x < bufferSize ; x ++) {
3756+ for (int y = 0 ; y < bufferSize ; y ++) {
3757+ for (int z = 0 ; z < bufferSize ; z ++) {
3758+ BlockState blockState = getBlock (position .add (x - ceilBrushSize - 1 , y - ceilBrushSize - 1 , z - ceilBrushSize - 1 ));
3759+ currentBuffer [x ][y ][z ] = blockState ;
3760+ nextBuffer [x ][y ][z ] = blockState ;
3761+ }
3762+ }
3763+ }
3764+
3765+ double brushSizeSq = brushSize * brushSize ;
3766+ Map <BlockState , Integer > blockStateFrequency = new HashMap <>();
3767+ int totalFaces ;
3768+ int highestFreq ;
3769+ BlockState highestState ;
3770+ for (int i = 0 ; i < numErodeIterations ; i ++) {
3771+ for (int x = 0 ; x <= ceilBrushSize * 2 ; x ++) {
3772+ int realX = x - ceilBrushSize ;
3773+ int xsqr = realX * realX ;
3774+ for (int y = 0 ; y <= ceilBrushSize * 2 ; y ++) {
3775+ int realY = y - ceilBrushSize ;
3776+ int ysqr = realY * realY ;
3777+ for (int z = 0 ; z <= ceilBrushSize * 2 ; z ++) {
3778+ int realZ = z - ceilBrushSize ;
3779+ int zsqr = realZ * realZ ;
3780+ if (xsqr + ysqr + zsqr > brushSizeSq ) {
3781+ continue ;
3782+ }
3783+
3784+ // Copy across changes
3785+ nextBuffer [x + 1 ][y + 1 ][z + 1 ] = currentBuffer [x + 1 ][y + 1 ][z + 1 ];
3786+
3787+ BlockState blockState = currentBuffer [x + 1 ][y + 1 ][z + 1 ];
3788+
3789+ if (blockState .getBlockType ().getMaterial ().isLiquid () || blockState .getBlockType ().getMaterial ().isAir ()) {
3790+ continue ;
3791+ }
3792+
3793+ blockStateFrequency .clear ();
3794+ totalFaces = 0 ;
3795+ highestFreq = 0 ;
3796+ highestState = blockState ;
3797+ for (BlockVector3 vec3 : recurseDirections ) {
3798+ BlockState adj = currentBuffer [x + 1 + vec3 .x ()][y + 1 + vec3 .y ()][z + 1 + vec3 .z ()];
3799+
3800+ if (!adj .getBlockType ().getMaterial ().isLiquid () && !adj .getBlockType ().getMaterial ().isAir ()) {
3801+ continue ;
3802+ }
3803+
3804+ totalFaces ++;
3805+ int newFreq = blockStateFrequency .getOrDefault (adj , 0 ) + 1 ;
3806+ blockStateFrequency .put (adj , newFreq );
3807+
3808+ if (newFreq > highestFreq ) {
3809+ highestFreq = newFreq ;
3810+ highestState = adj ;
3811+ }
3812+ }
3813+
3814+ if (totalFaces >= minErodeFaces ) {
3815+ nextBuffer [x + 1 ][y + 1 ][z + 1 ] = highestState ;
3816+ }
3817+ }
3818+ }
3819+ }
3820+ // Swap current and next
3821+ tmp = currentBuffer ;
3822+ currentBuffer = nextBuffer ;
3823+ nextBuffer = tmp ;
3824+ }
3825+
3826+ for (int i = 0 ; i < numDilateIterations ; i ++) {
3827+ for (int x = 0 ; x <= ceilBrushSize * 2 ; x ++) {
3828+ int realX = x - ceilBrushSize ;
3829+ int xsqr = realX * realX ;
3830+ for (int y = 0 ; y <= ceilBrushSize * 2 ; y ++) {
3831+ int realY = y - ceilBrushSize ;
3832+ int ysqr = realY * realY ;
3833+ for (int z = 0 ; z <= ceilBrushSize * 2 ; z ++) {
3834+ int realZ = z - ceilBrushSize ;
3835+ int zsqr = realZ * realZ ;
3836+ if (xsqr + ysqr + zsqr > brushSizeSq ) {
3837+ continue ;
3838+ }
3839+
3840+ // Copy across changes
3841+ nextBuffer [x + 1 ][y + 1 ][z + 1 ] = currentBuffer [x + 1 ][y + 1 ][z + 1 ];
3842+
3843+ BlockState blockState = currentBuffer [x + 1 ][y + 1 ][z + 1 ];
3844+ // Needs to be empty
3845+ if (!blockState .getBlockType ().getMaterial ().isLiquid () && !blockState .getBlockType ().getMaterial ().isAir ()) {
3846+ continue ;
3847+ }
3848+
3849+ blockStateFrequency .clear ();
3850+ totalFaces = 0 ;
3851+ highestFreq = 0 ;
3852+ highestState = blockState ;
3853+ for (BlockVector3 vec3 : recurseDirections ) {
3854+ BlockState adj = currentBuffer [x + 1 + vec3 .x ()][y + 1 + vec3 .y ()][z + 1 + vec3 .z ()];
3855+ if (adj .getBlockType ().getMaterial ().isLiquid () || adj .getBlockType ().getMaterial ().isAir ()) {
3856+ continue ;
3857+ }
3858+
3859+ totalFaces ++;
3860+ int newFreq = blockStateFrequency .getOrDefault (adj , 0 ) + 1 ;
3861+ blockStateFrequency .put (adj , newFreq );
3862+
3863+ if (newFreq > highestFreq ) {
3864+ highestFreq = newFreq ;
3865+ highestState = adj ;
3866+ }
3867+ }
3868+
3869+ if (totalFaces >= minDilateFaces ) {
3870+ nextBuffer [x + 1 ][y + 1 ][z + 1 ] = highestState ;
3871+ }
3872+ }
3873+ }
3874+ }
3875+ // Swap current and next
3876+ tmp = currentBuffer ;
3877+ currentBuffer = nextBuffer ;
3878+ nextBuffer = tmp ;
3879+ }
3880+
3881+ // Commit to world
3882+ int changed = 0 ;
3883+ for (int x = 0 ; x < bufferSize ; x ++) {
3884+ for (int y = 0 ; y < bufferSize ; y ++) {
3885+ for (int z = 0 ; z < bufferSize ; z ++) {
3886+ if (setBlock (position .add (x - ceilBrushSize - 1 , y - ceilBrushSize - 1 , z - ceilBrushSize - 1 ), currentBuffer [x ][y ][z ])) {
3887+ changed ++;
3888+ }
3889+ }
3890+ }
3891+ }
3892+
3893+ return changed ;
3894+ }
3895+
37433896 private static final BlockVector3 [] recurseDirections = {
37443897 Direction .NORTH .toBlockVector (),
37453898 Direction .EAST .toBlockVector (),
@@ -4060,4 +4213,5 @@ public int makeBlob(
40604213 return changes ;
40614214 }
40624215 //FAWE end
4216+
40634217}
0 commit comments