77import com .ferreusveritas .dynamictrees .init .DTRegistries ;
88import com .ferreusveritas .dynamictrees .systems .BranchConnectables ;
99import com .ferreusveritas .dynamictrees .util .CoordUtils ;
10- import com .ferreusveritas .dynamictrees .util .CoordUtils .Surround ;
10+ import com .ferreusveritas .dynamictrees .util .CoordUtils .ShellDirection ;
1111import net .minecraft .core .BlockPos ;
1212import net .minecraft .core .Direction ;
1313import net .minecraft .resources .ResourceLocation ;
1616import net .minecraft .world .level .Level ;
1717import net .minecraft .world .level .LevelAccessor ;
1818import net .minecraft .world .level .block .Block ;
19+ import net .minecraft .world .level .block .Blocks ;
1920import net .minecraft .world .level .block .state .BlockState ;
2021import net .minecraft .world .level .block .state .StateDefinition ;
2122import net .minecraft .world .level .block .state .properties .IntegerProperty ;
3132
3233public class ThickBranchBlock extends BasicBranchBlock implements Musable {
3334
34- public static final int MAX_RADIUS_THICK = 24 ;
35+ public static final int MAX_RADIUS_THICK = 56 ;
36+ public static final int RADIUS_TO_INNER_SHELL = 8 ; // > 8 needs 3×3
37+ public static final int RADIUS_TO_OUTER_SHELL = 24 ; // > 24 needs 5×5
38+ public static final int RADIUS_TO_OUTERMOST_SHELL = 40 ; // > 40 needs 7×7
3539
36- protected static final IntegerProperty RADIUS_DOUBLE = IntegerProperty .create ("radius" , 1 , MAX_RADIUS_THICK ); //39 ?
40+ protected static final IntegerProperty RADIUS_DOUBLE = IntegerProperty .create ("radius" , 1 , MAX_RADIUS_THICK );
3741
42+ @ Deprecated
3843 public ThickBranchBlock (ResourceLocation name , MapColor mapColor ) {
3944 this (name , Properties .of ().mapColor (mapColor ));
4045 }
@@ -52,8 +57,8 @@ public void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState
5257 builder .add (RADIUS_DOUBLE ).add (WATERLOGGED );
5358 }
5459
55- ///////////////////////////////////////////
56- // GROWTH
60+ ///////////////////////////////////////////
61+ // GROWTH
5762 ///////////////////////////////////////////
5863
5964 @ Override
@@ -69,7 +74,7 @@ public int setRadius(LevelAccessor level, BlockPos pos, int radius, @Nullable Di
6974 if (this .updateTrunkShells (level , pos , radius , flags )) {
7075 return super .setRadius (level , pos , radius , originDir , flags );
7176 }
72- return super .setRadius (level , pos , MAX_RADIUS , originDir , flags );
77+ return super .setRadius (level , pos , getRadius ( level . getBlockState ( pos )) , originDir , flags );
7378 }
7479
7580 @ Override
@@ -79,74 +84,118 @@ public void neighborChanged(BlockState state, Level level, BlockPos pos, Block b
7984 }
8085
8186 private boolean updateTrunkShells (LevelAccessor level , BlockPos pos , int radius , int flags ) {
82- // If the radius is <= 8 then we can just set the block as normal and move on.
83- if (radius <= MAX_RADIUS ) {
87+ boolean needsInnerRing = radius > RADIUS_TO_INNER_SHELL ; // > 8
88+ boolean needsOuterRing = radius > RADIUS_TO_OUTER_SHELL ; // > 24
89+ boolean needsOutermostRing = radius > RADIUS_TO_OUTERMOST_SHELL ; // > 40
90+
91+ // No shells needed
92+ if (!needsInnerRing ) {
8493 return true ;
8594 }
8695
87- boolean setable = true ;
88- final ReplaceableState [] repStates = new ReplaceableState [8 ];
89-
90- for (Surround dir : Surround .values ()) {
91- final BlockPos dPos = pos .offset (dir .getOffset ());
92- final ReplaceableState rep = getReplaceability (level , dPos , pos );
93-
94- repStates [dir .ordinal ()] = rep ;
95-
96+ // === Check inner ring ===
97+ final ReplaceableState [] innerRepStates = new ReplaceableState [8 ];
98+ ShellDirection [] innerDirs = ShellDirection .innerValues ();
99+ for (int i = 0 ; i < innerDirs .length ; i ++) {
100+ ShellDirection dir = innerDirs [i ];
101+ BlockPos dPos = pos .offset (dir .getOffset ());
102+ ReplaceableState rep = getReplaceability (level , dPos , pos , 1 );
103+ innerRepStates [i ] = rep ;
96104 if (rep == ReplaceableState .BLOCKING ) {
97- setable = false ;
98- break ;
105+ return false ;
99106 }
100107 }
101108
102- if (setable ) {
103- BlockState trunkState = level .getBlockState (pos );
104- boolean isWaterlogged = trunkState .hasProperty (WATERLOGGED ) && trunkState .getValue (WATERLOGGED );
105- for (Surround dir : Surround .values ()) {
106- final BlockPos dPos = pos .offset (dir .getOffset ());
107- final ReplaceableState rep = repStates [dir .ordinal ()];
108- final boolean replacingWater = isWaterlogged || level .getBlockState (dPos ).getFluidState () == Fluids .WATER .getSource (false );
109+ // === Check outer ring (if needed) ===
110+ final ReplaceableState [] outerRepStates = new ReplaceableState [16 ];
111+ ShellDirection [] outerDirs = ShellDirection .outerValues ();
112+ if (needsOuterRing ) {
113+ for (int i = 0 ; i < outerDirs .length ; i ++) {
114+ ShellDirection dir = outerDirs [i ];
115+ BlockPos dPos = pos .offset (dir .getOffset ());
116+ ReplaceableState rep = getReplaceability (level , dPos , pos , 2 );
117+ outerRepStates [i ] = rep ;
118+ if (rep == ReplaceableState .BLOCKING ) {
119+ return false ;
120+ }
121+ }
122+ }
109123
110- if (rep == ReplaceableState .REPLACEABLE ) {
111- level .setBlock (dPos , getTrunkShell ().defaultBlockState ().setValue (TrunkShellBlock .CORE_DIR , dir .getOpposite ()).setValue (TrunkShellBlock .WATERLOGGED , replacingWater ), flags );
124+ // === Check outermost ring (if needed) ===
125+ final ReplaceableState [] outermostRepStates = new ReplaceableState [24 ];
126+ ShellDirection [] outermostDirs = ShellDirection .outermostValues ();
127+ if (needsOutermostRing ) {
128+ for (int i = 0 ; i < outermostDirs .length ; i ++) {
129+ ShellDirection dir = outermostDirs [i ];
130+ BlockPos dPos = pos .offset (dir .getOffset ());
131+ ReplaceableState rep = getReplaceability (level , dPos , pos , 3 );
132+ outermostRepStates [i ] = rep ;
133+ if (rep == ReplaceableState .BLOCKING ) {
134+ return false ;
112135 }
113136 }
114- return true ;
115137 }
116- return false ;
117- }
118138
119- @ Override
120- public int getRadiusForConnection (BlockState state , BlockGetter level , BlockPos pos , BranchBlock from , Direction side , int fromRadius ) {
121- if (from instanceof ThickBranchBlock ) {
122- return getRadius (state );
139+ // === Place shells ===
140+ BlockState trunkState = level .getBlockState (pos );
141+ boolean isWaterlogged = trunkState .hasProperty (WATERLOGGED ) && trunkState .getValue (WATERLOGGED );
142+
143+ // Place inner ring
144+ for (int i = 0 ; i < innerDirs .length ; i ++) {
145+ ShellDirection dir = innerDirs [i ];
146+ BlockPos dPos = pos .offset (dir .getOffset ());
147+ ReplaceableState rep = innerRepStates [i ];
148+ boolean replacingWater = isWaterlogged || level .getBlockState (dPos ).getFluidState () == Fluids .WATER .getSource (false );
149+
150+ if (rep == ReplaceableState .REPLACEABLE ) {
151+ level .setBlock (dPos , getTrunkShell ().defaultBlockState ()
152+ .setValue (TrunkShellBlock .CORE_DIR , dir .getOpposite ())
153+ .setValue (TrunkShellBlock .WATERLOGGED , replacingWater ), flags );
154+ }
123155 }
124- return Math .min (getRadius (state ), MAX_RADIUS );
125- }
126156
127- @ Override
128- protected int getSideConnectionRadius (BlockGetter level , BlockPos pos , int radius , Direction side ) {
129- final BlockPos deltaPos = pos .relative (side );
130- final BlockState blockState = CoordUtils .getStateSafe (level , deltaPos );
157+ // Place outer ring (if needed)
158+ if (needsOuterRing ) {
159+ for (int i = 0 ; i < outerDirs .length ; i ++) {
160+ ShellDirection dir = outerDirs [i ];
161+ BlockPos dPos = pos .offset (dir .getOffset ());
162+ ReplaceableState rep = outerRepStates [i ];
163+ boolean replacingWater = isWaterlogged || level .getBlockState (dPos ).getFluidState () == Fluids .WATER .getSource (false );
131164
132- if (blockState == null ) {
133- return 0 ;
165+ if (rep == ReplaceableState .REPLACEABLE ) {
166+ level .setBlock (dPos , getTrunkShell ().defaultBlockState ()
167+ .setValue (TrunkShellBlock .CORE_DIR , dir .getOpposite ())
168+ .setValue (TrunkShellBlock .WATERLOGGED , replacingWater ), flags );
169+ }
170+ }
134171 }
135172
136- final int connectionRadius = TreeHelper .getTreePart (blockState ).getRadiusForConnection (blockState , level , deltaPos , this , side , radius );
173+ // Place outermost ring (if needed)
174+ if (needsOutermostRing ) {
175+ for (int i = 0 ; i < outermostDirs .length ; i ++) {
176+ ShellDirection dir = outermostDirs [i ];
177+ BlockPos dPos = pos .offset (dir .getOffset ());
178+ ReplaceableState rep = outermostRepStates [i ];
179+ boolean replacingWater = isWaterlogged || level .getBlockState (dPos ).getFluidState () == Fluids .WATER .getSource (false );
137180
138- return Math .min (MAX_RADIUS , connectionRadius );
139- }
181+ if (rep == ReplaceableState .REPLACEABLE ) {
182+ level .setBlock (dPos , getTrunkShell ().defaultBlockState ()
183+ .setValue (TrunkShellBlock .CORE_DIR , dir .getOpposite ())
184+ .setValue (TrunkShellBlock .WATERLOGGED , replacingWater ), flags );
185+ }
186+ }
187+ }
140188
141- public ReplaceableState getReplaceability (LevelAccessor level , BlockPos pos , BlockPos corePos ) {
189+ return true ;
190+ }
142191
192+ public ReplaceableState getReplaceability (LevelAccessor level , BlockPos pos , BlockPos corePos , int ringLevel ) {
143193 final BlockState state = level .getBlockState (pos );
144194 final Block block = state .getBlock ();
145195
146196 if (block instanceof TrunkShellBlock ) {
147- // Determine if this shell belongs to the trunk. Block otherwise.
148- Surround surr = state .getValue (TrunkShellBlock .CORE_DIR );
149- return pos .offset (surr .getOffset ()).equals (corePos ) ? ReplaceableState .SHELL : ReplaceableState .BLOCKING ;
197+ ShellDirection dir = state .getValue (TrunkShellBlock .CORE_DIR );
198+ return pos .offset (dir .getOffset ()).equals (corePos ) ? ReplaceableState .SHELL : ReplaceableState .BLOCKING ;
150199 }
151200
152201 if (state .canBeReplaced () || state .is (DTBlockTags .FOLIAGE )) {
@@ -165,32 +214,74 @@ public ReplaceableState getReplaceability(LevelAccessor level, BlockPos pos, Blo
165214 return ReplaceableState .TREEPART ;
166215 }
167216
168- if (block instanceof FruitBlock || block instanceof PodBlock ){
217+ if (block instanceof FruitBlock || block instanceof PodBlock ) {
169218 return ReplaceableState .TREEPART ;
170219 }
171220
172221 if (this .getFamily ().getCommonSpecies ().isAcceptableSoilForWorldgen (level , pos , state )) {
173222 return ReplaceableState .REPLACEABLE ;
174223 }
175224
225+ if (ringLevel == 1 ) {
226+ float hardness = state .getDestroySpeed (level , pos );
227+ if (hardness >= 0 && hardness < 1 ) {
228+ return ReplaceableState .REPLACEABLE ;
229+ }
230+ }
231+
232+ if (ringLevel == 2 ) {
233+ float hardness = state .getDestroySpeed (level , pos );
234+ if (hardness >= 0 && hardness < 3 ) {
235+ return ReplaceableState .REPLACEABLE ;
236+ }
237+ }
238+
239+ // Outermost ring can break most hard blocks
240+ if (ringLevel == 3 ) {
241+ float hardness = state .getDestroySpeed (level , pos );
242+ if (hardness >= 0 && hardness < 5 ) {
243+ return ReplaceableState .REPLACEABLE ;
244+ }
245+ }
246+
176247 return ReplaceableState .BLOCKING ;
177248 }
178249
179250 enum ReplaceableState {
180- SHELL , // This indicates that the block is already a shell.
181- REPLACEABLE , // This indicates that the block is truly replaceable and will be erased.
182- BLOCKING , // This indicates that the block is not replaceable, will NOT be erased, and will prevent the tree from growing.
183- TREEPART // This indicates that the block is part of a tree, will NOT be erase, and will NOT prevent the tree from growing.
251+ SHELL ,
252+ REPLACEABLE ,
253+ BLOCKING ,
254+ TREEPART
255+ }
256+
257+ @ Override
258+ public int getRadiusForConnection (BlockState state , BlockGetter level , BlockPos pos , BranchBlock from , Direction side , int fromRadius ) {
259+ if (from instanceof ThickBranchBlock ) {
260+ return getRadius (state );
261+ }
262+ return Math .min (getRadius (state ), MAX_RADIUS );
263+ }
264+
265+ @ Override
266+ protected int getSideConnectionRadius (BlockGetter level , BlockPos pos , int radius , Direction side ) {
267+ final BlockPos deltaPos = pos .relative (side );
268+ final BlockState blockState = CoordUtils .getStateSafe (level , deltaPos );
269+
270+ if (blockState == null ) {
271+ return 0 ;
272+ }
273+
274+ final int connectionRadius = TreeHelper .getTreePart (blockState ).getRadiusForConnection (blockState , level , deltaPos , this , side , radius );
275+ return Math .min (MAX_RADIUS , connectionRadius );
184276 }
185277
186278 @ Override
187279 public int getMaxRadius () {
188280 return MAX_RADIUS_THICK ;
189281 }
190282
191-
192- ///////////////////////////////////////////
193- // PHYSICAL BOUNDS
283+ ///////////////////////////////////////////
284+ // PHYSICAL BOUNDS
194285 ///////////////////////////////////////////
195286
196287 @ Nonnull
@@ -209,5 +300,4 @@ public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, Co
209300 public boolean isMusable (BlockGetter level , BlockState state , BlockPos pos ) {
210301 return getRadius (state ) > 8 ;
211302 }
212-
213- }
303+ }
0 commit comments