3131import com .sk89q .worldedit .world .block .BlockType ;
3232import com .sk89q .worldedit .world .block .BlockTypes ;
3333
34- import java .util .Locale ;
34+ import java .util .Comparator ;
3535
36+ //FAWE start - rewrite simulator
3637public class SnowSimulator implements LayerFunction {
3738
38- //FAWE start
39- public static final BooleanProperty snowy = (BooleanProperty ) (Property <?>) BlockTypes .GRASS_BLOCK .getProperty ("snowy" );
40- private static final EnumProperty slab = (EnumProperty ) (Property <?>) BlockTypes .SANDSTONE_SLAB .getProperty ("type" );
41- private static final EnumProperty stair = (EnumProperty ) (Property <?>) BlockTypes .SANDSTONE_STAIRS .getProperty ("half" );
42- private static final EnumProperty trapdoor = (EnumProperty ) (Property <?>) BlockTypes .ACACIA_TRAPDOOR .getProperty ("half" );
43- private static final BooleanProperty trapdoorOpen = (BooleanProperty ) (Property <?>) BlockTypes .ACACIA_TRAPDOOR .getProperty (
44- "open" );
45- //FAWE end
39+ public static final BooleanProperty SNOWY = (BooleanProperty ) (Property <?>) BlockTypes .GRASS_BLOCK .getProperty ("snowy" );
40+ private static final EnumProperty PROPERTY_SLAB = (EnumProperty ) (Property <?>) BlockTypes .OAK_SLAB .getProperty ("type" );
41+ private static final EnumProperty PROPERTY_STAIR = (EnumProperty ) (Property <?>) BlockTypes .OAK_STAIRS .getProperty ("half" );
42+ private static final EnumProperty PROPERTY_TRAPDOOR = (EnumProperty ) (Property <?>) BlockTypes .OAK_TRAPDOOR .getProperty ("half" );
43+ private static final BooleanProperty PROPERTY_TRAPDOOR_OPEN = (BooleanProperty ) (Property <?>) BlockTypes .OAK_TRAPDOOR .getProperty ("open" );
4644
47- private final BlockState ice = BlockTypes .ICE .getDefaultState ();
48- private final BlockState snow = BlockTypes .SNOW .getDefaultState ();
49- private final BlockState snowBlock = BlockTypes .SNOW_BLOCK .getDefaultState ();
45+ private static final BlockState ICE = BlockTypes .ICE .getDefaultState ();
46+ private static final BlockState SNOW = BlockTypes .SNOW .getDefaultState ();
47+ private static final BlockState SNOW_BLOCK = BlockTypes .SNOW_BLOCK .getDefaultState ();
5048
51- private final Property <Integer > snowLayersProperty = BlockTypes .SNOW .getProperty ("layers" );
52- private final Property <Integer > waterLevelProperty = BlockTypes .WATER .getProperty ("level" );
49+ private static final Property <Integer > PROPERTY_SNOW_LAYERS = BlockTypes .SNOW .getProperty ("layers" );
50+ private static final Property <Integer > PROPERTY_WATER_LEVEL = BlockTypes .WATER .getProperty ("level" );
51+
52+ private static final String PROPERTY_VALUE_TOP = "top" ;
53+ private static final String PROPERTY_VALUE_BOTTOM = "bottom" ;
54+ private static final int MAX_SNOW_LAYER = PROPERTY_SNOW_LAYERS .getValues ().stream ().max (Comparator .naturalOrder ()).orElse (8 );
5355
5456 private final Extent extent ;
5557 private final boolean stack ;
56-
5758 private int affected ;
5859
5960 public SnowSimulator (Extent extent , boolean stack ) {
60-
6161 this .extent = extent ;
6262 this .stack = stack ;
63-
6463 this .affected = 0 ;
6564 }
6665
@@ -70,20 +69,21 @@ public int getAffected() {
7069
7170 @ Override
7271 public boolean isGround (BlockVector3 position ) {
73- BlockState block = this .extent .getBlock (position );
72+ final BlockState block = this .extent .getBlock (position );
73+ final BlockType blockType = block .getBlockType ();
7474
75- // We're returning the first block we can place *on top of*
76- if (block . getBlockType (). getMaterial ().isAir () || (stack && block . getBlockType () == BlockTypes .SNOW )) {
75+ // We're returning the first block we can (potentially) place *on top of*
76+ if (blockType . getMaterial ().isAir () || (stack && blockType == BlockTypes .SNOW )) {
7777 return false ;
7878 }
7979
8080 // Unless it's water
81- if (block . getBlockType () == BlockTypes .WATER ) {
81+ if (blockType == BlockTypes .WATER ) {
8282 return true ;
8383 }
8484
8585 // Stop searching when we hit a movement blocker
86- return block . getBlockType () .getMaterial ().isMovementBlocker ();
86+ return blockType .getMaterial ().isMovementBlocker ();
8787 }
8888
8989 @ Override
@@ -93,18 +93,17 @@ public boolean apply(BlockVector3 position, int depth) throws WorldEditException
9393 return false ;
9494 }
9595
96- BlockState block = this .extent .getBlock (position );
96+ final BlockState block = this .extent .getBlock (position );
97+ final BlockType blockType = block .getBlockType ();
9798
98- if (block .getBlockType () == BlockTypes .WATER ) {
99- if (block .getState (waterLevelProperty ) == 0 ) {
100- if (this .extent .setBlock (position , ice )) {
101- affected ++;
102- }
99+ // If affected block is water, replace with ice
100+ if (blockType == BlockTypes .WATER ) {
101+ if (shouldFreeze (position , block ) && this .extent .setBlock (position .x (), position .y (), position .z (), ICE )) {
102+ affected ++;
103103 }
104104 return false ;
105105 }
106106
107-
108107 // Can't put snow this far up
109108 if (position .y () == this .extent .getMaximumPoint ().y ()) {
110109 return false ;
@@ -113,57 +112,91 @@ public boolean apply(BlockVector3 position, int depth) throws WorldEditException
113112 BlockVector3 abovePosition = position .add (0 , 1 , 0 );
114113 BlockState above = this .extent .getBlock (abovePosition );
115114
116- // Can only replace air (or snow in stack mode)
117- if (!above .getBlockType ().getMaterial ().isAir () && (!stack || above .getBlockType () != BlockTypes .SNOW )) {
118- return false ;
119- //FAWE start
120- } else if (!block .getBlockType ().id ().toLowerCase (Locale .ROOT ).contains ("ice" ) && this .extent .getEmittedLight (
121- abovePosition ) > 10 ) {
115+ if (!shouldSnow (block , above )) {
122116 return false ;
123- } else if (!block .getBlockType ().getMaterial ().isFullCube ()) {
124- BlockType type = block .getBlockType ();
125- if (type .hasProperty (slab ) && block .getState (slab ).equalsIgnoreCase ("bottom" )) {
126- return false ;
127- } else if ((type .hasProperty (trapdoorOpen ) && block .getState (trapdoorOpen )) ||
128- (type .hasProperty (trapdoor ) && block .getState (trapdoor ).equalsIgnoreCase ("bottom" ))) {
117+ }
118+
119+ // in stack mode, we want to increase existing snow layers
120+ if (stack && above .getBlockType () == BlockTypes .SNOW ) {
121+ int layers = above .getState (PROPERTY_SNOW_LAYERS );
122+ // if we would place the last possible layer (in current versions layer 8) we just replace with a snow block and
123+ // set the block beneath snowy (if property is applicable, example would be grass with snow texture on top)
124+ if (layers == MAX_SNOW_LAYER - 1 && !this .extent .setBlock (abovePosition , SNOW_BLOCK )) {
129125 return false ;
130- } else if (type .hasProperty (stair ) && block .getState (stair ).equalsIgnoreCase ("bottom" )) {
126+ }
127+ // we've not reached the top snow layer yet, so just add another layer
128+ if (!this .extent .setBlock (abovePosition , above .with (PROPERTY_SNOW_LAYERS , layers + 1 ))) {
131129 return false ;
132- } else {
130+ }
131+ } else {
132+ if (!this .extent .setBlock (abovePosition , SNOW )) {
133133 return false ;
134134 }
135- //FAWE end
136- } else if (!BlockCategories .SNOW_LAYER_CAN_SURVIVE_ON .contains (block .getBlockType ())) {
135+ }
136+ // set block beneath snow (layers) snowy, if applicable
137+ if (block .getBlockType ().hasProperty (SNOWY )) {
138+ this .extent .setBlock (position , block .with (SNOWY , true ));
139+ }
140+ this .affected ++;
141+ return false ;
142+ }
143+
144+ /**
145+ * Check if snow should be placed at {@code above}
146+ *
147+ * @param blockState The block under the snow layer
148+ * @param above The block which will hold the snow layer
149+ * @return if snow should be placed
150+ */
151+ private boolean shouldSnow (BlockState blockState , BlockState above ) {
152+ // simplified net.minecraft.world.level.biome.Biome#shouldSnow
153+ // if the block, where the snow should be actually placed at, is not air or snow (if in stack mode), we can't place
154+ // anything
155+ if (!(above .isAir () || (above .getBlockType () == BlockTypes .SNOW && stack ))) {
156+ return false ;
157+ }
158+ // net.minecraft.world.level.block.SnowLayerBlock#canSurvive
159+ if (BlockCategories .SNOW_LAYER_CANNOT_SURVIVE_ON .contains (blockState )) {
137160 return false ;
138161 }
162+ if (BlockCategories .SNOW_LAYER_CAN_SURVIVE_ON .contains (blockState )) {
163+ return true ;
164+ }
165+ BlockType type = blockState .getBlockType ();
139166
140- if (stack && above .getBlockType () == BlockTypes .SNOW ) {
141- int currentHeight = above .getState (snowLayersProperty );
142- // We've hit the highest layer (If it doesn't contain current + 2 it means it's 1 away from full)
143- if (!snowLayersProperty .getValues ().contains (currentHeight + 2 )) {
144- if (this .extent .setBlock (abovePosition , snowBlock )) {
145- if (block .getBlockType ().hasProperty (snowy )) {
146- this .extent .setBlock (position , block .with (snowy , true ));
147- }
148- this .affected ++;
149- }
150- } else {
151- if (this .extent .setBlock (abovePosition , above .with (snowLayersProperty , currentHeight + 1 ))) {
152- if (block .getBlockType ().hasProperty (snowy )) {
153- this .extent .setBlock (position , block .with (snowy , true ));
154- }
155- this .affected ++;
156- }
157- }
167+ // net.minecraft.world.level.block.Block.isFaceFull (block has 1x1x1 bounding box)
168+ if (type .getMaterial ().isFullCube ()) {
169+ return true ;
170+ }
171+ // if block beneath potential snow layer has snow layers, we can place snow if all possible layers are present.
172+ if (type == BlockTypes .SNOW && blockState .getState (PROPERTY_SNOW_LAYERS ) == MAX_SNOW_LAYER ) {
173+ return true ;
174+ }
175+ // return potential non-full blocks, which could hold snow layers due to block states
176+ // if block is a slab, needs to be on the upper part of the block
177+ if (type .hasProperty (PROPERTY_SLAB )) {
178+ return PROPERTY_VALUE_TOP .equals (blockState .getState (PROPERTY_SLAB ));
179+ }
180+ // if block is a trapdoor, the trapdoor must NOT be open
181+ if (type .hasProperty (PROPERTY_TRAPDOOR_OPEN ) && blockState .getState (PROPERTY_TRAPDOOR_OPEN )) {
158182 return false ;
159183 }
160- if (this .extent .setBlock (abovePosition , snow )) {
161- if (block .getBlockType ().hasProperty (snowy )) {
162- this .extent .setBlock (position , block .with (snowy , true ));
163- }
164- this .affected ++;
184+ // if block is a closed trapdoor, the trapdoor must be aligned at the top part of the block
185+ if (type .hasProperty (PROPERTY_TRAPDOOR )) {
186+ return PROPERTY_VALUE_TOP .equals (blockState .getState (PROPERTY_TRAPDOOR ));
187+ }
188+ // if block is a stair, it must be "bottom" (upside-down)
189+ if (type .hasProperty (PROPERTY_STAIR )) {
190+ return PROPERTY_VALUE_BOTTOM .equals (blockState .getState (PROPERTY_STAIR ));
165191 }
166192 return false ;
167193 }
168194
195+ // net.minecraft.world.level.biome.Biome#shouldFreeze
196+ private boolean shouldFreeze (BlockVector3 position , BlockState blockState ) {
197+ return blockState .getBlockType () == BlockTypes .WATER &&
198+ blockState .getState (PROPERTY_WATER_LEVEL ) == 0 &&
199+ this .extent .getEmittedLight (position ) < 10 ;
200+ }
201+
169202}
0 commit comments