From f2c6cccb0a7a314edda791d3a6d0942a14be3feb Mon Sep 17 00:00:00 2001 From: TGGamesYT Date: Sun, 22 Mar 2026 18:04:53 +0100 Subject: [PATCH] chess --- build.gradle | 1 + .../tggamesyt/szar/client/ChessScreen.java | 294 +++++++++++++++++ .../dev/tggamesyt/szar/client/SzarClient.java | 29 ++ .../java/dev/tggamesyt/szar/ChessBlock.java | 72 +++- .../dev/tggamesyt/szar/ChessBlockEntity.java | 308 +++++++++++++++++- src/main/java/dev/tggamesyt/szar/Szar.java | 32 ++ .../assets/szar/blockstates/chess.json | 5 + .../resources/assets/szar/lang/en_us.json | 3 +- .../assets/szar/models/block/chess.json | 24 ++ .../assets/szar/models/item/chess.json | 3 + .../assets/szar/textures/block/chess.png | Bin 0 -> 562 bytes .../assets/szar/textures/gui/chess_bb.png | Bin 0 -> 390 bytes .../assets/szar/textures/gui/chess_bk.png | Bin 0 -> 438 bytes .../assets/szar/textures/gui/chess_bn.png | Bin 0 -> 398 bytes .../assets/szar/textures/gui/chess_bp.png | Bin 0 -> 337 bytes .../assets/szar/textures/gui/chess_bq.png | Bin 0 -> 390 bytes .../assets/szar/textures/gui/chess_br.png | Bin 0 -> 379 bytes .../assets/szar/textures/gui/chess_wb.png | Bin 0 -> 376 bytes .../assets/szar/textures/gui/chess_wk.png | Bin 0 -> 403 bytes .../assets/szar/textures/gui/chess_wn.png | Bin 0 -> 381 bytes .../assets/szar/textures/gui/chess_wp.png | Bin 0 -> 313 bytes .../assets/szar/textures/gui/chess_wq.png | Bin 0 -> 378 bytes .../assets/szar/textures/gui/chess_wr.png | Bin 0 -> 378 bytes .../minecraft/tags/blocks/mineable/axe.json | 3 +- .../data/szar/loot_tables/blocks/chess.json | 14 + .../resources/data/szar/recipes/chess.json | 23 ++ 26 files changed, 806 insertions(+), 5 deletions(-) create mode 100644 src/client/java/dev/tggamesyt/szar/client/ChessScreen.java create mode 100644 src/main/resources/assets/szar/blockstates/chess.json create mode 100644 src/main/resources/assets/szar/models/block/chess.json create mode 100644 src/main/resources/assets/szar/models/item/chess.json create mode 100644 src/main/resources/assets/szar/textures/block/chess.png create mode 100644 src/main/resources/assets/szar/textures/gui/chess_bb.png create mode 100644 src/main/resources/assets/szar/textures/gui/chess_bk.png create mode 100644 src/main/resources/assets/szar/textures/gui/chess_bn.png create mode 100644 src/main/resources/assets/szar/textures/gui/chess_bp.png create mode 100644 src/main/resources/assets/szar/textures/gui/chess_bq.png create mode 100644 src/main/resources/assets/szar/textures/gui/chess_br.png create mode 100644 src/main/resources/assets/szar/textures/gui/chess_wb.png create mode 100644 src/main/resources/assets/szar/textures/gui/chess_wk.png create mode 100644 src/main/resources/assets/szar/textures/gui/chess_wn.png create mode 100644 src/main/resources/assets/szar/textures/gui/chess_wp.png create mode 100644 src/main/resources/assets/szar/textures/gui/chess_wq.png create mode 100644 src/main/resources/assets/szar/textures/gui/chess_wr.png create mode 100644 src/main/resources/data/szar/loot_tables/blocks/chess.json create mode 100644 src/main/resources/data/szar/recipes/chess.json diff --git a/build.gradle b/build.gradle index 8dd736d..d148f3d 100644 --- a/build.gradle +++ b/build.gradle @@ -52,6 +52,7 @@ dependencies { modImplementation("com.terraformersmc:modmenu:${project.modmenu_version}") implementation 'com.github.bhlangonijr:chesslib:1.3.6' + include 'com.github.bhlangonijr:chesslib:1.3.3' } processResources { diff --git a/src/client/java/dev/tggamesyt/szar/client/ChessScreen.java b/src/client/java/dev/tggamesyt/szar/client/ChessScreen.java new file mode 100644 index 0000000..5041380 --- /dev/null +++ b/src/client/java/dev/tggamesyt/szar/client/ChessScreen.java @@ -0,0 +1,294 @@ +package dev.tggamesyt.szar.client; + +import com.github.bhlangonijr.chesslib.Board; +import com.github.bhlangonijr.chesslib.Piece; +import com.github.bhlangonijr.chesslib.Side; +import com.github.bhlangonijr.chesslib.Square; +import com.github.bhlangonijr.chesslib.move.Move; +import dev.tggamesyt.szar.ChessBlockEntity; +import dev.tggamesyt.szar.Szar; +import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; +import net.fabricmc.fabric.api.networking.v1.PacketByteBufs; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.network.PacketByteBuf; +import net.minecraft.text.Text; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; + +import java.util.*; + +public class ChessScreen extends Screen { + + // Board background texture + private static final Identifier BOARD_TEX = + new Identifier("szar", "textures/gui/chess_board.png"); + + // Piece textures — one per piece type + // Naming: chess_wp.png (white pawn), chess_bn.png (black knight) etc. + private static final Map PIECE_TEXTURES = new HashMap<>(); + + static { + String[] colors = {"w", "b"}; + String[] types = {"p", "n", "b", "r", "q", "k"}; + Piece[] whitePieces = {Piece.WHITE_PAWN, Piece.WHITE_KNIGHT, Piece.WHITE_BISHOP, + Piece.WHITE_ROOK, Piece.WHITE_QUEEN, Piece.WHITE_KING}; + Piece[] blackPieces = {Piece.BLACK_PAWN, Piece.BLACK_KNIGHT, Piece.BLACK_BISHOP, + Piece.BLACK_ROOK, Piece.BLACK_QUEEN, Piece.BLACK_KING}; + + for (int i = 0; i < 6; i++) { + PIECE_TEXTURES.put(whitePieces[i], + new Identifier("szar", "textures/gui/chess_w" + types[i] + ".png")); + PIECE_TEXTURES.put(blackPieces[i], + new Identifier("szar", "textures/gui/chess_b" + types[i] + ".png")); + } + } + + private static final int CELL = 24; // pixels per square + private static final int BOARD_PIXELS = CELL * 8; // 192 + private static final int GUI_WIDTH = BOARD_PIXELS + 16; + private static final int GUI_HEIGHT = BOARD_PIXELS + 32; + private static final int BOARD_OFFSET_X = 8; + private static final int BOARD_OFFSET_Y = 8; + + // Colors + private static final int LIGHT_SQUARE = 0xFFF0D9B5; + private static final int DARK_SQUARE = 0xFFB58863; + private static final int SELECTED = 0xAA7FC97F; + private static final int VALID_MOVE = 0xAA7FC97F; + private static final int LAST_MOVE = 0xAA99CC66; + + private ChessBlockEntity.State state; + private final BlockPos blockPos; + private final UUID localPlayer; + private boolean isSpectator; + + private Square selectedSquare = null; + private Set validMoveTargets = new HashSet<>(); + + public ChessScreen(BlockPos pos, ChessBlockEntity.State state) { + super(Text.literal("Chess")); + this.blockPos = pos; + this.state = state; + this.localPlayer = MinecraftClient.getInstance().player.getUuid(); + this.isSpectator = state.isSpectator; + } + + public void updateState(ChessBlockEntity.State newState) { + this.state = newState; + this.isSpectator = newState.isSpectator; + // Clear selection on state update + selectedSquare = null; + validMoveTargets.clear(); + } + + @Override + public void render(DrawContext context, int mouseX, int mouseY, float delta) { + renderBackground(context); + + int bx = (this.width - GUI_WIDTH) / 2 + BOARD_OFFSET_X; + int by = (this.height - GUI_HEIGHT) / 2 + BOARD_OFFSET_Y; + + Board board = new Board(); + board.loadFromFen(state.fen); + + // Draw squares + for (int rank = 0; rank < 8; rank++) { + for (int file = 0; file < 8; file++) { + int drawFile = state.isWhite ? file : (7 - file); + int drawRank = state.isWhite ? (7 - rank) : rank; + + int sx = bx + drawFile * CELL; + int sy = by + drawRank * CELL; + + boolean light = (file + rank) % 2 == 0; + int squareColor = light ? LIGHT_SQUARE : DARK_SQUARE; + + // Check if selected or valid move target + Square sq = getSquare(file, rank); + if (sq != null && sq.equals(selectedSquare)) { + squareColor = SELECTED; + } else if (sq != null && validMoveTargets.contains(sq)) { + squareColor = VALID_MOVE; + } + + context.fill(sx, sy, sx + CELL, sy + CELL, squareColor); + } + } + + // Draw pieces + for (int rank = 0; rank < 8; rank++) { + for (int file = 0; file < 8; file++) { + Square sq = getSquare(file, rank); + if (sq == null) continue; + + Piece piece = board.getPiece(sq); + if (piece == null || piece == Piece.NONE) continue; + + Identifier tex = PIECE_TEXTURES.get(piece); + if (tex == null) continue; + + int drawFile = state.isWhite ? file : (7 - file); + int drawRank = state.isWhite ? (7 - rank) : rank; + + int sx = bx + drawFile * CELL + 1; + int sy = by + drawRank * CELL + 1; + + context.drawTexture(tex, sx, sy, 0, 0, + CELL - 2, CELL - 2, CELL - 2, CELL - 2); + } + } + + // Draw rank/file labels + for (int i = 0; i < 8; i++) { + String fileLabel = String.valueOf((char)('a' + (state.isWhite ? i : 7 - i))); + String rankLabel = String.valueOf(state.isWhite ? 8 - i : i + 1); + context.drawText(this.textRenderer, fileLabel, + bx + i * CELL + CELL / 2 - 3, + by + BOARD_PIXELS + 2, 0xFFFFFF, true); + context.drawText(this.textRenderer, rankLabel, + bx - 7, by + i * CELL + CELL / 2 - 4, + 0xFFFFFF, true); + } + + // Status text + String status; + if (state.winner == 1) status = "§fWhite wins!"; + else if (state.winner == 2) status = "§8Black wins!"; + else if (state.winner == 3) status = "§7Draw! " + state.statusMessage; + else if (!state.statusMessage.isEmpty()) status = "§e" + state.statusMessage; + else if (isSpectator) status = "§7Spectating..."; + else { + Side sideToMove = board.getSideToMove(); + boolean myTurn = (sideToMove == Side.WHITE && localPlayer.equals(state.player1)) + || (sideToMove == Side.BLACK && localPlayer.equals(state.player2)); + status = myTurn ? "§aYour turn!" : "§7Opponent's turn..."; + } + + int statusX = (this.width - GUI_WIDTH) / 2 + GUI_WIDTH / 2 + - this.textRenderer.getWidth(status) / 2; + int statusY = (this.height - GUI_HEIGHT) / 2 + BOARD_OFFSET_Y + + BOARD_PIXELS + 16; + context.drawTextWithShadow(this.textRenderer, Text.literal(status), + statusX, statusY, 0xFFFFFF); + + super.render(context, mouseX, mouseY, delta); + } + + @Override + public boolean mouseClicked(double mouseX, double mouseY, int button) { + if (isSpectator || button != 0 || state.winner != 0) + return super.mouseClicked(mouseX, mouseY, button); + + Board board = new Board(); + board.loadFromFen(state.fen); + + Side sideToMove = board.getSideToMove(); + boolean myTurn = (sideToMove == Side.WHITE && localPlayer.equals(state.player1)) + || (sideToMove == Side.BLACK && localPlayer.equals(state.player2)); + if (!myTurn) return super.mouseClicked(mouseX, mouseY, button); + + int bx = (this.width - GUI_WIDTH) / 2 + BOARD_OFFSET_X; + int by = (this.height - GUI_HEIGHT) / 2 + BOARD_OFFSET_Y; + + // Check if click is on the board + if (mouseX < bx || mouseX >= bx + BOARD_PIXELS + || mouseY < by || mouseY >= by + BOARD_PIXELS) + return super.mouseClicked(mouseX, mouseY, button); + + int drawFile = (int)((mouseX - bx) / CELL); + int drawRank = (int)((mouseY - by) / CELL); + + // Convert draw coords back to board coords + int file = state.isWhite ? drawFile : (7 - drawFile); + int rank = state.isWhite ? (7 - drawRank) : drawRank; + + Square clicked = getSquare(file, rank); + if (clicked == null) return super.mouseClicked(mouseX, mouseY, button); + + if (selectedSquare == null) { + // Select a piece + Piece piece = board.getPiece(clicked); + if (piece != Piece.NONE) { + boolean ownPiece = (sideToMove == Side.WHITE && piece.getPieceSide() == Side.WHITE) + || (sideToMove == Side.BLACK && piece.getPieceSide() == Side.BLACK); + if (ownPiece) { + selectedSquare = clicked; + // Calculate valid moves for this piece + validMoveTargets.clear(); + for (Move move : board.legalMoves()) { + if (move.getFrom().equals(clicked)) { + validMoveTargets.add(move.getTo()); + } + } + } + } + } else { + if (clicked.equals(selectedSquare)) { + // Deselect + selectedSquare = null; + validMoveTargets.clear(); + } else if (validMoveTargets.contains(clicked)) { + // Make the move + String uci = selectedSquare.value().toLowerCase() + + clicked.value().toLowerCase(); + + // Auto-promote to queen if pawn reaches last rank + Piece moving = board.getPiece(selectedSquare); + if (moving == Piece.WHITE_PAWN && clicked.getRank().ordinal() == 7) { + uci += "q"; + } else if (moving == Piece.BLACK_PAWN && clicked.getRank().ordinal() == 0) { + uci += "q"; + } + + sendMove(uci); + selectedSquare = null; + validMoveTargets.clear(); + } else { + // Try selecting a different piece + Piece piece = board.getPiece(clicked); + if (piece != Piece.NONE) { + boolean ownPiece = (sideToMove == Side.WHITE && piece.getPieceSide() == Side.WHITE) + || (sideToMove == Side.BLACK && piece.getPieceSide() == Side.BLACK); + if (ownPiece) { + selectedSquare = clicked; + validMoveTargets.clear(); + for (Move move : board.legalMoves()) { + if (move.getFrom().equals(clicked)) { + validMoveTargets.add(move.getTo()); + } + } + } else { + selectedSquare = null; + validMoveTargets.clear(); + } + } else { + selectedSquare = null; + validMoveTargets.clear(); + } + } + } + + return true; + } + + private Square getSquare(int file, int rank) { + try { + String name = String.valueOf((char)('A' + file)) + (rank + 1); + return Square.valueOf(name); + } catch (Exception e) { + return null; + } + } + + private void sendMove(String uci) { + PacketByteBuf buf = PacketByteBufs.create(); + buf.writeBlockPos(blockPos); + buf.writeString(uci); + ClientPlayNetworking.send(Szar.CHESS_MAKE_MOVE, buf); + } + + @Override + public boolean shouldPause() { return false; } +} \ No newline at end of file diff --git a/src/client/java/dev/tggamesyt/szar/client/SzarClient.java b/src/client/java/dev/tggamesyt/szar/client/SzarClient.java index b753bd5..1813c32 100644 --- a/src/client/java/dev/tggamesyt/szar/client/SzarClient.java +++ b/src/client/java/dev/tggamesyt/szar/client/SzarClient.java @@ -90,6 +90,35 @@ public class SzarClient implements ClientModInitializer { ); @Override public void onInitializeClient() { + ClientPlayNetworking.registerGlobalReceiver(Szar.CHESS_OPEN_SCREEN, (client, handler, buf, sender) -> { + BlockPos pos = buf.readBlockPos(); + ChessBlockEntity.State state = ChessBlockEntity.readStateFromBuf(buf); + client.execute(() -> { + if (client.currentScreen instanceof ChessScreen existing) { + existing.updateState(state); + } else { + client.setScreen(new ChessScreen(pos, state)); + } + }); + }); + + ClientPlayNetworking.registerGlobalReceiver(Szar.CHESS_STATE_SYNC, (client, handler, buf, sender) -> { + BlockPos pos = buf.readBlockPos(); + ChessBlockEntity.State state = ChessBlockEntity.readStateFromBuf(buf); + client.execute(() -> { + if (client.currentScreen instanceof ChessScreen screen) { + screen.updateState(state); + } + }); + }); + + ClientPlayNetworking.registerGlobalReceiver(Szar.CHESS_CLOSE_SCREEN, (client, handler, buf, sender) -> { + client.execute(() -> { + if (client.currentScreen instanceof ChessScreen) { + client.setScreen(null); + } + }); + }); // TTT ClientPlayNetworking.registerGlobalReceiver(Szar.TTT_OPEN_SCREEN, (client, handler, buf, sender) -> { BlockPos pos = buf.readBlockPos(); diff --git a/src/main/java/dev/tggamesyt/szar/ChessBlock.java b/src/main/java/dev/tggamesyt/szar/ChessBlock.java index 184a70f..cc34db0 100644 --- a/src/main/java/dev/tggamesyt/szar/ChessBlock.java +++ b/src/main/java/dev/tggamesyt/szar/ChessBlock.java @@ -1,4 +1,74 @@ package dev.tggamesyt.szar; -public class ChessBlock { +import net.minecraft.block.*; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.block.entity.BlockEntityTicker; +import net.minecraft.block.entity.BlockEntityType; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.util.ActionResult; +import net.minecraft.util.Hand; +import net.minecraft.util.hit.BlockHitResult; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.shape.VoxelShape; +import net.minecraft.util.shape.VoxelShapes; +import net.minecraft.world.BlockView; +import net.minecraft.world.World; +import org.jetbrains.annotations.Nullable; + +public class ChessBlock extends BlockWithEntity { + + private static final VoxelShape SHAPE = VoxelShapes.union( + VoxelShapes.cuboid(0f, 0f, 0f, 1f, 0.75f, 1f) + ); + + public ChessBlock(Settings settings) { super(settings); } + + @Override + public BlockRenderType getRenderType(BlockState state) { + return BlockRenderType.MODEL; + } + + @Override + public VoxelShape getOutlineShape(BlockState state, BlockView world, + BlockPos pos, ShapeContext ctx) { return SHAPE; } + + @Override + public VoxelShape getCollisionShape(BlockState state, BlockView world, + BlockPos pos, ShapeContext ctx) { return SHAPE; } + + @Nullable + @Override + public BlockEntity createBlockEntity(BlockPos pos, BlockState state) { + return new ChessBlockEntity(pos, state); + } + + @Override + public ActionResult onUse(BlockState state, World world, BlockPos pos, + PlayerEntity player, Hand hand, BlockHitResult hit) { + if (world.isClient) return ActionResult.SUCCESS; + if (!(player instanceof ServerPlayerEntity sp)) return ActionResult.PASS; + if (!(world.getBlockEntity(pos) instanceof ChessBlockEntity be)) return ActionResult.PASS; + be.handlePlayerJoin(sp, pos); + return ActionResult.SUCCESS; + } + + @Override + public BlockEntityTicker getTicker( + World world, BlockState state, BlockEntityType type) { + if (world.isClient) return null; + return type == Szar.CHESS_ENTITY + ? (w, pos, s, be) -> ChessBlockEntity.tick(w, pos, s, (ChessBlockEntity) be) + : null; + } + + @Override + public void onBreak(World world, BlockPos pos, BlockState state, PlayerEntity player) { + if (!world.isClient && world.getBlockEntity(pos) instanceof ChessBlockEntity be) { + be.closeScreenForAll(world.getServer()); + if (be.player1 != null) Szar.chessActivePlayers.remove(be.player1); + if (be.player2 != null) Szar.chessActivePlayers.remove(be.player2); + } + super.onBreak(world, pos, state, player); + } } diff --git a/src/main/java/dev/tggamesyt/szar/ChessBlockEntity.java b/src/main/java/dev/tggamesyt/szar/ChessBlockEntity.java index b54ee5e..69ccc84 100644 --- a/src/main/java/dev/tggamesyt/szar/ChessBlockEntity.java +++ b/src/main/java/dev/tggamesyt/szar/ChessBlockEntity.java @@ -1,4 +1,308 @@ package dev.tggamesyt.szar; -public class ChessBlockEntity { -} +import com.github.bhlangonijr.chesslib.Board; +import com.github.bhlangonijr.chesslib.Side; +import com.github.bhlangonijr.chesslib.move.Move; +import com.github.bhlangonijr.chesslib.Square; +import net.fabricmc.fabric.api.networking.v1.PacketByteBufs; +import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; +import net.minecraft.block.BlockState; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.network.PacketByteBuf; +import net.minecraft.network.packet.s2c.play.BlockEntityUpdateS2CPacket; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.text.Text; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +public class ChessBlockEntity extends BlockEntity { + public String DEFAULT_POSITION = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; + // FEN string stores full board state — chesslib handles all logic + public String fen = DEFAULT_POSITION; + public UUID player1 = null; // white + public UUID player2 = null; // black + public int winner = 0; // 0=ongoing, 1=white, 2=black, 3=draw + public String statusMessage = ""; + public Set spectators = new HashSet<>(); + public int resetTimer = -1; + + public ChessBlockEntity(BlockPos pos, BlockState state) { + super(Szar.CHESS_ENTITY, pos, state); + } + + public static void tick(World world, BlockPos pos, BlockState state, + ChessBlockEntity entity) { + if (!world.isClient && entity.resetTimer > 0) { + entity.resetTimer--; + if (entity.resetTimer == 0) { + entity.resetTimer = -1; + var server = ((net.minecraft.server.world.ServerWorld) world).getServer(); + entity.resetGame(server); + entity.syncToPlayers(server); + } + } + } + + public void handlePlayerJoin(ServerPlayerEntity player, BlockPos pos) { + UUID uuid = player.getUuid(); + + BlockPos activePos = Szar.chessActivePlayers.get(uuid); + if (activePos != null && !activePos.equals(pos)) { + player.sendMessage(Text.literal("§cYou are already in a chess game elsewhere!"), true); + return; + } + + if (uuid.equals(player1) || uuid.equals(player2)) { + if (player1 != null && player2 != null) { + openScreen(player); + } else { + // Leave if waiting + if (uuid.equals(player1)) { + Szar.chessActivePlayers.remove(player1); + player1 = null; + } else { + Szar.chessActivePlayers.remove(player2); + player2 = null; + } + player.sendMessage(Text.literal("§7Left the game."), true); + markDirty(); + } + return; + } + + if (player1 == null) { + player1 = uuid; + Szar.chessActivePlayers.put(uuid, pos); + player.sendMessage(Text.literal("§aYou are §fWhite§a! Waiting for second player..."), true); + markDirty(); + return; + } + + if (player2 == null && !uuid.equals(player1)) { + player2 = uuid; + Szar.chessActivePlayers.put(uuid, pos); + player.sendMessage(Text.literal("§aYou are §8Black§a! Game starting!"), true); + ServerPlayerEntity p1 = player.getServer().getPlayerManager().getPlayer(player1); + if (p1 != null) p1.sendMessage(Text.literal("§aSecond player joined! Your turn (White)!"), true); + openScreenForBoth(player); + markDirty(); + return; + } + + if (player1 != null && player2 != null) { + spectators.add(uuid); + player.sendMessage(Text.literal("§7Spectating the match..."), true); + openScreen(player); + markDirty(); + } + } + + public void handleMove(ServerPlayerEntity player, String uciMove) { + if (winner != 0) return; + + UUID uuid = player.getUuid(); + Board board = new Board(); + board.loadFromFen(fen); + + // Check it's the right player's turn + Side sideToMove = board.getSideToMove(); + boolean isWhite = uuid.equals(player1); + boolean isBlack = uuid.equals(player2); + if (!isWhite && !isBlack) return; + if (sideToMove == Side.WHITE && !isWhite) { + player.sendMessage(Text.literal("§cNot your turn!"), true); + return; + } + if (sideToMove == Side.BLACK && !isBlack) { + player.sendMessage(Text.literal("§cNot your turn!"), true); + return; + } + + // Validate and apply move + try { + Square from = Square.valueOf(uciMove.substring(0, 2).toUpperCase()); + Square to = Square.valueOf(uciMove.substring(2, 4).toUpperCase()); + + // Handle promotion — default to queen + String promotion = uciMove.length() > 4 ? uciMove.substring(4) : ""; + Move move; + if (!promotion.isEmpty()) { + com.github.bhlangonijr.chesslib.Piece promoPiece = + sideToMove == Side.WHITE + ? com.github.bhlangonijr.chesslib.Piece.WHITE_QUEEN + : com.github.bhlangonijr.chesslib.Piece.BLACK_QUEEN; + move = new Move(from, to, promoPiece); + } else { + move = new Move(from, to); + } + + // Check move is legal + if (!board.legalMoves().contains(move)) { + player.sendMessage(Text.literal("§cIllegal move!"), true); + return; + } + + board.doMove(move); + fen = board.getFen(); + + // Check game end conditions + if (board.isMated()) { + winner = sideToMove == Side.WHITE ? 1 : 2; + statusMessage = (sideToMove == Side.WHITE ? "White" : "Black") + " wins by checkmate!"; + resetTimer = 100; + } else if (board.isStaleMate()) { + winner = 3; + statusMessage = "Draw by stalemate!"; + resetTimer = 100; + } else if (board.isInsufficientMaterial()) { + winner = 3; + statusMessage = "Draw by insufficient material!"; + resetTimer = 100; + } else if (board.isRepetition()) { + winner = 3; + statusMessage = "Draw by repetition!"; + resetTimer = 100; + } else if (board.isKingAttacked()) { + statusMessage = "Check!"; + } else { + statusMessage = ""; + } + + markDirty(); + syncToPlayers(player.getServer()); + + } catch (Exception e) { + player.sendMessage(Text.literal("§cInvalid move format!"), true); + } + } + + public void openScreenForBoth(ServerPlayerEntity joiner) { + openScreen(joiner); + ServerPlayerEntity p1 = joiner.getServer().getPlayerManager().getPlayer(player1); + if (p1 != null) openScreen(p1); + } + + public void openScreen(ServerPlayerEntity player) { + PacketByteBuf buf = PacketByteBufs.create(); + buf.writeBlockPos(this.pos); + writeStateToBuf(buf, player.getUuid()); + ServerPlayNetworking.send(player, Szar.CHESS_OPEN_SCREEN, buf); + } + + public void syncToPlayers(net.minecraft.server.MinecraftServer server) { + sendToPlayer(server, player1); + sendToPlayer(server, player2); + for (UUID uuid : spectators) sendToPlayer(server, uuid); + } + + private void sendToPlayer(net.minecraft.server.MinecraftServer server, UUID uuid) { + if (uuid == null) return; + ServerPlayerEntity p = server.getPlayerManager().getPlayer(uuid); + if (p == null) return; + PacketByteBuf buf = PacketByteBufs.create(); + buf.writeBlockPos(this.pos); + writeStateToBuf(buf, uuid); + ServerPlayNetworking.send(p, Szar.CHESS_STATE_SYNC, buf); + } + + public void writeStateToBuf(PacketByteBuf buf, UUID viewerUuid) { + buf.writeString(fen); + buf.writeBoolean(player1 != null); + if (player1 != null) buf.writeUuid(player1); + buf.writeBoolean(player2 != null); + if (player2 != null) buf.writeUuid(player2); + buf.writeInt(winner); + buf.writeString(statusMessage); + boolean isSpectator = viewerUuid != null + && !viewerUuid.equals(player1) + && !viewerUuid.equals(player2); + buf.writeBoolean(isSpectator); + // Is this viewer playing white or black + boolean isWhite = viewerUuid != null && viewerUuid.equals(player1); + buf.writeBoolean(isWhite); + } + + public static State readStateFromBuf(PacketByteBuf buf) { + State s = new State(); + s.fen = buf.readString(); + if (buf.readBoolean()) s.player1 = buf.readUuid(); + if (buf.readBoolean()) s.player2 = buf.readUuid(); + s.winner = buf.readInt(); + s.statusMessage = buf.readString(); + s.isSpectator = buf.readBoolean(); + s.isWhite = buf.readBoolean(); + return s; + } + + public static class State { + public String fen; + public UUID player1, player2; + public int winner; + public String statusMessage; + public boolean isSpectator; + public boolean isWhite; // true = viewing from white's perspective + } + + public void resetGame(net.minecraft.server.MinecraftServer server) { + closeScreenForAll(server); + if (player1 != null) Szar.chessActivePlayers.remove(player1); + if (player2 != null) Szar.chessActivePlayers.remove(player2); + spectators.clear(); + fen = DEFAULT_POSITION; + player1 = null; + player2 = null; + winner = 0; + statusMessage = ""; + resetTimer = -1; + markDirty(); + } + + public void closeScreenForAll(net.minecraft.server.MinecraftServer server) { + closeScreenForPlayer(server, player1); + closeScreenForPlayer(server, player2); + for (UUID uuid : spectators) closeScreenForPlayer(server, uuid); + } + + private void closeScreenForPlayer(net.minecraft.server.MinecraftServer server, UUID uuid) { + if (uuid == null) return; + ServerPlayerEntity p = server.getPlayerManager().getPlayer(uuid); + if (p == null) return; + ServerPlayNetworking.send(p, Szar.CHESS_CLOSE_SCREEN, PacketByteBufs.empty()); + } + + @Override + public void writeNbt(NbtCompound nbt) { + super.writeNbt(nbt); + nbt.putString("Fen", fen); + if (player1 != null) nbt.putUuid("Player1", player1); + if (player2 != null) nbt.putUuid("Player2", player2); + nbt.putInt("Winner", winner); + nbt.putString("Status", statusMessage); + nbt.putInt("ResetTimer", resetTimer); + } + + @Override + public void readNbt(NbtCompound nbt) { + super.readNbt(nbt); + fen = nbt.getString("Fen"); + if (fen.isEmpty()) fen = DEFAULT_POSITION; + if (nbt.containsUuid("Player1")) player1 = nbt.getUuid("Player1"); + if (nbt.containsUuid("Player2")) player2 = nbt.getUuid("Player2"); + winner = nbt.getInt("Winner"); + statusMessage = nbt.getString("Status"); + resetTimer = nbt.getInt("ResetTimer"); + } + + @Override + public NbtCompound toInitialChunkDataNbt() { return createNbt(); } + + @Override + public BlockEntityUpdateS2CPacket toUpdatePacket() { + return BlockEntityUpdateS2CPacket.create(this); + } +} \ No newline at end of file diff --git a/src/main/java/dev/tggamesyt/szar/Szar.java b/src/main/java/dev/tggamesyt/szar/Szar.java index 63da258..7a93d53 100644 --- a/src/main/java/dev/tggamesyt/szar/Szar.java +++ b/src/main/java/dev/tggamesyt/szar/Szar.java @@ -386,6 +386,7 @@ public class Szar implements ModInitializer { entries.add(Szar.KEBAB); entries.add(Szar.TIC_TAC_TOE_ITEM); entries.add(Szar.CONNECT_FOUR_ITEM); + entries.add(Szar.CHESS_ITEM); // crazy weponary entries.add(Szar.BULLET_ITEM); entries.add(Szar.AK47); @@ -1347,6 +1348,15 @@ public class Szar implements ModInitializer { } }); }); + ServerPlayNetworking.registerGlobalReceiver(CHESS_MAKE_MOVE, (server, player, handler, buf, sender) -> { + BlockPos pos = buf.readBlockPos(); + String move = buf.readString(); // UCI format e.g. "e2e4" + server.execute(() -> { + if (player.getWorld().getBlockEntity(pos) instanceof ChessBlockEntity be) { + be.handleMove(player, move); + } + }); + }); } public static final Block TIC_TAC_TOE_BLOCK = Registry.register( @@ -1390,6 +1400,28 @@ public class Szar implements ModInitializer { public static final Identifier C4_CLOSE_SCREEN = new Identifier(MOD_ID, "c4_close"); public static final Map c4ActivePlayers = new java.util.HashMap<>(); + + public static final Block CHESS_BLOCK = Registry.register( + Registries.BLOCK, new Identifier(MOD_ID, "chess"), + new ChessBlock(AbstractBlock.Settings.create().strength(2f)) + ); + public static final BlockItem CHESS_ITEM = Registry.register( + Registries.ITEM, new Identifier(MOD_ID, "chess"), + new BlockItem(CHESS_BLOCK, new Item.Settings()) + ); + public static final BlockEntityType CHESS_ENTITY = + Registry.register( + Registries.BLOCK_ENTITY_TYPE, new Identifier(MOD_ID, "chess"), + FabricBlockEntityTypeBuilder.create(ChessBlockEntity::new, CHESS_BLOCK).build() + ); + + public static final Identifier CHESS_OPEN_SCREEN = new Identifier(MOD_ID, "chess_open"); + public static final Identifier CHESS_MAKE_MOVE = new Identifier(MOD_ID, "chess_move"); + public static final Identifier CHESS_STATE_SYNC = new Identifier(MOD_ID, "chess_sync"); + public static final Identifier CHESS_CLOSE_SCREEN = new Identifier(MOD_ID, "chess_close"); + + public static final Map chessActivePlayers = new java.util.HashMap<>(); + // Blocks public static final TrackerBlock TRACKER_BLOCK = Registry.register( Registries.BLOCK, new Identifier(MOD_ID, "tracker"), diff --git a/src/main/resources/assets/szar/blockstates/chess.json b/src/main/resources/assets/szar/blockstates/chess.json new file mode 100644 index 0000000..e1d0393 --- /dev/null +++ b/src/main/resources/assets/szar/blockstates/chess.json @@ -0,0 +1,5 @@ +{ + "variants": { + "": { "model": "szar:block/chess" } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/szar/lang/en_us.json b/src/main/resources/assets/szar/lang/en_us.json index d9635c1..7b2f230 100644 --- a/src/main/resources/assets/szar/lang/en_us.json +++ b/src/main/resources/assets/szar/lang/en_us.json @@ -188,5 +188,6 @@ "advancement.szar.backrooms.description": "Step into the Backrooms", "block.szar.tictactoe": "Tic Tac Toe", - "block.szar.connectfour": "Connect Four" + "block.szar.connectfour": "Connect Four", + "block.szar.chess": "Chess" } diff --git a/src/main/resources/assets/szar/models/block/chess.json b/src/main/resources/assets/szar/models/block/chess.json new file mode 100644 index 0000000..5eb41c8 --- /dev/null +++ b/src/main/resources/assets/szar/models/block/chess.json @@ -0,0 +1,24 @@ +{ + "parent": "block/block", + "format_version": "1.9.0", + "credit": "Made with Blockbench", + "textures": { + "0": "szar:block/chess", + "1": "szar:block/plank", + "particle": "szar:block/chess" + }, + "elements": [ + { + "from": [0, 0, 0], + "to": [16, 12, 16], + "faces": { + "north": {"uv": [0, 0, 16, 12], "texture": "#1"}, + "east": {"uv": [0, 0, 16, 12], "texture": "#1"}, + "south": {"uv": [0, 0, 16, 12], "texture": "#1"}, + "west": {"uv": [0, 0, 16, 12], "texture": "#1"}, + "up": {"uv": [0, 0, 16, 16], "texture": "#0"}, + "down": {"uv": [0, 0, 16, 16], "texture": "#1"} + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/szar/models/item/chess.json b/src/main/resources/assets/szar/models/item/chess.json new file mode 100644 index 0000000..1c28450 --- /dev/null +++ b/src/main/resources/assets/szar/models/item/chess.json @@ -0,0 +1,3 @@ +{ + "parent": "szar:block/chess" +} \ No newline at end of file diff --git a/src/main/resources/assets/szar/textures/block/chess.png b/src/main/resources/assets/szar/textures/block/chess.png new file mode 100644 index 0000000000000000000000000000000000000000..8af07ec184eaebfa8ca661df3bfb9e1e43bb2fca GIT binary patch literal 562 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sBugD~Uq{1quc4egmB5hW46K32*3xq68y`AMmI6}bgK)eHls47YguJQ{>uF6ifOi{PD zbs{}UK3djZt>nqvW6s4qD1-ZCERRDRmN*N_31y=g{ z<>lpi<;HsXMd|v6mX?v90`zGE;%B09k2gXakl<5wp<;IRwdJb`TMuUx6%m z$bfy}!dB_wtkK+KfX_^=-#x_lZ9=9Y=cSY_;4-1(@WsF+UGX;wV*3gr`n;E6S>F9+5|uG( kLeE$~puL`C5|uIf1Q3)=9Mibi3IG5A07*qoM6N<$g5XK4=Kufz literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/szar/textures/gui/chess_bk.png b/src/main/resources/assets/szar/textures/gui/chess_bk.png new file mode 100644 index 0000000000000000000000000000000000000000..720cfa0f02bdf5b385d81d821a1cff1d0171836f GIT binary patch literal 438 zcmeAS@N?(olHy`uVBq!ia0vp^S|H591|*LjJ{irxz*y_);usRad3D;}-ew1pmba6; z6;#xemNR=TaPV5d{7XUQ(ekF^j)(xb4`*A0Nvk&mNE7aev+Kzg=tJEvj4b zr+CHh?z3iY=aU%UEz+3q-Ng68V+$wtkKg|02yHd|m7teiGVgiBxfg*pnjnE)=B+bc z$Y$+{`?~zg2i+a=rL$dfdyW}0%gaAVjbu8wN#{cBbw_r=_h0vH+qv9I@(TAMwLs~` zpAMwo$-Qk9wxFk=_;2Umuu##G``<6@F+08K?AALulUMWxcFs%g|MD#PK*!GbhtKAT z2IyJg*$f=$Uu9E$+JLEdFm9yDlDXvKPO)T=u&**i@gLU4QoXolO0*$IX_FtNhfG z&;QIiZ%Nqp%qxsp5&sLM{YBi98DExVI?S~H-#8!B1JCY-j(zZ&j+ dNdY;+e*VY!rRViueF4S?gQu&X%Q~loCICE-*5m*H literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/szar/textures/gui/chess_bn.png b/src/main/resources/assets/szar/textures/gui/chess_bn.png new file mode 100644 index 0000000000000000000000000000000000000000..c534b8d6c843a501d7a6077f79644620896fedd8 GIT binary patch literal 398 zcmeAS@N?(olHy`uVBq!ia0vp^S|H591|*LjJ{irxz!>W3;usRa`F4gcSF?dgOZLmm zJx$sA4;=S4WUqg~%M!jz;Lf(CQxiFURyu0)Y+3F3tLJb{C1-Bo=GuANj6y^M)H{EB zzW!4qDY-bz#o@jQ@1^MLqE#~PFA^mdC?ERu?!1su=P8M^UG>L1as#e!ovxG5tT*Gi zQ#wQuWi|+E>LRIhVR;bDz6pbXFz#O4IeF(~rnn zJt?-H8?OVhIO2NorvtT5+-3AJ&XV_qOEN*>;=ro45Vh ptPa!?C|q@{r14wTB%qZqxVf#eZtoBMTLKJc22WQ%mvv4FO#le6xoZFb literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/szar/textures/gui/chess_bp.png b/src/main/resources/assets/szar/textures/gui/chess_bp.png new file mode 100644 index 0000000000000000000000000000000000000000..b90295d768481aabbcab80c91e2f73a34b8ee300 GIT binary patch literal 337 zcmV-X0j~auP)wN5M*t7hQ=`7!THoWwc1%mH)Y zzZ|$V{_c}mzq-FWv!kCCR}*Lr3g8~c5e~2G3Qff{O?b>XL+^ssw;D&)1p1&XxSHZl zDRHZ_%ByiyO`uKaw`~KyuIr$eWr3CwY8+J)C@blnU_|oo`yQVu`WPeL@1nXtHIAwY zv}V==H(l#XDHJiZ_a1s*>rt0G4AeMkfr7V?Fh_-qL^IV1_}v0#aR>p8Qeg?7x^X34 zgC4ktVL1I4y4#=U8D;En2P-ZhzC{n*bbaR@?plU{C4A}8!rta_aZ3ek=%t)I+F{T7 jqoVNrV;qKF*m3s)g-%E80%ZYs00000NkvXXu0mjfG8>R2 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/szar/textures/gui/chess_bq.png b/src/main/resources/assets/szar/textures/gui/chess_bq.png new file mode 100644 index 0000000000000000000000000000000000000000..b238f175b078cf1237374221e89d771584a9e76b GIT binary patch literal 390 zcmeAS@N?(olHy`uVBq!ia0vp^S|H591|*LjJ{irx!07Ag;usRad3D-G-ev<4m&qE; z4##=c9AN7?u%O4u-<>t7sbz`9+5|&B>-e5U8V!f{usCB|ckD^y2fu%D%Rh%1b)J$a-90ba|DxQx8@8!Hw^UvE z>cbw-@P7Weql!D6t>%6Q>CP~zIyNz|{tMXgi%(p98GF6)lSSVJ#s9xz;u%1evn^zQ heZM2tA7o%H(>wMcTc=l#BZ1+|;OXk;vd$@?2>^8svUmUh literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/szar/textures/gui/chess_br.png b/src/main/resources/assets/szar/textures/gui/chess_br.png new file mode 100644 index 0000000000000000000000000000000000000000..f1f6d2e99a7ae62600afff9457ea074c59ed76c7 GIT binary patch literal 379 zcmeAS@N?(olHy`uVBq!ia0vp^S|H591|*LjJ{irx!06=Z;usRa`8L9lugO7x`SHY- z;~yB_*)+I+GUJ31jQ;}!A-f*Q%Ymm&p3pJ=4HvaANjqHU9OFB2BV~FYKL0n`8OON7nl7yq&rU& zB4F<;yk_NVsTqvX=d-pQOBI=_`0Cu%>ALZ^F9u5Qn3DqHpOT6E_@mn_V;0-X%6W}9 z{kDI;d#=R#^;b@M%w1l|7u%ZtDE;)t%{qA#E=vecf=Z z?(AkUBcSMXfCQFT2p&9GbCwebp6xXyq8GoGA&~pC5w>8J_d*`&0 RFkqlEc)I$ztaD0e0sv^Os|Nr8 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/szar/textures/gui/chess_wb.png b/src/main/resources/assets/szar/textures/gui/chess_wb.png new file mode 100644 index 0000000000000000000000000000000000000000..a75a9af74b75ec252a25178285963ac3fb59f7d8 GIT binary patch literal 376 zcmV-;0f+vHP)@riX+_v|6FVIiDRRCpoXx;j=e8v9VI!@_n8c zQ&kuBsPa8(^bZHSF`9}6xW{os-rKf8_k9n22=jVw5_Mzr!CX>w(==#j^e83@H;KA2 zYTktcNt^(uXIa*8lc*b`saSw}Sr!nVrU|+n_=w`qgyp~&aqs(%uU6MJ^db1?h2B%0 z$BWv{W^!Ycv51ksi$KP^n{jh<+qOI}dWT`au-MbUO`>j$T6R6~1q5(k-TVW+;v+YS zx-t3{^o(2NXehc#v=}we4Yrg-gYg(1@;h1Ej>CMOXR&8pSFzr89oM7QF6cSB99kPc zaOYT5nB$|5f?h~1sT7JFxHY$WcEP(|^Ut24h_ literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/szar/textures/gui/chess_wk.png b/src/main/resources/assets/szar/textures/gui/chess_wk.png new file mode 100644 index 0000000000000000000000000000000000000000..8da73d0ccbfd88f223ca0936aa764831dce47eec GIT binary patch literal 403 zcmeAS@N?(olHy`uVBq!ia0vp^S|H591|*LjJ{irxz!>G};usRad3D-d-X;fuma8|| zOD{;c25`;|U^iLx{n3-t&&2xYXB=Ce7~6NUW;^@$+ok)%H(jw^`CiMutn~lJo5x;6 z>KwB5o%|{8dT5md+e;-r`}1mn(u@6zEEn%ycloW$`bVCLlUMX!T&sD-B|qTt-^hAz ze))rSS96878cHRL*~vYald(}_(fjJSV_WV1hh9J9*Y<`LtmDf8^>QlZRV&Ce!$jmMHOONdftvsc)`nK7&y0<`Vvz8b zh@S`5J9n?ke(*SRgAVt#>!qz}f_r&Wq^5HIN|JF?FWlT-`1zM?)Uv>t3&dgzo zHrepCmj7?}gv=1N6|V714m;bW%e67eKV`Gy@l(9K!Y80x?*3ovBp(K6cDc>-K1<&& z&rby!mvzy2x=hkH2J`tPW-&`aBCif+zZ5Ne-d6KvyK=kxP?78^OnzL2~uUt%;F#J(@! W9A~N8>R$j1Rt8U3KbLh*2~7ahG^X$X literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/szar/textures/gui/chess_wp.png b/src/main/resources/assets/szar/textures/gui/chess_wp.png new file mode 100644 index 0000000000000000000000000000000000000000..c34cce530913fb24015c67f8965081fdd918c2f1 GIT binary patch literal 313 zcmV-90mlA`P)9*KYQFvp&Asxol=6s%d$YT$T{P7nkMK&h;o~8)J&ldW+By#d)qeY>!Rn) zIBKTQD)h`Qgn-*mYy&fnnkiJ&biwu|(ER6lMwHQySkal#pa*WA@4PN=`0Pfn`YL+hzB1oS zi>;aa-tbGVYUAR=-8awjyeLP{ZnV(izrID%lW)AO!_l9(Ru$R-51UCjUY0rR00000 LNkvXXu0mjfx@?ph literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/szar/textures/gui/chess_wq.png b/src/main/resources/assets/szar/textures/gui/chess_wq.png new file mode 100644 index 0000000000000000000000000000000000000000..90f2667276bafb7672d71ddf8adb82c6b4fb9cd8 GIT binary patch literal 378 zcmeAS@N?(olHy`uVBq!ia0vp^S|H591|*LjJ{irx!071d;usRad3D-B-ev<4XYZ@F z78`{pr*!xnQsX(udG5`HOL97|wdbp;gr@rW{otG?FMsLotyur`)#qmF&)O#*`hR1@ zu?$_Y2lJM3R_5PklXYsaJM#4TNfoW+F9&0?O74G;IQPOZQZq$ps$tbmZef*=tOnm_ z**L2ON-oaV=~=_6*1Ru`Bj2d=l*HB*f?FS^zCQEz{D?({c|P+v-amO?YL%}#c}35~HS5w@mfkuXyQ|-4k&)Z^UGrW(;C}G; z@=go(a=+!D=Yh=Cbvth%^=iEx*x_Q9R(qet-+#re0Mf1Vg?G1g`syX>AY1k^Jlq!Y UGSIK95*Vfop00i_>zopr05i0yeEk=2QHi@A4PEzz66}@FWOcB=HcMChWK~A&@kfQ)g#Pn(7_bq%7>$!!Dsx zLKTIlpW02ypNXa$)Sn9(qjwL;zVFdi*EO^y+O|b$+cszx<2d4N(=^bQkRd8#)D^uI z$FZKB^s1_G`MR#qQ6WQA#%MVDuIrMiP1BTI=S7}$UiWAjqB2I4=jtnp>_mGa`=BT3 zNrtG5(Inq YpAPP03WgzLvH$=807*qoM6N<$f}b(0_W%F@ literal 0 HcmV?d00001 diff --git a/src/main/resources/data/minecraft/tags/blocks/mineable/axe.json b/src/main/resources/data/minecraft/tags/blocks/mineable/axe.json index d5cd026..6ef030f 100644 --- a/src/main/resources/data/minecraft/tags/blocks/mineable/axe.json +++ b/src/main/resources/data/minecraft/tags/blocks/mineable/axe.json @@ -3,6 +3,7 @@ "szar:roulette", "szar:slot_machine", "szar:tictactoe", - "szar:connectfour" + "szar:connectfour", + "szar:chess" ] } \ No newline at end of file diff --git a/src/main/resources/data/szar/loot_tables/blocks/chess.json b/src/main/resources/data/szar/loot_tables/blocks/chess.json new file mode 100644 index 0000000..0cea3f7 --- /dev/null +++ b/src/main/resources/data/szar/loot_tables/blocks/chess.json @@ -0,0 +1,14 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:item", + "name": "szar:chess" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/data/szar/recipes/chess.json b/src/main/resources/data/szar/recipes/chess.json new file mode 100644 index 0000000..891c3a9 --- /dev/null +++ b/src/main/resources/data/szar/recipes/chess.json @@ -0,0 +1,23 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "RBR", + "BPB", + "RBR" + ], + "key": { + "R": { + "item": "minecraft:white_dye" + }, + "B": { + "item": "minecraft:black_dye" + }, + "P": { + "tag": "minecraft:planks" + } + }, + "result": { + "item": "szar:chess", + "count": 1 + } +} \ No newline at end of file