connect four, and some minor fixes

This commit is contained in:
2026-03-22 17:31:09 +01:00
parent df80fed3fa
commit ff8e52f95d
18 changed files with 741 additions and 7 deletions

View File

@@ -0,0 +1,153 @@
package dev.tggamesyt.szar.client;
import dev.tggamesyt.szar.ConnectFourBlockEntity;
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.UUID;
public class ConnectFourScreen extends Screen {
private static final Identifier BG =
new Identifier("szar", "textures/gui/connectfour.png");
private static final Identifier RED_TEX =
new Identifier("szar", "textures/gui/c4_red.png");
private static final Identifier YELLOW_TEX =
new Identifier("szar", "textures/gui/c4_yellow.png");
private static final Identifier HOVER_TEX =
new Identifier("szar", "textures/gui/c4_hover.png");
// GUI dimensions — adjust to match your texture
private static final int GUI_WIDTH = 204; // 7 cells * ~28px + padding
private static final int GUI_HEIGHT = 196; // 6 cells * ~28px + padding + status
private static final int BOARD_X = 18;
private static final int BOARD_Y = 18;
private static final int CELL_SIZE = 24;
private ConnectFourBlockEntity.State state;
private final BlockPos blockPos;
private final UUID localPlayer;
private boolean isSpectator;
private int hoveredCol = -1;
public ConnectFourScreen(BlockPos pos, ConnectFourBlockEntity.State state) {
super(Text.literal("Connect Four"));
this.blockPos = pos;
this.state = state;
this.localPlayer = MinecraftClient.getInstance().player.getUuid();
this.isSpectator = state.isSpectator;
}
public void updateState(ConnectFourBlockEntity.State newState) {
this.state = newState;
this.isSpectator = newState.isSpectator;
}
@Override
public void render(DrawContext context, int mouseX, int mouseY, float delta) {
renderBackground(context);
int x = (this.width - GUI_WIDTH) / 2;
int y = (this.height - GUI_HEIGHT) / 2;
// Draw background
context.drawTexture(BG, x, y, 0, 0, GUI_WIDTH, GUI_HEIGHT, GUI_WIDTH, GUI_HEIGHT);
// Update hovered column
hoveredCol = -1;
for (int col = 0; col < ConnectFourBlockEntity.COLS; col++) {
int cx = x + BOARD_X + col * CELL_SIZE;
if (mouseX >= cx && mouseX < cx + CELL_SIZE) {
hoveredCol = col;
break;
}
}
// Draw hover indicator on top row if it's our turn
boolean myTurn = !isSpectator && state.winner == 0
&& ((state.currentTurn == 1 && localPlayer.equals(state.player1))
|| (state.currentTurn == 2 && localPlayer.equals(state.player2)));
if (myTurn && hoveredCol >= 0) {
int cx = x + BOARD_X + hoveredCol * CELL_SIZE;
context.drawTexture(HOVER_TEX, cx + 2, y + BOARD_Y - CELL_SIZE + 2, 0, 0,
CELL_SIZE - 4, CELL_SIZE - 4, CELL_SIZE - 4, CELL_SIZE - 4);
}
// Draw board — row 0 is bottom, so render rows in reverse
for (int row = 0; row < ConnectFourBlockEntity.ROWS; row++) {
for (int col = 0; col < ConnectFourBlockEntity.COLS; col++) {
int cx = x + BOARD_X + col * CELL_SIZE;
// Flip row so row 0 (bottom) renders at the bottom of the GUI
int displayRow = ConnectFourBlockEntity.ROWS - 1 - row;
int cy = y + BOARD_Y + displayRow * CELL_SIZE;
int val = state.board[row][col];
if (val == 1) {
context.drawTexture(RED_TEX, cx + 2, cy + 2, 0, 0,
CELL_SIZE - 4, CELL_SIZE - 4,
CELL_SIZE - 4, CELL_SIZE - 4);
} else if (val == 2) {
context.drawTexture(YELLOW_TEX, cx + 2, cy + 2, 0, 0,
CELL_SIZE - 4, CELL_SIZE - 4,
CELL_SIZE - 4, CELL_SIZE - 4);
}
}
}
// Status text
String status;
if (state.winner == 1) status = "§cRed wins!";
else if (state.winner == 2) status = "§eYellow wins!";
else if (state.winner == 3) status = "§7Draw!";
else if (isSpectator) status = "§7Spectating...";
else status = myTurn ? "§aYour turn!" : "§7Opponent's turn...";
context.drawTextWithShadow(this.textRenderer, Text.literal(status),
x + GUI_WIDTH / 2 - this.textRenderer.getWidth(status) / 2,
y + BOARD_Y + ConnectFourBlockEntity.ROWS * CELL_SIZE + 6,
0xFFFFFF);
super.render(context, mouseX, mouseY, delta);
}
@Override
public boolean mouseClicked(double mouseX, double mouseY, int button) {
if (isSpectator) return super.mouseClicked(mouseX, mouseY, button);
if (button != 0) return super.mouseClicked(mouseX, mouseY, button);
if (state.winner != 0) return super.mouseClicked(mouseX, mouseY, button);
boolean myTurn = (state.currentTurn == 1 && localPlayer.equals(state.player1))
|| (state.currentTurn == 2 && localPlayer.equals(state.player2));
if (!myTurn) return super.mouseClicked(mouseX, mouseY, button);
int x = (this.width - GUI_WIDTH) / 2;
for (int col = 0; col < ConnectFourBlockEntity.COLS; col++) {
int cx = x + BOARD_X + col * CELL_SIZE;
if (mouseX >= cx && mouseX < cx + CELL_SIZE) {
sendMove(col);
return true;
}
}
return super.mouseClicked(mouseX, mouseY, button);
}
private void sendMove(int col) {
PacketByteBuf buf = PacketByteBufs.create();
buf.writeBlockPos(blockPos);
buf.writeInt(col);
ClientPlayNetworking.send(Szar.C4_MAKE_MOVE, buf);
}
@Override
public boolean shouldPause() { return false; }
}

