package com.playerblocklife; import org.bukkit.*; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.block.Skull; import org.bukkit.block.data.BlockData; import org.bukkit.block.data.Rotatable; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.SkullMeta; import org.bukkit.util.Vector; import org.yaml.snakeyaml.Yaml; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.util.*; import java.util.concurrent.ConcurrentHashMap; public class PlayerBlockManager { private final PlayerBlockLife plugin; private final SkinManager skinManager; private final Map> playerBlocks = new ConcurrentHashMap<>(); private final Map blockOwners = new ConcurrentHashMap<>(); private final Map playerBlockTypes = new ConcurrentHashMap<>(); private final File dataFile; private final Random random = new Random(); public PlayerBlockManager(PlayerBlockLife plugin, SkinManager skinManager) { this.plugin = plugin; this.skinManager = skinManager; this.dataFile = new File(plugin.getDataFolder(), "blockdata.yml"); } /** * 为玩家设置生命方块(兼容旧方法) */ public boolean setLifeBlocks(Player player, Location center) { ConfigManager config = plugin.getConfigManager(); int blockAmount = config.getBlocksPerPlayer(); int spreadRange = config.getSpreadRange(); boolean requireOpenSky = config.isRequireOpenSky(); int maxAttempts = config.getMaxAttempts(); return generateLifeBlocksForPlayer(player, blockAmount, spreadRange, requireOpenSky, maxAttempts); } /** * 为玩家生成生命方块(新方法,支持自动生成) */ public boolean generateLifeBlocksForPlayer(Player player, int blockAmount, int spreadRange, boolean requireOpenSky, int maxAttempts) { UUID playerId = player.getUniqueId(); String playerName = player.getName(); // 检查是否已有生命方块 if (hasLifeBlocks(playerId)) { return false; } // 检查玩家皮肤是否已加载 if (!skinManager.isSkinLoaded(playerId)) { return false; } List blocks = new ArrayList<>(); int blocksPlaced = 0; int attempts = 0; // 尝试生成指定数量的方块 while (blocksPlaced < blockAmount && attempts < maxAttempts) { Location blockLoc = findSurfaceLocation(player.getLocation(), spreadRange, requireOpenSky); attempts++; if (blockLoc != null && placePlayerHead(blockLoc, playerId, playerName)) { blocks.add(blockLoc); blockOwners.put(blockLoc, playerId); blocksPlaced++; // 添加放置效果 spawnPlaceEffects(blockLoc); } } if (blocksPlaced > 0) { playerBlocks.put(playerId, blocks); saveData(); return true; } else { return false; } } /** * 寻找地表位置(上方无方块覆盖) */ private Location findSurfaceLocation(Location center, int spreadRange, boolean requireOpenSky) { for (int i = 0; i < 10; i++) { int x = random.nextInt(spreadRange * 2 + 1) - spreadRange; int z = random.nextInt(spreadRange * 2 + 1) - spreadRange; // 从中心点上方开始向下寻找地表 Location testLoc = center.clone().add(x, 10, z); World world = testLoc.getWorld(); if (world == null) continue; // 向下寻找第一个非空气方块 Block groundBlock = null; for (int y = 10; y > world.getMinHeight(); y--) { testLoc.setY(y); Block block = testLoc.getBlock(); if (!block.getType().isAir()) { groundBlock = block; break; } } if (groundBlock == null) continue; // 检查地表方块上方位置 Location surfaceLoc = groundBlock.getLocation().add(0, 1, 0); Block surfaceBlock = surfaceLoc.getBlock(); // 检查是否已有方块 if (blockOwners.containsKey(surfaceLoc)) { continue; } // 检查地表方块是否合适 if (!isSuitableLocation(surfaceLoc)) { continue; } // 如果需要上方无方块覆盖,检查上方 if (requireOpenSky) { boolean hasCover = false; for (int y = 1; y <= 5; y++) { Block aboveBlock = surfaceLoc.clone().add(0, y, 0).getBlock(); if (!aboveBlock.getType().isAir()) { hasCover = true; break; } } if (hasCover) continue; } return surfaceLoc; } return null; } /** * 寻找合适的位置 */ private Location findSuitableLocation(Location center) { for (int i = 0; i < 10; i++) { int x = random.nextInt(11) - 5; // -5 到 5 int z = random.nextInt(11) - 5; Location testLoc = center.clone().add(x, -1, z); Block block = testLoc.getBlock(); // 检查位置是否合适 if (isSuitableLocation(testLoc)) { return testLoc; } } return null; } /** * 检查位置是否合适 * 修复:移除 isReplaceable() 方法调用 */ private boolean isSuitableLocation(Location location) { Block block = location.getBlock(); // 检查是否已有方块 if (blockOwners.containsKey(location)) { return false; } // 检查方块是否可替换(修复了 isReplaceable() 方法不存在的问题) Material type = block.getType(); if (!type.isAir()) { // 检查是否是固体方块,固体方块不能替换 if (type.isSolid()) { return false; } // 检查是否是液体 if (type == Material.WATER || type == Material.LAVA || type == Material.WATER_CAULDRON || type == Material.LAVA_CAULDRON) { return false; } // 检查一些特定的不可替换方块 switch (type) { case FIRE: case SOUL_FIRE: case COBWEB: case BAMBOO: case BAMBOO_SAPLING: case SCAFFOLDING: case LADDER: case VINE: case TWISTING_VINES: case WEEPING_VINES: case GLOW_LICHEN: // 这些方块可以替换 break; default: // 如果不是固体,也不是特定的透明方块,也不是空气,则检查是否是透明方块 if (!type.isTransparent()) { return false; } break; } } // 检查下方是否有支撑方块 Block below = location.clone().add(0, -1, 0).getBlock(); Material belowType = below.getType(); if (belowType.isAir() || !belowType.isSolid()) { return false; } // 检查是否在水或岩浆中 if (block.isLiquid() || below.isLiquid()) { return false; } return true; } /** * 放置玩家头颅方块 */ private boolean placePlayerHead(Location location, UUID playerId, String playerName) { try { Block block = location.getBlock(); // 检查方块是否已被占用 if (blockOwners.containsKey(location)) { return false; } // 设置方块为玩家头颅 block.setType(Material.PLAYER_HEAD); // 获取并设置头颅数据 Skull skullState = (Skull) block.getState(); OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(playerId); // 设置头颅所有者 skullState.setOwningPlayer(offlinePlayer); // 设置朝向(随机方向) BlockData blockData = block.getBlockData(); if (blockData instanceof Rotatable) { Rotatable rotatable = (Rotatable) blockData; BlockFace[] faces = {BlockFace.NORTH, BlockFace.EAST, BlockFace.SOUTH, BlockFace.WEST}; rotatable.setRotation(faces[random.nextInt(faces.length)]); block.setBlockData(rotatable); } // 更新方块 skullState.update(true, false); return true; } catch (Exception e) { plugin.logError("放置玩家头颅失败: " + location, e); return false; } } /** * 生成放置效果 */ private void spawnPlaceEffects(Location location) { World world = location.getWorld(); if (world == null) return; // 粒子效果 world.spawnParticle(Particle.ENCHANTMENT_TABLE, location.clone().add(0.5, 0.5, 0.5), 30, 0.3, 0.3, 0.3, 0.1); // 音效 world.playSound(location, Sound.BLOCK_ANVIL_PLACE, 0.5f, 1.2f); } /** * 检查方块是否属于某个玩家 */ public UUID getBlockOwner(Location location) { return blockOwners.get(location); } /** * 移除方块(当被挖掘时) */ public boolean removeBlock(Location location, Player breaker) { UUID ownerId = blockOwners.get(location); if (ownerId == null) { return false; } List blocks = playerBlocks.get(ownerId); if (blocks == null || !blocks.contains(location)) { return false; } // 移除方块 blocks.remove(location); blockOwners.remove(location); // 设置方块为空气 location.getBlock().setType(Material.AIR); // 生成破坏效果 spawnBreakEffects(location, breaker); // 检查玩家是否还有剩余方块 int remaining = blocks.size(); // 通知所有相关玩家 notifyBlockBreak(ownerId, breaker, remaining); // 保存数据 saveData(); return true; } /** * 生成破坏效果 */ private void spawnBreakEffects(Location location, Player breaker) { World world = location.getWorld(); if (world == null) return; // 粒子效果 world.spawnParticle(Particle.BLOCK_CRACK, location.clone().add(0.5, 0.5, 0.5), 50, 0.5, 0.5, 0.5, 0.5, Material.PLAYER_HEAD.createBlockData()); world.spawnParticle(Particle.SMOKE_LARGE, location.clone().add(0.5, 0.5, 0.5), 20, 0.3, 0.3, 0.3, 0.05); // 音效 world.playSound(location, Sound.ENTITY_ITEM_BREAK, 1.0f, 0.8f); world.playSound(location, Sound.BLOCK_GLASS_BREAK, 0.8f, 1.0f); // 对挖掘者造成轻微击退 if (breaker != null) { Location breakerLoc = breaker.getLocation(); Vector direction = location.toVector().subtract(breakerLoc.toVector()).normalize(); breaker.setVelocity(direction.multiply(-0.5).setY(0.3)); } } /** * 通知方块被破坏 */ private void notifyBlockBreak(UUID ownerId, Player breaker, int remaining) { Player owner = Bukkit.getPlayer(ownerId); String ownerName = Bukkit.getOfflinePlayer(ownerId).getName(); // 通知方块所有者 if (owner != null && owner.isOnline()) { owner.sendMessage("§c⚠ 警告!你的生命方块被破坏了!"); owner.sendMessage("§7破坏者: §e" + (breaker != null ? breaker.getName() : "未知")); owner.sendMessage("§7剩余生命方块: §a" + remaining + " §7/ §c5"); if (remaining <= 2) { owner.sendMessage("§4⚠ 警告!生命方块即将耗尽!"); } // 播放警告音效 owner.playSound(owner.getLocation(), Sound.ENTITY_ENDERMAN_TELEPORT, 0.8f, 0.5f); } // 通知破坏者 if (breaker != null && !breaker.getUniqueId().equals(ownerId)) { breaker.sendMessage("§6你破坏了一个生命方块!"); breaker.sendMessage("§7所有者: §e" + (ownerName != null ? ownerName : "未知玩家")); breaker.sendMessage("§7对方剩余生命方块: §a" + remaining); // 给予挖掘者经验奖励 breaker.giveExp(5); breaker.playSound(breaker.getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1.0f, 1.0f); } // 广播给附近玩家 World world = owner != null ? owner.getWorld() : (breaker != null ? breaker.getWorld() : null); if (world != null) { for (Player nearby : world.getPlayers()) { if (nearby != owner && nearby != breaker && nearby.getLocation().distance(owner != null ? owner.getLocation() : breaker.getLocation()) < 30) { nearby.sendMessage("§7[附近] §e一个生命方块被破坏了!"); } } } } /** * 获取玩家剩余方块数量 */ public int getRemainingBlocks(UUID playerId) { List blocks = playerBlocks.get(playerId); return blocks != null ? blocks.size() : 0; } /** * 检查玩家是否有生命方块 */ public boolean hasLifeBlocks(UUID playerId) { List blocks = playerBlocks.get(playerId); return blocks != null && !blocks.isEmpty(); } /** * 获取玩家的所有生命方块位置 */ public List getPlayerBlocks(UUID playerId) { return playerBlocks.getOrDefault(playerId, new ArrayList<>()); } /** * 清除玩家的所有生命方块 */ public void clearPlayerBlocks(UUID playerId) { List blocks = playerBlocks.remove(playerId); if (blocks != null) { for (Location loc : blocks) { blockOwners.remove(loc); loc.getBlock().setType(Material.AIR); } } saveData(); } /** * 重新生成玩家的生命方块 */ public boolean regeneratePlayerBlocks(Player player) { UUID playerId = player.getUniqueId(); // 清除旧方块 clearPlayerBlocks(playerId); // 生成新方块 return setLifeBlocks(player, player.getLocation()); } /** * 获取已注册玩家的数量 */ public int getPlayerBlocksCount() { return playerBlocks.size(); } /** * 获取总方块数量 */ public int getTotalBlocksCount() { int total = 0; for (List blocks : playerBlocks.values()) { total += blocks.size(); } return total; } /** * 加载数据 */ @SuppressWarnings("unchecked") public void loadData() { if (!dataFile.exists()) { return; } try { Yaml yaml = new Yaml(); Map data = yaml.load(new FileReader(dataFile)); if (data == null) { return; } // 加载方块数据 if (data.containsKey("blocks")) { Map>> blocksData = (Map>>) data.get("blocks"); for (Map.Entry>> entry : blocksData.entrySet()) { UUID playerId = UUID.fromString(entry.getKey()); List locations = new ArrayList<>(); for (Map locData : entry.getValue()) { String worldName = (String) locData.get("world"); double x = (double) locData.get("x"); double y = (double) locData.get("y"); double z = (double) locData.get("z"); World world = Bukkit.getWorld(worldName); if (world != null) { Location location = new Location(world, x, y, z); locations.add(location); blockOwners.put(location, playerId); } } if (!locations.isEmpty()) { playerBlocks.put(playerId, locations); } } } plugin.logInfo("已加载 " + playerBlocks.size() + " 个玩家的方块数据"); plugin.logInfo("总共 " + blockOwners.size() + " 个生命方块"); } catch (Exception e) { plugin.logError("加载方块数据失败", e); } } /** * 保存数据 */ public void saveData() { try { Map data = new HashMap<>(); Map>> blocksData = new HashMap<>(); // 保存方块数据 for (Map.Entry> entry : playerBlocks.entrySet()) { String playerId = entry.getKey().toString(); List> locations = new ArrayList<>(); for (Location loc : entry.getValue()) { Map locData = new HashMap<>(); locData.put("world", loc.getWorld().getName()); locData.put("x", loc.getX()); locData.put("y", loc.getY()); locData.put("z", loc.getZ()); locations.add(locData); } blocksData.put(playerId, locations); } data.put("blocks", blocksData); // 写入文件 Yaml yaml = new Yaml(); yaml.dump(data, new FileWriter(dataFile)); } catch (Exception e) { plugin.logError("保存方块数据失败", e); } } /** * 获取所有玩家的方块数据 */ public Map> getAllPlayerBlocks() { return new HashMap<>(playerBlocks); } /** * 获取所有方块的位置和所有者 */ public Map getAllBlockOwners() { return new HashMap<>(blockOwners); } /** * 检查位置是否包含生命方块 */ public boolean isLifeBlock(Location location) { return blockOwners.containsKey(location); } /** * 获取玩家的生命方块位置列表(用于显示) */ public List getPlayerBlockLocations(UUID playerId) { List blocks = playerBlocks.get(playerId); List locations = new ArrayList<>(); if (blocks != null) { for (Location loc : blocks) { String worldName = loc.getWorld() != null ? loc.getWorld().getName() : "未知世界"; locations.add(String.format("世界: %s, 坐标: %d, %d, %d", worldName, loc.getBlockX(), loc.getBlockY(), loc.getBlockZ())); } } return locations; } /** * 获取距离玩家最近的方块 */ public Location getNearestBlock(Player player) { UUID playerId = player.getUniqueId(); List blocks = playerBlocks.get(playerId); if (blocks == null || blocks.isEmpty()) { return null; } Location nearest = null; double nearestDistance = Double.MAX_VALUE; for (Location block : blocks) { double distance = player.getLocation().distance(block); if (distance < nearestDistance) { nearestDistance = distance; nearest = block; } } return nearest; } /** * 获取所有生命方块的统计信息 */ public Map getStats() { Map stats = new HashMap<>(); stats.put("totalPlayers", playerBlocks.size()); stats.put("totalBlocks", blockOwners.size()); // 按世界统计 Map blocksPerWorld = new HashMap<>(); for (Location loc : blockOwners.keySet()) { String worldName = loc.getWorld() != null ? loc.getWorld().getName() : "unknown"; blocksPerWorld.put(worldName, blocksPerWorld.getOrDefault(worldName, 0) + 1); } stats.put("blocksPerWorld", blocksPerWorld); return stats; } }