fixed placement issues in BuildBattle and added isInArea methode to Position.java
This commit is contained in:
1 parent
8dc165952a
commit
a18a6b0dca
4 files changed
+1227
-9
No files matched your search
+896
@@ -0,0 +1,896 @@
|
|||||||
|
package eu.mhsl.minenet.minigames.instance.game.stateless.types.buildBattle;
|
||||||
|
|
||||||
|
import net.minestom.server.coordinate.BlockVec;
|
||||||
|
import net.minestom.server.coordinate.Point;
|
||||||
|
import net.minestom.server.entity.Player;
|
||||||
|
import net.minestom.server.event.player.PlayerBlockBreakEvent;
|
||||||
|
import net.minestom.server.event.player.PlayerBlockInteractEvent;
|
||||||
|
import net.minestom.server.event.player.PlayerBlockPlaceEvent;
|
||||||
|
import net.minestom.server.instance.Instance;
|
||||||
|
import net.minestom.server.instance.block.Block;
|
||||||
|
import net.minestom.server.instance.block.BlockFace;
|
||||||
|
import net.minestom.server.utils.Direction;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public final class BlockPlacementHelper {
|
||||||
|
private BlockPlacementHelper() {}
|
||||||
|
|
||||||
|
private static final Set<String> SIX_DIR = Set.of(
|
||||||
|
"observer", "dispenser", "dropper", "piston", "sticky_piston",
|
||||||
|
"amethyst_cluster", "small_amethyst_bud", "medium_amethyst_bud", "large_amethyst_bud",
|
||||||
|
"end_rod", "lightning_rod", "shulker_box"
|
||||||
|
);
|
||||||
|
|
||||||
|
public static void apply(PlayerBlockPlaceEvent event) {
|
||||||
|
Block block = event.getBlock();
|
||||||
|
BlockFace face = event.getBlockFace();
|
||||||
|
BlockVec pos = event.getBlockPosition();
|
||||||
|
Point cursor = event.getCursorPosition();
|
||||||
|
Player player = event.getPlayer();
|
||||||
|
Instance instance = event.getInstance();
|
||||||
|
|
||||||
|
String path = block.key().value();
|
||||||
|
|
||||||
|
Block existing = instance.getBlock(pos);
|
||||||
|
if ((path.endsWith("_candle") || path.equals("candle")) && existing.compare(block)) {
|
||||||
|
int n = parseIntOr(existing.getProperty("candles"), 1);
|
||||||
|
event.setBlock(n < 4 ? existing.withProperty("candles", String.valueOf(n + 1)) : existing);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (path.equals("snow") && existing.compare(block)) {
|
||||||
|
int n = parseIntOr(existing.getProperty("layers"), 1);
|
||||||
|
event.setBlock(n < 8 ? existing.withProperty("layers", String.valueOf(n + 1)) : existing);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (path.equals("sea_pickle") && existing.compare(block)) {
|
||||||
|
int n = parseIntOr(existing.getProperty("pickles"), 1);
|
||||||
|
event.setBlock(n < 4 ? existing.withProperty("pickles", String.valueOf(n + 1)) : existing);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (path.equals("turtle_egg") && existing.compare(block)) {
|
||||||
|
int n = parseIntOr(existing.getProperty("eggs"), 1);
|
||||||
|
event.setBlock(n < 4 ? existing.withProperty("eggs", String.valueOf(n + 1)) : existing);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Block oriented = orient(block, path, face, cursor, player, instance, pos);
|
||||||
|
if (oriented != block) event.setBlock(oriented);
|
||||||
|
|
||||||
|
Block finalOriented = oriented;
|
||||||
|
String finalPath = finalOriented.key().value();
|
||||||
|
|
||||||
|
if (path.endsWith("_door")) {
|
||||||
|
Block lower = event.getBlock();
|
||||||
|
String facing = lower.getProperty("facing");
|
||||||
|
String hinge = lower.getProperty("hinge");
|
||||||
|
Block upper = lower.withProperty("half", "upper").withProperty("facing", facing).withProperty("hinge", hinge);
|
||||||
|
instance.scheduler().scheduleNextTick(() -> instance.setBlock(pos.add(0, 1, 0), upper));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.endsWith("_bed")) {
|
||||||
|
String facing = finalOriented.getProperty("facing");
|
||||||
|
Direction d = facingToDirection(facing);
|
||||||
|
Block head = finalOriented.withProperty("part", "head");
|
||||||
|
BlockVec headPos = new BlockVec(pos.blockX() + d.normalX(), pos.blockY(), pos.blockZ() + d.normalZ());
|
||||||
|
instance.scheduler().scheduleNextTick(() -> instance.setBlock(headPos, head));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.equals("big_dripleaf")) {
|
||||||
|
Block upper = finalOriented;
|
||||||
|
Block lower = Block.BIG_DRIPLEAF_STEM.withProperty("facing", finalOriented.getProperty("facing"));
|
||||||
|
instance.scheduler().scheduleNextTick(() -> instance.setBlock(pos.add(0, 1, 0), upper.withProperty("half", "upper")));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isConnecting(path)) {
|
||||||
|
instance.scheduler().scheduleNextTick(() -> {
|
||||||
|
for (Direction d : Direction.HORIZONTAL) {
|
||||||
|
BlockVec np = new BlockVec(pos.blockX() + d.normalX(), pos.blockY(), pos.blockZ() + d.normalZ());
|
||||||
|
Block nb = instance.getBlock(np);
|
||||||
|
if (isConnecting(nb.key().value())) instance.setBlock(np, computeConnections(nb, nb.key().value(), instance, np));
|
||||||
|
}
|
||||||
|
Block self = instance.getBlock(pos);
|
||||||
|
if (isConnecting(self.key().value())) instance.setBlock(pos, computeConnections(self, self.key().value(), instance, pos));
|
||||||
|
for (Direction d : Direction.HORIZONTAL) {
|
||||||
|
BlockVec np = new BlockVec(pos.blockX() + d.normalX(), pos.blockY(), pos.blockZ() + d.normalZ());
|
||||||
|
Block nb = instance.getBlock(np);
|
||||||
|
if (nb.key().value().endsWith("_fence_gate")) {
|
||||||
|
instance.setBlock(np, updateFenceGateInWall(nb, instance, np));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.endsWith("_fence_gate")) {
|
||||||
|
instance.scheduler().scheduleNextTick(() -> instance.setBlock(pos, updateFenceGateInWall(instance.getBlock(pos), instance, pos)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.equals("redstone_wire")) {
|
||||||
|
instance.scheduler().scheduleNextTick(() -> {
|
||||||
|
updateRedstoneWire(instance, pos);
|
||||||
|
for (Direction d : Direction.HORIZONTAL) {
|
||||||
|
BlockVec np = new BlockVec(pos.blockX() + d.normalX(), pos.blockY(), pos.blockZ() + d.normalZ());
|
||||||
|
if (instance.getBlock(np).key().value().equals("redstone_wire")) updateRedstoneWire(instance, np);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isRail(path)) {
|
||||||
|
instance.scheduler().scheduleNextTick(() -> updateRailShape(instance, pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.equals("scaffolding")) {
|
||||||
|
instance.scheduler().scheduleNextTick(() -> updateScaffolding(instance, pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (finalPath.equals("chest") || finalPath.equals("trapped_chest")) {
|
||||||
|
instance.scheduler().scheduleNextTick(() -> updateChestPair(instance, pos));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Block orient(Block block, String path, BlockFace face, Point cursor, Player player,
|
||||||
|
Instance instance, BlockVec pos) {
|
||||||
|
Map<String, String> props = block.properties();
|
||||||
|
Block result = block;
|
||||||
|
|
||||||
|
if (props.containsKey("axis")) {
|
||||||
|
result = result.withProperty("axis", switch (face) {
|
||||||
|
case TOP, BOTTOM -> "y";
|
||||||
|
case NORTH, SOUTH -> "z";
|
||||||
|
case EAST, WEST -> "x";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.containsKey("type") && path.endsWith("_slab")) {
|
||||||
|
Block existing = instance.getBlock(pos);
|
||||||
|
if (existing.compare(block)) {
|
||||||
|
result = result.withProperty("type", "double");
|
||||||
|
} else {
|
||||||
|
boolean top = face == BlockFace.BOTTOM || (face != BlockFace.TOP && cursor.y() > 0.5);
|
||||||
|
result = result.withProperty("type", top ? "top" : "bottom");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.endsWith("_stairs")) {
|
||||||
|
String facing = yawToFacing(player.getPosition().yaw());
|
||||||
|
boolean top = face == BlockFace.BOTTOM || (face != BlockFace.TOP && cursor.y() > 0.5);
|
||||||
|
result = result.withProperty("facing", facing).withProperty("half", top ? "top" : "bottom");
|
||||||
|
result = applyStairShape(result, instance, pos, facing, top);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.endsWith("_trapdoor")) {
|
||||||
|
String facing;
|
||||||
|
boolean top;
|
||||||
|
if (face == BlockFace.TOP) { top = false; facing = yawToFacing(player.getPosition().yaw() + 180); }
|
||||||
|
else if (face == BlockFace.BOTTOM) { top = true; facing = yawToFacing(player.getPosition().yaw() + 180); }
|
||||||
|
else { top = cursor.y() > 0.5; facing = oppositeFacing(faceName(face)); }
|
||||||
|
result = result.withProperty("facing", facing).withProperty("half", top ? "top" : "bottom")
|
||||||
|
.withProperty("open", "false").withProperty("powered", "false");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.endsWith("_door")) {
|
||||||
|
String facing = yawToFacing(player.getPosition().yaw());
|
||||||
|
String hinge = doorHinge(facing, cursor, instance, pos, path);
|
||||||
|
result = result.withProperty("facing", facing)
|
||||||
|
.withProperty("half", "lower")
|
||||||
|
.withProperty("hinge", hinge)
|
||||||
|
.withProperty("open", "false")
|
||||||
|
.withProperty("powered", "false");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.endsWith("_fence_gate")) {
|
||||||
|
String facing = yawToFacing(player.getPosition().yaw());
|
||||||
|
result = result.withProperty("facing", facing).withProperty("open", "false")
|
||||||
|
.withProperty("powered", "false").withProperty("in_wall", "false");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.endsWith("_bed")) {
|
||||||
|
String facing = yawToFacing(player.getPosition().yaw());
|
||||||
|
result = result.withProperty("facing", facing).withProperty("part", "foot").withProperty("occupied", "false");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.endsWith("_button") || path.equals("lever") || path.equals("grindstone")) {
|
||||||
|
String attachFace; String facing;
|
||||||
|
if (face == BlockFace.TOP) { attachFace = "floor"; facing = yawToFacing(player.getPosition().yaw()); }
|
||||||
|
else if (face == BlockFace.BOTTOM) { attachFace = "ceiling"; facing = yawToFacing(player.getPosition().yaw()); }
|
||||||
|
else { attachFace = "wall"; facing = oppositeFacing(faceName(face)); }
|
||||||
|
result = result.withProperty("face", attachFace).withProperty("facing", facing);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.equals("bell")) {
|
||||||
|
String attachment; String facing;
|
||||||
|
if (face == BlockFace.TOP) { attachment = "floor"; facing = yawToFacing(player.getPosition().yaw()); }
|
||||||
|
else if (face == BlockFace.BOTTOM) { attachment = "ceiling"; facing = yawToFacing(player.getPosition().yaw()); }
|
||||||
|
else {
|
||||||
|
attachment = "single_wall";
|
||||||
|
facing = oppositeFacing(faceName(face));
|
||||||
|
}
|
||||||
|
result = result.withProperty("attachment", attachment).withProperty("facing", facing);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.equals("lantern") || path.equals("soul_lantern")) {
|
||||||
|
result = result.withProperty("hanging", face == BlockFace.BOTTOM ? "true" : "false");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.equals("tripwire_hook")) {
|
||||||
|
String facing = face != BlockFace.TOP && face != BlockFace.BOTTOM
|
||||||
|
? oppositeFacing(faceName(face))
|
||||||
|
: yawToFacing(player.getPosition().yaw());
|
||||||
|
result = result.withProperty("facing", facing).withProperty("attached", "false").withProperty("powered", "false");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.equals("hopper")) {
|
||||||
|
String facing = face == BlockFace.TOP ? "down" : (face == BlockFace.BOTTOM ? "down" : oppositeFacing(faceName(face)));
|
||||||
|
result = result.withProperty("facing", facing).withProperty("enabled", "true");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.equals("campfire") || path.equals("soul_campfire")) {
|
||||||
|
Block below = instance.getBlock(new BlockVec(pos.blockX(), pos.blockY() - 1, pos.blockZ()));
|
||||||
|
String signalFire = below.key().value().equals("hay_block") ? "true" : "false";
|
||||||
|
result = result.withProperty("facing", yawToFacing(player.getPosition().yaw()))
|
||||||
|
.withProperty("lit", "true").withProperty("signal_fire", signalFire);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.equals("pointed_dripstone")) {
|
||||||
|
String dir = face == BlockFace.BOTTOM ? "down" : "up";
|
||||||
|
result = result.withProperty("vertical_direction", dir).withProperty("thickness", "tip");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.equals("chorus_flower")) {
|
||||||
|
result = result.withProperty("age", "0");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.equals("end_portal_frame") || path.equals("respawn_anchor")) {
|
||||||
|
if (props.containsKey("facing")) {
|
||||||
|
result = result.withProperty("facing", yawToFacing(player.getPosition().yaw()));
|
||||||
|
}
|
||||||
|
if (props.containsKey("eye")) result = result.withProperty("eye", "false");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((path.equals("torch") || path.equals("soul_torch") || path.equals("redstone_torch") || path.equals("copper_torch"))
|
||||||
|
&& face != BlockFace.TOP && face != BlockFace.BOTTOM) {
|
||||||
|
Block wall = switch (path) {
|
||||||
|
case "torch" -> Block.WALL_TORCH;
|
||||||
|
case "soul_torch" -> Block.SOUL_WALL_TORCH;
|
||||||
|
case "redstone_torch" -> Block.REDSTONE_WALL_TORCH;
|
||||||
|
case "copper_torch" -> Block.COPPER_WALL_TORCH;
|
||||||
|
default -> null;
|
||||||
|
};
|
||||||
|
if (wall != null) result = wall.withProperty("facing", oppositeFacing(faceName(face)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.endsWith("_skull") || path.endsWith("_head")) {
|
||||||
|
if (face != BlockFace.TOP && face != BlockFace.BOTTOM) {
|
||||||
|
String wallName = path.endsWith("_skull") ? path.replace("_skull", "_wall_skull") : path.replace("_head", "_wall_head");
|
||||||
|
Block wall = Block.fromKey("minecraft:" + wallName);
|
||||||
|
if (wall != null) result = wall.withProperty("facing", oppositeFacing(faceName(face)));
|
||||||
|
} else if (face == BlockFace.TOP && result.properties().containsKey("rotation")) {
|
||||||
|
int rot = (int) Math.floor(((player.getPosition().yaw() + 180.0) * 16.0 / 360.0) + 0.5) & 15;
|
||||||
|
result = result.withProperty("rotation", String.valueOf(rot));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.endsWith("_sign") && !path.endsWith("_wall_sign") && !path.contains("hanging")
|
||||||
|
&& face != BlockFace.TOP && face != BlockFace.BOTTOM) {
|
||||||
|
Block wallSign = Block.fromKey("minecraft:" + path.replace("_sign", "_wall_sign"));
|
||||||
|
if (wallSign != null) result = wallSign.withProperty("facing", oppositeFacing(faceName(face)));
|
||||||
|
} else if (path.endsWith("_sign") && !path.endsWith("_wall_sign") && !path.contains("hanging")
|
||||||
|
&& face == BlockFace.TOP) {
|
||||||
|
int rot = (int) Math.floor(((player.getPosition().yaw() + 180.0) * 16.0 / 360.0) + 0.5) & 15;
|
||||||
|
if (result.properties().containsKey("rotation")) result = result.withProperty("rotation", String.valueOf(rot));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.endsWith("_hanging_sign") && !path.contains("_wall_")
|
||||||
|
&& face != BlockFace.TOP && face != BlockFace.BOTTOM) {
|
||||||
|
Block wallHang = Block.fromKey("minecraft:" + path.replace("_hanging_sign", "_wall_hanging_sign"));
|
||||||
|
if (wallHang != null) result = wallHang.withProperty("facing", oppositeFacing(faceName(face)));
|
||||||
|
} else if (path.endsWith("_hanging_sign") && !path.contains("_wall_")) {
|
||||||
|
int rot = (int) Math.floor(((player.getPosition().yaw() + 180.0) * 16.0 / 360.0) + 0.5) & 15;
|
||||||
|
if (result.properties().containsKey("rotation")) result = result.withProperty("rotation", String.valueOf(rot));
|
||||||
|
if (result.properties().containsKey("attached")) result = result.withProperty("attached", face == BlockFace.BOTTOM ? "true" : "false");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isBanner(path)) {
|
||||||
|
if (face != BlockFace.TOP && face != BlockFace.BOTTOM) {
|
||||||
|
Block wallBanner = Block.fromKey("minecraft:" + path.replace("_banner", "_wall_banner"));
|
||||||
|
if (wallBanner != null) result = wallBanner.withProperty("facing", oppositeFacing(faceName(face)));
|
||||||
|
} else if (face == BlockFace.TOP) {
|
||||||
|
int rot = (int) Math.floor(((player.getPosition().yaw() + 180.0) * 16.0 / 360.0) + 0.5) & 15;
|
||||||
|
if (result.properties().containsKey("rotation")) result = result.withProperty("rotation", String.valueOf(rot));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.equals("vine")) {
|
||||||
|
Block existing = instance.getBlock(pos);
|
||||||
|
Block base = existing.compare(block) ? existing : result.withProperty("up", "false")
|
||||||
|
.withProperty("north", "false").withProperty("south", "false")
|
||||||
|
.withProperty("east", "false").withProperty("west", "false");
|
||||||
|
String side = switch (face) {
|
||||||
|
case BOTTOM -> "up";
|
||||||
|
case NORTH -> "south";
|
||||||
|
case SOUTH -> "north";
|
||||||
|
case EAST -> "west";
|
||||||
|
case WEST -> "east";
|
||||||
|
default -> null;
|
||||||
|
};
|
||||||
|
result = side != null ? base.withProperty(side, "true") : base;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.equals("glow_lichen") || path.equals("sculk_vein") || path.equals("resin_clump")) {
|
||||||
|
Block existing = instance.getBlock(pos);
|
||||||
|
Block base = existing.compare(block) ? existing : result
|
||||||
|
.withProperty("up", "false").withProperty("down", "false")
|
||||||
|
.withProperty("north", "false").withProperty("south", "false")
|
||||||
|
.withProperty("east", "false").withProperty("west", "false");
|
||||||
|
String side = switch (face) {
|
||||||
|
case TOP -> "down";
|
||||||
|
case BOTTOM -> "up";
|
||||||
|
case NORTH -> "south";
|
||||||
|
case SOUTH -> "north";
|
||||||
|
case EAST -> "west";
|
||||||
|
case WEST -> "east";
|
||||||
|
};
|
||||||
|
result = base.withProperty(side, "true");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.equals("red_mushroom_block") || path.equals("brown_mushroom_block") || path.equals("mushroom_stem")) {
|
||||||
|
result = result.withProperty("up", "true").withProperty("down", "true")
|
||||||
|
.withProperty("north", "true").withProperty("south", "true")
|
||||||
|
.withProperty("east", "true").withProperty("west", "true");
|
||||||
|
String hiddenFace = faceName(face);
|
||||||
|
if (result.properties().containsKey(hiddenFace)) result = result.withProperty(hiddenFace, "false");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.containsKey("facing") && !path.endsWith("_stairs") && !path.endsWith("_door")
|
||||||
|
&& !path.endsWith("_trapdoor") && !path.endsWith("_fence_gate") && !path.endsWith("_button")
|
||||||
|
&& !path.equals("lever") && !path.equals("grindstone") && !path.equals("bell")
|
||||||
|
&& !path.equals("hopper") && !path.equals("campfire") && !path.equals("soul_campfire")
|
||||||
|
&& !path.equals("tripwire_hook")
|
||||||
|
&& !path.endsWith("wall_torch") && !path.endsWith("_bed") && !path.endsWith("_sign")
|
||||||
|
&& !path.endsWith("_hanging_sign") && !path.endsWith("_banner") && !path.endsWith("_wall_banner")
|
||||||
|
&& !path.endsWith("_skull") && !path.endsWith("_head")
|
||||||
|
&& !path.equals("end_portal_frame") && !path.equals("respawn_anchor")) {
|
||||||
|
Direction fdir = faceDirection(face);
|
||||||
|
String facing;
|
||||||
|
if (fdir != null && SIX_DIR.contains(path)) {
|
||||||
|
facing = fdir.opposite().name().toLowerCase();
|
||||||
|
} else if (path.equals("repeater") || path.equals("comparator")) {
|
||||||
|
facing = oppositeFacing(yawToFacing(player.getPosition().yaw()));
|
||||||
|
} else {
|
||||||
|
facing = yawToFacing(player.getPosition().yaw());
|
||||||
|
}
|
||||||
|
try { result = result.withProperty("facing", facing); } catch (Exception ignored) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isConnecting(path)) result = computeConnections(result, path, instance, pos);
|
||||||
|
|
||||||
|
if (props.containsKey("waterlogged") && Block.WATER.compare(instance.getBlock(pos))) {
|
||||||
|
try { result = result.withProperty("waterlogged", "true"); } catch (Exception ignored) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isBanner(String path) {
|
||||||
|
return path.endsWith("_banner") && !path.endsWith("_wall_banner");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Block applyStairShape(Block stair, Instance instance, BlockVec pos, String facing, boolean top) {
|
||||||
|
Direction f = facingToDirection(facing);
|
||||||
|
Block front = instance.getBlock(new BlockVec(pos.blockX() + f.normalX(), pos.blockY(), pos.blockZ() + f.normalZ()));
|
||||||
|
if (front.key().value().endsWith("_stairs") && top == isTopStair(front)) {
|
||||||
|
Direction frontFacing = facingToDirection(front.getProperty("facing"));
|
||||||
|
if (frontFacing != f && frontFacing != f.opposite()) {
|
||||||
|
if (frontFacing == rotateCW(f)) return stair.withProperty("shape", "outer_right");
|
||||||
|
else return stair.withProperty("shape", "outer_left");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Block back = instance.getBlock(new BlockVec(pos.blockX() - f.normalX(), pos.blockY(), pos.blockZ() - f.normalZ()));
|
||||||
|
if (back.key().value().endsWith("_stairs") && top == isTopStair(back)) {
|
||||||
|
Direction backFacing = facingToDirection(back.getProperty("facing"));
|
||||||
|
if (backFacing != f && backFacing != f.opposite()) {
|
||||||
|
if (backFacing == rotateCW(f)) return stair.withProperty("shape", "inner_right");
|
||||||
|
else return stair.withProperty("shape", "inner_left");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return stair.withProperty("shape", "straight");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isTopStair(Block b) { return "top".equals(b.getProperty("half")); }
|
||||||
|
|
||||||
|
private static Block computeConnections(Block block, String path, Instance instance, BlockVec pos) {
|
||||||
|
boolean isWall = path.endsWith("_wall") && !path.endsWith("head_wall") && !path.endsWith("_wall_sign") && !path.endsWith("_wall_banner");
|
||||||
|
if (path.endsWith("_fence_gate")) return block;
|
||||||
|
|
||||||
|
Block result = block;
|
||||||
|
for (Direction d : Direction.HORIZONTAL) {
|
||||||
|
BlockVec np = new BlockVec(pos.blockX() + d.normalX(), pos.blockY(), pos.blockZ() + d.normalZ());
|
||||||
|
Block nb = instance.getBlock(np);
|
||||||
|
boolean connect = canConnectTo(nb, path, d);
|
||||||
|
String propName = d.name().toLowerCase();
|
||||||
|
if (isWall) {
|
||||||
|
boolean tall = connect && isSolidAbove(instance, pos);
|
||||||
|
result = result.withProperty(propName, connect ? (tall ? "tall" : "low") : "none");
|
||||||
|
} else {
|
||||||
|
result = result.withProperty(propName, connect ? "true" : "false");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isWall && result.properties().containsKey("up")) {
|
||||||
|
boolean up = computeWallUp(result, instance, pos);
|
||||||
|
result = result.withProperty("up", up ? "true" : "false");
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isSolidAbove(Instance instance, BlockVec pos) {
|
||||||
|
Block above = instance.getBlock(new BlockVec(pos.blockX(), pos.blockY() + 1, pos.blockZ()));
|
||||||
|
return above.isSolid();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean computeWallUp(Block wall, Instance instance, BlockVec pos) {
|
||||||
|
boolean n = !"none".equals(wall.getProperty("north"));
|
||||||
|
boolean s = !"none".equals(wall.getProperty("south"));
|
||||||
|
boolean e = !"none".equals(wall.getProperty("east"));
|
||||||
|
boolean w = !"none".equals(wall.getProperty("west"));
|
||||||
|
boolean straight = (n && s && !e && !w) || (!n && !s && e && w);
|
||||||
|
if (!straight) return true;
|
||||||
|
return isSolidAbove(instance, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean canConnectTo(Block neighbor, String selfPath, Direction toward) {
|
||||||
|
if (neighbor.isAir()) return false;
|
||||||
|
String np = neighbor.key().value();
|
||||||
|
if (np.endsWith("_fence_gate")) {
|
||||||
|
String facing = neighbor.getProperty("facing");
|
||||||
|
if (facing != null) {
|
||||||
|
Direction f = facingToDirection(facing);
|
||||||
|
return toward != f && toward != f.opposite();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (selfPath.endsWith("_fence") && np.endsWith("_fence")) {
|
||||||
|
boolean selfNether = selfPath.contains("nether_brick");
|
||||||
|
boolean nNether = np.contains("nether_brick");
|
||||||
|
return selfNether == nNether;
|
||||||
|
}
|
||||||
|
if (selfPath.endsWith("_wall") && np.endsWith("_wall")) return true;
|
||||||
|
if ((selfPath.equals("glass_pane") || selfPath.endsWith("_stained_glass_pane") || selfPath.equals("iron_bars"))
|
||||||
|
&& (np.equals("glass_pane") || np.endsWith("_stained_glass_pane") || np.equals("iron_bars"))) return true;
|
||||||
|
return neighbor.isSolid();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isConnecting(String path) {
|
||||||
|
return path.endsWith("_fence") || (path.endsWith("_wall") && !path.endsWith("head_wall")
|
||||||
|
&& !path.endsWith("_wall_sign") && !path.endsWith("_wall_banner") && !path.endsWith("_wall_torch")
|
||||||
|
&& !path.endsWith("_wall_skull") && !path.endsWith("_wall_head") && !path.endsWith("_wall_hanging_sign"))
|
||||||
|
|| path.equals("iron_bars") || path.equals("glass_pane") || path.endsWith("_stained_glass_pane");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Block updateFenceGateInWall(Block gate, Instance instance, BlockVec pos) {
|
||||||
|
if (!gate.key().value().endsWith("_fence_gate")) return gate;
|
||||||
|
String facing = gate.getProperty("facing");
|
||||||
|
if (facing == null) return gate;
|
||||||
|
Direction f = facingToDirection(facing);
|
||||||
|
Direction left = rotateCCW(f);
|
||||||
|
Direction right = rotateCW(f);
|
||||||
|
Block l = instance.getBlock(new BlockVec(pos.blockX() + left.normalX(), pos.blockY(), pos.blockZ() + left.normalZ()));
|
||||||
|
Block r = instance.getBlock(new BlockVec(pos.blockX() + right.normalX(), pos.blockY(), pos.blockZ() + right.normalZ()));
|
||||||
|
boolean inWall = l.key().value().endsWith("_wall") || r.key().value().endsWith("_wall");
|
||||||
|
return gate.withProperty("in_wall", inWall ? "true" : "false");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void updateRedstoneWire(Instance instance, BlockVec pos) {
|
||||||
|
Block wire = instance.getBlock(pos);
|
||||||
|
if (!wire.key().value().equals("redstone_wire")) return;
|
||||||
|
Block result = wire;
|
||||||
|
for (Direction d : Direction.HORIZONTAL) {
|
||||||
|
BlockVec side = new BlockVec(pos.blockX() + d.normalX(), pos.blockY(), pos.blockZ() + d.normalZ());
|
||||||
|
Block sideBlock = instance.getBlock(side);
|
||||||
|
Block upBlock = instance.getBlock(new BlockVec(side.blockX(), side.blockY() + 1, side.blockZ()));
|
||||||
|
String state;
|
||||||
|
if (upBlock.key().value().equals("redstone_wire")) state = "up";
|
||||||
|
else if (isRedstoneConnectable(sideBlock)
|
||||||
|
|| instance.getBlock(new BlockVec(side.blockX(), side.blockY() - 1, side.blockZ())).key().value().equals("redstone_wire")) state = "side";
|
||||||
|
else state = "none";
|
||||||
|
result = result.withProperty(d.name().toLowerCase(), state);
|
||||||
|
}
|
||||||
|
instance.setBlock(pos, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isRedstoneConnectable(Block b) {
|
||||||
|
String p = b.key().value();
|
||||||
|
return p.equals("redstone_wire") || p.equals("lever") || p.endsWith("_button")
|
||||||
|
|| p.equals("repeater") || p.equals("comparator") || p.equals("redstone_torch")
|
||||||
|
|| p.equals("redstone_wall_torch") || p.equals("observer") || p.equals("daylight_detector")
|
||||||
|
|| p.endsWith("_pressure_plate") || p.equals("target") || p.equals("redstone_block")
|
||||||
|
|| p.equals("tripwire_hook");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isRail(String path) {
|
||||||
|
return path.equals("rail") || path.equals("powered_rail") || path.equals("detector_rail") || path.equals("activator_rail");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void updateRailShape(Instance instance, BlockVec pos) {
|
||||||
|
Block rail = instance.getBlock(pos);
|
||||||
|
String path = rail.key().value();
|
||||||
|
if (!isRail(path)) return;
|
||||||
|
boolean canCurve = path.equals("rail");
|
||||||
|
boolean[] adj = new boolean[4];
|
||||||
|
boolean[] ascAdj = new boolean[4];
|
||||||
|
Direction[] dirs = {Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST};
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
BlockVec np = new BlockVec(pos.blockX() + dirs[i].normalX(), pos.blockY(), pos.blockZ() + dirs[i].normalZ());
|
||||||
|
adj[i] = isRail(instance.getBlock(np).key().value());
|
||||||
|
BlockVec upNp = new BlockVec(np.blockX(), np.blockY() + 1, np.blockZ());
|
||||||
|
ascAdj[i] = isRail(instance.getBlock(upNp).key().value());
|
||||||
|
}
|
||||||
|
String shape;
|
||||||
|
if (canCurve) {
|
||||||
|
if (adj[0] && adj[1]) shape = "north_east";
|
||||||
|
else if (adj[1] && adj[2]) shape = "south_east";
|
||||||
|
else if (adj[2] && adj[3]) shape = "south_west";
|
||||||
|
else if (adj[3] && adj[0]) shape = "north_west";
|
||||||
|
else if (ascAdj[0]) shape = "ascending_north";
|
||||||
|
else if (ascAdj[1]) shape = "ascending_east";
|
||||||
|
else if (ascAdj[2]) shape = "ascending_south";
|
||||||
|
else if (ascAdj[3]) shape = "ascending_west";
|
||||||
|
else if (adj[1] || adj[3]) shape = "east_west";
|
||||||
|
else shape = "north_south";
|
||||||
|
} else {
|
||||||
|
if (ascAdj[0]) shape = "ascending_north";
|
||||||
|
else if (ascAdj[1]) shape = "ascending_east";
|
||||||
|
else if (ascAdj[2]) shape = "ascending_south";
|
||||||
|
else if (ascAdj[3]) shape = "ascending_west";
|
||||||
|
else if (adj[1] || adj[3]) shape = "east_west";
|
||||||
|
else shape = "north_south";
|
||||||
|
}
|
||||||
|
instance.setBlock(pos, rail.withProperty("shape", shape));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void updateScaffolding(Instance instance, BlockVec pos) {
|
||||||
|
Block s = instance.getBlock(pos);
|
||||||
|
if (!s.key().value().equals("scaffolding")) return;
|
||||||
|
Block below = instance.getBlock(new BlockVec(pos.blockX(), pos.blockY() - 1, pos.blockZ()));
|
||||||
|
boolean bottom;
|
||||||
|
int distance;
|
||||||
|
if (below.isSolid() && !below.key().value().equals("scaffolding")) { bottom = false; distance = 0; }
|
||||||
|
else if (below.key().value().equals("scaffolding")) { bottom = false; distance = 0; }
|
||||||
|
else {
|
||||||
|
int min = 7;
|
||||||
|
for (Direction d : Direction.HORIZONTAL) {
|
||||||
|
Block n = instance.getBlock(new BlockVec(pos.blockX() + d.normalX(), pos.blockY(), pos.blockZ() + d.normalZ()));
|
||||||
|
if (n.key().value().equals("scaffolding")) min = Math.min(min, parseIntOr(n.getProperty("distance"), 7) + 1);
|
||||||
|
}
|
||||||
|
bottom = true;
|
||||||
|
distance = min;
|
||||||
|
}
|
||||||
|
instance.setBlock(pos, s.withProperty("bottom", String.valueOf(bottom)).withProperty("distance", String.valueOf(distance)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void updateChestPair(Instance instance, BlockVec pos) {
|
||||||
|
Block chest = instance.getBlock(pos);
|
||||||
|
String path = chest.key().value();
|
||||||
|
if (!(path.equals("chest") || path.equals("trapped_chest"))) return;
|
||||||
|
String facing = chest.getProperty("facing");
|
||||||
|
if (facing == null) return;
|
||||||
|
Direction f = facingToDirection(facing);
|
||||||
|
Direction left = rotateCCW(f);
|
||||||
|
Direction right = rotateCW(f);
|
||||||
|
BlockVec leftPos = new BlockVec(pos.blockX() + left.normalX(), pos.blockY(), pos.blockZ() + left.normalZ());
|
||||||
|
BlockVec rightPos = new BlockVec(pos.blockX() + right.normalX(), pos.blockY(), pos.blockZ() + right.normalZ());
|
||||||
|
Block leftBlock = instance.getBlock(leftPos);
|
||||||
|
Block rightBlock = instance.getBlock(rightPos);
|
||||||
|
if (leftBlock.key().value().equals(path) && facing.equals(leftBlock.getProperty("facing"))
|
||||||
|
&& "single".equals(leftBlock.getProperty("type"))) {
|
||||||
|
instance.setBlock(pos, chest.withProperty("type", "right"));
|
||||||
|
instance.setBlock(leftPos, leftBlock.withProperty("type", "left"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (rightBlock.key().value().equals(path) && facing.equals(rightBlock.getProperty("facing"))
|
||||||
|
&& "single".equals(rightBlock.getProperty("type"))) {
|
||||||
|
instance.setBlock(pos, chest.withProperty("type", "left"));
|
||||||
|
instance.setBlock(rightPos, rightBlock.withProperty("type", "right"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String yawToFacing(float yaw) {
|
||||||
|
float y = ((yaw % 360) + 360) % 360;
|
||||||
|
if (y >= 315 || y < 45) return "south";
|
||||||
|
if (y < 135) return "west";
|
||||||
|
if (y < 225) return "north";
|
||||||
|
return "east";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String faceName(BlockFace face) {
|
||||||
|
return switch (face) {
|
||||||
|
case NORTH -> "north";
|
||||||
|
case SOUTH -> "south";
|
||||||
|
case EAST -> "east";
|
||||||
|
case WEST -> "west";
|
||||||
|
case TOP -> "up";
|
||||||
|
case BOTTOM -> "down";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String oppositeFacing(String f) {
|
||||||
|
return switch (f) {
|
||||||
|
case "north" -> "south";
|
||||||
|
case "south" -> "north";
|
||||||
|
case "east" -> "west";
|
||||||
|
case "west" -> "east";
|
||||||
|
case "up" -> "down";
|
||||||
|
case "down" -> "up";
|
||||||
|
default -> f;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Direction faceDirection(BlockFace face) {
|
||||||
|
return switch (face) {
|
||||||
|
case NORTH -> Direction.NORTH;
|
||||||
|
case SOUTH -> Direction.SOUTH;
|
||||||
|
case EAST -> Direction.EAST;
|
||||||
|
case WEST -> Direction.WEST;
|
||||||
|
case TOP -> Direction.UP;
|
||||||
|
case BOTTOM -> Direction.DOWN;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Direction facingToDirection(String f) {
|
||||||
|
if (f == null) return Direction.NORTH;
|
||||||
|
return switch (f) {
|
||||||
|
case "north" -> Direction.NORTH;
|
||||||
|
case "south" -> Direction.SOUTH;
|
||||||
|
case "east" -> Direction.EAST;
|
||||||
|
case "west" -> Direction.WEST;
|
||||||
|
case "up" -> Direction.UP;
|
||||||
|
case "down" -> Direction.DOWN;
|
||||||
|
default -> Direction.NORTH;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Direction rotateCW(Direction d) {
|
||||||
|
return switch (d) {
|
||||||
|
case NORTH -> Direction.EAST;
|
||||||
|
case EAST -> Direction.SOUTH;
|
||||||
|
case SOUTH -> Direction.WEST;
|
||||||
|
case WEST -> Direction.NORTH;
|
||||||
|
default -> d;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Direction rotateCCW(Direction d) {
|
||||||
|
return switch (d) {
|
||||||
|
case NORTH -> Direction.WEST;
|
||||||
|
case WEST -> Direction.SOUTH;
|
||||||
|
case SOUTH -> Direction.EAST;
|
||||||
|
case EAST -> Direction.NORTH;
|
||||||
|
default -> d;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String doorHinge(String facing, Point cursor, Instance instance, BlockVec pos, String path) {
|
||||||
|
Direction f = facingToDirection(facing);
|
||||||
|
Direction left = rotateCCW(f);
|
||||||
|
BlockVec leftPos = new BlockVec(pos.blockX() + left.normalX(), pos.blockY(), pos.blockZ() + left.normalZ());
|
||||||
|
Block leftBlock = instance.getBlock(leftPos);
|
||||||
|
if (leftBlock.key().value().equals(path) && facing.equals(leftBlock.getProperty("facing"))) {
|
||||||
|
return "right";
|
||||||
|
}
|
||||||
|
double x = cursor.x() - 0.5;
|
||||||
|
double z = cursor.z() - 0.5;
|
||||||
|
return switch (facing) {
|
||||||
|
case "north" -> x < 0 ? "right" : "left";
|
||||||
|
case "south" -> x > 0 ? "right" : "left";
|
||||||
|
case "west" -> z > 0 ? "right" : "left";
|
||||||
|
case "east" -> z < 0 ? "right" : "left";
|
||||||
|
default -> "left";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void onInteract(PlayerBlockInteractEvent event) {
|
||||||
|
Block block = event.getBlock();
|
||||||
|
String path = block.key().value();
|
||||||
|
Instance instance = event.getInstance();
|
||||||
|
BlockVec pos = event.getBlockPosition();
|
||||||
|
|
||||||
|
if (path.endsWith("_door")) {
|
||||||
|
String half = block.getProperty("half");
|
||||||
|
BlockVec lowerPos = "upper".equals(half)
|
||||||
|
? new BlockVec(pos.blockX(), pos.blockY() - 1, pos.blockZ())
|
||||||
|
: pos;
|
||||||
|
BlockVec upperPos = new BlockVec(lowerPos.blockX(), lowerPos.blockY() + 1, lowerPos.blockZ());
|
||||||
|
Block lower = instance.getBlock(lowerPos);
|
||||||
|
Block upper = instance.getBlock(upperPos);
|
||||||
|
if (!lower.key().value().equals(path) || !upper.key().value().equals(path)) return;
|
||||||
|
boolean open = !"true".equals(lower.getProperty("open"));
|
||||||
|
instance.setBlock(lowerPos, lower.withProperty("open", String.valueOf(open)));
|
||||||
|
instance.setBlock(upperPos, upper.withProperty("open", String.valueOf(open)));
|
||||||
|
|
||||||
|
String facing = lower.getProperty("facing");
|
||||||
|
String hinge = lower.getProperty("hinge");
|
||||||
|
if (facing != null && hinge != null) {
|
||||||
|
Direction f = facingToDirection(facing);
|
||||||
|
Direction side = "left".equals(hinge) ? rotateCW(f) : rotateCCW(f);
|
||||||
|
BlockVec partnerPos = new BlockVec(lowerPos.blockX() + side.normalX(), lowerPos.blockY(), lowerPos.blockZ() + side.normalZ());
|
||||||
|
Block partner = instance.getBlock(partnerPos);
|
||||||
|
if (partner.key().value().equals(path) && facing.equals(partner.getProperty("facing"))
|
||||||
|
&& !hinge.equals(partner.getProperty("hinge"))) {
|
||||||
|
Block partnerUpper = instance.getBlock(new BlockVec(partnerPos.blockX(), partnerPos.blockY() + 1, partnerPos.blockZ()));
|
||||||
|
instance.setBlock(partnerPos, partner.withProperty("open", String.valueOf(open)));
|
||||||
|
if (partnerUpper.key().value().equals(path)) {
|
||||||
|
instance.setBlock(new BlockVec(partnerPos.blockX(), partnerPos.blockY() + 1, partnerPos.blockZ()),
|
||||||
|
partnerUpper.withProperty("open", String.valueOf(open)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
event.setBlockingItemUse(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.endsWith("_fence_gate")) {
|
||||||
|
boolean open = !"true".equals(block.getProperty("open"));
|
||||||
|
Block updated = block.withProperty("open", String.valueOf(open));
|
||||||
|
if (open) {
|
||||||
|
String facing = block.getProperty("facing");
|
||||||
|
if (facing != null) {
|
||||||
|
Direction f = facingToDirection(facing);
|
||||||
|
Direction playerDir = horizontalFacingToDirection(yawToFacing(event.getPlayer().getPosition().yaw()));
|
||||||
|
if (playerDir == f) updated = updated.withProperty("facing", oppositeFacing(facing));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
instance.setBlock(pos, updated);
|
||||||
|
event.setBlockingItemUse(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.endsWith("_trapdoor")) {
|
||||||
|
boolean open = !"true".equals(block.getProperty("open"));
|
||||||
|
instance.setBlock(pos, block.withProperty("open", String.valueOf(open)));
|
||||||
|
event.setBlockingItemUse(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Direction horizontalFacingToDirection(String f) {
|
||||||
|
return facingToDirection(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void onBreak(PlayerBlockBreakEvent event) {
|
||||||
|
Instance instance = event.getInstance();
|
||||||
|
BlockVec pos = event.getBlockPosition();
|
||||||
|
Block broken = event.getBlock();
|
||||||
|
String path = broken.key().value();
|
||||||
|
|
||||||
|
if (path.endsWith("_door")) {
|
||||||
|
String half = broken.getProperty("half");
|
||||||
|
BlockVec other = "upper".equals(half)
|
||||||
|
? new BlockVec(pos.blockX(), pos.blockY() - 1, pos.blockZ())
|
||||||
|
: new BlockVec(pos.blockX(), pos.blockY() + 1, pos.blockZ());
|
||||||
|
instance.scheduler().scheduleNextTick(() -> {
|
||||||
|
Block ob = instance.getBlock(other);
|
||||||
|
if (ob.key().value().equals(path)) instance.setBlock(other, Block.AIR);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.endsWith("_bed")) {
|
||||||
|
String part = broken.getProperty("part");
|
||||||
|
String facing = broken.getProperty("facing");
|
||||||
|
if (facing != null) {
|
||||||
|
Direction d = facingToDirection(facing);
|
||||||
|
if ("foot".equals(part)) {
|
||||||
|
BlockVec headPos = new BlockVec(pos.blockX() + d.normalX(), pos.blockY(), pos.blockZ() + d.normalZ());
|
||||||
|
instance.scheduler().scheduleNextTick(() -> {
|
||||||
|
if (instance.getBlock(headPos).key().value().equals(path)) instance.setBlock(headPos, Block.AIR);
|
||||||
|
});
|
||||||
|
} else if ("head".equals(part)) {
|
||||||
|
BlockVec footPos = new BlockVec(pos.blockX() - d.normalX(), pos.blockY(), pos.blockZ() - d.normalZ());
|
||||||
|
instance.scheduler().scheduleNextTick(() -> {
|
||||||
|
if (instance.getBlock(footPos).key().value().equals(path)) instance.setBlock(footPos, Block.AIR);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.equals("big_dripleaf") || path.equals("big_dripleaf_stem")) {
|
||||||
|
String half = broken.getProperty("half");
|
||||||
|
BlockVec other = "upper".equals(half)
|
||||||
|
? new BlockVec(pos.blockX(), pos.blockY() - 1, pos.blockZ())
|
||||||
|
: new BlockVec(pos.blockX(), pos.blockY() + 1, pos.blockZ());
|
||||||
|
instance.scheduler().scheduleNextTick(() -> {
|
||||||
|
String op = instance.getBlock(other).key().value();
|
||||||
|
if (op.equals("big_dripleaf") || op.equals("big_dripleaf_stem")) instance.setBlock(other, Block.AIR);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
instance.scheduler().scheduleNextTick(() -> {
|
||||||
|
if (isConnecting(path)) {
|
||||||
|
for (Direction d : Direction.HORIZONTAL) {
|
||||||
|
BlockVec np = new BlockVec(pos.blockX() + d.normalX(), pos.blockY(), pos.blockZ() + d.normalZ());
|
||||||
|
Block nb = instance.getBlock(np);
|
||||||
|
if (isConnecting(nb.key().value())) instance.setBlock(np, computeConnections(nb, nb.key().value(), instance, np));
|
||||||
|
if (nb.key().value().endsWith("_fence_gate")) instance.setBlock(np, updateFenceGateInWall(nb, instance, np));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.endsWith("_wall")) {
|
||||||
|
for (Direction d : Direction.HORIZONTAL) {
|
||||||
|
BlockVec np = new BlockVec(pos.blockX() + d.normalX(), pos.blockY(), pos.blockZ() + d.normalZ());
|
||||||
|
Block nb = instance.getBlock(np);
|
||||||
|
if (nb.key().value().endsWith("_fence_gate")) instance.setBlock(np, updateFenceGateInWall(nb, instance, np));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Direction d : Direction.HORIZONTAL) {
|
||||||
|
BlockVec np = new BlockVec(pos.blockX() + d.normalX(), pos.blockY(), pos.blockZ() + d.normalZ());
|
||||||
|
Block nb = instance.getBlock(np);
|
||||||
|
String npath = nb.key().value();
|
||||||
|
if (npath.equals("redstone_wire")) updateRedstoneWire(instance, np);
|
||||||
|
if (isRail(npath)) updateRailShape(instance, np);
|
||||||
|
}
|
||||||
|
BlockVec above = new BlockVec(pos.blockX(), pos.blockY() + 1, pos.blockZ());
|
||||||
|
if (instance.getBlock(above).key().value().equals("redstone_wire")) updateRedstoneWire(instance, above);
|
||||||
|
BlockVec belowRail = new BlockVec(pos.blockX(), pos.blockY() - 1, pos.blockZ());
|
||||||
|
if (isRail(instance.getBlock(belowRail).key().value())) updateRailShape(instance, belowRail);
|
||||||
|
|
||||||
|
if (path.equals("scaffolding") || isAnyScaffoldingAround(instance, pos)) {
|
||||||
|
recomputeScaffoldingColumn(instance, pos);
|
||||||
|
for (Direction d : Direction.HORIZONTAL) {
|
||||||
|
BlockVec np = new BlockVec(pos.blockX() + d.normalX(), pos.blockY(), pos.blockZ() + d.normalZ());
|
||||||
|
if (instance.getBlock(np).key().value().equals("scaffolding")) recomputeScaffoldingColumn(instance, np);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.equals("chest") || path.equals("trapped_chest")) {
|
||||||
|
String facing = broken.getProperty("facing");
|
||||||
|
if (facing != null) {
|
||||||
|
Direction f = facingToDirection(facing);
|
||||||
|
for (Direction d : new Direction[]{rotateCW(f), rotateCCW(f)}) {
|
||||||
|
BlockVec np = new BlockVec(pos.blockX() + d.normalX(), pos.blockY(), pos.blockZ() + d.normalZ());
|
||||||
|
Block nb = instance.getBlock(np);
|
||||||
|
if (nb.key().value().equals(path) && facing.equals(nb.getProperty("facing"))) {
|
||||||
|
String t = nb.getProperty("type");
|
||||||
|
if ("left".equals(t) || "right".equals(t)) instance.setBlock(np, nb.withProperty("type", "single"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isStairLike(path)) {
|
||||||
|
for (Direction d : Direction.HORIZONTAL) {
|
||||||
|
BlockVec np = new BlockVec(pos.blockX() + d.normalX(), pos.blockY(), pos.blockZ() + d.normalZ());
|
||||||
|
Block nb = instance.getBlock(np);
|
||||||
|
if (nb.key().value().endsWith("_stairs")) {
|
||||||
|
String nf = nb.getProperty("facing");
|
||||||
|
boolean nt = isTopStair(nb);
|
||||||
|
instance.setBlock(np, applyStairShape(nb, instance, np, nf, nt));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isStairLike(String path) { return path.endsWith("_stairs"); }
|
||||||
|
|
||||||
|
private static boolean isAnyScaffoldingAround(Instance instance, BlockVec pos) {
|
||||||
|
for (Direction d : Direction.HORIZONTAL) {
|
||||||
|
BlockVec np = new BlockVec(pos.blockX() + d.normalX(), pos.blockY(), pos.blockZ() + d.normalZ());
|
||||||
|
if (instance.getBlock(np).key().value().equals("scaffolding")) return true;
|
||||||
|
}
|
||||||
|
BlockVec above = new BlockVec(pos.blockX(), pos.blockY() + 1, pos.blockZ());
|
||||||
|
return instance.getBlock(above).key().value().equals("scaffolding");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void recomputeScaffoldingColumn(Instance instance, BlockVec start) {
|
||||||
|
int x = start.blockX(), z = start.blockZ();
|
||||||
|
for (int y = start.blockY(); y < start.blockY() + 32; y++) {
|
||||||
|
BlockVec p = new BlockVec(x, y, z);
|
||||||
|
if (!instance.getBlock(p).key().value().equals("scaffolding")) break;
|
||||||
|
updateScaffolding(instance, p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int parseIntOr(String s, int d) {
|
||||||
|
if (s == null) return d;
|
||||||
|
try { return Integer.parseInt(s); } catch (NumberFormatException e) { return d; }
|
||||||
|
}
|
||||||
|
}
|
||||||
+50
-9
@@ -11,8 +11,10 @@ import net.minestom.server.coordinate.Pos;
|
|||||||
import net.minestom.server.entity.GameMode;
|
import net.minestom.server.entity.GameMode;
|
||||||
import net.minestom.server.entity.Player;
|
import net.minestom.server.entity.Player;
|
||||||
import net.minestom.server.event.player.PlayerBlockBreakEvent;
|
import net.minestom.server.event.player.PlayerBlockBreakEvent;
|
||||||
|
import net.minestom.server.event.player.PlayerBlockInteractEvent;
|
||||||
import net.minestom.server.event.player.PlayerBlockPlaceEvent;
|
import net.minestom.server.event.player.PlayerBlockPlaceEvent;
|
||||||
import net.minestom.server.event.player.PlayerUseItemEvent;
|
import net.minestom.server.event.player.PlayerUseItemEvent;
|
||||||
|
import net.minestom.server.event.player.PlayerUseItemOnBlockEvent;
|
||||||
import net.minestom.server.item.ItemStack;
|
import net.minestom.server.item.ItemStack;
|
||||||
import net.minestom.server.item.Material;
|
import net.minestom.server.item.Material;
|
||||||
import net.minestom.server.timer.TaskSchedule;
|
import net.minestom.server.timer.TaskSchedule;
|
||||||
@@ -61,6 +63,10 @@ public class BuildBattle extends StatelessGame {
|
|||||||
this.seconds = seconds;
|
this.seconds = seconds;
|
||||||
this.setGenerator(new BuildBattleChunkgenerator(height));
|
this.setGenerator(new BuildBattleChunkgenerator(height));
|
||||||
this.eventNode().addListener(PlayerUseItemEvent.class, this::onRatingItemUse);
|
this.eventNode().addListener(PlayerUseItemEvent.class, this::onRatingItemUse);
|
||||||
|
this.eventNode().addListener(PlayerUseItemOnBlockEvent.class, e -> FluidFlowHelper.onBucketUse(e, p -> this.isInArea(p, e.getPlayer())));
|
||||||
|
this.eventNode().addListener(PlayerBlockInteractEvent.class, e -> {
|
||||||
|
if (this.isInArea(e.getBlockPosition(), e.getPlayer())) BlockPlacementHelper.onInteract(e);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -76,6 +82,21 @@ public class BuildBattle extends StatelessGame {
|
|||||||
this.getPlayers().forEach(player -> player.setGameMode(GameMode.CREATIVE));
|
this.getPlayers().forEach(player -> player.setGameMode(GameMode.CREATIVE));
|
||||||
|
|
||||||
this.scheduler().scheduleTask(this::endBuildPhase, TaskSchedule.seconds(this.seconds), TaskSchedule.stop());
|
this.scheduler().scheduleTask(this::endBuildPhase, TaskSchedule.seconds(this.seconds), TaskSchedule.stop());
|
||||||
|
this.scheduleCountdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void scheduleCountdown() {
|
||||||
|
int[] marks = {30, 10, 5, 4, 3, 2, 1};
|
||||||
|
for (int m : marks) {
|
||||||
|
if (this.seconds <= m) continue;
|
||||||
|
int remaining = m;
|
||||||
|
this.scheduler().scheduleTask(() -> {
|
||||||
|
NamedTextColor color = remaining <= 5 ? NamedTextColor.RED : (remaining <= 10 ? NamedTextColor.GOLD : NamedTextColor.YELLOW);
|
||||||
|
this.sendMessage(Component.text("Noch ", NamedTextColor.GRAY)
|
||||||
|
.append(Component.text(remaining, color))
|
||||||
|
.append(Component.text(" Sekunde" + (remaining == 1 ? "" : "n") + " Bauzeit!", NamedTextColor.GRAY)));
|
||||||
|
}, TaskSchedule.seconds(this.seconds - m), TaskSchedule.stop());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void endBuildPhase() {
|
private void endBuildPhase() {
|
||||||
@@ -109,12 +130,34 @@ public class BuildBattle extends StatelessGame {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onBlockBreak(@NotNull PlayerBlockBreakEvent event) {
|
protected void onBlockBreak(@NotNull PlayerBlockBreakEvent event) {
|
||||||
event.setCancelled(!this.isInArea(event.getBlockPosition(), event.getPlayer()));
|
if (!this.isInArea(event.getBlockPosition(), event.getPlayer())) {
|
||||||
|
event.setCancelled(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
BlockPlacementHelper.onBreak(event);
|
||||||
|
FluidFlowHelper.onBreak(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onBlockPlace(@NotNull PlayerBlockPlaceEvent event) {
|
protected void onBlockPlace(@NotNull PlayerBlockPlaceEvent event) {
|
||||||
event.setCancelled(!this.isInArea(event.getBlockPosition(), event.getPlayer()));
|
if (!this.isInArea(event.getBlockPosition(), event.getPlayer())) {
|
||||||
|
event.setCancelled(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
BlockPlacementHelper.apply(event);
|
||||||
|
FluidFlowHelper.onPlace(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Pos safeSpawn(Player owner) {
|
||||||
|
Pos center = this.spawnPositions.get(owner);
|
||||||
|
int topY = center.blockY();
|
||||||
|
for (int y = 300; y >= center.blockY(); y--) {
|
||||||
|
if (!this.getBlock(center.blockX(), y, center.blockZ()).isAir()) {
|
||||||
|
topY = y;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new Pos(center.x(), topY + 3, center.z()).withView(center.yaw(), center.pitch());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void rating() {
|
private void rating() {
|
||||||
@@ -136,7 +179,7 @@ public class BuildBattle extends StatelessGame {
|
|||||||
this.currentOwner = owner;
|
this.currentOwner = owner;
|
||||||
this.currentVotes.clear();
|
this.currentVotes.clear();
|
||||||
|
|
||||||
Pos plot = this.spawnPositions.get(owner).add(0, 1.5, 0);
|
Pos plot = this.safeSpawn(owner);
|
||||||
this.sendMessage(Component.text("Bewerte das Grundstück von " + owner.getUsername() + "!"));
|
this.sendMessage(Component.text("Bewerte das Grundstück von " + owner.getUsername() + "!"));
|
||||||
this.getPlayers().forEach(p -> {
|
this.getPlayers().forEach(p -> {
|
||||||
p.teleport(plot);
|
p.teleport(plot);
|
||||||
@@ -176,11 +219,9 @@ public class BuildBattle extends StatelessGame {
|
|||||||
|
|
||||||
private boolean isInArea(Point block, Player player) {
|
private boolean isInArea(Point block, Player player) {
|
||||||
Pos center = this.spawnPositions.get(player);
|
Pos center = this.spawnPositions.get(player);
|
||||||
double dx = block.x() - center.x();
|
if (center == null) return false;
|
||||||
double dy = block.y() - center.y();
|
Pos min = new Pos(center.x() - plotHalf, center.y() + 1, center.z() - plotHalf);
|
||||||
double dz = block.z() - center.z();
|
Pos max = new Pos(center.x() + plotHalf - 1, 300, center.z() + plotHalf - 1);
|
||||||
return dx >= -plotHalf && dx < plotHalf
|
return eu.mhsl.minenet.minigames.util.Position.isInArea(block, min, max);
|
||||||
&& dy >= 1 && dy <= 300
|
|
||||||
&& dz >= -plotHalf && dz < plotHalf;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+268
@@ -0,0 +1,268 @@
|
|||||||
|
package eu.mhsl.minenet.minigames.instance.game.stateless.types.buildBattle;
|
||||||
|
|
||||||
|
import net.minestom.server.coordinate.BlockVec;
|
||||||
|
import net.minestom.server.coordinate.Point;
|
||||||
|
import net.minestom.server.entity.Player;
|
||||||
|
import net.minestom.server.event.player.PlayerBlockBreakEvent;
|
||||||
|
import net.minestom.server.event.player.PlayerBlockPlaceEvent;
|
||||||
|
import net.minestom.server.event.player.PlayerUseItemOnBlockEvent;
|
||||||
|
import net.minestom.server.instance.Instance;
|
||||||
|
import net.minestom.server.instance.block.Block;
|
||||||
|
import net.minestom.server.item.Material;
|
||||||
|
import net.minestom.server.timer.TaskSchedule;
|
||||||
|
import net.minestom.server.utils.Direction;
|
||||||
|
|
||||||
|
import java.util.ArrayDeque;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
public final class FluidFlowHelper {
|
||||||
|
private FluidFlowHelper() {}
|
||||||
|
|
||||||
|
private static final int WATER_TICK = 5;
|
||||||
|
private static final int LAVA_TICK = 30;
|
||||||
|
private static final int WATER_RANGE = 7;
|
||||||
|
private static final int LAVA_RANGE = 3;
|
||||||
|
private static final int FALLING = 8;
|
||||||
|
|
||||||
|
public static void onBucketUse(PlayerUseItemOnBlockEvent event, Predicate<Point> inArea) {
|
||||||
|
Material m = event.getItemStack().material();
|
||||||
|
Instance inst = event.getPlayer().getInstance();
|
||||||
|
if (inst == null) return;
|
||||||
|
Point hit = event.getPosition();
|
||||||
|
var face = event.getBlockFace();
|
||||||
|
BlockVec placePos = new BlockVec(
|
||||||
|
hit.blockX() + face.toDirection().normalX(),
|
||||||
|
hit.blockY() + face.toDirection().normalY(),
|
||||||
|
hit.blockZ() + face.toDirection().normalZ()
|
||||||
|
);
|
||||||
|
if (!inArea.test(placePos)) return;
|
||||||
|
|
||||||
|
if (m == Material.WATER_BUCKET) {
|
||||||
|
inst.setBlock(placePos, Block.WATER.withProperty("level", "0"));
|
||||||
|
scheduleFlow(inst, placePos, true);
|
||||||
|
} else if (m == Material.LAVA_BUCKET) {
|
||||||
|
inst.setBlock(placePos, Block.LAVA.withProperty("level", "0"));
|
||||||
|
scheduleFlow(inst, placePos, false);
|
||||||
|
} else if (m == Material.BUCKET) {
|
||||||
|
Block existing = inst.getBlock(placePos);
|
||||||
|
if (isWater(existing) || isLava(existing)) inst.setBlock(placePos, Block.AIR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void onPlace(PlayerBlockPlaceEvent event) {
|
||||||
|
Block b = event.getBlock();
|
||||||
|
if (isWater(b) || isLava(b)) {
|
||||||
|
Instance inst = event.getInstance();
|
||||||
|
BlockVec pos = event.getBlockPosition();
|
||||||
|
scheduleFlow(inst, pos, isWater(b));
|
||||||
|
}
|
||||||
|
triggerNeighbors(event.getInstance(), event.getBlockPosition());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void onBreak(PlayerBlockBreakEvent event) {
|
||||||
|
triggerNeighbors(event.getInstance(), event.getBlockPosition());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void triggerNeighbors(Instance inst, BlockVec pos) {
|
||||||
|
for (Direction d : Direction.values()) {
|
||||||
|
BlockVec np = new BlockVec(pos.blockX() + d.normalX(), pos.blockY() + d.normalY(), pos.blockZ() + d.normalZ());
|
||||||
|
Block nb = inst.getBlock(np);
|
||||||
|
if (isWater(nb) || isLava(nb)) scheduleFlow(inst, np, isWater(nb));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void scheduleFlow(Instance inst, BlockVec pos, boolean water) {
|
||||||
|
int delay = water ? WATER_TICK : LAVA_TICK;
|
||||||
|
inst.scheduler().buildTask(() -> step(inst, pos, water)).delay(TaskSchedule.tick(delay)).schedule();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void step(Instance inst, BlockVec pos, boolean water) {
|
||||||
|
Block self = inst.getBlock(pos);
|
||||||
|
if (water ? !isWater(self) : !isLava(self)) return;
|
||||||
|
|
||||||
|
int level = parseLevel(self);
|
||||||
|
boolean source = level == 0;
|
||||||
|
boolean falling = level >= FALLING;
|
||||||
|
int flowLevel = source ? 1 : (falling ? 1 : level + 1);
|
||||||
|
int maxRange = water ? WATER_RANGE : LAVA_RANGE;
|
||||||
|
|
||||||
|
if (!source && !falling) {
|
||||||
|
int newLevel = computeIncomingLevel(inst, pos, water, maxRange);
|
||||||
|
if (newLevel == -1) {
|
||||||
|
inst.setBlock(pos, Block.AIR);
|
||||||
|
triggerNeighbors(inst, pos);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (newLevel != level) {
|
||||||
|
inst.setBlock(pos, (water ? Block.WATER : Block.LAVA).withProperty("level", String.valueOf(newLevel)));
|
||||||
|
level = newLevel;
|
||||||
|
flowLevel = level + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (water && !source && countAdjacentSources(inst, pos, true) >= 2
|
||||||
|
&& hasSolidBelow(inst, pos)) {
|
||||||
|
inst.setBlock(pos, Block.WATER.withProperty("level", "0"));
|
||||||
|
scheduleFlow(inst, pos, true);
|
||||||
|
triggerNeighbors(inst, pos);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockVec below = new BlockVec(pos.blockX(), pos.blockY() - 1, pos.blockZ());
|
||||||
|
Block belowBlock = inst.getBlock(below);
|
||||||
|
if (isReplaceable(belowBlock) || (water ? isWater(belowBlock) : isLava(belowBlock))) {
|
||||||
|
if (handleFluidCollision(inst, below, water)) {
|
||||||
|
triggerNeighbors(inst, below);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Block target = (water ? Block.WATER : Block.LAVA).withProperty("level", String.valueOf(FALLING));
|
||||||
|
if (!belowBlock.compare(target) || !target.getProperty("level").equals(belowBlock.getProperty("level"))) {
|
||||||
|
if (!(isWater(belowBlock) && parseLevel(belowBlock) == 0)
|
||||||
|
&& !(isLava(belowBlock) && parseLevel(belowBlock) == 0)) {
|
||||||
|
inst.setBlock(below, target);
|
||||||
|
scheduleFlow(inst, below, water);
|
||||||
|
triggerNeighbors(inst, below);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!hasSolidBelow(inst, pos) && !source) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flowLevel > maxRange) return;
|
||||||
|
|
||||||
|
Set<Direction> targets = findFlowTargets(inst, pos, water, maxRange - (source ? 0 : level));
|
||||||
|
for (Direction d : Direction.HORIZONTAL) {
|
||||||
|
if (!targets.contains(d)) continue;
|
||||||
|
BlockVec np = new BlockVec(pos.blockX() + d.normalX(), pos.blockY(), pos.blockZ() + d.normalZ());
|
||||||
|
Block nb = inst.getBlock(np);
|
||||||
|
if (handleFluidCollision(inst, np, water)) continue;
|
||||||
|
if (!isReplaceable(nb) && !(water ? isWater(nb) : isLava(nb))) continue;
|
||||||
|
int existingLevel = (water ? isWater(nb) : isLava(nb)) ? parseLevel(nb) : Integer.MAX_VALUE;
|
||||||
|
if (existingLevel == 0) continue;
|
||||||
|
if (flowLevel < existingLevel) {
|
||||||
|
inst.setBlock(np, (water ? Block.WATER : Block.LAVA).withProperty("level", String.valueOf(flowLevel)));
|
||||||
|
scheduleFlow(inst, np, water);
|
||||||
|
triggerNeighbors(inst, np);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int computeIncomingLevel(Instance inst, BlockVec pos, boolean water, int maxRange) {
|
||||||
|
int min = Integer.MAX_VALUE;
|
||||||
|
BlockVec above = new BlockVec(pos.blockX(), pos.blockY() + 1, pos.blockZ());
|
||||||
|
Block ab = inst.getBlock(above);
|
||||||
|
if ((water ? isWater(ab) : isLava(ab))) return FALLING;
|
||||||
|
for (Direction d : Direction.HORIZONTAL) {
|
||||||
|
BlockVec np = new BlockVec(pos.blockX() + d.normalX(), pos.blockY(), pos.blockZ() + d.normalZ());
|
||||||
|
Block nb = inst.getBlock(np);
|
||||||
|
if (water ? isWater(nb) : isLava(nb)) {
|
||||||
|
int nl = parseLevel(nb);
|
||||||
|
if (nl >= FALLING) nl = 0;
|
||||||
|
if (nl < min) min = nl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (min == Integer.MAX_VALUE) return -1;
|
||||||
|
int result = min + 1;
|
||||||
|
if (result > maxRange) return -1;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Set<Direction> findFlowTargets(Instance inst, BlockVec pos, boolean water, int remaining) {
|
||||||
|
Set<Direction> result = new HashSet<>();
|
||||||
|
if (remaining <= 0) return result;
|
||||||
|
int bestDist = Integer.MAX_VALUE;
|
||||||
|
int[] dist = new int[4];
|
||||||
|
Direction[] dirs = {Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST};
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
BlockVec np = new BlockVec(pos.blockX() + dirs[i].normalX(), pos.blockY(), pos.blockZ() + dirs[i].normalZ());
|
||||||
|
Block nb = inst.getBlock(np);
|
||||||
|
if (!isReplaceable(nb) && !(water ? isWater(nb) : isLava(nb))) { dist[i] = Integer.MAX_VALUE; continue; }
|
||||||
|
dist[i] = bfsDownhill(inst, np, water, remaining);
|
||||||
|
if (dist[i] < bestDist) bestDist = dist[i];
|
||||||
|
}
|
||||||
|
if (bestDist == Integer.MAX_VALUE) {
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
if (dist[i] < Integer.MAX_VALUE) result.add(dirs[i]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
if (dist[i] == bestDist) result.add(dirs[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int bfsDownhill(Instance inst, BlockVec start, boolean water, int remaining) {
|
||||||
|
ArrayDeque<int[]> queue = new ArrayDeque<>();
|
||||||
|
Set<Long> visited = new HashSet<>();
|
||||||
|
queue.add(new int[]{start.blockX(), start.blockY(), start.blockZ(), 0});
|
||||||
|
visited.add(key(start.blockX(), start.blockY(), start.blockZ()));
|
||||||
|
while (!queue.isEmpty()) {
|
||||||
|
int[] c = queue.poll();
|
||||||
|
BlockVec cp = new BlockVec(c[0], c[1], c[2]);
|
||||||
|
Block below = inst.getBlock(new BlockVec(c[0], c[1] - 1, c[2]));
|
||||||
|
if (isReplaceable(below) && !(water ? isWater(below) : isLava(below))) return c[3];
|
||||||
|
if (c[3] >= remaining) continue;
|
||||||
|
for (Direction d : Direction.HORIZONTAL) {
|
||||||
|
int nx = c[0] + d.normalX(), nz = c[2] + d.normalZ();
|
||||||
|
long k = key(nx, c[1], nz);
|
||||||
|
if (visited.contains(k)) continue;
|
||||||
|
Block nb = inst.getBlock(new BlockVec(nx, c[1], nz));
|
||||||
|
if (!isReplaceable(nb) && !(water ? isWater(nb) : isLava(nb))) continue;
|
||||||
|
visited.add(k);
|
||||||
|
queue.add(new int[]{nx, c[1], nz, c[3] + 1});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Integer.MAX_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long key(int x, int y, int z) {
|
||||||
|
return ((long) x & 0x3FFFFFFL) | (((long) z & 0x3FFFFFFL) << 26) | (((long) y & 0xFFFL) << 52);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int countAdjacentSources(Instance inst, BlockVec pos, boolean water) {
|
||||||
|
int c = 0;
|
||||||
|
for (Direction d : Direction.HORIZONTAL) {
|
||||||
|
Block nb = inst.getBlock(new BlockVec(pos.blockX() + d.normalX(), pos.blockY(), pos.blockZ() + d.normalZ()));
|
||||||
|
if ((water ? isWater(nb) : isLava(nb)) && parseLevel(nb) == 0) c++;
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean hasSolidBelow(Instance inst, BlockVec pos) {
|
||||||
|
Block below = inst.getBlock(new BlockVec(pos.blockX(), pos.blockY() - 1, pos.blockZ()));
|
||||||
|
return below.isSolid();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean handleFluidCollision(Instance inst, BlockVec pos, boolean incomingIsWater) {
|
||||||
|
Block target = inst.getBlock(pos);
|
||||||
|
if (incomingIsWater && isLava(target)) {
|
||||||
|
boolean lavaSource = parseLevel(target) == 0;
|
||||||
|
inst.setBlock(pos, lavaSource ? Block.OBSIDIAN : Block.COBBLESTONE);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!incomingIsWater && isWater(target)) {
|
||||||
|
inst.setBlock(pos, Block.STONE);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isReplaceable(Block b) {
|
||||||
|
if (b.isAir()) return true;
|
||||||
|
String p = b.key().value();
|
||||||
|
return p.equals("short_grass") || p.equals("tall_grass") || p.equals("fern") || p.equals("large_fern")
|
||||||
|
|| p.equals("dead_bush") || p.equals("seagrass") || p.equals("tall_seagrass") || p.equals("vine")
|
||||||
|
|| p.equals("snow") || p.equals("fire") || p.equals("soul_fire");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isWater(Block b) { return b.compare(Block.WATER); }
|
||||||
|
private static boolean isLava(Block b) { return b.compare(Block.LAVA); }
|
||||||
|
|
||||||
|
private static int parseLevel(Block b) {
|
||||||
|
String s = b.getProperty("level");
|
||||||
|
if (s == null) return 0;
|
||||||
|
try { return Integer.parseInt(s); } catch (NumberFormatException e) { return 0; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -44,4 +44,17 @@ public class Position {
|
|||||||
public static boolean hasPositionChanged(Pos oldPos, Pos newPos) {
|
public static boolean hasPositionChanged(Pos oldPos, Pos newPos) {
|
||||||
return !oldPos.withView(0, 0).equals(newPos.withView(0, 0));
|
return !oldPos.withView(0, 0).equals(newPos.withView(0, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isInArea(Point point, Point pos1, Point pos2) {
|
||||||
|
double minX = Math.min(pos1.x(), pos2.x());
|
||||||
|
double maxX = Math.max(pos1.x(), pos2.x());
|
||||||
|
double minY = Math.min(pos1.y(), pos2.y());
|
||||||
|
double maxY = Math.max(pos1.y(), pos2.y());
|
||||||
|
double minZ = Math.min(pos1.z(), pos2.z());
|
||||||
|
double maxZ = Math.max(pos1.z(), pos2.z());
|
||||||
|
|
||||||
|
return point.x() >= minX && point.x() <= maxX
|
||||||
|
&& point.y() >= minY && point.y() <= maxY
|
||||||
|
&& point.z() >= minZ && point.z() <= maxZ;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user