2121
2222import com .google .common .collect .ImmutableList ;
2323import com .google .errorprone .annotations .InlineMe ;
24+ import com .sk89q .worldedit .blocks .ShapeType ;
2425import com .sk89q .worldedit .entity .BaseEntity ;
2526import com .sk89q .worldedit .entity .Entity ;
2627import com .sk89q .worldedit .event .extent .EditSessionEvent ;
129130import com .sk89q .worldedit .world .block .BlockType ;
130131import com .sk89q .worldedit .world .block .BlockTypes ;
131132import com .sk89q .worldedit .world .generation .TreeType ;
133+ import com .sk89q .worldedit .world .registry .BlockMaterial ;
132134import com .sk89q .worldedit .world .registry .LegacyMapper ;
133135import org .apache .logging .log4j .Logger ;
134136
@@ -2658,6 +2660,43 @@ public int deformRegion(final Region region, final Transform targetTransform, fi
26582660 return affected ;
26592661 }
26602662
2663+ private Direction [] getBlockedDirections (BlockVector3 position ) {
2664+ BlockState blockState = getBlock (position );
2665+ BlockType blockType = blockState .getBlockType ();
2666+ BlockMaterial material = blockState .getMaterial ();
2667+
2668+ if (material .isAir ()) {
2669+ return NO_DIRECTIONS ;
2670+ }
2671+ if (material .isLiquid ()) {
2672+ return NO_DIRECTIONS ;
2673+ }
2674+ Direction [] blockedDirections = BLOCKED_DIRECTIONS_OVERRIDE .get (blockType );
2675+ if (blockedDirections != null ) {
2676+ return blockedDirections ;
2677+ }
2678+ if (material .isFullCube (ShapeType .VISUAL_SHAPE )) {
2679+ return CARDINAL_UPRIGHT_DIRECTIONS ;
2680+ }
2681+
2682+ Direction [] result = new Direction [CARDINAL_UPRIGHT_DIRECTIONS .length ];
2683+ int count = 0 ;
2684+
2685+ for (Direction direction : CARDINAL_UPRIGHT_DIRECTIONS ) {
2686+ if (material .isFullFace (ShapeType .VISUAL_SHAPE , direction )) {
2687+ result [count ++] = direction ;
2688+ }
2689+ }
2690+
2691+ // Short-cut for blocks that are blocked in all directions but for some reason not isFullCube
2692+ // This enables == comparison later
2693+ if (count == 6 ) {
2694+ return CARDINAL_UPRIGHT_DIRECTIONS ;
2695+ }
2696+
2697+ return Arrays .copyOf (result , count );
2698+ }
2699+
26612700 /**
26622701 * Hollows out the region (Semi-well-defined for non-cuboid selections).
26632702 *
@@ -2667,46 +2706,70 @@ public int deformRegion(final Region region, final Transform targetTransform, fi
26672706 *
26682707 * @return number of blocks affected
26692708 * @throws MaxChangedBlocksException thrown if too many blocks are changed
2709+ * @deprecated Use {@link EditSession#hollowOutRegion(Region, int, Pattern, boolean, Collection, boolean)} instead.
26702710 */
2671- public int hollowOutRegion (Region region , int thickness , Pattern pattern ) throws MaxChangedBlocksException {
2672- // Positions to consider as 'outside'
2673- Collection <BlockVector3 > startingPositions = new ArrayList <>();
2711+ @ InlineMe (replacement = "this.hollowOutRegion(region, thickness, pattern, true, null, false)" )
2712+ @ Deprecated
2713+ public final int hollowOutRegion (Region region , int thickness , Pattern pattern ) throws MaxChangedBlocksException {
2714+ return hollowOutRegion (region , thickness , pattern , true , null , false );
2715+ }
26742716
2675- // Initialize BFS with selection bounding box
2676- final BlockVector3 min = region .getMinimumPoint ();
2677- final BlockVector3 max = region .getMaximumPoint ();
2717+ /**
2718+ * Hollows out the region (bounding-box mode is semi-well-defined for non-cuboid selections).
2719+ * The startingPositions parameter takes precedence over usePlacementPosition.
2720+ *
2721+ * @param region the region to hollow out.
2722+ * @param thickness the thickness of the shell to leave (manhattan distance)
2723+ * @param pattern The block pattern to use
2724+ * @param openSides Open up faces touching the bounding box. This matches the legacy behaviour.
2725+ * @param startingPositions Positions to consider as 'outside'. If null, use the selection bounding box
2726+ * @param useBlockGeometry Consider block geometry for visibility calculation
2727+ * @return number of blocks affected
2728+ * @throws MaxChangedBlocksException thrown if too many blocks are changed
2729+ */
2730+ public int hollowOutRegion (Region region , int thickness , Pattern pattern , boolean openSides , Collection <BlockVector3 > startingPositions , boolean useBlockGeometry ) throws MaxChangedBlocksException {
2731+ if (startingPositions == null ) {
2732+ // If no startingPositions are specified, use the selection bounding box
2733+ startingPositions = new ArrayList <>();
2734+
2735+ // Initialize BFS with selection bounding box
2736+ final BlockVector3 min = region .getMinimumPoint ();
2737+ final BlockVector3 max = region .getMaximumPoint ();
2738+
2739+ final int minX = min .x ();
2740+ final int minY = min .y ();
2741+ final int minZ = min .z ();
2742+ final int maxX = max .x ();
2743+ final int maxY = max .y ();
2744+ final int maxZ = max .z ();
26782745
2679- final int minX = min . x ();
2680- final int minY = min . y ();
2681- final int minZ = min . z ( );
2682- final int maxX = max . x ( );
2683- final int maxY = max . y ();
2684- final int maxZ = max . z ();
2746+ for ( int x = minX ; x <= maxX ; ++ x ) {
2747+ for ( int y = minY ; y <= maxY ; ++ y ) {
2748+ startingPositions . add ( BlockVector3 . at ( x , y , minZ ) );
2749+ startingPositions . add ( BlockVector3 . at ( x , y , maxZ ) );
2750+ }
2751+ }
26852752
2686- for (int x = minX ; x <= maxX ; ++x ) {
26872753 for (int y = minY ; y <= maxY ; ++y ) {
2688- startingPositions .add (BlockVector3 .at (x , y , minZ ));
2689- startingPositions .add (BlockVector3 .at (x , y , maxZ ));
2754+ for (int z = minZ ; z <= maxZ ; ++z ) {
2755+ startingPositions .add (BlockVector3 .at (minX , y , z ));
2756+ startingPositions .add (BlockVector3 .at (maxX , y , z ));
2757+ }
26902758 }
2691- }
26922759
2693- for (int y = minY ; y <= maxY ; ++y ) {
26942760 for (int z = minZ ; z <= maxZ ; ++z ) {
2695- startingPositions .add (BlockVector3 .at (minX , y , z ));
2696- startingPositions .add (BlockVector3 .at (maxX , y , z ));
2761+ for (int x = minX ; x <= maxX ; ++x ) {
2762+ startingPositions .add (BlockVector3 .at (x , minY , z ));
2763+ startingPositions .add (BlockVector3 .at (x , maxY , z ));
2764+ }
26972765 }
2698- }
26992766
2700- for (int z = minZ ; z <= maxZ ; ++z ) {
2701- for (int x = minX ; x <= maxX ; ++x ) {
2702- startingPositions .add (BlockVector3 .at (x , minY , z ));
2703- startingPositions .add (BlockVector3 .at (x , maxY , z ));
2767+ if (openSides ) {
2768+ // Remove movement blockers from visible list
2769+ startingPositions .removeIf (blockVector3 -> getBlock (blockVector3 ).getBlockType ().getMaterial ().isMovementBlocker ());
27042770 }
27052771 }
27062772
2707- // Remove movement blockers from visible list
2708- startingPositions .removeIf (blockVector3 -> getBlock (blockVector3 ).getBlockType ().getMaterial ().isMovementBlocker ());
2709-
27102773 // The set of blocks that were seen
27112774 final Set <BlockVector3 > visible = new HashSet <>(startingPositions );
27122775
@@ -2715,13 +2778,36 @@ public int hollowOutRegion(Region region, int thickness, Pattern pattern) throws
27152778 while (!queue .isEmpty ()) {
27162779 final BlockVector3 current = queue .poll ();
27172780
2718- final BlockState block = getBlock (current );
2719- if (block .getBlockType ().getMaterial ().isMovementBlocker ()) {
2720- continue ;
2781+ Direction [] blockedDirections ;
2782+ if (useBlockGeometry ) {
2783+ // Get blocked directions for this block
2784+ blockedDirections = getBlockedDirections (current );
2785+
2786+ // Short-cut if blocked in all directions
2787+ if (blockedDirections == CARDINAL_UPRIGHT_DIRECTIONS ) {
2788+ continue ;
2789+ }
2790+ } else {
2791+ final BlockState block = getBlock (current );
2792+ if (block .getBlockType ().getMaterial ().isMovementBlocker ()) {
2793+ // In non-geometry mode, all movement blockers are assumed to be blocked in all directions, so short-cut here
2794+ continue ;
2795+ }
2796+
2797+ // All other blocks are assumed to have no blocked directions
2798+ blockedDirections = NO_DIRECTIONS ;
27212799 }
27222800
2723- for (BlockVector3 recurseDirection : CARDINAL_UPRIGHT_OFFSETS ) {
2724- final BlockVector3 neighbor = current .add (recurseDirection );
2801+ outer :
2802+ for (Direction direction : CARDINAL_UPRIGHT_DIRECTIONS ) {
2803+ // Check if this direction is blocked
2804+ for (Direction blockedDirection : blockedDirections ) {
2805+ if (blockedDirection == direction ) {
2806+ continue outer ; // skip this direction
2807+ }
2808+ }
2809+
2810+ final BlockVector3 neighbor = current .add (direction .toBlockVector ());
27252811
27262812 // Abort if we're outside the region
27272813 if (!region .contains (neighbor )) {
@@ -3180,6 +3266,10 @@ public int morph(BlockVector3 position, double brushSize, int minErodeFaces, int
31803266 return changed ;
31813267 }
31823268
3269+ /**
3270+ * Contains no directions.
3271+ */
3272+ private static final Direction [] NO_DIRECTIONS = {};
31833273 /**
31843274 * Contains all cardinal and upright directions.
31853275 */
@@ -3193,6 +3283,64 @@ public int morph(BlockVector3 position, double brushSize, int minErodeFaces, int
31933283 .map (Direction ::toBlockVector )
31943284 .toArray (BlockVector3 []::new );
31953285
3286+ /**
3287+ * Some blocks need a special case for one reason or another.
3288+ */
3289+ private static final Map <BlockType , Direction []> BLOCKED_DIRECTIONS_OVERRIDE = new HashMap <>();
3290+
3291+ static {
3292+ Arrays .asList (
3293+ // fullcubes that you can see through on all 6 sides
3294+ BlockTypes .SPAWNER ,
3295+ BlockTypes .BEACON ,
3296+ BlockTypes .MANGROVE_ROOTS ,
3297+ BlockTypes .ICE ,
3298+
3299+ // You can see through some of the doors/trapdoors, which is not reflected in their visual shape
3300+ // Commented lines are opaque and kept for reference
3301+ BlockTypes .OAK_DOOR ,
3302+ BlockTypes .OAK_TRAPDOOR ,
3303+ // BlockTypes.SPRUCE_DOOR,
3304+ // BlockTypes.SPRUCE_TRAPDOOR,
3305+ // BlockTypes.BIRCH_DOOR,
3306+ // BlockTypes.BIRCH_TRAPDOOR,
3307+ BlockTypes .JUNGLE_DOOR ,
3308+ BlockTypes .JUNGLE_TRAPDOOR ,
3309+ BlockTypes .ACACIA_DOOR ,
3310+ BlockTypes .ACACIA_TRAPDOOR ,
3311+ // BlockTypes.DARK_OAK_DOOR,
3312+ // BlockTypes.DARK_OAK_TRAPDOOR,
3313+ // BlockTypes.MANGROVE_DOOR,
3314+ BlockTypes .MANGROVE_TRAPDOOR ,
3315+ BlockTypes .CHERRY_DOOR ,
3316+ BlockTypes .CHERRY_TRAPDOOR ,
3317+ // BlockTypes.PALE_OAK_DOOR,
3318+ // BlockTypes.PALE_OAK_TRAPDOOR,
3319+ BlockTypes .BAMBOO_DOOR ,
3320+ BlockTypes .BAMBOO_TRAPDOOR ,
3321+ BlockTypes .CRIMSON_DOOR ,
3322+ BlockTypes .CRIMSON_TRAPDOOR ,
3323+ BlockTypes .WARPED_DOOR ,
3324+ BlockTypes .WARPED_TRAPDOOR ,
3325+ BlockTypes .IRON_DOOR ,
3326+ BlockTypes .IRON_TRAPDOOR
3327+ ).forEach (blockType -> {
3328+ BLOCKED_DIRECTIONS_OVERRIDE .put (blockType , NO_DIRECTIONS );
3329+ });
3330+
3331+ // The copper doors/trapdoors are too many to list individually
3332+ java .util .regex .Pattern pattern = java .util .regex .Pattern .compile ("^.*:(?:.*_)?(?:copper_door|copper_trapdoor)(?:_.*)?$" );
3333+ for (BlockType blockType : BlockType .REGISTRY ) {
3334+ if (pattern .matcher (blockType .id ()).matches ()) {
3335+ BLOCKED_DIRECTIONS_OVERRIDE .put (blockType , NO_DIRECTIONS );
3336+ }
3337+ }
3338+
3339+ // These are visual fullcubes, but they're mostly open:
3340+ BLOCKED_DIRECTIONS_OVERRIDE .put (BlockTypes .TRIAL_SPAWNER , new Direction [] { Direction .UP });
3341+ BLOCKED_DIRECTIONS_OVERRIDE .put (BlockTypes .VAULT , new Direction [] { Direction .UP , Direction .DOWN });
3342+ }
3343+
31963344 private static double lengthSq (double x , double y , double z ) {
31973345 return (x * x ) + (y * y ) + (z * z );
31983346 }
0 commit comments