View File

@@ -90,11 +90,17 @@ public class SzarClient implements ClientModInitializer {
); );
@Override @Override
public void onInitializeClient() { public void onInitializeClient() {
// Open screen // TTT
ClientPlayNetworking.registerGlobalReceiver(Szar.TTT_OPEN_SCREEN, (client, handler, buf, sender) -> { ClientPlayNetworking.registerGlobalReceiver(Szar.TTT_OPEN_SCREEN, (client, handler, buf, sender) -> {
BlockPos pos = buf.readBlockPos(); BlockPos pos = buf.readBlockPos();
TicTacToeBlockEntity.State state = TicTacToeBlockEntity.readStateFromBuf(buf); TicTacToeBlockEntity.State state = TicTacToeBlockEntity.readStateFromBuf(buf);
client.execute(() -> client.setScreen(new TicTacToeScreen(pos, state))); client.execute(() -> {
if (client.currentScreen instanceof TicTacToeScreen existing) {
existing.updateState(state); // just update, don't replace
} else {
client.setScreen(new TicTacToeScreen(pos, state));
}
});
}); });
ClientPlayNetworking.registerGlobalReceiver(Szar.TTT_CLOSE_SCREEN, (client, handler, buf, sender) -> { ClientPlayNetworking.registerGlobalReceiver(Szar.TTT_CLOSE_SCREEN, (client, handler, buf, sender) -> {
client.execute(() -> { client.execute(() -> {
@@ -112,6 +118,37 @@ public class SzarClient implements ClientModInitializer {
} }
}); });
}); });
// C4
ClientPlayNetworking.registerGlobalReceiver(Szar.C4_OPEN_SCREEN, (client, handler, buf, sender) -> {
BlockPos pos = buf.readBlockPos();
ConnectFourBlockEntity.State state = ConnectFourBlockEntity.readStateFromBuf(buf);
client.execute(() -> {
if (client.currentScreen instanceof ConnectFourScreen existing) {
existing.updateState(state);
} else {
client.setScreen(new ConnectFourScreen(pos, state));
}
});
});
ClientPlayNetworking.registerGlobalReceiver(Szar.C4_STATE_SYNC, (client, handler, buf, sender) -> {
BlockPos pos = buf.readBlockPos();
ConnectFourBlockEntity.State state = ConnectFourBlockEntity.readStateFromBuf(buf);
client.execute(() -> {
if (client.currentScreen instanceof ConnectFourScreen screen) {
screen.updateState(state);
}
});
});
ClientPlayNetworking.registerGlobalReceiver(Szar.C4_CLOSE_SCREEN, (client, handler, buf, sender) -> {
client.execute(() -> {
if (client.currentScreen instanceof ConnectFourScreen) {
client.setScreen(null);
}
});
});
ClientPlayNetworking.registerGlobalReceiver(Szar.DRUNK_TYPE_PACKET, (client, handler, buf, responseSender) -> { ClientPlayNetworking.registerGlobalReceiver(Szar.DRUNK_TYPE_PACKET, (client, handler, buf, responseSender) -> {
String typeName = buf.readString(); String typeName = buf.readString();
client.execute(() -> DrunkEffect.setDisplayType(typeName)); client.execute(() -> DrunkEffect.setDisplayType(typeName));

View File

@@ -0,0 +1,81 @@
package dev.tggamesyt.szar;
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 ConnectFourBlock extends BlockWithEntity {
private static final VoxelShape SHAPE = VoxelShapes.union(
VoxelShapes.cuboid(0f, 0f, 0f, 1f, 0.75f, 1f)
);
public ConnectFourBlock(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 ConnectFourBlockEntity(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 ConnectFourBlockEntity be)) return ActionResult.PASS;
be.handlePlayerJoin(sp, pos);
return ActionResult.SUCCESS;
}
@Override
public <T extends BlockEntity> BlockEntityTicker<T> getTicker(
World world, BlockState state, BlockEntityType<T> type) {
if (world.isClient) return null;
return type == Szar.CONNECT_FOUR_ENTITY
? (w, pos, s, be) -> ConnectFourBlockEntity.tick(w, pos, s,
(ConnectFourBlockEntity) be)
: null;
}
@Override
public void onBreak(World world, BlockPos pos, BlockState state, PlayerEntity player) {
if (!world.isClient && world.getBlockEntity(pos) instanceof ConnectFourBlockEntity be) {
be.closeScreenForAll(world.getServer());
if (be.player1 != null) Szar.c4ActivePlayers.remove(be.player1);
if (be.player2 != null) Szar.c4ActivePlayers.remove(be.player2);
}
super.onBreak(world, pos, state, player);
}
}

View File

@@ -0,0 +1,308 @@
package dev.tggamesyt.szar;
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 ConnectFourBlockEntity extends BlockEntity {
public static final int COLS = 7;
public static final int ROWS = 6;
// board[row][col], 0=empty, 1=player1(red), 2=player2(yellow)
// row 0 = bottom
public int[][] board = new int[ROWS][COLS];
public UUID player1 = null; // red
public UUID player2 = null; // yellow
public int currentTurn = 1;
public int winner = 0; // 0=ongoing, 1=p1, 2=p2, 3=draw
public Set<UUID> spectators = new HashSet<>();
public int resetTimer = -1;
public ConnectFourBlockEntity(BlockPos pos, BlockState state) {
super(Szar.CONNECT_FOUR_ENTITY, pos, state);
}
public static void tick(World world, BlockPos pos, BlockState state,
ConnectFourBlockEntity 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.c4ActivePlayers.get(uuid);
if (activePos != null && !activePos.equals(pos)) {
player.sendMessage(Text.literal("§cYou are already in a game at another board!"), true);
return;
}
// Rejoin existing game
if (uuid.equals(player1) || uuid.equals(player2)) {
if (player1 != null && player2 != null) {
// Game in progress — reopen screen
openScreen(player);
} else {
// Still waiting for second player — leave
if (uuid.equals(player1)) {
Szar.c4ActivePlayers.remove(player1);
player1 = null;
} else {
Szar.c4ActivePlayers.remove(player2);
player2 = null;
}
player.sendMessage(Text.literal("§7Left the game."), true);
markDirty();
}
return;
}
if (player1 == null) {
player1 = uuid;
Szar.c4ActivePlayers.put(uuid, pos);
player.sendMessage(Text.literal("§aYou are §cRed§a! Waiting for second player..."), true);
markDirty();
return;
}
if (player2 == null && !uuid.equals(player1)) {
player2 = uuid;
Szar.c4ActivePlayers.put(uuid, pos);
player.sendMessage(Text.literal("§aYou are §eYellow§a! Game starting!"), true);
ServerPlayerEntity p1 = player.getServer().getPlayerManager().getPlayer(player1);
if (p1 != null) p1.sendMessage(Text.literal("§aSecond player joined! Your turn!"), 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, int col) {
if (winner != 0) return;
if (col < 0 || col >= COLS) return;
UUID uuid = player.getUuid();
int playerNum = uuid.equals(player1) ? 1 : uuid.equals(player2) ? 2 : 0;
if (playerNum == 0) return;
if (playerNum != currentTurn) {
player.sendMessage(Text.literal("§cNot your turn!"), true);
return;
}
// Find lowest empty row in this column (gravity)
int targetRow = -1;
for (int row = 0; row < ROWS; row++) {
if (board[row][col] == 0) {
targetRow = row;
break;
}
}
if (targetRow == -1) {
player.sendMessage(Text.literal("§cColumn is full!"), true);
return;
}
board[targetRow][col] = playerNum;
currentTurn = (currentTurn == 1) ? 2 : 1;
checkWinner();
markDirty();
syncToPlayers(player.getServer());
}
private void checkWinner() {
// Check all 4-in-a-row directions
int[][] directions = {{0,1},{1,0},{1,1},{1,-1}};
for (int row = 0; row < ROWS; row++) {
for (int col = 0; col < COLS; col++) {
int val = board[row][col];
if (val == 0) continue;
for (int[] dir : directions) {
int count = 1;
for (int i = 1; i < 4; i++) {
int r = row + dir[0] * i;
int c = col + dir[1] * i;
if (r < 0 || r >= ROWS || c < 0 || c >= COLS) break;
if (board[r][c] != val) break;
count++;
}
if (count == 4) {
winner = val;
resetTimer = 100; // 5 seconds
markDirty();
return;
}
}
}
}
// Check draw — top row full
boolean full = true;
for (int col = 0; col < COLS; col++) {
if (board[ROWS - 1][col] == 0) { full = false; break; }
}
if (full) {
winner = 3;
resetTimer = 100;
markDirty();
}
}
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.C4_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.C4_STATE_SYNC, buf);
}
public void writeStateToBuf(PacketByteBuf buf, UUID viewerUuid) {
for (int row = 0; row < ROWS; row++)
for (int col = 0; col < COLS; col++)
buf.writeInt(board[row][col]);
buf.writeBoolean(player1 != null);
if (player1 != null) buf.writeUuid(player1);
buf.writeBoolean(player2 != null);
if (player2 != null) buf.writeUuid(player2);
buf.writeInt(currentTurn);
buf.writeInt(winner);
boolean isSpectator = viewerUuid != null
&& !viewerUuid.equals(player1)
&& !viewerUuid.equals(player2);
buf.writeBoolean(isSpectator);
}
public static State readStateFromBuf(PacketByteBuf buf) {
State s = new State();
s.board = new int[ROWS][COLS];
for (int row = 0; row < ROWS; row++)
for (int col = 0; col < COLS; col++)
s.board[row][col] = buf.readInt();
if (buf.readBoolean()) s.player1 = buf.readUuid();
if (buf.readBoolean()) s.player2 = buf.readUuid();
s.currentTurn = buf.readInt();
s.winner = buf.readInt();
s.isSpectator = buf.readBoolean();
return s;
}
public static class State {
public int[][] board;
public UUID player1, player2;
public int currentTurn, winner;
public boolean isSpectator;
}
public void resetGame(net.minecraft.server.MinecraftServer server) {
closeScreenForAll(server);
if (player1 != null) Szar.c4ActivePlayers.remove(player1);
if (player2 != null) Szar.c4ActivePlayers.remove(player2);
spectators.clear();
board = new int[ROWS][COLS];
player1 = null;
player2 = null;
currentTurn = 1;
winner = 0;
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.C4_CLOSE_SCREEN, PacketByteBufs.empty());
}
@Override
public void writeNbt(NbtCompound nbt) {
super.writeNbt(nbt);
int[] flat = new int[ROWS * COLS];
for (int r = 0; r < ROWS; r++)
for (int c = 0; c < COLS; c++)
flat[r * COLS + c] = board[r][c];
nbt.putIntArray("Board", flat);
if (player1 != null) nbt.putUuid("Player1", player1);
if (player2 != null) nbt.putUuid("Player2", player2);
nbt.putInt("Turn", currentTurn);
nbt.putInt("Winner", winner);
nbt.putInt("ResetTimer", resetTimer);
}
@Override
public void readNbt(NbtCompound nbt) {
super.readNbt(nbt);
int[] flat = nbt.getIntArray("Board");
if (flat.length == ROWS * COLS) {
for (int r = 0; r < ROWS; r++)
for (int c = 0; c < COLS; c++)
board[r][c] = flat[r * COLS + c];
}
if (nbt.containsUuid("Player1")) player1 = nbt.getUuid("Player1");
if (nbt.containsUuid("Player2")) player2 = nbt.getUuid("Player2");
currentTurn = nbt.getInt("Turn");
winner = nbt.getInt("Winner");
resetTimer = nbt.getInt("ResetTimer");
}
@Override
public NbtCompound toInitialChunkDataNbt() { return createNbt(); }
@Override
public BlockEntityUpdateS2CPacket toUpdatePacket() {
return BlockEntityUpdateS2CPacket.create(this);
}
}

View File

@@ -385,6 +385,7 @@ public class Szar implements ModInitializer {
entries.add(Szar.ALMOND_WATER); entries.add(Szar.ALMOND_WATER);
entries.add(Szar.KEBAB); entries.add(Szar.KEBAB);
entries.add(Szar.TIC_TAC_TOE_ITEM); entries.add(Szar.TIC_TAC_TOE_ITEM);
entries.add(Szar.CONNECT_FOUR_ITEM);
// crazy weponary // crazy weponary
entries.add(Szar.BULLET_ITEM); entries.add(Szar.BULLET_ITEM);
entries.add(Szar.AK47); entries.add(Szar.AK47);
@@ -398,11 +399,11 @@ public class Szar implements ModInitializer {
entries.add(Szar.WHEEL); entries.add(Szar.WHEEL);
entries.add(Szar.PLANE); entries.add(Szar.PLANE);
// drugs // drugs
entries.add(Szar.BEER);
entries.add(Szar.CHEMICAL_WORKBENCH_ITEM);
entries.add(Szar.CANNABIS_ITEM); entries.add(Szar.CANNABIS_ITEM);
entries.add(Szar.WEED_ITEM); entries.add(Szar.WEED_ITEM);
entries.add(Szar.WEED_JOINT_ITEM); entries.add(Szar.WEED_JOINT_ITEM);
entries.add(Szar.CHEMICAL_WORKBENCH_ITEM);
entries.add(Szar.BEER);
// war guys // war guys
entries.add(Szar.HITTER_SPAWNEGG); entries.add(Szar.HITTER_SPAWNEGG);
entries.add(Szar.NAZI_SPAWNEGG); entries.add(Szar.NAZI_SPAWNEGG);
@@ -1337,6 +1338,15 @@ public class Szar implements ModInitializer {
} }
}); });
}); });
ServerPlayNetworking.registerGlobalReceiver(C4_MAKE_MOVE, (server, player, handler, buf, sender) -> {
BlockPos pos = buf.readBlockPos();
int col = buf.readInt();
server.execute(() -> {
if (player.getWorld().getBlockEntity(pos) instanceof ConnectFourBlockEntity be) {
be.handleMove(player, col);
}
});
});
} }
public static final Block TIC_TAC_TOE_BLOCK = Registry.register( public static final Block TIC_TAC_TOE_BLOCK = Registry.register(
@@ -1359,7 +1369,27 @@ public class Szar implements ModInitializer {
public static final Identifier TTT_MAKE_MOVE = new Identifier(MOD_ID, "ttt_move"); public static final Identifier TTT_MAKE_MOVE = new Identifier(MOD_ID, "ttt_move");
public static final Identifier TTT_STATE_SYNC = new Identifier(MOD_ID, "ttt_sync"); public static final Identifier TTT_STATE_SYNC = new Identifier(MOD_ID, "ttt_sync");
public static final Identifier TTT_CLOSE_SCREEN = new Identifier(MOD_ID, "ttt_close"); public static final Identifier TTT_CLOSE_SCREEN = new Identifier(MOD_ID, "ttt_close");
public static final Block CONNECT_FOUR_BLOCK = Registry.register(
Registries.BLOCK, new Identifier(MOD_ID, "connectfour"),
new ConnectFourBlock(AbstractBlock.Settings.create().strength(2f))
);
public static final BlockItem CONNECT_FOUR_ITEM = Registry.register(
Registries.ITEM, new Identifier(MOD_ID, "connectfour"),
new BlockItem(CONNECT_FOUR_BLOCK, new Item.Settings())
);
public static final BlockEntityType<ConnectFourBlockEntity> CONNECT_FOUR_ENTITY =
Registry.register(
Registries.BLOCK_ENTITY_TYPE, new Identifier(MOD_ID, "connectfour"),
FabricBlockEntityTypeBuilder.create(ConnectFourBlockEntity::new,
CONNECT_FOUR_BLOCK).build()
);
public static final Identifier C4_OPEN_SCREEN = new Identifier(MOD_ID, "c4_open");
public static final Identifier C4_MAKE_MOVE = new Identifier(MOD_ID, "c4_move");
public static final Identifier C4_STATE_SYNC = new Identifier(MOD_ID, "c4_sync");
public static final Identifier C4_CLOSE_SCREEN = new Identifier(MOD_ID, "c4_close");
public static final Map<UUID, BlockPos> c4ActivePlayers = new java.util.HashMap<>();
// Blocks // Blocks
public static final TrackerBlock TRACKER_BLOCK = Registry.register( public static final TrackerBlock TRACKER_BLOCK = Registry.register(
Registries.BLOCK, new Identifier(MOD_ID, "tracker"), Registries.BLOCK, new Identifier(MOD_ID, "tracker"),

View File

@@ -52,7 +52,21 @@ public class TicTacToeBlockEntity extends BlockEntity {
// Rejoin existing game // Rejoin existing game
if (uuid.equals(player1) || uuid.equals(player2)) { if (uuid.equals(player1) || uuid.equals(player2)) {
openScreen(player); if (player1 != null && player2 != null) {
// Game in progress — reopen screen
openScreen(player);
} else {
// Still waiting for second player — leave
if (uuid.equals(player1)) {
Szar.tttActivePlayers.remove(player1); // use c4ActivePlayers for ConnectFour
player1 = null;
} else {
Szar.tttActivePlayers.remove(player2);
player2 = null;
}
player.sendMessage(Text.literal("§7Left the game."), true);
markDirty();
}
return; return;
} }

View File

@@ -0,0 +1,5 @@
{
"variants": {
"": { "model": "szar:block/connectfour" }
}
}

View File

@@ -187,5 +187,6 @@
"advancement.szar.backrooms.title": "Where did the world go?", "advancement.szar.backrooms.title": "Where did the world go?",
"advancement.szar.backrooms.description": "Step into the Backrooms", "advancement.szar.backrooms.description": "Step into the Backrooms",
"block.szar.tictactoe": "Tic Tac Toe" "block.szar.tictactoe": "Tic Tac Toe",
"block.szar.connectfour": "Connect Four"
} }

View File

@@ -0,0 +1,64 @@
{
"format_version": "1.9.0",
"credit": "Made with Blockbench",
"parent": "block/block",
"texture_size": [32, 32],
"textures": {
"0": "szar:block/plank",
"1": "szar:block/connect4",
"particle": "szar:block/plank"
},
"elements": [
{
"from": [0, 0, 0],
"to": [16, 12, 16],
"faces": {
"north": {"uv": [0, 0, 16, 12], "texture": "#0"},
"east": {"uv": [0, 0, 16, 12], "texture": "#0"},
"south": {"uv": [0, 0, 16, 12], "texture": "#0"},
"west": {"uv": [0, 0, 16, 12], "texture": "#0"},
"up": {"uv": [0, 0, 16, 16], "texture": "#0"},
"down": {"uv": [0, 0, 16, 16], "texture": "#0"}
}
},
{
"from": [7, 14, 0],
"to": [9, 24, 16],
"rotation": {"angle": 0, "axis": "y", "origin": [1, 12, 0]},
"faces": {
"north": {"uv": [9, 0, 10, 5], "texture": "#1"},
"east": {"uv": [0, 0, 8, 5], "texture": "#1"},
"south": {"uv": [9, 5, 10, 10], "texture": "#1"},
"west": {"uv": [0, 5, 8, 10], "texture": "#1"},
"up": {"uv": [9, 8, 8, 0], "texture": "#1"},
"down": {"uv": [9, 8, 8, 16], "texture": "#1"}
}
},
{
"from": [7, 12, 0],
"to": [9, 14, 1],
"rotation": {"angle": 0, "axis": "y", "origin": [1, 10, 0]},
"faces": {
"north": {"uv": [0, 10, 1, 11], "texture": "#1"},
"east": {"uv": [2, 10, 2.5, 11], "texture": "#1"},
"south": {"uv": [10, 0, 11, 1], "texture": "#1"},
"west": {"uv": [10, 2, 10.5, 3], "texture": "#1"},
"up": {"uv": [3.5, 10.5, 2.5, 10], "texture": "#1"},
"down": {"uv": [11, 3, 10, 3.5], "texture": "#1"}
}
},
{
"from": [7, 12, 15],
"to": [9, 14, 16],
"rotation": {"angle": 0, "axis": "y", "origin": [1, 10, 15]},
"faces": {
"north": {"uv": [1, 10, 2, 11], "texture": "#1"},
"east": {"uv": [3.5, 10, 4, 11], "texture": "#1"},
"south": {"uv": [10, 1, 11, 2], "texture": "#1"},
"west": {"uv": [10, 3.5, 10.5, 4.5], "texture": "#1"},
"up": {"uv": [5, 10.5, 4, 10], "texture": "#1"},
"down": {"uv": [11, 4.5, 10, 5], "texture": "#1"}
}
}
]
}

View File

@@ -0,0 +1,3 @@
{
"parent": "szar:block/connectfour"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 257 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 761 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 694 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 688 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -2,6 +2,7 @@
"values": [ "values": [
"szar:roulette", "szar:roulette",
"szar:slot_machine", "szar:slot_machine",
"szar:tictactoe" "szar:tictactoe",
"szar:connectfour"
] ]
} }

View File

@@ -0,0 +1,14 @@
{
"type": "minecraft:block",
"pools": [
{
"rolls": 1,
"entries": [
{
"type": "minecraft:item",
"name": "szar:connectfour"
}
]
}
]
}

View File

@@ -0,0 +1,23 @@
{
"type": "minecraft:crafting_shaped",
"pattern": [
"RBR",
"BPB",
"RBR"
],
"key": {
"R": {
"item": "minecraft:red_dye"
},
"B": {
"item": "minecraft:yellow_dye"
},
"P": {
"tag": "minecraft:planks"
}
},
"result": {
"item": "szar:connectfour",
"count": 1
}
}