package mineplex.core.gadget.gadgets.item; import java.time.Month; import java.time.YearMonth; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; import org.bukkit.Effect; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.scheduler.BukkitRunnable; import mineplex.core.common.util.C; import mineplex.core.common.util.F; import mineplex.core.common.util.MapUtil; import mineplex.core.common.util.UtilBlock; import mineplex.core.common.util.UtilTextMiddle; import mineplex.core.common.util.UtilTime; import mineplex.core.gadget.GadgetManager; import mineplex.core.gadget.event.GadgetBlockEvent; import mineplex.core.gadget.util.CostConstants; import mineplex.core.recharge.Recharge; import mineplex.core.updater.UpdateType; import mineplex.core.updater.event.UpdateEvent; public class ItemConnect4 extends GameItemGadget { private static final long COOLDOWN_USE = TimeUnit.SECONDS.toMillis(20); private static final long TIMEOUT = TimeUnit.MINUTES.toMillis(5); private static final int ROWS = 6; private static final int COLUMNS = 7; private final Set _gameBoards; public ItemConnect4(GadgetManager manager) { super(manager, "Connect 4", new String[] { C.cGray + "Play Connect 4", C.cGray + "with other players!", C.blankLine, C.cWhite + "Left click the quartz block", C.cWhite + "to place a counter in that", C.cWhite + "column", C.cWhite + "First person to get 4", C.cWhite + "in a row" }, CostConstants.POWERPLAY_BONUS, Material.STAINED_CLAY, (byte) 4, null); setPPCYearMonth(YearMonth.of(2018, Month.MARCH)); Free = false; _gameBoards = new HashSet<>(); } public boolean activatePreprocess(Player player) { for (GameBoard board : _gameBoards) { if (board.PlayerA.equals(player) || board.PlayerB.equals(player)) { return false; } } return true; } @Override public void disableCustom(Player player, boolean message) { super.disableCustom(player, message); remove(player); } private void remove(Player player) { _gameBoards.removeIf(board -> { if (board.PlayerA.equals(player) || board.PlayerB.equals(player)) { cleanupBoard(board, false); return true; } return false; }); _invites.remove(player.getName()); } @EventHandler public void updateTimeout(UpdateEvent event) { if (event.getType() != UpdateType.SLOWER) { return; } _gameBoards.removeIf(board -> { if (UtilTime.elapsed(board.StartTime, TIMEOUT)) { cleanupBoard(board, false); return true; } return false; }); } @Override protected void startGame(Player invitee, Player inviter) { Location location = inviter.getLocation(); GameBoard board = new GameBoard(inviter, invitee, location); if (!Manager.selectLocation(this, location) || !Manager.selectBlocks(this, board.Blocks) || board.Blocks.stream() .anyMatch(block -> block.getType() != Material.AIR)) { String message = F.main(Manager.getName(), "You must find a more open area to play " + F.name(getName())); invitee.sendMessage(message); inviter.sendMessage(message); return; } board.Blocks.forEach(block -> Manager.getBlockRestore().restore(block)); board.Ceiling.forEach(block -> MapUtil.QuickChangeBlockAt(block.getLocation(), Material.QUARTZ_BLOCK)); board.Floor.forEach(block -> MapUtil.QuickChangeBlockAt(block.getLocation(), Material.QUARTZ_BLOCK)); location.setYaw(0); { Location teleport = location.clone().add(0, 0, 2); teleport.setYaw(180); inviter.teleport(teleport); } { Location teleport = location.clone().subtract(0, 0, 2); teleport.setYaw(0); invitee.teleport(teleport); } _gameBoards.add(board); } @EventHandler public void blockInteract(PlayerInteractEvent evnet) { Block block = evnet.getClickedBlock(); if (block == null) { return; } Player player = evnet.getPlayer(); for (GameBoard board : _gameBoards) { if (!board.AllowInteract) { continue; } boolean playerA = board.PlayerA.equals(player); boolean playerB = board.PlayerB.equals(player); boolean playerATurn = board.Turn % 2 == 0; int column = board.Floor.indexOf(block); if (!playerA && !playerB || column == -1) { continue; } else if (playerA && !playerATurn || playerB && playerATurn) { player.sendMessage(F.main(Manager.getName(), "It's not your turn.")); continue; } board.AllowInteract = false; Manager.runSyncTimer(new BukkitRunnable() { Block top = block.getRelative(0, ROWS + 1, 0); int row = 0; @Override public void run() { if (!isActive(board.PlayerA)) { cancel(); return; } if (row != 0) { MapUtil.QuickChangeBlockAt(top.getLocation(), Material.AIR); } top = top.getRelative(BlockFace.DOWN); Location location = top.getLocation().add(0.5, 0.5, 0.5); if (top.getType() != Material.AIR) { if (row == 0) { board.AllowInteract = true; player.sendMessage(F.main("Game", "That column is full.")); cancel(); return; } top = top.getRelative(BlockFace.UP); location = top.getLocation().add(0.5, 0.5, 0.5); board.OwnedTiles[--row][column] = playerATurn ? 1 : 2; location.getWorld().playEffect(location, Effect.STEP_SOUND, playerATurn ? Material.REDSTONE_BLOCK : Material.GOLD_BLOCK); cancel(); if (board.nextTurn()) { return; } } MapUtil.QuickChangeBlockAt(location, Material.STAINED_CLAY, (byte) (playerATurn ? 14 : 4)); row++; } }, 0, 4); } } private void cleanupBoard(GameBoard board, boolean remove) { Recharge.Instance.use(board.PlayerA, getName(), COOLDOWN_USE, true, true); board.Blocks.forEach(block -> { Manager.getBlockRestore().restore(block); MapUtil.QuickChangeBlockAt(block.getLocation(), Material.AIR); }); if (remove) { _gameBoards.remove(board); } } @EventHandler public void gadgetBlock(GadgetBlockEvent event) { for (GameBoard board : _gameBoards) { event.getBlocks().removeIf(board.Blocks::contains); } } private class GameBoard { final Player PlayerA, PlayerB; final int[][] OwnedTiles; final Location TopLeft; final List Blocks, Floor, Ceiling; final long StartTime; int Turn; boolean First = true, AllowInteract; GameBoard(Player playerA, Player playerB, Location center) { PlayerA = playerA; PlayerB = playerB; OwnedTiles = new int[ROWS][COLUMNS]; for (int[] i : OwnedTiles) { Arrays.fill(i, 0); } double xMod = Math.floor(COLUMNS / 2D), yMod = ROWS + 1; TopLeft = center.clone().add(-xMod, ROWS, 0); Blocks = UtilBlock.getInBoundingBox(center.clone().add(-xMod, 1, 0), center.clone().add(xMod, yMod, 0), false); Floor = UtilBlock.getInBoundingBox(center.clone().add(-xMod, 0, 0), center.clone().add(xMod, 0, 0), false); Ceiling = UtilBlock.getInBoundingBox(center.clone().add(-xMod, yMod, 0), center.clone().add(xMod, yMod, 0), false); Blocks.addAll(Floor); Blocks.addAll(Ceiling); StartTime = System.currentTimeMillis(); if (Math.random() > 0.5) { Turn = 1; } nextTurn(); } boolean nextTurn() { boolean playerATurn = Turn % 2 == 0; if (hasDrawn()) { drawGame(PlayerA, PlayerB); cleanupBoard(this, true); return true; } else if (hasWon(playerATurn ? 1 : 2)) { Player winner = playerATurn ? PlayerA : PlayerB; Player loser = playerATurn ? PlayerB : PlayerA; endGame(winner, loser); Manager.runSyncLater(() -> cleanupBoard(this, true), 60); return true; } else { Turn++; playerATurn = !playerATurn; } if (First) { boolean finalPlayerATurn = playerATurn; Manager.runSyncLater(() -> informTurn(finalPlayerATurn), 20); First = false; } else { informTurn(playerATurn); } AllowInteract = true; return false; } private void informTurn(boolean playerATurn) { UtilTextMiddle.display("", C.cYellowB + "Your Turn", 5, 15, 5, playerATurn ? PlayerA : PlayerB); } boolean hasDrawn() { for (int[] columns : OwnedTiles) { for (int column : columns) { if (column == 0) { return false; } } } return true; } boolean hasWon(int valueToCheck) { for (int j = 0; j < COLUMNS - 3; j++) { for (int i = 0; i < ROWS; i++) { if (OwnedTiles[i][j] == valueToCheck && OwnedTiles[i][j + 1] == valueToCheck && OwnedTiles[i][j + 2] == valueToCheck && OwnedTiles[i][j + 3] == valueToCheck) { return true; } } } for (int i = 0; i < ROWS - 3; i++) { for (int j = 0; j < COLUMNS; j++) { if (OwnedTiles[i][j] == valueToCheck && OwnedTiles[i + 1][j] == valueToCheck && OwnedTiles[i + 2][j] == valueToCheck && OwnedTiles[i + 3][j] == valueToCheck) { return true; } } } for (int i = 3; i < ROWS; i++) { for (int j = 0; j < COLUMNS - 3; j++) { if (OwnedTiles[i][j] == valueToCheck && OwnedTiles[i - 1][j + 1] == valueToCheck && OwnedTiles[i - 2][j + 2] == valueToCheck && OwnedTiles[i - 3][j + 3] == valueToCheck) { return true; } } } for (int i = 3; i < ROWS; i++) { for (int j = 3; j < COLUMNS; j++) { if (OwnedTiles[i][j] == valueToCheck && OwnedTiles[i - 1][j - 1] == valueToCheck && OwnedTiles[i - 2][j - 2] == valueToCheck && OwnedTiles[i - 3][j - 3] == valueToCheck) { return true; } } } return false; } } }