diff --git a/src/main/java/xyz/jpenilla/chesscraft/BoardManager.java b/src/main/java/xyz/jpenilla/chesscraft/BoardManager.java index cb06de1..14c2b2f 100644 --- a/src/main/java/xyz/jpenilla/chesscraft/BoardManager.java +++ b/src/main/java/xyz/jpenilla/chesscraft/BoardManager.java @@ -95,8 +95,8 @@ public Collection activeBoards() { return this.boards().stream().filter(ChessBoard::hasGame).toList(); } - public void createBoard(final String name, final World world, final Vec3 pos, final CardinalDirection facing) { - final ChessBoard board = new ChessBoard(this.plugin, name, pos, facing, world.getKey(), this.stockfishPath); + public void createBoard(final String name, final World world, final Vec3 pos, final CardinalDirection facing, final int scale) { + final ChessBoard board = new ChessBoard(this.plugin, name, pos, facing, scale, world.getKey(), this.stockfishPath); this.boards.put(name, board); this.saveBoards(); } @@ -135,7 +135,15 @@ public void load() { private void loadBoards() { final Map read = ConfigHelper.loadConfig(new TypeToken>() {}, this.file, HashMap::new); - read.forEach((key, data) -> this.boards.put(key, new ChessBoard(this.plugin, key, data.position, data.facing, data.dimension, this.stockfishPath))); + read.forEach((key, data) -> this.boards.put(key, new ChessBoard( + this.plugin, + key, + data.position, + data.facing, + data.scale, + data.dimension, + this.stockfishPath + ))); } public void close() { @@ -153,7 +161,7 @@ public void close() { private void saveBoards() { final Map collect = this.boards.values().stream() - .map(b -> Map.entry(b.name(), new BoardData(b.worldKey(), b.loc(), b.facing()))) + .map(b -> Map.entry(b.name(), new BoardData(b.worldKey(), b.loc(), b.facing(), b.scale()))) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); ConfigHelper.saveConfig(this.file, new TypeToken<>() {}, collect); } @@ -247,15 +255,17 @@ public static final class BoardData { private NamespacedKey dimension = NamespacedKey.minecraft("overworld"); private Vec3 position = new Vec3(0, 0, 0); private CardinalDirection facing = CardinalDirection.NORTH; + private int scale = 1; @SuppressWarnings("unused") BoardData() { } - BoardData(final NamespacedKey dimension, final Vec3 position, final CardinalDirection facing) { + BoardData(final NamespacedKey dimension, final Vec3 position, final CardinalDirection facing, final int scale) { this.dimension = dimension; this.position = position; this.facing = facing; + this.scale = scale; } } } diff --git a/src/main/java/xyz/jpenilla/chesscraft/ChessBoard.java b/src/main/java/xyz/jpenilla/chesscraft/ChessBoard.java index 200ffe9..a9ebd46 100644 --- a/src/main/java/xyz/jpenilla/chesscraft/ChessBoard.java +++ b/src/main/java/xyz/jpenilla/chesscraft/ChessBoard.java @@ -21,6 +21,7 @@ import java.nio.file.Path; import java.util.Objects; import java.util.function.Consumer; +import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.NamespacedKey; import org.bukkit.World; @@ -37,6 +38,7 @@ public final class ChessBoard { // southwest corner pos private final Vec3 loc; private final CardinalDirection facing; + private final int scale; private final String name; private final ChessCraft plugin; private final NamespacedKey worldKey; @@ -49,6 +51,7 @@ public ChessBoard( final String name, final Vec3 loc, final CardinalDirection facing, + final int scale, final NamespacedKey world, final Path stockfishPath ) { @@ -56,6 +59,7 @@ public ChessBoard( this.name = name; this.loc = loc; this.facing = facing; + this.scale = scale; this.worldKey = world; this.stockfishPath = stockfishPath; this.pieceHandler = plugin.config().pieces().createHandler(); @@ -81,15 +85,19 @@ public CardinalDirection facing() { return this.facing; } + public int scale() { + return this.scale; + } + public Vec3 toWorld(final BoardPosition boardPosition) { return this.toWorld0(rotate(boardPosition, this.facing.radians())); } private Vec3 toWorld0(final BoardPosition boardPosition) { return new Vec3( - this.loc.x() + boardPosition.file(), + this.loc.x() + boardPosition.file() * this.scale, this.loc.y(), - this.loc.z() + boardPosition.rank() - 7 + this.loc.z() + (boardPosition.rank() - 7) * this.scale ); } @@ -100,8 +108,8 @@ private BoardPosition toBoard(final int worldX, final int worldZ) { private BoardPosition toBoard0(final int worldX, final int worldZ) { return new BoardPosition( - 7 + worldZ - this.loc.z(), - worldX - this.loc.x() + (7 * this.scale + worldZ - this.loc.z()) / this.scale, + (worldX - this.loc.x()) / this.scale ); } @@ -145,9 +153,9 @@ public boolean handleInteract(final Player player, final int x, final int y, fin } private boolean contains(final int x, final int y, final int z) { - return x <= this.loc.x() + 7 && x >= this.loc.x() - && z >= this.loc.z() - 7 && z <= this.loc.z() - && y >= this.loc.y() - 1 && y <= this.loc.y() + 1; + return x <= this.loc.x() + 7 * this.scale + this.scale - 1 && x >= this.loc.x() + && z >= this.loc.z() - 7 * this.scale && z <= this.loc.z() + this.scale - 1 + && y >= this.loc.y() - 1 && y <= this.loc.y() + this.scale * 2 - 1; } public NamespacedKey worldKey() { @@ -207,17 +215,32 @@ public void applyCheckerboard( this.forEachPosition(pos -> { final Vec3 loc = this.toWorld(pos); final Material material = (pos.rank() * 7 + pos.file()) % 2 == 0 ? white : black; - world.setType(loc.x(), loc.y() - 1, loc.z(), material); + for (int i = 0; i < this.scale; i++) { + for (int h = 0; h < this.scale; h++) { + world.setType(loc.x() + i, loc.y() - 1, loc.z() + h, material); + } + } }); if (border == null) { return; } - for (int dx = -1; dx <= 8; dx++) { - for (int dz = -1; dz <= 8; dz++) { - if (dx == -1 || dz == -1 || dx == 8 || dz == 8) { - world.setType(this.loc.x() + dx, this.loc.y() - 1, this.loc.z() - dz, border); - } - } + + this.applyBorder(world, border); + } + + private void applyBorder(final World world, final Material border) { + final int minX = this.loc.x() - 1; + final int maxZ = this.loc.z() + this.scale; + final int minZ = this.loc.z() - this.scale * 7 - 1; + final int maxX = this.loc.x() + this.scale * 8; + + for (int x = minX; x <= maxX; x++) { + world.setType(new Location(world, x, this.loc.y() - 1, minZ), border); + world.setType(new Location(world, x, this.loc.y() - 1, maxZ), border); + } + for (int z = minZ; z <= maxZ; z++) { + world.setType(new Location(world, minX, this.loc.y() - 1, z), border); + world.setType(new Location(world, maxX, this.loc.y() - 1, z), border); } } diff --git a/src/main/java/xyz/jpenilla/chesscraft/ChessGame.java b/src/main/java/xyz/jpenilla/chesscraft/ChessGame.java index 8e7d669..8842316 100644 --- a/src/main/java/xyz/jpenilla/chesscraft/ChessGame.java +++ b/src/main/java/xyz/jpenilla/chesscraft/ChessGame.java @@ -164,19 +164,44 @@ public void displayParticles() { } private void blockParticles(final Player player, final Vec3 block, final Color particleColor) { - for (double dx = 0.2; dx <= 0.8; dx += 0.2) { - for (double dz = 0.2; dz <= 0.8; dz += 0.2) { - if (dx == 0.2 || dz == 0.2 || dx == 0.8 || dz == 0.8) { - new ParticleBuilder(Particle.REDSTONE) - .count(1) - .color(particleColor) - .offset(0, 0, 0) - .location(player.getWorld(), block.x() + dx, block.y(), block.z() + dz) - .receivers(player) - .spawn(); - } + // 0.02 buffer around pieces + final double min = 0.18D * this.board.scale(); + final double max = 0.82D * this.board.scale(); + final double minX = block.x() + min; + final double minZ = block.z() + min; + final double maxX = block.x() + max; + final double maxZ = block.z() + max; + + boolean handledMaxCorner = false; + for (double x = minX; x <= maxX; x += 0.2) { + particle(player, particleColor, x, block.y(), minZ); + particle(player, particleColor, x, block.y(), maxZ); + if (x == maxX) { + handledMaxCorner = true; } } + + // handle maximum corners if we didn't before (distance indivisible by increment) + if (!handledMaxCorner) { + particle(player, particleColor, maxX, block.y(), minZ); + particle(player, particleColor, maxX, block.y(), maxZ); + } + + // skip corners this time + for (double z = minZ + 0.2; z <= maxZ - 0.2; z += 0.2) { + particle(player, particleColor, minX, block.y(), z); + particle(player, particleColor, maxX, block.y(), z); + } + } + + private static void particle(final Player player, final Color particleColor, final double x, final double y, final double z) { + new ParticleBuilder(Particle.REDSTONE) + .count(1) + .color(particleColor) + .offset(0, 0, 0) + .location(player.getWorld(), x, y, z) + .receivers(player) + .spawn(); } @Override diff --git a/src/main/java/xyz/jpenilla/chesscraft/PieceHandler.java b/src/main/java/xyz/jpenilla/chesscraft/PieceHandler.java index e8b1389..926e492 100644 --- a/src/main/java/xyz/jpenilla/chesscraft/PieceHandler.java +++ b/src/main/java/xyz/jpenilla/chesscraft/PieceHandler.java @@ -63,7 +63,7 @@ public DisplayEntity(final PieceOptions.DisplayEntity options) { public void applyToWorld(final ChessBoard board, final BoardStateHolder game, final World world) { board.forEachPosition(boardPosition -> { final Vec3 pos = board.toWorld(boardPosition); - removePieceAt(world, pos); + removePieceAt(world, board, pos); final Piece piece = game.piece(boardPosition); if (piece == null) { @@ -71,13 +71,13 @@ public void applyToWorld(final ChessBoard board, final BoardStateHolder game, fi } world.spawn(pos.toLocation(world), ItemDisplay.class, itemDisplay -> { itemDisplay.setTransformation(new Transformation( - // center on block - new Vector3f(0.5f, 0, 0.5f), + // center + new Vector3f(0.5f * board.scale(), 0, 0.5f * board.scale()), // flip upwards new AxisAngle4f((float) Math.toRadians(90.0D), 1, 0, 0), // scale - new Vector3f(0.5f), - // piece rotation + new Vector3f(0.5f * board.scale()), + // rotate new AxisAngle4f((float) Math.toRadians(rotation(board.facing(), piece)), 0, 0, 1) )); itemDisplay.setItemDisplayTransform(ItemDisplay.ItemDisplayTransform.FIXED); @@ -85,12 +85,12 @@ public void applyToWorld(final ChessBoard board, final BoardStateHolder game, fi itemDisplay.setInvulnerable(true); itemDisplay.getPersistentDataContainer().set(BoardManager.PIECE_KEY, PersistentDataType.STRING, board.name()); }); - world.spawn(new Location(world, pos.x() + 0.5, pos.y(), pos.z() + 0.5), Interaction.class, interaction -> { + world.spawn(new Location(world, pos.x() + 0.5 * board.scale(), pos.y(), pos.z() + 0.5 * board.scale()), Interaction.class, interaction -> { interaction.setInvulnerable(true); interaction.getPersistentDataContainer().set(BoardManager.PIECE_KEY, PersistentDataType.STRING, board.name()); interaction.setResponsive(true); - interaction.setInteractionHeight((float) this.options.height(piece.type())); - interaction.setInteractionWidth(0.5f); + interaction.setInteractionHeight((float) this.options.height(piece.type()) * board.scale()); + interaction.setInteractionWidth(0.5f * board.scale()); }); }); } @@ -105,10 +105,10 @@ private static float rotation(final CardinalDirection facing, final Piece piece) @Override public void removeFromWorld(final ChessBoard board, final World world) { - board.forEachPosition(pos -> removePieceAt(world, board.toWorld(pos))); + board.forEachPosition(pos -> removePieceAt(world, board, board.toWorld(pos))); } - private static void removePieceAt(final World world, final Vec3 pos) { + private static void removePieceAt(final World world, final ChessBoard board, final Vec3 pos) { final List entities = new ArrayList<>(); entities.addAll(world.getNearbyEntities( pos.toLocation(world), @@ -118,7 +118,7 @@ private static void removePieceAt(final World world, final Vec3 pos) { e -> e instanceof Display )); entities.addAll(world.getNearbyEntities( - new Location(world, pos.x() + 0.5, pos.y(), pos.z() + 0.5), + new Location(world, pos.x() + 0.5 * board.scale(), pos.y(), pos.z() + 0.5 * board.scale()), 0.25, 0.5, 0.25, diff --git a/src/main/java/xyz/jpenilla/chesscraft/command/Commands.java b/src/main/java/xyz/jpenilla/chesscraft/command/Commands.java index 0c651ce..8a12d0f 100644 --- a/src/main/java/xyz/jpenilla/chesscraft/command/Commands.java +++ b/src/main/java/xyz/jpenilla/chesscraft/command/Commands.java @@ -99,6 +99,7 @@ public void register() { this.mgr.command(chess.literal("create_board") .argument(StringArgument.single("name")) .argument(EnumArgument.of(CardinalDirection.class, "facing")) + .argument(IntegerArgument.optional("scale", 1)) .senderType(Player.class) .permission("chesscraft.command.create_board") .handler(this::createBoard)); @@ -207,7 +208,8 @@ private void createBoard(final CommandContext ctx) { name, sender.getWorld(), Vec3.fromLocation(sender.getLocation()), - ctx.get("facing") + ctx.get("facing"), + ctx.get("scale") ); sender.sendMessage(this.messages().boardCreated(name)); }