diff --git a/BauSystem/BauSystem_RegionDynamic/src/de/steamwar/bausystem/region/dynamic/DynamicRegion.java b/BauSystem/BauSystem_RegionDynamic/src/de/steamwar/bausystem/region/dynamic/DynamicRegion.java index e38973a8..2144a7e3 100644 --- a/BauSystem/BauSystem_RegionDynamic/src/de/steamwar/bausystem/region/dynamic/DynamicRegion.java +++ b/BauSystem/BauSystem_RegionDynamic/src/de/steamwar/bausystem/region/dynamic/DynamicRegion.java @@ -71,17 +71,7 @@ public abstract class DynamicRegion implements Region { Point minPoint = getArea().getMinPoint(false); Point maxPoint = getArea().getMaxPoint(false); - - EditSession editSession = WorldEdit.getInstance() - .newEditSessionBuilder() - .world(BukkitAdapter.adapt(Bukkit.getWorlds().get(0))) - .checkMemory(false) - .allowedRegionsEverywhere() - .limitUnlimited() - .changeSetNull() - .build(); - editSession.setBlocks((com.sk89q.worldedit.regions.Region) new CuboidRegion(minPoint.toBlockVector3(), maxPoint.toBlockVector3()), BlockTypes.AIR.getDefaultState()); - editSession.close(); + PasteUtils.reset(minPoint, maxPoint); DynamicRegionSystem.INSTANCE.getNeighbours(this).collect(Collectors.toList()) .forEach(r -> r.update(this)); diff --git a/BauSystem/BauSystem_RegionDynamic/src/de/steamwar/bausystem/region/dynamic/PasteUtils.java b/BauSystem/BauSystem_RegionDynamic/src/de/steamwar/bausystem/region/dynamic/PasteUtils.java index 02b5be6c..e2b97287 100644 --- a/BauSystem/BauSystem_RegionDynamic/src/de/steamwar/bausystem/region/dynamic/PasteUtils.java +++ b/BauSystem/BauSystem_RegionDynamic/src/de/steamwar/bausystem/region/dynamic/PasteUtils.java @@ -19,10 +19,14 @@ package de.steamwar.bausystem.region.dynamic; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.transform.AffineTransform; +import com.sk89q.worldedit.regions.CuboidRegion; +import com.sk89q.worldedit.world.block.BlockTypes; import de.steamwar.bausystem.region.Point; import de.steamwar.bausystem.utils.FlatteningWrapper; import lombok.experimental.UtilityClass; @@ -33,6 +37,19 @@ import java.io.File; @UtilityClass public class PasteUtils { + public static void reset(Point minPoint, Point maxPoint) { + EditSession editSession = WorldEdit.getInstance() + .newEditSessionBuilder() + .world(BukkitAdapter.adapt(Bukkit.getWorlds().get(0))) + .checkMemory(false) + .allowedRegionsEverywhere() + .limitUnlimited() + .changeSetNull() + .build(); + editSession.setBlocks((com.sk89q.worldedit.regions.Region) new CuboidRegion(minPoint.toBlockVector3(), maxPoint.toBlockVector3()), BlockTypes.AIR.getDefaultState()); + editSession.close(); + } + public static void paste(File file, Point minPoint, int rotate) { Clipboard clipboard = FlatteningWrapper.impl.loadSchematic(file); BlockVector3 offset = clipboard.getRegion().getMinimumPoint().subtract(clipboard.getOrigin()); diff --git a/BauSystem/BauSystem_RegionDynamic/src/de/steamwar/bausystem/region/dynamic/path/PathArea.java b/BauSystem/BauSystem_RegionDynamic/src/de/steamwar/bausystem/region/dynamic/path/PathArea.java index 2571860c..c783e375 100644 --- a/BauSystem/BauSystem_RegionDynamic/src/de/steamwar/bausystem/region/dynamic/path/PathArea.java +++ b/BauSystem/BauSystem_RegionDynamic/src/de/steamwar/bausystem/region/dynamic/path/PathArea.java @@ -26,6 +26,7 @@ import de.steamwar.bausystem.region.RegionType; import de.steamwar.bausystem.region.dynamic.PasteUtils; import de.steamwar.bausystem.region.dynamic.Tile; import de.steamwar.bausystem.region.dynamic.VariantSelector; +import de.steamwar.bausystem.shared.Pair; import de.steamwar.bausystem.utils.PasteBuilder; import lombok.NonNull; import lombok.RequiredArgsConstructor; @@ -36,15 +37,34 @@ import java.io.File; import java.util.Optional; import java.util.UUID; +import static de.steamwar.bausystem.region.RegionType.ConnectionType.Global; +import static de.steamwar.bausystem.region.RegionType.ConnectionType.Path; + public class PathArea implements Region.Area { private static final File PATH_DIR = new File(Bukkit.getWorlds().get(0).getWorldFolder(), "sections/path"); private static final VariantSelector CENTER_NORMAL = VariantSelector.Get(new File(PATH_DIR, "center/normal")); private static final VariantSelector SIDE_GLOBAL = VariantSelector.Get(new File(PATH_DIR, "side/global")); + private static final VariantSelector CORNER_INNER_GLOBAL = VariantSelector.Get(new File(PATH_DIR, "cinner/global")); + private static final VariantSelector CORNER_OUTER_GLOBAL = VariantSelector.Get(new File(PATH_DIR, "couter/global")); + + private static final SelectorSide SELECTOR_SIDE = new SelectorSide() + .Case(Path, CENTER_NORMAL) + .Case(Global, SIDE_GLOBAL); + + private static final SelectorCorner SELECTOR_CORNER = new SelectorCorner() + .Case(Path, Global, Global, SIDE_GLOBAL, RotationCorrection.WithCorrection) + .Case(Global, Path, Path, SIDE_GLOBAL) + .Case(Global, Path, Global, SIDE_GLOBAL) + .Case(Path, Global, Path, SIDE_GLOBAL, RotationCorrection.WithCorrection) + .Case(Path, Path, Path, CENTER_NORMAL, RotationCorrection.UsingOrdinal) + .Case(Global, Global, Global, CORNER_OUTER_GLOBAL, RotationCorrection.UsingOrdinal) + .Case(Path, Path, Global, CORNER_INNER_GLOBAL, RotationCorrection.UsingOrdinal) + ; @RequiredArgsConstructor - private enum Side { + protected enum Side { North(0, -1, 7, 0, 0), South(0, 1, 7, 14, 180), West(-1, 0, 0, 7, 90), @@ -59,21 +79,27 @@ public class PathArea implements Region.Area { } @RequiredArgsConstructor - private enum Corner { + protected enum Corner { NorthEast(Side.North, Side.East, 14, 0, 0, -90), NorthWest(Side.North, Side.West, 0, 0, 0, 90), SouthEast(Side.South, Side.East, 14, 14, 180, 90), SouthWest(Side.South, Side.West, 0, 14, 180, -90), ; - private final Side side1; - private final Side side2; + protected final Side side1; + protected final Side side2; private final int pasteOffsetX; private final int pasteOffsetZ; private final int rotate; private final int rotateCorrection; } + protected enum RotationCorrection { + Unchanged, + WithCorrection, + UsingOrdinal, + } + private final UUID regionIdentifier; private final Tile tile; private final Point minPoint; @@ -121,79 +147,34 @@ public class PathArea implements Region.Area { } for (Side side : Side.values()) { - RegionType.ConnectionType connectionType = getConnectionType(side, null); - - VariantSelector selector = switch (connectionType) { - case Path -> CENTER_NORMAL; - case Water -> null; - case Closed -> null; - case Global -> SIDE_GLOBAL; - }; - if (selector == null) { - continue; - } + VariantSelector selector = SELECTOR_SIDE.Select(tile, side); + if (selector == null) continue; resetFile = selector.select(regionIdentifier, side.ordinal() + side.rotate).orElse(null); - if (resetFile == null) { - continue; - } + if (resetFile == null) continue; PasteUtils.paste(resetFile, minPoint.add(side.pasteOffsetX, 0, side.pasteOffsetZ), side.rotate); } for (Corner corner : Corner.values()) { - RegionType.ConnectionType connectionType1 = getConnectionType(corner.side1, null); - RegionType.ConnectionType connectionType2 = getConnectionType(corner.side2, null); - RegionType.ConnectionType connectionType3 = getConnectionType(corner.side1, corner.side2); - - VariantSelector selector = null; - int rotate = corner.rotate; - if (connectionType1 == RegionType.ConnectionType.Path && connectionType2 == connectionType3) { - selector = switch (connectionType2) { - case Global -> SIDE_GLOBAL; - case Closed -> null; - case Water -> null; - case Path -> null; - }; - rotate += corner.rotateCorrection; - } - if (connectionType1 == RegionType.ConnectionType.Global && connectionType2 == connectionType3) { - selector = switch (connectionType2) { - case Global -> null; - case Closed -> null; - case Water -> null; - case Path -> SIDE_GLOBAL; - }; - } - - if (connectionType2 == RegionType.ConnectionType.Path && connectionType1 == connectionType3) { - selector = switch (connectionType1) { - case Global -> SIDE_GLOBAL; - case Closed -> null; - case Water -> null; - case Path -> null; - }; - } - if (connectionType2 == RegionType.ConnectionType.Global && connectionType1 == connectionType3) { - selector = switch (connectionType1) { - case Global -> null; - case Closed -> null; - case Water -> null; - case Path -> SIDE_GLOBAL; - }; - rotate += corner.rotateCorrection; - } - if (connectionType1 == RegionType.ConnectionType.Path && connectionType1 == connectionType2 && connectionType1 == connectionType3) { - selector = CENTER_NORMAL; - rotate = 90 * corner.ordinal(); - } - if (selector == null) { - continue; - } + Pair pair = SELECTOR_CORNER.Select(tile, corner); + VariantSelector selector = pair.getKey(); + if (selector == null) continue; + RotationCorrection rotationCorrection = pair.getValue(); resetFile = selector.select(regionIdentifier, corner.side1.ordinal() * corner.side2.ordinal() + corner.side1.rotate * corner.side2.rotate).orElse(null); - if (resetFile == null) { - continue; + if (resetFile == null) continue; + + int rotate = corner.rotate; + switch (rotationCorrection) { + case Unchanged: + break; + case UsingOrdinal: + rotate = corner.ordinal() * 90; + break; + case WithCorrection: + rotate += corner.rotateCorrection; + break; } PasteUtils.paste(resetFile, minPoint.add(corner.pasteOffsetX, 0, corner.pasteOffsetZ), rotate); @@ -216,12 +197,12 @@ public class PathArea implements Region.Area { return true; } - private RegionType.ConnectionType getConnectionType(Side side, Side optionalSide) { - Optional optionalTile = this.tile.add(side.tileOffsetX, side.tileOffsetZ); + protected static RegionType.ConnectionType getConnectionType(Tile tile, Side side, Side optionalSide) { + Optional optionalTile = tile.add(side.tileOffsetX, side.tileOffsetZ); if (optionalSide != null) { - optionalTile = optionalTile.flatMap(tile -> tile.add(optionalSide.tileOffsetX, optionalSide.tileOffsetZ)); + optionalTile = optionalTile.flatMap(t -> t.add(optionalSide.tileOffsetX, optionalSide.tileOffsetZ)); } - return optionalTile.map(tile -> DynamicRegionSystem.INSTANCE.get(tile.getCenterLocation()).getType().getConnectionType()) + return optionalTile.map(t -> DynamicRegionSystem.INSTANCE.get(t.getCenterLocation()).getType().getConnectionType()) .orElse(RegionType.ConnectionType.Global); } } diff --git a/BauSystem/BauSystem_RegionDynamic/src/de/steamwar/bausystem/region/dynamic/path/SelectorCorner.java b/BauSystem/BauSystem_RegionDynamic/src/de/steamwar/bausystem/region/dynamic/path/SelectorCorner.java new file mode 100644 index 00000000..a22e5dad --- /dev/null +++ b/BauSystem/BauSystem_RegionDynamic/src/de/steamwar/bausystem/region/dynamic/path/SelectorCorner.java @@ -0,0 +1,68 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2026 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.bausystem.region.dynamic.path; + +import de.steamwar.bausystem.region.RegionType; +import de.steamwar.bausystem.region.dynamic.Tile; +import de.steamwar.bausystem.region.dynamic.VariantSelector; +import de.steamwar.bausystem.shared.Pair; + +class SelectorCorner { + + private static final int BITS; + private static final int SELECTOR_COUNT; + + static { + int values = RegionType.ConnectionType.values().length; + BITS = (int) (Math.log(values) / Math.log(2)); + SELECTOR_COUNT = (int) Math.pow(2, BITS * 3); + } + + private VariantSelector[] selectors = new VariantSelector[SELECTOR_COUNT]; + private PathArea.RotationCorrection[] rotationCorrections = new PathArea.RotationCorrection[SELECTOR_COUNT]; + + private int getIndex(RegionType.ConnectionType left, RegionType.ConnectionType right, RegionType.ConnectionType diagonal) { + int index = left.ordinal(); + index = index << BITS | right.ordinal(); + return index << BITS | diagonal.ordinal(); + } + + public SelectorCorner Case(RegionType.ConnectionType left, RegionType.ConnectionType right, RegionType.ConnectionType diagonal, VariantSelector selector) { + int index = getIndex(left, right, diagonal); + selectors[index] = selector; + rotationCorrections[index] = PathArea.RotationCorrection.Unchanged; + return this; + } + + public SelectorCorner Case(RegionType.ConnectionType left, RegionType.ConnectionType right, RegionType.ConnectionType diagonal, VariantSelector selector, PathArea.RotationCorrection rotationCorrection) { + int index = getIndex(left, right, diagonal); + selectors[index] = selector; + rotationCorrections[index] = rotationCorrection; + return this; + } + + public Pair Select(Tile tile, PathArea.Corner corner) { + RegionType.ConnectionType left = PathArea.getConnectionType(tile, corner.side1, null); + RegionType.ConnectionType right = PathArea.getConnectionType(tile, corner.side2, null); + RegionType.ConnectionType diagonal = PathArea.getConnectionType(tile, corner.side1, corner.side2); + int index = getIndex(left, right, diagonal); + return new Pair<>(selectors[index], rotationCorrections[index]); + } +} diff --git a/BauSystem/BauSystem_RegionDynamic/src/de/steamwar/bausystem/region/dynamic/path/SelectorSide.java b/BauSystem/BauSystem_RegionDynamic/src/de/steamwar/bausystem/region/dynamic/path/SelectorSide.java new file mode 100644 index 00000000..62237977 --- /dev/null +++ b/BauSystem/BauSystem_RegionDynamic/src/de/steamwar/bausystem/region/dynamic/path/SelectorSide.java @@ -0,0 +1,39 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2026 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.bausystem.region.dynamic.path; + +import de.steamwar.bausystem.region.RegionType; +import de.steamwar.bausystem.region.dynamic.Tile; +import de.steamwar.bausystem.region.dynamic.VariantSelector; + +class SelectorSide { + + private VariantSelector[] selectors = new VariantSelector[RegionType.ConnectionType.values().length]; + + protected SelectorSide Case(RegionType.ConnectionType type, VariantSelector selector) { + selectors[type.ordinal()] = selector; + return this; + } + + public VariantSelector Select(Tile tile, PathArea.Side side) { + RegionType.ConnectionType type = PathArea.getConnectionType(tile, side, null); + return selectors[type.ordinal()]; + } +}