Files
PlayerBlockLife/src/main/java/com/playerblocklife/PlayerBlockManager.java
xiaobai fef26e5b79 1.0.0
2026-02-13 18:50:05 +08:00

593 lines
19 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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<UUID, List<Location>> playerBlocks = new ConcurrentHashMap<>();
private final Map<Location, UUID> blockOwners = new ConcurrentHashMap<>();
private final Map<UUID, Material> 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) {
UUID playerId = player.getUniqueId();
String playerName = player.getName();
// 检查是否已有生命方块
if (hasLifeBlocks(playerId)) {
player.sendMessage("§c你已经有生命方块了");
player.sendMessage("§e使用 /checklifeblocks 查看位置");
return false;
}
// 检查玩家皮肤是否已加载
if (!skinManager.isSkinLoaded(playerId)) {
player.sendMessage("§e你的皮肤正在加载中请稍候...");
player.sendMessage("§7(如果长时间未加载完成,请重新加入服务器)");
return false;
}
List<Location> blocks = new ArrayList<>();
int blocksPlaced = 0;
// 尝试在中心周围生成5个方块
for (int attempt = 0; attempt < 20 && blocksPlaced < 5; attempt++) {
Location blockLoc = findSuitableLocation(center);
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();
player.sendMessage("§a========================================");
player.sendMessage("§a成功生成 §e" + blocksPlaced + " §a个生命方块");
player.sendMessage("§6方块使用了你的皮肤头像");
player.sendMessage("§c⚠ 警告: 方块被挖光时,你将死亡!");
player.sendMessage("§7使用 /checklifeblocks 查看方块位置");
player.sendMessage("§a========================================");
return true;
} else {
player.sendMessage("§c无法生成生命方块");
player.sendMessage("§7请确保周围有足够的空间至少5个可放置位置");
return false;
}
}
/**
* 寻找合适的位置
*/
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<Location> 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<Location> blocks = playerBlocks.get(playerId);
return blocks != null ? blocks.size() : 0;
}
/**
* 检查玩家是否有生命方块
*/
public boolean hasLifeBlocks(UUID playerId) {
List<Location> blocks = playerBlocks.get(playerId);
return blocks != null && !blocks.isEmpty();
}
/**
* 获取玩家的所有生命方块位置
*/
public List<Location> getPlayerBlocks(UUID playerId) {
return playerBlocks.getOrDefault(playerId, new ArrayList<>());
}
/**
* 清除玩家的所有生命方块
*/
public void clearPlayerBlocks(UUID playerId) {
List<Location> 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<Location> blocks : playerBlocks.values()) {
total += blocks.size();
}
return total;
}
/**
* 加载数据
*/
@SuppressWarnings("unchecked")
public void loadData() {
if (!dataFile.exists()) {
return;
}
try {
Yaml yaml = new Yaml();
Map<String, Object> data = yaml.load(new FileReader(dataFile));
if (data == null) {
return;
}
// 加载方块数据
if (data.containsKey("blocks")) {
Map<String, List<Map<String, Object>>> blocksData =
(Map<String, List<Map<String, Object>>>) data.get("blocks");
for (Map.Entry<String, List<Map<String, Object>>> entry : blocksData.entrySet()) {
UUID playerId = UUID.fromString(entry.getKey());
List<Location> locations = new ArrayList<>();
for (Map<String, Object> 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<String, Object> data = new HashMap<>();
Map<String, List<Map<String, Object>>> blocksData = new HashMap<>();
// 保存方块数据
for (Map.Entry<UUID, List<Location>> entry : playerBlocks.entrySet()) {
String playerId = entry.getKey().toString();
List<Map<String, Object>> locations = new ArrayList<>();
for (Location loc : entry.getValue()) {
Map<String, Object> 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<UUID, List<Location>> getAllPlayerBlocks() {
return new HashMap<>(playerBlocks);
}
/**
* 获取所有方块的位置和所有者
*/
public Map<Location, UUID> getAllBlockOwners() {
return new HashMap<>(blockOwners);
}
/**
* 检查位置是否包含生命方块
*/
public boolean isLifeBlock(Location location) {
return blockOwners.containsKey(location);
}
/**
* 获取玩家的生命方块位置列表(用于显示)
*/
public List<String> getPlayerBlockLocations(UUID playerId) {
List<Location> blocks = playerBlocks.get(playerId);
List<String> 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<Location> 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<String, Object> getStats() {
Map<String, Object> stats = new HashMap<>();
stats.put("totalPlayers", playerBlocks.size());
stats.put("totalBlocks", blockOwners.size());
// 按世界统计
Map<String, Integer> 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;
}
}