2.2.0-1.20.4
将生命方块由玩家头换为其他原版多颜色方块
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package com.playerblocklife;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
@@ -8,77 +9,72 @@ import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* AdminCommands - PlayerBlockLife游戏模式的管理员命令处理器
|
||||
*
|
||||
* <p>处理插件的传统管理员命令,适配新的PlayerBlockLife游戏模式:<br>
|
||||
* <ul>
|
||||
* <li>/pblreload - 重载插件配置</li>
|
||||
* <li>/pbldelete - 删除指定玩家的生命方块</li>
|
||||
* <li>/pblrevive - 复活被淘汰的玩家</li>
|
||||
* <li>/pblstats - 显示PlayerBlockLife游戏统计信息</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>这些命令在新模式下会与GameStateManager交互,以正确管理游戏状态。</p>
|
||||
*
|
||||
* @author xiaobai
|
||||
* @version 2.2.0-1.20.4
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class AdminCommands implements CommandExecutor {
|
||||
private final PlayerBlockLife plugin;
|
||||
|
||||
/**
|
||||
* 构造一个新的管理员命令执行器
|
||||
*
|
||||
* @param plugin 插件主类实例
|
||||
*/
|
||||
public AdminCommands(PlayerBlockLife plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
MessageManager msgManager = plugin.getMessageManager();
|
||||
ConfigManager config = plugin.getConfigManager();
|
||||
|
||||
String commandName = command.getName().toLowerCase();
|
||||
|
||||
// 检查命令是否启用
|
||||
if (!config.isCommandEnabled(commandName)) {
|
||||
String message = msgManager.getMessage("game.errors.command_disabled",
|
||||
"&c此命令已被禁用!");
|
||||
sender.sendMessage(message);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 检查管理员权限和配置
|
||||
if (!sender.hasPermission("playerblocklife.admin") || config.isAdminOnly(commandName)) {
|
||||
String message = msgManager.getMessage("game.errors.no_permission",
|
||||
"&c你没有权限使用此命令!");
|
||||
sender.sendMessage(message);
|
||||
if (!sender.hasPermission("playerblocklife.admin")) {
|
||||
sender.sendMessage(ChatColor.RED + "你没有权限使用此命令!");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (commandName.equals("pblreload")) {
|
||||
// 调用插件的完整重载方法
|
||||
// 重载配置
|
||||
plugin.reloadPluginConfig();
|
||||
String message = msgManager.getCommandMessage("pblreload", "success",
|
||||
"&a配置已重载!");
|
||||
sender.sendMessage(message);
|
||||
sender.sendMessage(ChatColor.GREEN + "配置已重载!");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (commandName.equals("pbldelete")) {
|
||||
if (args.length < 1) {
|
||||
String usage = msgManager.getCommandMessage("pbldelete", "usage",
|
||||
"&c用法: /pbldelete <玩家>");
|
||||
sender.sendMessage(usage);
|
||||
sender.sendMessage(ChatColor.RED + "用法: /pbldelete <玩家>");
|
||||
return true;
|
||||
}
|
||||
|
||||
String targetName = args[0];
|
||||
Player target = Bukkit.getPlayer(targetName);
|
||||
UUID targetId;
|
||||
|
||||
if (target != null) {
|
||||
targetId = target.getUniqueId();
|
||||
} else {
|
||||
// 尝试从离线玩家获取UUID
|
||||
try {
|
||||
targetId = Bukkit.getOfflinePlayer(targetName).getUniqueId();
|
||||
} catch (Exception e) {
|
||||
String message = msgManager.getMessage("game.errors.player_not_found",
|
||||
"&c找不到玩家: {player}");
|
||||
message = message.replace("{player}", targetName);
|
||||
sender.sendMessage(message);
|
||||
return true;
|
||||
}
|
||||
if (target == null) {
|
||||
sender.sendMessage(ChatColor.RED + "找不到玩家: " + targetName);
|
||||
return true;
|
||||
}
|
||||
|
||||
plugin.getBlockManager().clearPlayerBlocks(targetId);
|
||||
String message = msgManager.getCommandMessage("pbldelete", "success",
|
||||
"&a已删除玩家 {player} 的生命方块!");
|
||||
message = message.replace("{player}", targetName);
|
||||
sender.sendMessage(message);
|
||||
// 在新模式下,我们使用GameStateManager来清除玩家方块
|
||||
// 清除玩家的方块数据
|
||||
UUID targetId = target.getUniqueId();
|
||||
plugin.getGameStateManager().clearPlayerBlocks(targetId);
|
||||
|
||||
sender.sendMessage(ChatColor.GREEN + "已删除玩家 " + targetName + " 的生命方块!");
|
||||
target.sendMessage(ChatColor.YELLOW + "你的生命方块已被管理员清除。");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -87,87 +83,98 @@ public class AdminCommands implements CommandExecutor {
|
||||
|
||||
if (args.length < 1) {
|
||||
if (!(sender instanceof Player)) {
|
||||
String usage = msgManager.getCommandMessage("pblrevive", "usage",
|
||||
"&c用法: /pblrevive [玩家]");
|
||||
sender.sendMessage(usage);
|
||||
sender.sendMessage(ChatColor.RED + "用法: /pblrevive <玩家>");
|
||||
return true;
|
||||
}
|
||||
target = (Player) sender;
|
||||
} else {
|
||||
target = Bukkit.getPlayer(args[0]);
|
||||
if (target == null) {
|
||||
String message = msgManager.getMessage("game.errors.player_offline",
|
||||
"&c玩家 {player} 不在线!");
|
||||
message = message.replace("{player}", args[0]);
|
||||
sender.sendMessage(message);
|
||||
sender.sendMessage(ChatColor.RED + "玩家 " + args[0] + " 不在线!");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (plugin.getLifeSystem() != null) {
|
||||
plugin.getLifeSystem().revivePlayer(target);
|
||||
String message = msgManager.getCommandMessage("pblrevive", "success",
|
||||
"&a玩家 {player} 已复活!");
|
||||
message = message.replace("{player}", target.getName());
|
||||
sender.sendMessage(message);
|
||||
// 在新模式下,复活玩家需要将其从淘汰列表中移除并恢复生存模式
|
||||
UUID targetId = target.getUniqueId();
|
||||
if (plugin.getGameStateManager().getRemainingBlocks(targetId) <= 0) {
|
||||
// 如果玩家已被淘汰,需要将其重新添加到存活玩家列表
|
||||
java.util.List<UUID> alivePlayers = plugin.getGameStateManager().getAlivePlayers();
|
||||
if (!alivePlayers.contains(targetId)) {
|
||||
// 在新模式下,复活需要重新生成方块或将其重新加入游戏
|
||||
sender.sendMessage(ChatColor.RED + "在新模式下,复活功能需要重新开始游戏或为玩家重新生成方块。");
|
||||
sender.sendMessage(ChatColor.YELLOW + "建议使用 /pbl rstgm 重置游戏。");
|
||||
} else {
|
||||
target.setGameMode(org.bukkit.GameMode.SURVIVAL);
|
||||
target.sendMessage(ChatColor.GREEN + "你已被复活!");
|
||||
sender.sendMessage(ChatColor.GREEN + "玩家 " + target.getName() + " 已被复活!");
|
||||
}
|
||||
} else {
|
||||
sender.sendMessage("§c复活失败:生命系统未初始化");
|
||||
target.setGameMode(org.bukkit.GameMode.SURVIVAL);
|
||||
target.sendMessage(ChatColor.GREEN + "你已被复活!");
|
||||
sender.sendMessage(ChatColor.GREEN + "玩家 " + target.getName() + " 已被复活!");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (commandName.equals("pblstats")) {
|
||||
if (plugin.getBlockManager() == null) {
|
||||
sender.sendMessage("§c方块管理器未初始化");
|
||||
return true;
|
||||
}
|
||||
|
||||
int totalPlayers = plugin.getBlockManager().getPlayerBlocksCount();
|
||||
int totalBlocks = plugin.getBlockManager().getTotalBlocksCount();
|
||||
// 显示新模式下的游戏统计
|
||||
GameStateManager gameStateManager = plugin.getGameStateManager();
|
||||
GameStateManager.GameState currentState = gameStateManager.getCurrentState();
|
||||
|
||||
// 获取统计标题
|
||||
String title = msgManager.getCommandMessage("pblstats", "title",
|
||||
"&6=== PlayerBlockLife 统计 ===");
|
||||
sender.sendMessage(title);
|
||||
sender.sendMessage(ChatColor.GOLD + "=== PlayerBlockLife 游戏统计 ===");
|
||||
sender.sendMessage(ChatColor.AQUA + "游戏状态: " + getStateText(currentState));
|
||||
|
||||
// 在线玩家统计
|
||||
String onlineMsg = msgManager.getCommandMessage("pblstats", "online_players",
|
||||
"&e在线玩家: {count}");
|
||||
onlineMsg = onlineMsg.replace("{count}", String.valueOf(Bukkit.getOnlinePlayers().size()));
|
||||
sender.sendMessage(onlineMsg);
|
||||
int onlineCount = Bukkit.getOnlinePlayers().size();
|
||||
int opCount = 0;
|
||||
int participantCount = 0;
|
||||
|
||||
// 总方块统计
|
||||
String blocksMsg = msgManager.getCommandMessage("pblstats", "total_blocks",
|
||||
"&e总生命方块: {count}");
|
||||
blocksMsg = blocksMsg.replace("{count}", String.valueOf(totalBlocks));
|
||||
sender.sendMessage(blocksMsg);
|
||||
|
||||
// 淘汰玩家统计
|
||||
int eliminatedCount = 0;
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
if (plugin.getBlockManager().getRemainingBlocks(player.getUniqueId()) == 0) {
|
||||
eliminatedCount++;
|
||||
if (player.isOp()) {
|
||||
opCount++;
|
||||
} else {
|
||||
participantCount++;
|
||||
}
|
||||
}
|
||||
String eliminatedMsg = msgManager.getCommandMessage("pblstats", "eliminated_players",
|
||||
"&e已淘汰玩家: {count}");
|
||||
eliminatedMsg = eliminatedMsg.replace("{count}", String.valueOf(eliminatedCount));
|
||||
sender.sendMessage(eliminatedMsg);
|
||||
|
||||
sender.sendMessage("§7在线玩家详情:");
|
||||
sender.sendMessage(ChatColor.YELLOW + "在线玩家: " + onlineCount +
|
||||
" (参与者: " + participantCount + ", OP: " + opCount + ")");
|
||||
|
||||
if (currentState == GameStateManager.GameState.STARTED) {
|
||||
int aliveCount = gameStateManager.getAlivePlayersCount();
|
||||
sender.sendMessage(ChatColor.GREEN + "存活玩家: " + aliveCount);
|
||||
|
||||
if (gameStateManager.isLimitedTime()) {
|
||||
long currentTime = System.currentTimeMillis();
|
||||
long elapsedMinutes = (currentTime - gameStateManager.getGameStartTime()) / (1000 * 60);
|
||||
long remainingMinutes = Math.max(0, gameStateManager.getGameDuration() - elapsedMinutes);
|
||||
sender.sendMessage(ChatColor.YELLOW + "剩余时间: " + remainingMinutes + " 分钟");
|
||||
}
|
||||
}
|
||||
|
||||
sender.sendMessage(ChatColor.GRAY + "-------------------");
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
int blocks = plugin.getBlockManager().getRemainingBlocks(player.getUniqueId());
|
||||
String status = blocks > 0 ? "§a存活" : "§c已淘汰";
|
||||
sender.sendMessage("§7- " + player.getName() + ": §e" + blocks + " §7/ §a5 §7(" + status + "§7)");
|
||||
int blocks = gameStateManager.getRemainingBlocks(player.getUniqueId());
|
||||
String status = player.isOp() ? ChatColor.GOLD + "OP" :
|
||||
(blocks > 0 ? ChatColor.GREEN + "存活" : ChatColor.RED + "淘汰");
|
||||
sender.sendMessage(ChatColor.WHITE + "- " + player.getName() + ": " +
|
||||
ChatColor.YELLOW + blocks + "/5 " +
|
||||
ChatColor.GRAY + "(" + status + ChatColor.GRAY + ")");
|
||||
}
|
||||
|
||||
sender.sendMessage("§a=================================");
|
||||
return true;
|
||||
}
|
||||
|
||||
String unknownMsg = msgManager.getMessage("game.errors.invalid_arguments",
|
||||
"&c未知的管理员命令!");
|
||||
sender.sendMessage(unknownMsg);
|
||||
sender.sendMessage(ChatColor.RED + "未知的管理员命令!");
|
||||
return true;
|
||||
}
|
||||
|
||||
private String getStateText(GameStateManager.GameState state) {
|
||||
switch (state) {
|
||||
case WAITING: return ChatColor.YELLOW + "等待中";
|
||||
case STARTED: return ChatColor.GREEN + "进行中";
|
||||
case FINISHED: return ChatColor.RED + "已结束";
|
||||
default: return ChatColor.GRAY + "未知";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,114 +16,159 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* BlockBreakListener - PlayerBlockLife游戏模式下的方块破坏和放置监听器
|
||||
*
|
||||
* <p>监听玩家对生命方块的破坏行为,并在PlayerBlockLife游戏模式下进行相应处理:
|
||||
* <ul>
|
||||
* <li>检查被破坏的方块是否为生命方块(羊毛、玻璃、水泥)</li>
|
||||
* <li>验证破坏者是否有权限破坏该方块</li>
|
||||
* <li>更新玩家剩余生命方块数量</li>
|
||||
* <li>处理玩家淘汰逻辑</li>
|
||||
* <li>在游戏未开始时阻止方块破坏</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>此监听器与GameStateManager协作,确保游戏规则正确执行。</p>
|
||||
*
|
||||
* @author xiaobai
|
||||
* @version 2.2.0-1.20.4
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class BlockBreakListener implements Listener {
|
||||
private final PlayerBlockLife plugin;
|
||||
|
||||
/**
|
||||
* 构造一个新的方块破坏监听器
|
||||
*
|
||||
* @param plugin 插件主类实例
|
||||
*/
|
||||
public BlockBreakListener(PlayerBlockLife plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理方块被破坏的事件
|
||||
*
|
||||
* @param event 方块破坏事件
|
||||
*/
|
||||
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
|
||||
public void onBlockBreak(BlockBreakEvent event) {
|
||||
Player breaker = event.getPlayer();
|
||||
Block block = event.getBlock();
|
||||
Location location = block.getLocation();
|
||||
|
||||
if (block.getType() != Material.PLAYER_HEAD &&
|
||||
block.getType() != Material.PLAYER_WALL_HEAD) {
|
||||
// 检查是否为生命方块(羊毛、玻璃或水泥块)
|
||||
if (!isLifeBlockMaterial(block.getType())) {
|
||||
return;
|
||||
}
|
||||
|
||||
UUID ownerId = plugin.getBlockManager().getBlockOwner(location);
|
||||
if (ownerId == null) {
|
||||
// 检查游戏是否已开始
|
||||
if (plugin.getGameStateManager().getCurrentState() != GameStateManager.GameState.STARTED) {
|
||||
event.setCancelled(true);
|
||||
breaker.sendMessage(org.bukkit.ChatColor.RED + "游戏尚未开始,无法破坏方块!");
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查是否为玩家的生命方块
|
||||
UUID ownerId = getBlockOwner(location);
|
||||
if (ownerId == null) {
|
||||
return; // 不是生命方块
|
||||
}
|
||||
|
||||
if (ownerId.equals(breaker.getUniqueId())) {
|
||||
if (breaker.getGameMode() != GameMode.CREATIVE) {
|
||||
String message = plugin.getMessageManager().getMessage("game.errors.cannot_break_own_block",
|
||||
"&c你不能挖掘自己的生命方块!");
|
||||
breaker.sendMessage(message);
|
||||
breaker.sendMessage(org.bukkit.ChatColor.RED + "你不能挖掘自己的生命方块!");
|
||||
event.setCancelled(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (plugin.getBlockManager().removeBlock(location, breaker)) {
|
||||
int remaining = plugin.getBlockManager().getRemainingBlocks(ownerId);
|
||||
if (plugin.getGameStateManager().removeBlock(location, breaker)) {
|
||||
int remaining = plugin.getGameStateManager().getRemainingBlocks(ownerId);
|
||||
Player owner = Bukkit.getPlayer(ownerId);
|
||||
String ownerName = owner != null ? owner.getName() : Bukkit.getOfflinePlayer(ownerId).getName();
|
||||
|
||||
if (remaining <= 0) {
|
||||
plugin.getLifeSystem().handlePlayerDeath(ownerId);
|
||||
}
|
||||
|
||||
// 通知破坏者
|
||||
Map<String, String> variables = new HashMap<>();
|
||||
variables.put("owner", ownerName != null ? ownerName : "未知玩家");
|
||||
variables.put("remaining", String.valueOf(remaining));
|
||||
variables.put("total", String.valueOf(plugin.getConfigManager().getBlocksPerPlayer()));
|
||||
variables.put("total", String.valueOf(5)); // 使用固定值5,因为配置已移除
|
||||
|
||||
String breakerMsg = plugin.getMessageManager().getFormattedMessage("game.block.destroyed.breaker",
|
||||
"&a你破坏了 {owner} 的生命方块!", variables);
|
||||
breaker.sendMessage(breakerMsg);
|
||||
|
||||
String remainingMsg = plugin.getMessageManager().getFormattedMessage("game.block.destroyed.remaining",
|
||||
"&7剩余方块: {remaining}/{total}", variables);
|
||||
breaker.sendMessage(remainingMsg);
|
||||
breaker.sendMessage(org.bukkit.ChatColor.GREEN + "你破坏了 " + org.bukkit.ChatColor.YELLOW +
|
||||
(ownerName != null ? ownerName : "未知玩家") + org.bukkit.ChatColor.GREEN + " 的生命方块!");
|
||||
breaker.sendMessage(org.bukkit.ChatColor.GRAY + "剩余方块: " + org.bukkit.ChatColor.YELLOW +
|
||||
remaining + org.bukkit.ChatColor.GRAY + "/" + org.bukkit.ChatColor.RED + 5);
|
||||
|
||||
if (remaining == 1) {
|
||||
String lastBlockMsg = plugin.getMessageManager().getMessage("game.block.last_block_warning",
|
||||
"&6⚡ 对方只剩最后1个生命方块了!");
|
||||
breaker.sendMessage(lastBlockMsg);
|
||||
breaker.sendMessage(org.bukkit.ChatColor.GOLD + "⚡ 对方只剩最后1个生命方块了!");
|
||||
breaker.playSound(breaker.getLocation(),
|
||||
org.bukkit.Sound.ENTITY_PLAYER_LEVELUP, 1.0f, 1.5f);
|
||||
}
|
||||
|
||||
// 通知方块所有者
|
||||
if (owner != null && owner.isOnline()) {
|
||||
variables.put("breaker", breaker.getName());
|
||||
String ownerMsg = plugin.getMessageManager().getFormattedMessage("game.block.destroyed.owner",
|
||||
"&c⚠ 警告!你的生命方块被 {breaker} 破坏了!剩余: {remaining}/{total}", variables);
|
||||
owner.sendMessage(ownerMsg);
|
||||
owner.sendMessage(org.bukkit.ChatColor.RED + "⚠ 警告!你的生命方块被 " +
|
||||
org.bukkit.ChatColor.YELLOW + breaker.getName() + org.bukkit.ChatColor.RED + " 破坏了!");
|
||||
owner.sendMessage(org.bukkit.ChatColor.GRAY + "剩余方块: " + org.bukkit.ChatColor.YELLOW +
|
||||
remaining + org.bukkit.ChatColor.GRAY + "/" + org.bukkit.ChatColor.RED + 5);
|
||||
|
||||
if (remaining == 0) {
|
||||
String allDestroyedMsg = plugin.getMessageManager().getMessage("game.block.all_destroyed",
|
||||
"&c☠ 你的所有生命方块已被破坏!你已被淘汰!");
|
||||
owner.sendMessage(allDestroyedMsg);
|
||||
owner.sendMessage(org.bukkit.ChatColor.DARK_RED + "☠ 你的所有生命方块已被破坏!你已被淘汰!");
|
||||
}
|
||||
}
|
||||
|
||||
// 广播(如果启用)
|
||||
if (plugin.getConfigManager().isBroadcastOnBlockBreak()) {
|
||||
String broadcastMsg = plugin.getMessageManager().getFormattedMessage("broadcast.block_destroyed",
|
||||
"&6{breaker} &7破坏了 &c{owner} &7的生命方块!", variables);
|
||||
int range = plugin.getConfigManager().getBroadcastRange();
|
||||
for (Player nearby : breaker.getWorld().getPlayers()) {
|
||||
if (nearby.getLocation().distance(breaker.getLocation()) <= range &&
|
||||
nearby != breaker && (owner == null || nearby != owner)) {
|
||||
nearby.sendMessage(broadcastMsg);
|
||||
nearby.sendMessage(org.bukkit.ChatColor.GOLD + breaker.getName() +
|
||||
org.bukkit.ChatColor.GRAY + " 破坏了 " + org.bukkit.ChatColor.YELLOW +
|
||||
(ownerName != null ? ownerName : "某人") + org.bukkit.ChatColor.GRAY + " 的生命方块!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理方块被放置的事件
|
||||
*
|
||||
* @param event 方块放置事件
|
||||
*/
|
||||
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
|
||||
public void onBlockPlace(BlockPlaceEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
Block block = event.getBlock();
|
||||
|
||||
if (block.getType() == Material.PLAYER_HEAD ||
|
||||
block.getType() == Material.PLAYER_WALL_HEAD) {
|
||||
|
||||
UUID ownerId = plugin.getBlockManager().getBlockOwner(block.getLocation());
|
||||
if (ownerId != null && !ownerId.equals(player.getUniqueId())) {
|
||||
String message = plugin.getMessageManager().getMessage("game.errors.cannot_place_in_block_area",
|
||||
"&c你不能在这里放置方块,这是别人的生命方块区域!");
|
||||
player.sendMessage(message);
|
||||
event.setCancelled(true);
|
||||
}
|
||||
// 检查是否为生命方块材料
|
||||
if (isLifeBlockMaterial(block.getType())) {
|
||||
player.sendMessage(org.bukkit.ChatColor.RED + "你不能放置生命方块!");
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查材料是否为生命方块材料
|
||||
*/
|
||||
private boolean isLifeBlockMaterial(Material material) {
|
||||
return material.name().endsWith("_WOOL") ||
|
||||
material.name().endsWith("_STAINED_GLASS") ||
|
||||
material.name().endsWith("_CONCRETE");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取方块所有者
|
||||
*/
|
||||
private UUID getBlockOwner(Location location) {
|
||||
// 检查是否有任何玩家的方块在此位置
|
||||
for (Map.Entry<UUID, java.util.List<Location>> entry : plugin.getGameStateManager().getPlayerBlocks().entrySet()) {
|
||||
if (entry.getValue().contains(location)) {
|
||||
return entry.getKey();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,101 +1,94 @@
|
||||
package com.playerblocklife;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* CheckLifeBlocksCommand - 显示玩家在PlayerBlockLife游戏模式下的生命方块状态
|
||||
*
|
||||
* <p>在PlayerBlockLife游戏模式下,此命令显示玩家当前的剩余生命方块数、游戏状态和分配的颜色。
|
||||
* 与旧模式不同,此命令不再显示生命方块的具体位置,而是提供当前游戏状态信息。</p>
|
||||
*
|
||||
* @author xiaobai
|
||||
* @version 2.2.0-1.20.4
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class CheckLifeBlocksCommand implements CommandExecutor {
|
||||
private final PlayerBlockLife plugin;
|
||||
|
||||
/**
|
||||
* 构造一个新的检查生命方块命令执行器
|
||||
*
|
||||
* @param plugin 插件主类实例
|
||||
*/
|
||||
public CheckLifeBlocksCommand(PlayerBlockLife plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
// 检查命令是否启用
|
||||
if (!plugin.getConfigManager().isCommandEnabled("checklifeblocks")) {
|
||||
String message = plugin.getMessageManager().getMessage("game.errors.command_disabled",
|
||||
"&c此命令已被禁用!");
|
||||
sender.sendMessage(message);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(sender instanceof Player)) {
|
||||
String message = plugin.getMessageManager().getMessage("game.errors.no_permission",
|
||||
"&c只有玩家可以使用此命令!");
|
||||
sender.sendMessage(message);
|
||||
sender.sendMessage(ChatColor.RED + "此命令只能由玩家执行!控制台无法查看具体玩家的生命方块状态。");
|
||||
return true;
|
||||
}
|
||||
|
||||
Player player = (Player) sender;
|
||||
UUID playerId = player.getUniqueId();
|
||||
|
||||
// 获取当前玩家在新模式下的剩余方块数
|
||||
int remainingBlocks = plugin.getGameStateManager().getRemainingBlocks(player.getUniqueId());
|
||||
|
||||
// 检查是否允许玩家自己使用
|
||||
if (!plugin.getConfigManager().isSelfUseAllowed("checklifeblocks")) {
|
||||
String message = plugin.getMessageManager().getMessage("game.errors.no_permission",
|
||||
"&c你没有权限使用此命令!");
|
||||
player.sendMessage(message);
|
||||
return true;
|
||||
}
|
||||
|
||||
List<Location> blocks = plugin.getBlockManager().getPlayerBlocks(playerId);
|
||||
int remaining = blocks.size();
|
||||
MessageManager msgManager = plugin.getMessageManager();
|
||||
|
||||
if (remaining == 0) {
|
||||
String noBlocksMsg = msgManager.getCommandMessage("checklifeblocks", "no_blocks",
|
||||
"&c你还没有生命方块!");
|
||||
player.sendMessage(noBlocksMsg);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 获取消息
|
||||
String successMsg = msgManager.getCommandMessage("checklifeblocks", "success",
|
||||
"&e你的生命方块位置:");
|
||||
player.sendMessage(successMsg);
|
||||
player.sendMessage(ChatColor.AQUA + "=== 你的生命方块状态 ===");
|
||||
player.sendMessage(ChatColor.GRAY + "剩余方块数量: " + ChatColor.YELLOW + remainingBlocks +
|
||||
ChatColor.GRAY + " / " + ChatColor.RED + 5);
|
||||
|
||||
player.sendMessage("§7剩余方块数量: §e" + remaining + " §7/ §a5");
|
||||
// 根据游戏状态显示不同信息
|
||||
GameStateManager.GameState state = plugin.getGameStateManager().getCurrentState();
|
||||
switch (state) {
|
||||
case WAITING:
|
||||
player.sendMessage(ChatColor.YELLOW + "游戏状态: 等待开始");
|
||||
break;
|
||||
case STARTED:
|
||||
player.sendMessage(ChatColor.GREEN + "游戏状态: 进行中");
|
||||
break;
|
||||
case FINISHED:
|
||||
player.sendMessage(ChatColor.RED + "游戏状态: 已结束");
|
||||
break;
|
||||
}
|
||||
|
||||
// 显示生命值(如果启用)
|
||||
if (plugin.getConfigManager().isHealthSystemEnabled()) {
|
||||
Integer health = plugin.getLifeSystem().getPlayerHealth(playerId);
|
||||
player.sendMessage("§7当前生命值: §c" + (health != null ? health : "20") + " ❤");
|
||||
// 显示玩家颜色
|
||||
org.bukkit.Material playerColor = plugin.getGameStateManager().getPlayerColor(player.getUniqueId());
|
||||
if (playerColor != null) {
|
||||
String colorName = getColorName(playerColor);
|
||||
player.sendMessage(ChatColor.GRAY + "你的方块颜色: " + ChatColor.AQUA + colorName);
|
||||
}
|
||||
|
||||
if (remaining <= 2) {
|
||||
String warningMsg = msgManager.getMessage("game.block.warning_low_blocks",
|
||||
"&c⚠ 警告!生命方块即将耗尽!");
|
||||
player.sendMessage(warningMsg);
|
||||
}
|
||||
|
||||
player.sendMessage("§7方块位置:");
|
||||
for (int i = 0; i < blocks.size(); i++) {
|
||||
Location loc = blocks.get(i);
|
||||
String worldName = loc.getWorld() != null ? loc.getWorld().getName() : "未知世界";
|
||||
String locationMsg = msgManager.getMessage("game.block.location_item",
|
||||
"&7- {world} ({x}, {y}, {z})");
|
||||
locationMsg = locationMsg.replace("{world}", worldName)
|
||||
.replace("{x}", String.valueOf(loc.getBlockX()))
|
||||
.replace("{y}", String.valueOf(loc.getBlockY()))
|
||||
.replace("{z}", String.valueOf(loc.getBlockZ()));
|
||||
player.sendMessage(locationMsg);
|
||||
}
|
||||
|
||||
if (remaining > 0) {
|
||||
Location nearestBlock = plugin.getBlockManager().getNearestBlock(player);
|
||||
if (nearestBlock != null) {
|
||||
double distance = player.getLocation().distance(nearestBlock);
|
||||
player.sendMessage("§7最近方块距离: §a" + (int)distance + " §7格");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取材料颜色名称
|
||||
*/
|
||||
private String getColorName(org.bukkit.Material material) {
|
||||
String name = material.name();
|
||||
if (name.contains("WHITE")) return "白色";
|
||||
if (name.contains("ORANGE")) return "橙色";
|
||||
if (name.contains("MAGENTA")) return "品红色";
|
||||
if (name.contains("LIGHT_BLUE")) return "淡蓝色";
|
||||
if (name.contains("YELLOW")) return "黄色";
|
||||
if (name.contains("LIME")) return "黄绿色";
|
||||
if (name.contains("PINK")) return "粉色";
|
||||
if (name.contains("GRAY")) return "灰色";
|
||||
if (name.contains("LIGHT_GRAY")) return "淡灰色";
|
||||
if (name.contains("CYAN")) return "青色";
|
||||
if (name.contains("PURPLE")) return "紫色";
|
||||
if (name.contains("BLUE")) return "蓝色";
|
||||
if (name.contains("BROWN")) return "棕色";
|
||||
if (name.contains("GREEN")) return "绿色";
|
||||
if (name.contains("RED")) return "红色";
|
||||
if (name.contains("BLACK")) return "黑色";
|
||||
return "未知";
|
||||
}
|
||||
}
|
||||
@@ -22,17 +22,16 @@ import java.nio.charset.StandardCharsets;
|
||||
* <li>支持多种皮肤来源的优先级配置</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p><b>SkinsRestorer配置支持:</b>
|
||||
* <p><b>SkinsRestorer配置支持:</b></p>
|
||||
* <ul>
|
||||
* <li><code>skin.source</code>:皮肤来源优先级(skinsrestorer/player_profile/local_cache)</li>
|
||||
* <li><code>skin.use-skinsrestorer</code>:是否启用SkinsRestorer支持</li>
|
||||
* <li><code>skin.cache.expire_days</code>:皮肤缓存过期时间</li>
|
||||
* <li>默认配置已优化,优先使用SkinsRestorer以支持离线服务器</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* @author xiaobai
|
||||
* @version 2.1.0
|
||||
* @version 2.2.0-1.20.4
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class ConfigManager {
|
||||
@@ -126,14 +125,6 @@ public class ConfigManager {
|
||||
*/
|
||||
private void updateConfig(int fromVersion, int toVersion) {
|
||||
if (fromVersion == 1 && toVersion == 2) {
|
||||
// 添加自动生成配置
|
||||
if (!config.contains("auto-generation.enabled")) {
|
||||
config.set("auto-generation.enabled", true);
|
||||
config.set("auto-generation.require_open_sky", true);
|
||||
config.set("auto-generation.max_attempts", 50);
|
||||
config.set("auto-generation.on_failure", "notify");
|
||||
}
|
||||
|
||||
// 添加命令启用配置
|
||||
if (!config.contains("commands.setlifeblocks.enabled")) {
|
||||
config.set("commands.setlifeblocks.enabled", true);
|
||||
@@ -206,195 +197,201 @@ public class ConfigManager {
|
||||
return config;
|
||||
}
|
||||
|
||||
// 以下为配置项的获取方法
|
||||
|
||||
public int getBlocksPerPlayer() {
|
||||
return getConfig().getInt("blocks.amount", 5);
|
||||
}
|
||||
|
||||
public int getSpreadRange() {
|
||||
return getConfig().getInt("blocks.spread", 5);
|
||||
}
|
||||
|
||||
public int getMinDistance() {
|
||||
return getConfig().getInt("blocks.min-distance", 10);
|
||||
}
|
||||
|
||||
public int getDepth() {
|
||||
return getConfig().getInt("blocks.depth", -1);
|
||||
}
|
||||
|
||||
public String getBlockMaterial() {
|
||||
return getConfig().getString("blocks.material", "player_head");
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查玩家方块被挖光时是否死亡
|
||||
*
|
||||
* @return 如果玩家方块被挖光时死亡则返回true,否则返回false
|
||||
*/
|
||||
public boolean isDieWhenBlocksGone() {
|
||||
return getConfig().getBoolean("game.die_when_blocks_gone", true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查玩家死亡后是否成为观察者
|
||||
*
|
||||
* @return 如果玩家死亡后成为观察者则返回true,否则返回false
|
||||
*/
|
||||
public boolean isBecomeSpectator() {
|
||||
return getConfig().getBoolean("game.become_spectator", true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否启用生命值系统
|
||||
*
|
||||
* @return 如果启用生命值系统则返回true,否则返回false
|
||||
*/
|
||||
public boolean isHealthSystemEnabled() {
|
||||
return getConfig().getBoolean("game.health_system", true);
|
||||
}
|
||||
|
||||
public boolean isSkinSystemEnabled() {
|
||||
return getConfig().getBoolean("skin.enabled", true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取皮肤来源配置
|
||||
* 检查是否启用自动保存
|
||||
*
|
||||
* <p>支持的皮肤来源:
|
||||
* <ul>
|
||||
* <li><b>skinsrestorer</b>:优先从SkinsRestorer插件获取皮肤纹理数据
|
||||
* <ul>
|
||||
* <li>推荐用于离线服务器</li>
|
||||
* <li>支持玩家自定义皮肤</li>
|
||||
* <li>避免默认Steve皮肤问题</li>
|
||||
* </ul>
|
||||
* </li>
|
||||
* <li><b>player_profile</b>:优先使用Bukkit的PlayerProfile API
|
||||
* <ul>
|
||||
* <li>需要玩家在线验证</li>
|
||||
* <li>适合在线服务器</li>
|
||||
* <li>支持Mojang官方皮肤</li>
|
||||
* </ul>
|
||||
* </li>
|
||||
* <li><b>local_cache</b>:优先从本地缓存加载皮肤数据
|
||||
* <ul>
|
||||
* <li>减少网络请求</li>
|
||||
* <li>提高加载速度</li>
|
||||
* <li>支持离线使用</li>
|
||||
* </ul>
|
||||
* </li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* <p>默认配置已将此值设为"skinsrestorer",以优化离线服务器体验。</p>
|
||||
*
|
||||
* @return 皮肤来源配置值
|
||||
* @see #useSkinsRestorer()
|
||||
* @see SkinManager#loadPlayerSkinAsync()
|
||||
* @return 如果启用自动保存则返回true,否则返回false
|
||||
*/
|
||||
public String getSkinSource() {
|
||||
return getConfig().getString("skin.source", "skinsrestorer");
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否启用SkinsRestorer插件支持
|
||||
*
|
||||
* <p>当此方法返回true时,插件将:
|
||||
* <ul>
|
||||
* <li>优先从SkinsRestorer插件获取玩家皮肤纹理</li>
|
||||
* <li>支持离线服务器获取玩家自定义皮肤</li>
|
||||
* <li>避免方块总是显示默认Steve皮肤的问题</li>
|
||||
* <li>使用反射安全调用SkinsRestorer API,无需硬依赖</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* <p>默认配置已将此值设为true,以优化离线服务器体验。</p>
|
||||
*
|
||||
* @return 如果启用SkinsRestorer支持返回true,否则返回false
|
||||
* @see #getSkinSource()
|
||||
*/
|
||||
public boolean useSkinsRestorer() {
|
||||
return getConfig().getBoolean("skin.use-skinsrestorer", true);
|
||||
}
|
||||
|
||||
public int getCacheExpireDays() {
|
||||
return getConfig().getInt("skin.cache.expire_days", 7);
|
||||
}
|
||||
|
||||
public boolean isAutoSaveEnabled() {
|
||||
return getConfig().getBoolean("storage.auto_save.enabled", true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取自动保存间隔
|
||||
*
|
||||
* @return 自动保存间隔(秒),默认为300秒(5分钟)
|
||||
*/
|
||||
public int getAutoSaveInterval() {
|
||||
return getConfig().getInt("storage.auto_save.interval", 300);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取存储类型配置
|
||||
*
|
||||
* @return 存储类型配置值,默认为"yaml"
|
||||
*/
|
||||
public String getStorageType() {
|
||||
return getConfig().getString("storage.type", "yaml");
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否在方块被破坏时广播消息
|
||||
*
|
||||
* @return 如果在方块被破坏时广播消息则返回true,否则返回false
|
||||
*/
|
||||
public boolean isBroadcastOnBlockBreak() {
|
||||
return getConfig().getBoolean("game.broadcast.on_block_break", true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否在玩家死亡时广播消息
|
||||
*
|
||||
* @return 如果在玩家死亡时广播消息则返回true,否则返回false
|
||||
*/
|
||||
public boolean isBroadcastOnPlayerDeath() {
|
||||
return getConfig().getBoolean("game.broadcast.on_player_death", true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取广播范围
|
||||
*
|
||||
* @return 广播范围(方块),默认为30
|
||||
*/
|
||||
public int getBroadcastRange() {
|
||||
return getConfig().getInt("game.broadcast.range", 30);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否给予挖掘奖励经验
|
||||
*
|
||||
* @return 如果给予挖掘奖励经验则返回true,否则返回false
|
||||
*/
|
||||
public boolean isGiveExpReward() {
|
||||
return getConfig().getBoolean("game.break_rewards.give_exp", true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取奖励经验数量
|
||||
*
|
||||
* @return 奖励经验数量,默认为5
|
||||
*/
|
||||
public int getExpRewardAmount() {
|
||||
return getConfig().getInt("game.break_rewards.exp_amount", 5);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否保护方块免受爆炸破坏
|
||||
*
|
||||
* @return 如果保护方块免受爆炸破坏则返回true,否则返回false
|
||||
*/
|
||||
public boolean isProtectFromExplosions() {
|
||||
return getConfig().getBoolean("protection.protect_from_explosions", true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否保护方块免受火灾破坏
|
||||
*
|
||||
* @return 如果保护方块免受火灾破坏则返回true,否则返回false
|
||||
*/
|
||||
public boolean isProtectFromFire() {
|
||||
return getConfig().getBoolean("protection.protect_from_fire", true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否保护方块免受活塞推动
|
||||
*
|
||||
* @return 如果保护方块免受活塞推动则返回true,否则返回false
|
||||
*/
|
||||
public boolean isProtectFromPistons() {
|
||||
return getConfig().getBoolean("protection.protect_from_pistons", true);
|
||||
}
|
||||
|
||||
// 自动生成配置获取方法
|
||||
public boolean isAutoGenerationEnabled() {
|
||||
return getConfig().getBoolean("auto-generation.enabled", true);
|
||||
}
|
||||
|
||||
public boolean isRequireOpenSky() {
|
||||
return getConfig().getBoolean("auto-generation.require_open_sky", true);
|
||||
}
|
||||
|
||||
public int getMaxAttempts() {
|
||||
return getConfig().getInt("auto-generation.max_attempts", 50);
|
||||
}
|
||||
|
||||
public String getOnFailureAction() {
|
||||
return getConfig().getString("auto-generation.on_failure", "notify");
|
||||
}
|
||||
|
||||
// 命令启用配置获取方法
|
||||
/**
|
||||
* 检查命令是否启用
|
||||
*
|
||||
* @param commandName 命令名称
|
||||
* @return 如果命令启用则返回true,否则返回false
|
||||
*/
|
||||
public boolean isCommandEnabled(String commandName) {
|
||||
return getConfig().getBoolean("commands." + commandName + ".enabled", true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否允许玩家自己使用命令
|
||||
*
|
||||
* @param commandName 命令名称
|
||||
* @return 如果允许玩家自己使用命令则返回true,否则返回false
|
||||
*/
|
||||
public boolean isSelfUseAllowed(String commandName) {
|
||||
return getConfig().getBoolean("commands." + commandName + ".allow_self_use", true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否允许管理员使用命令
|
||||
*
|
||||
* @param commandName 命令名称
|
||||
* @return 如果允许管理员使用命令则返回true,否则返回false
|
||||
*/
|
||||
public boolean isAdminUseAllowed(String commandName) {
|
||||
return getConfig().getBoolean("commands." + commandName + ".allow_admin_use", true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查命令是否仅管理员可用
|
||||
*
|
||||
* @param commandName 命令名称
|
||||
* @return 如果命令仅管理员可用则返回true,否则返回false
|
||||
*/
|
||||
public boolean isAdminOnly(String commandName) {
|
||||
return getConfig().getBoolean("commands." + commandName + ".admin_only", false);
|
||||
}
|
||||
|
||||
// 消息文件配置获取方法
|
||||
/**
|
||||
* 检查是否使用外部消息文件
|
||||
*
|
||||
* @return 如果使用外部消息文件则返回true,否则返回false
|
||||
*/
|
||||
public boolean useExternalMessageFile() {
|
||||
return getConfig().getBoolean("messages.use_external_file", true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取外部消息文件名
|
||||
*
|
||||
* @return 外部消息文件名,默认为"messages.yml"
|
||||
*/
|
||||
public String getExternalMessageFileName() {
|
||||
return getConfig().getString("messages.external_file", "messages.yml");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取消息
|
||||
*
|
||||
* @param path 消息路径
|
||||
* @param defaultValue 默认值
|
||||
* @return 消息内容
|
||||
*/
|
||||
public String getMessage(String path, String defaultValue) {
|
||||
// 优先从外部消息文件获取
|
||||
if (useExternalMessageFile()) {
|
||||
|
||||
883
src/main/java/com/playerblocklife/GameStateManager.java
Normal file
883
src/main/java/com/playerblocklife/GameStateManager.java
Normal file
@@ -0,0 +1,883 @@
|
||||
package com.playerblocklife;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.scoreboard.DisplaySlot;
|
||||
import org.bukkit.scoreboard.Objective;
|
||||
import org.bukkit.scoreboard.Scoreboard;
|
||||
import org.bukkit.scoreboard.ScoreboardManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 游戏状态管理器 - PlayerBlockLife生存游戏模式的核心管理器
|
||||
*
|
||||
* <p>主要功能包括:
|
||||
* <ul>
|
||||
* <li>管理游戏状态(等待中/游戏中/已结束)</li>
|
||||
* <li>为非OP玩家分配独特颜色的生命方块</li>
|
||||
* <li>处理游戏开始时的生命方块生成</li>
|
||||
* <li>控制游戏开始和结束逻辑</li>
|
||||
* <li>管理实时计分板显示(游戏状态、剩余方块、限时等)</li>
|
||||
* <li>处理游戏重置和玩家淘汰</li>
|
||||
* <li>监控游戏结束条件(仅剩一名存活玩家或限时结束)</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p><b>游戏模式特性:</b>
|
||||
* <ul>
|
||||
* <li>OP玩家始终处于观察者模式,不参与游戏</li>
|
||||
* <li>非OP玩家在等待时处于冒险模式,手中持有对应颜色方块</li>
|
||||
* <li>游戏开始后非OP玩家转为生存模式并清空背包</li>
|
||||
* <li>使用不同颜色的羊毛、玻璃、水泥方块作为生命方块</li>
|
||||
* <li>支持限时游戏模式</li>
|
||||
* <li>游戏结束时显示胜利玩家</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author xiaobai
|
||||
* @version 2.2.0-1.20.4
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public class GameStateManager {
|
||||
public enum GameState {
|
||||
WAITING, // 等待开始
|
||||
STARTED, // 游戏进行中
|
||||
FINISHED // 游戏结束
|
||||
}
|
||||
|
||||
private final PlayerBlockLife plugin;
|
||||
private GameState currentState = GameState.WAITING;
|
||||
private final Map<UUID, Material> playerColors = new HashMap<>(); // 玩家颜色分配
|
||||
private final Map<UUID, List<Location>> playerBlocks = new HashMap<>(); // 玩家方块位置
|
||||
private final List<UUID> alivePlayers = new ArrayList<>(); // 存活玩家
|
||||
private int gameDuration = 0; // 限时游戏持续时间(分钟)
|
||||
private long gameStartTime = 0; // 游戏开始时间戳
|
||||
private boolean isLimitedTime = false; // 是否为限时游戏
|
||||
private final ScoreboardManager scoreboardManager;
|
||||
private Scoreboard gameScoreboard;
|
||||
private Objective gameObjective;
|
||||
|
||||
// 可用的羊毛、玻璃、水泥颜色
|
||||
private static final Material[] WOOL_COLORS = {
|
||||
Material.WHITE_WOOL, Material.ORANGE_WOOL, Material.MAGENTA_WOOL, Material.LIGHT_BLUE_WOOL,
|
||||
Material.YELLOW_WOOL, Material.LIME_WOOL, Material.PINK_WOOL, Material.GRAY_WOOL,
|
||||
Material.LIGHT_GRAY_WOOL, Material.CYAN_WOOL, Material.PURPLE_WOOL, Material.BLUE_WOOL,
|
||||
Material.BROWN_WOOL, Material.GREEN_WOOL, Material.RED_WOOL, Material.BLACK_WOOL
|
||||
};
|
||||
|
||||
private static final Material[] GLASS_COLORS = {
|
||||
Material.WHITE_STAINED_GLASS, Material.ORANGE_STAINED_GLASS, Material.MAGENTA_STAINED_GLASS, Material.LIGHT_BLUE_STAINED_GLASS,
|
||||
Material.YELLOW_STAINED_GLASS, Material.LIME_STAINED_GLASS, Material.PINK_STAINED_GLASS, Material.GRAY_STAINED_GLASS,
|
||||
Material.LIGHT_GRAY_STAINED_GLASS, Material.CYAN_STAINED_GLASS, Material.PURPLE_STAINED_GLASS, Material.BLUE_STAINED_GLASS,
|
||||
Material.BROWN_STAINED_GLASS, Material.GREEN_STAINED_GLASS, Material.RED_STAINED_GLASS, Material.BLACK_STAINED_GLASS
|
||||
};
|
||||
|
||||
private static final Material[] CONCRETE_COLORS = {
|
||||
Material.WHITE_CONCRETE, Material.ORANGE_CONCRETE, Material.MAGENTA_CONCRETE, Material.LIGHT_BLUE_CONCRETE,
|
||||
Material.YELLOW_CONCRETE, Material.LIME_CONCRETE, Material.PINK_CONCRETE, Material.GRAY_CONCRETE,
|
||||
Material.LIGHT_GRAY_CONCRETE, Material.CYAN_CONCRETE, Material.PURPLE_CONCRETE, Material.BLUE_CONCRETE,
|
||||
Material.BROWN_CONCRETE, Material.GREEN_CONCRETE, Material.RED_CONCRETE, Material.BLACK_CONCRETE
|
||||
};
|
||||
|
||||
public GameStateManager(PlayerBlockLife plugin) {
|
||||
this.plugin = plugin;
|
||||
this.scoreboardManager = Bukkit.getScoreboardManager();
|
||||
createGameScoreboard();
|
||||
}
|
||||
|
||||
private void createGameScoreboard() {
|
||||
this.gameScoreboard = scoreboardManager.getNewScoreboard();
|
||||
this.gameObjective = gameScoreboard.registerNewObjective("pbl_game", "dummy", ChatColor.GOLD + "PlayerBlockLife 游戏");
|
||||
this.gameObjective.setDisplaySlot(DisplaySlot.SIDEBAR);
|
||||
|
||||
// 初始化计分板内容
|
||||
updateScoreboard();
|
||||
}
|
||||
|
||||
public GameState getCurrentState() {
|
||||
return currentState;
|
||||
}
|
||||
|
||||
public void setCurrentState(GameState state) {
|
||||
this.currentState = state;
|
||||
updateScoreboard();
|
||||
|
||||
// 根据新状态更新玩家
|
||||
switch (state) {
|
||||
case WAITING:
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
if (player.isOp()) {
|
||||
player.setGameMode(GameMode.SPECTATOR);
|
||||
} else {
|
||||
player.setGameMode(GameMode.ADVENTURE);
|
||||
player.getInventory().clear();
|
||||
giveColorBlockToPlayer(player);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case STARTED:
|
||||
// 游戏开始后,非OP玩家设置为生存模式并清空背包
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
if (!player.isOp()) {
|
||||
player.setGameMode(GameMode.SURVIVAL);
|
||||
player.getInventory().clear();
|
||||
// 添加玩家到存活列表(如果他们有分配的颜色,意味着他们是参与者)
|
||||
if (playerColors.containsKey(player.getUniqueId())) {
|
||||
if (!alivePlayers.contains(player.getUniqueId())) {
|
||||
alivePlayers.add(player.getUniqueId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
gameStartTime = System.currentTimeMillis();
|
||||
break;
|
||||
case FINISHED:
|
||||
// 游戏结束后,所有玩家设置为冒险模式
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
if (player.isOp()) {
|
||||
player.setGameMode(GameMode.SPECTATOR);
|
||||
} else {
|
||||
player.setGameMode(GameMode.ADVENTURE);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 分配玩家颜色
|
||||
*/
|
||||
/**
|
||||
* 为玩家分配颜色
|
||||
*
|
||||
* @param player 要分配颜色的玩家
|
||||
*/
|
||||
public void assignPlayerColor(Player player) {
|
||||
if (playerColors.containsKey(player.getUniqueId())) {
|
||||
return; // 已分配颜色
|
||||
}
|
||||
|
||||
// 如果所有颜色都已分配,将玩家设置为观察者模式
|
||||
if (playerColors.size() >= WOOL_COLORS.length) {
|
||||
player.setGameMode(GameMode.SPECTATOR);
|
||||
player.sendMessage(ChatColor.YELLOW + "所有颜色都已分配,您将作为观察者参与游戏。");
|
||||
return;
|
||||
}
|
||||
|
||||
// 随机分配一个未使用的颜色
|
||||
List<Material> availableColors = new ArrayList<>();
|
||||
for (Material wool : WOOL_COLORS) {
|
||||
boolean isUsed = false;
|
||||
for (Material usedColor : playerColors.values()) {
|
||||
if (usedColor == wool) {
|
||||
isUsed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!isUsed) {
|
||||
availableColors.add(wool);
|
||||
}
|
||||
}
|
||||
|
||||
if (!availableColors.isEmpty()) {
|
||||
Material selectedColor = availableColors.get(new Random().nextInt(availableColors.size()));
|
||||
playerColors.put(player.getUniqueId(), selectedColor);
|
||||
|
||||
// 给玩家对应颜色的方块
|
||||
giveColorBlockToPlayer(player);
|
||||
|
||||
String colorName = getMaterialColorName(selectedColor);
|
||||
player.sendMessage(ChatColor.GREEN + "您被分配了 " + ChatColor.AQUA + colorName + ChatColor.GREEN + " 颜色的生命方块!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 给玩家对应颜色的方块
|
||||
*/
|
||||
private void giveColorBlockToPlayer(Player player) {
|
||||
Material color = playerColors.get(player.getUniqueId());
|
||||
if (color != null) {
|
||||
player.getInventory().clear();
|
||||
player.getInventory().addItem(new org.bukkit.inventory.ItemStack(color, 1));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取材料的颜色名称
|
||||
*/
|
||||
private String getMaterialColorName(Material material) {
|
||||
String name = material.name();
|
||||
if (name.contains("WHITE")) return "白色";
|
||||
if (name.contains("ORANGE")) return "橙色";
|
||||
if (name.contains("MAGENTA")) return "品红色";
|
||||
if (name.contains("LIGHT_BLUE")) return "淡蓝色";
|
||||
if (name.contains("YELLOW")) return "黄色";
|
||||
if (name.contains("LIME")) return "黄绿色";
|
||||
if (name.contains("PINK")) return "粉色";
|
||||
if (name.contains("GRAY")) return "灰色";
|
||||
if (name.contains("LIGHT_GRAY")) return "淡灰色";
|
||||
if (name.contains("CYAN")) return "青色";
|
||||
if (name.contains("PURPLE")) return "紫色";
|
||||
if (name.contains("BLUE")) return "蓝色";
|
||||
if (name.contains("BROWN")) return "棕色";
|
||||
if (name.contains("GREEN")) return "绿色";
|
||||
if (name.contains("RED")) return "红色";
|
||||
if (name.contains("BLACK")) return "黑色";
|
||||
return "未知";
|
||||
}
|
||||
|
||||
/**
|
||||
* 为玩家生成生命方块
|
||||
*/
|
||||
/**
|
||||
* 为玩家生成生命方块
|
||||
*
|
||||
* @param player 要生成生命方块的玩家
|
||||
* @return 生成成功返回true,否则返回false
|
||||
*/
|
||||
public boolean generateLifeBlocksForPlayer(Player player) {
|
||||
if (!playerColors.containsKey(player.getUniqueId()) || currentState != GameState.STARTED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Material color = playerColors.get(player.getUniqueId());
|
||||
ConfigManager config = plugin.getConfigManager();
|
||||
int blockAmount = 5; // 使用固定值,因为配置已移除
|
||||
int spreadRange = 5; // 使用固定值,因为配置已移除
|
||||
|
||||
List<Location> blocks = new ArrayList<>();
|
||||
int blocksPlaced = 0;
|
||||
int attempts = 0;
|
||||
Random random = new Random();
|
||||
|
||||
// 尝试生成指定数量的方块
|
||||
while (blocksPlaced < blockAmount && attempts < 50) {
|
||||
Location center = player.getLocation();
|
||||
int x = random.nextInt(spreadRange * 2 + 1) - spreadRange;
|
||||
int z = random.nextInt(spreadRange * 2 + 1) - spreadRange;
|
||||
|
||||
Location testLoc = center.clone().add(x, 0, z);
|
||||
World world = testLoc.getWorld();
|
||||
if (world == null) continue;
|
||||
|
||||
// 从世界最高点向下寻找第一个固体方块
|
||||
int maxHeight = world.getMaxHeight();
|
||||
Block groundBlock = null;
|
||||
for (int y = maxHeight; y > world.getMinHeight(); y--) {
|
||||
testLoc.setY(y);
|
||||
Block block = testLoc.getBlock();
|
||||
Material type = block.getType();
|
||||
|
||||
// 检查是否是固体方块(可以作为支撑)
|
||||
if (type.isSolid() && type.isBlock() && !type.isTransparent()) {
|
||||
groundBlock = block;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (groundBlock == null) {
|
||||
attempts++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 地表位置 = 固体方块上方一格
|
||||
Location surfaceLoc = groundBlock.getLocation().add(0, 1, 0);
|
||||
|
||||
// 检查是否已有方块
|
||||
if (isLifeBlock(surfaceLoc)) {
|
||||
attempts++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 检查位置是否合适
|
||||
if (!isSuitableLocation(surfaceLoc)) {
|
||||
attempts++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 放置方块
|
||||
surfaceLoc.getBlock().setType(color);
|
||||
blocks.add(surfaceLoc);
|
||||
blocksPlaced++;
|
||||
|
||||
// 添加放置效果
|
||||
spawnPlaceEffects(surfaceLoc);
|
||||
}
|
||||
|
||||
if (blocksPlaced > 0) {
|
||||
playerBlocks.put(player.getUniqueId(), blocks);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查位置是否适合放置生命方块
|
||||
*/
|
||||
private boolean isSuitableLocation(Location location) {
|
||||
Block block = location.getBlock();
|
||||
|
||||
// 检查是否已有方块
|
||||
if (isLifeBlock(location)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查方块是否可替换
|
||||
Material type = block.getType();
|
||||
if (!type.isAir()) {
|
||||
// 固体方块不能替换
|
||||
if (type.isSolid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 液体方块不能替换
|
||||
if (type == Material.WATER || type == Material.LAVA) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查下方是否有支撑方块
|
||||
Block below = location.clone().add(0, -1, 0).getBlock();
|
||||
Material belowType = below.getType();
|
||||
|
||||
if (belowType.isAir() || belowType == Material.WATER || belowType == Material.LAVA) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成放置效果
|
||||
*/
|
||||
private void spawnPlaceEffects(Location location) {
|
||||
if (location.getWorld() != null) {
|
||||
// 粒子效果
|
||||
location.getWorld().spawnParticle(org.bukkit.Particle.ENCHANTMENT_TABLE,
|
||||
location.clone().add(0.5, 0.5, 0.5),
|
||||
30, 0.3, 0.3, 0.3, 0.1);
|
||||
|
||||
// 音效
|
||||
location.getWorld().playSound(location, org.bukkit.Sound.BLOCK_ANVIL_PLACE, 0.5f, 1.2f);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否为生命方块
|
||||
*/
|
||||
private boolean isLifeBlock(Location location) {
|
||||
// 检查是否有任何玩家的方块在此位置
|
||||
for (List<Location> playerBlockList : playerBlocks.values()) {
|
||||
if (playerBlockList.contains(location)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除被破坏的生命方块
|
||||
*/
|
||||
/**
|
||||
* 移除被破坏的生命方块
|
||||
*
|
||||
* @param location 方块位置
|
||||
* @param breaker 破坏方块的玩家
|
||||
* @return 移除成功返回true,否则返回false
|
||||
*/
|
||||
public boolean removeBlock(Location location, Player breaker) {
|
||||
// 找到此位置属于哪个玩家的方块
|
||||
UUID ownerId = null;
|
||||
for (Map.Entry<UUID, List<Location>> entry : playerBlocks.entrySet()) {
|
||||
if (entry.getValue().contains(location)) {
|
||||
ownerId = entry.getKey();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ownerId == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
List<Location> blocks = playerBlocks.get(ownerId);
|
||||
if (blocks == null || !blocks.contains(location)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 移除方块
|
||||
blocks.remove(location);
|
||||
location.getBlock().setType(Material.AIR);
|
||||
|
||||
// 生成破坏效果
|
||||
spawnBreakEffects(location, breaker);
|
||||
|
||||
// 通知所有相关玩家
|
||||
notifyBlockBreak(ownerId, breaker, blocks.size());
|
||||
|
||||
// 检查玩家是否还有剩余方块
|
||||
if (blocks.isEmpty()) {
|
||||
handlePlayerElimination(ownerId);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成破坏效果
|
||||
*/
|
||||
private void spawnBreakEffects(Location location, Player breaker) {
|
||||
if (location.getWorld() != null) {
|
||||
// 粒子效果
|
||||
location.getWorld().spawnParticle(org.bukkit.Particle.BLOCK_CRACK,
|
||||
location.clone().add(0.5, 0.5, 0.5),
|
||||
50, 0.5, 0.5, 0.5, 0.5, location.getBlock().getBlockData());
|
||||
|
||||
location.getWorld().spawnParticle(org.bukkit.Particle.SMOKE_LARGE,
|
||||
location.clone().add(0.5, 0.5, 0.5),
|
||||
20, 0.3, 0.3, 0.3, 0.05);
|
||||
|
||||
// 音效
|
||||
location.getWorld().playSound(location, org.bukkit.Sound.ENTITY_ITEM_BREAK, 1.0f, 0.8f);
|
||||
location.getWorld().playSound(location, org.bukkit.Sound.BLOCK_GLASS_BREAK, 0.8f, 1.0f);
|
||||
|
||||
// 对挖掘者造成轻微击退
|
||||
if (breaker != null) {
|
||||
Location breakerLoc = breaker.getLocation();
|
||||
org.bukkit.util.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(ChatColor.RED + "⚠ 警告!你的生命方块被破坏了!");
|
||||
owner.sendMessage(ChatColor.GRAY + "破坏者: " + ChatColor.YELLOW + (breaker != null ? breaker.getName() : "未知"));
|
||||
owner.sendMessage(ChatColor.GRAY + "剩余生命方块: " + ChatColor.GREEN + String.valueOf(remaining) + " " + ChatColor.GRAY + "/ " + ChatColor.RED + 5);
|
||||
|
||||
if (remaining <= 2) {
|
||||
owner.sendMessage(ChatColor.DARK_RED + "⚠ 警告!生命方块即将耗尽!");
|
||||
}
|
||||
|
||||
// 播放警告音效
|
||||
owner.playSound(owner.getLocation(), org.bukkit.Sound.ENTITY_ENDERMAN_TELEPORT, 0.8f, 0.5f);
|
||||
}
|
||||
|
||||
// 通知破坏者
|
||||
if (breaker != null && !breaker.getUniqueId().equals(ownerId)) {
|
||||
breaker.sendMessage(ChatColor.GOLD + "你破坏了一个生命方块!");
|
||||
breaker.sendMessage(ChatColor.GRAY + "所有者: " + ChatColor.YELLOW + (ownerName != null ? ownerName : "未知玩家"));
|
||||
breaker.sendMessage(ChatColor.GRAY + "对方剩余生命方块: " + ChatColor.GREEN + String.valueOf(remaining));
|
||||
|
||||
// 给予挖掘者经验奖励
|
||||
breaker.giveExp(5);
|
||||
breaker.playSound(breaker.getLocation(), org.bukkit.Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1.0f, 1.0f);
|
||||
}
|
||||
|
||||
// 广播给附近玩家
|
||||
org.bukkit.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(ChatColor.GRAY + "[附近] " + ChatColor.YELLOW + "一个生命方块被破坏了!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理玩家淘汰
|
||||
*/
|
||||
private void handlePlayerElimination(UUID playerId) {
|
||||
Player player = Bukkit.getPlayer(playerId);
|
||||
if (player == null || !player.isOnline()) {
|
||||
return;
|
||||
}
|
||||
|
||||
player.setGameMode(GameMode.SPECTATOR);
|
||||
player.sendTitle(ChatColor.DARK_RED + "☠ 你被淘汰了!", ChatColor.RED + "所有生命方块已被挖光", 20, 100, 20);
|
||||
|
||||
// 从存活玩家列表中移除
|
||||
alivePlayers.remove(playerId);
|
||||
|
||||
// 检查是否只剩一名玩家
|
||||
checkGameEnd();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查游戏是否结束
|
||||
*/
|
||||
public void checkGameEnd() {
|
||||
// 如果限时游戏且时间到了
|
||||
if (isLimitedTime && gameStartTime > 0) {
|
||||
long currentTime = System.currentTimeMillis();
|
||||
long elapsedMinutes = (currentTime - gameStartTime) / (1000 * 60);
|
||||
|
||||
if (elapsedMinutes >= gameDuration) {
|
||||
endGame();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果只剩一名玩家存活
|
||||
if (alivePlayers.size() <= 1) {
|
||||
endGame();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 结束游戏
|
||||
*/
|
||||
private void endGame() {
|
||||
if (currentState != GameState.STARTED) {
|
||||
return;
|
||||
}
|
||||
|
||||
setCurrentState(GameState.FINISHED);
|
||||
|
||||
Player winner = null;
|
||||
if (!alivePlayers.isEmpty()) {
|
||||
winner = Bukkit.getPlayer(alivePlayers.get(0));
|
||||
}
|
||||
|
||||
// 广播胜利消息
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
if (winner != null) {
|
||||
player.sendTitle(ChatColor.GOLD + "🎉 恭喜!", ChatColor.YELLOW + winner.getName() + " 获得了胜利!", 10, 70, 20);
|
||||
player.sendMessage(ChatColor.GOLD + "🎉 游戏结束!" + ChatColor.YELLOW + winner.getName() + ChatColor.GOLD + " 获得了胜利!");
|
||||
} else {
|
||||
player.sendTitle(ChatColor.GOLD + "游戏结束!", ChatColor.YELLOW + "平局!", 10, 70, 20);
|
||||
player.sendMessage(ChatColor.GOLD + "游戏结束!没有获胜者!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始限时游戏
|
||||
*/
|
||||
/**
|
||||
* 开始限时游戏
|
||||
*
|
||||
* @param minutes 游戏限时(分钟)
|
||||
*/
|
||||
public void startTimedGame(int minutes) {
|
||||
this.gameDuration = minutes;
|
||||
this.isLimitedTime = true;
|
||||
startGame();
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始游戏
|
||||
*/
|
||||
public void startGame() {
|
||||
if (currentState != GameState.WAITING) {
|
||||
return;
|
||||
}
|
||||
|
||||
setCurrentState(GameState.STARTED);
|
||||
|
||||
// 为所有玩家生成生命方块
|
||||
for (UUID playerId : playerColors.keySet()) {
|
||||
Player player = Bukkit.getPlayer(playerId);
|
||||
if (player != null && player.isOnline() && !player.isOp()) { // 只为非OP玩家生成方块
|
||||
generateLifeBlocksForPlayer(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置游戏
|
||||
*/
|
||||
public void resetGame() {
|
||||
// 清空所有数据
|
||||
playerColors.clear();
|
||||
playerBlocks.clear();
|
||||
alivePlayers.clear();
|
||||
gameDuration = 0;
|
||||
isLimitedTime = false;
|
||||
gameStartTime = 0;
|
||||
|
||||
// 将所有玩家恢复到适当模式,并重新分配颜色
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
if (player.isOp()) {
|
||||
player.setGameMode(GameMode.SPECTATOR);
|
||||
} else {
|
||||
player.setGameMode(GameMode.ADVENTURE);
|
||||
player.getInventory().clear();
|
||||
// 重新分配颜色
|
||||
assignPlayerColor(player);
|
||||
}
|
||||
}
|
||||
|
||||
setCurrentState(GameState.WAITING);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取玩家剩余方块数量
|
||||
*/
|
||||
/**
|
||||
* 获取玩家剩余方块数量
|
||||
*
|
||||
* @param playerId 玩家UUID
|
||||
* @return 玩家剩余方块数量
|
||||
*/
|
||||
public int getRemainingBlocks(UUID playerId) {
|
||||
List<Location> blocks = playerBlocks.get(playerId);
|
||||
return blocks != null ? blocks.size() : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取玩家颜色
|
||||
*/
|
||||
/**
|
||||
* 获取玩家颜色
|
||||
*
|
||||
* @param playerId 玩家UUID
|
||||
* @return 玩家的颜色材料
|
||||
*/
|
||||
public Material getPlayerColor(UUID playerId) {
|
||||
return playerColors.get(playerId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新计分板
|
||||
*/
|
||||
public void updateScoreboard() {
|
||||
if (gameObjective == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 清空现有计分板条目
|
||||
for (String entry : gameScoreboard.getEntries()) {
|
||||
gameScoreboard.resetScores(entry);
|
||||
}
|
||||
|
||||
// 设置标题
|
||||
gameObjective.setDisplayName(ChatColor.GOLD + "PlayerBlockLife 游戏");
|
||||
|
||||
// 添加内容
|
||||
int line = 1;
|
||||
String statusText = "";
|
||||
switch (currentState) {
|
||||
case WAITING:
|
||||
statusText = ChatColor.YELLOW + "等待中...";
|
||||
break;
|
||||
case STARTED:
|
||||
statusText = ChatColor.GREEN + "开始游戏";
|
||||
break;
|
||||
case FINISHED:
|
||||
statusText = ChatColor.RED + "游戏结束";
|
||||
break;
|
||||
}
|
||||
|
||||
gameScoreboard.getObjective(DisplaySlot.SIDEBAR).getScore(ChatColor.WHITE + "").setScore(line++);
|
||||
gameScoreboard.getObjective(DisplaySlot.SIDEBAR).getScore(ChatColor.AQUA + "状态:").setScore(line++);
|
||||
gameScoreboard.getObjective(DisplaySlot.SIDEBAR).getScore(statusText).setScore(line++);
|
||||
|
||||
// 如果是游戏中,显示剩余时间和玩家方块数
|
||||
if (currentState == GameState.STARTED) {
|
||||
gameScoreboard.getObjective(DisplaySlot.SIDEBAR).getScore(" ").setScore(line++);
|
||||
|
||||
// 显示剩余时间(如果有限时)
|
||||
if (isLimitedTime) {
|
||||
long currentTime = System.currentTimeMillis();
|
||||
long elapsedMinutes = (currentTime - gameStartTime) / (1000 * 60);
|
||||
long remainingMinutes = Math.max(0, gameDuration - elapsedMinutes);
|
||||
|
||||
gameScoreboard.getObjective(DisplaySlot.SIDEBAR).getScore(ChatColor.AQUA + "剩余时间:").setScore(line++);
|
||||
gameScoreboard.getObjective(DisplaySlot.SIDEBAR).getScore(ChatColor.YELLOW + String.valueOf(remainingMinutes) + "分钟").setScore(line++);
|
||||
gameScoreboard.getObjective(DisplaySlot.SIDEBAR).getScore(" ").setScore(line++);
|
||||
}
|
||||
|
||||
// 显示当前玩家剩余方块数
|
||||
gameScoreboard.getObjective(DisplaySlot.SIDEBAR).getScore(ChatColor.AQUA + "我的方块:").setScore(line++);
|
||||
// 这个会在PlayerJoinListener中根据每个玩家更新
|
||||
}
|
||||
|
||||
gameScoreboard.getObjective(DisplaySlot.SIDEBAR).getScore(" ").setScore(line++);
|
||||
gameScoreboard.getObjective(DisplaySlot.SIDEBAR).getScore(ChatColor.GRAY + "PlayerBlockLife v" + plugin.getDescription().getVersion()).setScore(line++);
|
||||
|
||||
// 给在线玩家设置计分板
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
player.setScoreboard(gameScoreboard);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新玩家特定的计分板信息
|
||||
*/
|
||||
/**
|
||||
* 更新玩家特定的计分板信息
|
||||
*
|
||||
* @param player 要更新计分板的玩家
|
||||
*/
|
||||
public void updatePlayerScoreboard(Player player) {
|
||||
if (currentState == GameState.STARTED && gameObjective != null) {
|
||||
// 为特定玩家更新剩余方块数
|
||||
int remainingBlocks = getRemainingBlocks(player.getUniqueId());
|
||||
String blocksText = ChatColor.YELLOW + String.valueOf(remainingBlocks) + "/5";
|
||||
|
||||
// 临时更新计分板,为特定玩家显示他们的剩余方块
|
||||
Scoreboard playerScoreboard = player.getScoreboard();
|
||||
if (playerScoreboard != gameScoreboard) {
|
||||
playerScoreboard = gameScoreboard;
|
||||
}
|
||||
|
||||
// 为每个玩家创建一个自定义的计分板
|
||||
Scoreboard customScoreboard = scoreboardManager.getNewScoreboard();
|
||||
Objective customObjective = customScoreboard.registerNewObjective("pbl_player", "dummy", ChatColor.GOLD + "PlayerBlockLife 游戏");
|
||||
customObjective.setDisplaySlot(DisplaySlot.SIDEBAR);
|
||||
|
||||
// 复制原始计分板内容
|
||||
int line = 1;
|
||||
String statusText = "";
|
||||
switch (currentState) {
|
||||
case WAITING:
|
||||
statusText = ChatColor.YELLOW + "等待中...";
|
||||
break;
|
||||
case STARTED:
|
||||
statusText = ChatColor.GREEN + "开始游戏";
|
||||
break;
|
||||
case FINISHED:
|
||||
statusText = ChatColor.RED + "游戏结束";
|
||||
break;
|
||||
}
|
||||
|
||||
customScoreboard.getObjective(DisplaySlot.SIDEBAR).getScore(ChatColor.WHITE + "").setScore(line++);
|
||||
customScoreboard.getObjective(DisplaySlot.SIDEBAR).getScore(ChatColor.AQUA + "状态:").setScore(line++);
|
||||
customScoreboard.getObjective(DisplaySlot.SIDEBAR).getScore(statusText).setScore(line++);
|
||||
|
||||
if (isLimitedTime) {
|
||||
long currentTime = System.currentTimeMillis();
|
||||
long elapsedMinutes = (currentTime - gameStartTime) / (1000 * 60);
|
||||
long remainingMinutes = Math.max(0, gameDuration - elapsedMinutes);
|
||||
|
||||
customScoreboard.getObjective(DisplaySlot.SIDEBAR).getScore(" ").setScore(line++);
|
||||
customScoreboard.getObjective(DisplaySlot.SIDEBAR).getScore(ChatColor.AQUA + "剩余时间:").setScore(line++);
|
||||
customScoreboard.getObjective(DisplaySlot.SIDEBAR).getScore(ChatColor.YELLOW + String.valueOf(remainingMinutes) + "分钟").setScore(line++);
|
||||
}
|
||||
|
||||
customScoreboard.getObjective(DisplaySlot.SIDEBAR).getScore(" ").setScore(line++);
|
||||
customScoreboard.getObjective(DisplaySlot.SIDEBAR).getScore(ChatColor.AQUA + "我的方块:").setScore(line++);
|
||||
customScoreboard.getObjective(DisplaySlot.SIDEBAR).getScore(blocksText).setScore(line++);
|
||||
|
||||
customScoreboard.getObjective(DisplaySlot.SIDEBAR).getScore(" ").setScore(line++);
|
||||
customScoreboard.getObjective(DisplaySlot.SIDEBAR).getScore(ChatColor.GRAY + "PlayerBlockLife v" + plugin.getDescription().getVersion()).setScore(line++);
|
||||
|
||||
player.setScoreboard(customScoreboard);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取存活玩家数量
|
||||
*/
|
||||
/**
|
||||
* 获取存活玩家数量
|
||||
*
|
||||
* @return 存活玩家数量
|
||||
*/
|
||||
public int getAlivePlayersCount() {
|
||||
return alivePlayers.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有存活玩家
|
||||
*/
|
||||
/**
|
||||
* 获取所有存活玩家
|
||||
*
|
||||
* @return 存活玩家UUID列表
|
||||
*/
|
||||
public List<UUID> getAlivePlayers() {
|
||||
return new ArrayList<>(alivePlayers);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有玩家的方块位置
|
||||
*/
|
||||
/**
|
||||
* 获取所有玩家的方块位置
|
||||
*
|
||||
* @return 玩家UUID到方块位置列表的映射
|
||||
*/
|
||||
public Map<UUID, List<Location>> getPlayerBlocks() {
|
||||
return new HashMap<>(playerBlocks);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除指定玩家的方块
|
||||
*/
|
||||
/**
|
||||
* 清除指定玩家的方块
|
||||
*
|
||||
* @param playerId 要清除方块的玩家UUID
|
||||
*/
|
||||
public void clearPlayerBlocks(UUID playerId) {
|
||||
List<Location> blocks = playerBlocks.remove(playerId);
|
||||
if (blocks != null) {
|
||||
for (Location loc : blocks) {
|
||||
if (loc.getWorld() != null) {
|
||||
loc.getBlock().setType(org.bukkit.Material.AIR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 从存活玩家列表中移除
|
||||
alivePlayers.remove(playerId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取游戏持续时间(分钟)
|
||||
*/
|
||||
/**
|
||||
* 获取游戏持续时间(分钟)
|
||||
*
|
||||
* @return 游戏持续时间(分钟)
|
||||
*/
|
||||
public int getGameDuration() {
|
||||
return gameDuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取游戏开始时间戳
|
||||
*/
|
||||
/**
|
||||
* 获取游戏开始时间戳
|
||||
*
|
||||
* @return 游戏开始时间戳
|
||||
*/
|
||||
public long getGameStartTime() {
|
||||
return gameStartTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否为限时游戏
|
||||
*/
|
||||
/**
|
||||
* 检查是否为限时游戏
|
||||
*
|
||||
* @return 如果是限时游戏返回true,否则返回false
|
||||
*/
|
||||
public boolean isLimitedTime() {
|
||||
return isLimitedTime;
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,7 @@ import java.util.UUID;
|
||||
* </pre>
|
||||
*
|
||||
* @author xiaobai
|
||||
* @version 2.1.0
|
||||
* @version 2.2.0
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class LifeSystem {
|
||||
@@ -46,8 +46,14 @@ public class LifeSystem {
|
||||
}
|
||||
|
||||
public void checkAllPlayers() {
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
checkPlayerHealth(player);
|
||||
// 只在游戏进行中时检查玩家生命值,等待和旁观状态的玩家不检查
|
||||
if (plugin.getGameStateManager().getCurrentState() == GameStateManager.GameState.STARTED) {
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
// 只检查生存模式的玩家(非旁观者)
|
||||
if (player.getGameMode() == GameMode.SURVIVAL) {
|
||||
checkPlayerHealth(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,11 @@ public class MessageManager {
|
||||
private File messageFile;
|
||||
private final Map<String, String> messageCache = new HashMap<>();
|
||||
|
||||
/**
|
||||
* 构造一个新的消息管理器
|
||||
*
|
||||
* @param plugin 插件主类实例
|
||||
*/
|
||||
public MessageManager(PlayerBlockLife plugin) {
|
||||
this.plugin = plugin;
|
||||
this.messageFile = new File(plugin.getDataFolder(), "messages.yml");
|
||||
@@ -86,6 +91,13 @@ public class MessageManager {
|
||||
/**
|
||||
* 获取消息
|
||||
*/
|
||||
/**
|
||||
* 获取消息
|
||||
*
|
||||
* @param path 消息路径
|
||||
* @param defaultValue 默认值
|
||||
* @return 消息内容
|
||||
*/
|
||||
public String getMessage(String path, String defaultValue) {
|
||||
ConfigManager config = plugin.getConfigManager();
|
||||
|
||||
@@ -122,6 +134,14 @@ public class MessageManager {
|
||||
/**
|
||||
* 获取格式化消息(替换变量)
|
||||
*/
|
||||
/**
|
||||
* 获取格式化消息(替换变量)
|
||||
*
|
||||
* @param path 消息路径
|
||||
* @param defaultValue 默认值
|
||||
* @param variables 变量映射
|
||||
* @return 格式化后的消息内容
|
||||
*/
|
||||
public String getFormattedMessage(String path, String defaultValue, Map<String, String> variables) {
|
||||
String message = getMessage(path, defaultValue);
|
||||
|
||||
@@ -137,6 +157,13 @@ public class MessageManager {
|
||||
/**
|
||||
* 获取控制台消息
|
||||
*/
|
||||
/**
|
||||
* 获取控制台消息
|
||||
*
|
||||
* @param path 消息路径
|
||||
* @param defaultValue 默认值
|
||||
* @return 控制台消息内容
|
||||
*/
|
||||
public String getConsoleMessage(String path, String defaultValue) {
|
||||
String message = getMessage("console." + path, defaultValue);
|
||||
// 移除颜色代码(控制台不需要)
|
||||
@@ -149,6 +176,13 @@ public class MessageManager {
|
||||
/**
|
||||
* 获取游戏内消息
|
||||
*/
|
||||
/**
|
||||
* 获取游戏内消息
|
||||
*
|
||||
* @param path 消息路径
|
||||
* @param defaultValue 默认值
|
||||
* @return 游戏内消息内容
|
||||
*/
|
||||
public String getGameMessage(String path, String defaultValue) {
|
||||
return getMessage("game." + path, defaultValue);
|
||||
}
|
||||
@@ -156,6 +190,14 @@ public class MessageManager {
|
||||
/**
|
||||
* 获取命令消息
|
||||
*/
|
||||
/**
|
||||
* 获取命令消息
|
||||
*
|
||||
* @param command 命令名称
|
||||
* @param path 消息路径
|
||||
* @param defaultValue 默认值
|
||||
* @return 命令消息内容
|
||||
*/
|
||||
public String getCommandMessage(String command, String path, String defaultValue) {
|
||||
return getMessage("commands." + command + "." + path, defaultValue);
|
||||
}
|
||||
@@ -163,6 +205,13 @@ public class MessageManager {
|
||||
/**
|
||||
* 获取广播消息
|
||||
*/
|
||||
/**
|
||||
* 获取广播消息
|
||||
*
|
||||
* @param path 消息路径
|
||||
* @param defaultValue 默认值
|
||||
* @return 广播消息内容
|
||||
*/
|
||||
public String getBroadcastMessage(String path, String defaultValue) {
|
||||
return getMessage("broadcast." + path, defaultValue);
|
||||
}
|
||||
@@ -170,6 +219,11 @@ public class MessageManager {
|
||||
/**
|
||||
* 检查消息文件是否存在
|
||||
*/
|
||||
/**
|
||||
* 检查消息文件是否存在
|
||||
*
|
||||
* @return 如果外部消息文件存在则返回true,否则返回false
|
||||
*/
|
||||
public boolean hasExternalMessageFile() {
|
||||
return messageFile.exists();
|
||||
}
|
||||
@@ -177,6 +231,11 @@ public class MessageManager {
|
||||
/**
|
||||
* 获取消息文件路径
|
||||
*/
|
||||
/**
|
||||
* 获取消息文件路径
|
||||
*
|
||||
* @return 消息文件的绝对路径
|
||||
*/
|
||||
public String getMessageFilePath() {
|
||||
return messageFile.getAbsolutePath();
|
||||
}
|
||||
|
||||
139
src/main/java/com/playerblocklife/PBLCommands.java
Normal file
139
src/main/java/com/playerblocklife/PBLCommands.java
Normal file
@@ -0,0 +1,139 @@
|
||||
package com.playerblocklife;
|
||||
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
/**
|
||||
* PlayerBlockLife命令执行器 - 处理PlayerBlockLife游戏模式的专用命令
|
||||
*
|
||||
* <p>提供PlayerBlockLife游戏模式的核心控制命令,包括:
|
||||
* <ul>
|
||||
* <li>/PlayerBlockLife start [时间] - 开始游戏,支持可选的限时模式</li>
|
||||
* <li>/PlayerBlockLife rstgm - 重置游戏,重新分配玩家颜色</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>这些命令仅允许服务器管理员(OP)执行,用于控制PlayerBlockLife游戏的生命周期。</p>
|
||||
*
|
||||
* @author xiaobai
|
||||
* @version 2.2.0-1.20.4
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public class PBLCommands implements CommandExecutor {
|
||||
private final PlayerBlockLife plugin;
|
||||
|
||||
public PBLCommands(PlayerBlockLife plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
if (!(sender instanceof Player) && !sender.hasPermission("playerblocklife.admin")) {
|
||||
// 控制台默认有权限
|
||||
if (!sender.isOp() && !sender.hasPermission("playerblocklife.admin")) {
|
||||
sender.sendMessage(ChatColor.RED + "你没有权限执行此命令!");
|
||||
return true;
|
||||
}
|
||||
} else if (sender instanceof Player) {
|
||||
Player player = (Player) sender;
|
||||
if (!player.isOp() && !player.hasPermission("playerblocklife.admin")) {
|
||||
player.sendMessage(ChatColor.RED + "你没有权限执行此命令!");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (args.length == 0) {
|
||||
if (sender instanceof Player) {
|
||||
showHelp((Player) sender);
|
||||
} else {
|
||||
showConsoleHelp(sender);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
String subCommand = args[0].toLowerCase();
|
||||
|
||||
if (subCommand.equals("start")) {
|
||||
handleStartCommand(sender, args);
|
||||
} else if (subCommand.equals("rstgm") || subCommand.equals("reset")) {
|
||||
handleResetCommand(sender);
|
||||
} else {
|
||||
if (sender instanceof Player) {
|
||||
showHelp((Player) sender);
|
||||
} else {
|
||||
showConsoleHelp(sender);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void handleStartCommand(CommandSender sender, String[] args) {
|
||||
if (plugin.getGameStateManager().getCurrentState() == GameStateManager.GameState.STARTED) {
|
||||
sender.sendMessage(ChatColor.RED + "游戏已在进行中!");
|
||||
return;
|
||||
}
|
||||
|
||||
int duration = 0;
|
||||
boolean isTimed = false;
|
||||
|
||||
// 检查是否有时间参数
|
||||
if (args.length > 1) {
|
||||
try {
|
||||
duration = Integer.parseInt(args[1]);
|
||||
if (duration <= 0) {
|
||||
sender.sendMessage(ChatColor.RED + "游戏时间必须大于0分钟!");
|
||||
return;
|
||||
}
|
||||
isTimed = true;
|
||||
sender.sendMessage(ChatColor.GREEN + "限时游戏开始,持续时间: " + duration + " 分钟!");
|
||||
} catch (NumberFormatException e) {
|
||||
sender.sendMessage(ChatColor.RED + "无效的时间参数!请输入分钟数,例如: /pbl start 30");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
sender.sendMessage(ChatColor.GREEN + "游戏开始!");
|
||||
}
|
||||
|
||||
// 开始游戏
|
||||
if (isTimed) {
|
||||
plugin.getGameStateManager().startTimedGame(duration);
|
||||
} else {
|
||||
plugin.getGameStateManager().startGame();
|
||||
}
|
||||
|
||||
// 广播游戏开始消息
|
||||
for (Player onlinePlayer : org.bukkit.Bukkit.getOnlinePlayers()) {
|
||||
onlinePlayer.sendMessage(ChatColor.GOLD + "游戏开始!保护好你的生命方块!");
|
||||
if (isTimed) {
|
||||
onlinePlayer.sendMessage(ChatColor.YELLOW + "限时: " + duration + " 分钟");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleResetCommand(CommandSender sender) {
|
||||
plugin.getGameStateManager().resetGame();
|
||||
sender.sendMessage(ChatColor.YELLOW + "游戏已重置,所有玩家回到等待状态。");
|
||||
|
||||
// 广播重置消息
|
||||
for (Player onlinePlayer : org.bukkit.Bukkit.getOnlinePlayers()) {
|
||||
onlinePlayer.sendMessage(ChatColor.GOLD + "游戏已重置,请等待重新分配颜色。");
|
||||
}
|
||||
}
|
||||
|
||||
private void showHelp(Player player) {
|
||||
player.sendMessage(ChatColor.GOLD + "=== PlayerBlockLife 命令 ===");
|
||||
player.sendMessage(ChatColor.YELLOW + "/pbl start [时间(分钟)] - 开始游戏,可选限时");
|
||||
player.sendMessage(ChatColor.YELLOW + "/pbl rstgm - 重置游戏");
|
||||
player.sendMessage(ChatColor.GRAY + "注:只有管理员(权限)可以执行这些命令");
|
||||
}
|
||||
|
||||
private void showConsoleHelp(CommandSender sender) {
|
||||
sender.sendMessage(ChatColor.GOLD + "=== PlayerBlockLife 命令 ===");
|
||||
sender.sendMessage(ChatColor.YELLOW + "/pbl start [时间(分钟)] - 开始游戏,可选限时");
|
||||
sender.sendMessage(ChatColor.YELLOW + "/pbl rstgm - 重置游戏");
|
||||
sender.sendMessage(ChatColor.GRAY + "注:控制台拥有执行这些命令的权限");
|
||||
}
|
||||
}
|
||||
@@ -6,22 +6,25 @@ import org.bukkit.plugin.java.JavaPlugin;
|
||||
import java.util.logging.Level;
|
||||
|
||||
/**
|
||||
* PlayerBlockLife插件主类 - 玩家生命方块系统的核心控制器
|
||||
* PlayerBlockLife插件主类 - PlayerBlockLife生存游戏模式的核心控制器
|
||||
*
|
||||
* <p>这个插件为Minecraft服务器添加了一个独特的游戏机制:每个玩家拥有一定数量的生命方块,
|
||||
* 这些方块使用玩家的皮肤作为材质。当其他玩家挖光某个玩家的所有生命方块时,该玩家会被淘汰。</p>
|
||||
* <p>这个插件为Minecraft服务器添加了一个独特的生存游戏模式:每个非OP玩家拥有一定数量的生命方块,
|
||||
* 这些方块使用不同颜色的羊毛、玻璃或水泥方块表示。当其他玩家挖光某个玩家的所有生命方块时,该玩家会被淘汰。
|
||||
* 游戏需要管理员使用/pbl start命令开始,支持限时模式,最后存活的玩家获胜。</p>
|
||||
*
|
||||
* <p>主要功能:
|
||||
* <p>主要功能:</p>
|
||||
* <ul>
|
||||
* <li>管理玩家生命方块的生成和销毁</li>
|
||||
* <li>处理玩家皮肤的获取和应用</li>
|
||||
* <li>监控玩家生命值状态</li>
|
||||
* <li>提供完整的命令和权限系统</li>
|
||||
* <li>支持配置热重载和数据持久化</li>
|
||||
* <li>管理PlayerBlockLife游戏的完整生命周期(等待、进行、结束)</li>
|
||||
* <li>为非OP玩家分配独特的颜色生命方块</li>
|
||||
* <li>处理生命方块的生成和销毁</li>
|
||||
* <li>监控游戏状态和玩家存活情况</li>
|
||||
* <li>提供PlayerBlockLife专用命令系统(/pbl start, /pbl rstgm)</li>
|
||||
* <li>支持游戏计分板显示</li>
|
||||
* <li>管理员可使用传统命令进行管理</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author xiaobai
|
||||
* @version 3.0.0-experimental-1.20.4
|
||||
* @version 2.2.0-1.20.4
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class PlayerBlockLife extends JavaPlugin {
|
||||
@@ -31,6 +34,7 @@ public class PlayerBlockLife extends JavaPlugin {
|
||||
private LifeSystem lifeSystem;
|
||||
private ConfigManager configManager;
|
||||
private MessageManager messageManager;
|
||||
private GameStateManager gameStateManager;
|
||||
|
||||
/**
|
||||
* 插件启用时调用,执行初始化操作
|
||||
@@ -78,12 +82,16 @@ public class PlayerBlockLife extends JavaPlugin {
|
||||
getCommand("pbldelete").setExecutor(new AdminCommands(this));
|
||||
getCommand("pblrevive").setExecutor(new AdminCommands(this));
|
||||
getCommand("pblstats").setExecutor(new AdminCommands(this));
|
||||
getCommand("pbl").setExecutor(new PBLCommands(this));
|
||||
|
||||
// 第六步:加载其他数据
|
||||
blockManager.loadData();
|
||||
skinManager.loadAllSkins();
|
||||
// 第六步:初始化游戏状态管理器(先于其他数据加载)
|
||||
this.gameStateManager = new GameStateManager(this);
|
||||
|
||||
// 第七步:启动定时任务
|
||||
// 第七步:加载其他数据
|
||||
// 注意:在新模式下,我们不再使用原有的blockManager和skinManager
|
||||
// 因此不再调用blockManager.loadData()和skinManager.loadAllSkins()
|
||||
|
||||
// 第八步:启动定时任务
|
||||
startScheduler();
|
||||
|
||||
getLogger().info("§a========================================");
|
||||
@@ -172,6 +180,10 @@ public class PlayerBlockLife extends JavaPlugin {
|
||||
if (lifeSystem != null) {
|
||||
lifeSystem.checkAllPlayers();
|
||||
}
|
||||
// 每10秒检查游戏结束条件
|
||||
if (gameStateManager != null) {
|
||||
gameStateManager.checkGameEnd();
|
||||
}
|
||||
}, 200L, 200L);
|
||||
|
||||
// 每分钟清理一次过期缓存
|
||||
@@ -180,6 +192,13 @@ public class PlayerBlockLife extends JavaPlugin {
|
||||
skinManager.cleanupOldCache();
|
||||
}
|
||||
}, 1200L, 1200L);
|
||||
|
||||
// 每秒更新计分板
|
||||
getServer().getScheduler().runTaskTimer(this, () -> {
|
||||
if (gameStateManager != null) {
|
||||
gameStateManager.updateScoreboard();
|
||||
}
|
||||
}, 20L, 20L);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -194,34 +213,84 @@ public class PlayerBlockLife extends JavaPlugin {
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取方块管理器
|
||||
*
|
||||
* @return 方块管理器实例
|
||||
*/
|
||||
public PlayerBlockManager getBlockManager() {
|
||||
return blockManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取皮肤管理器
|
||||
*
|
||||
* @return 皮肤管理器实例
|
||||
*/
|
||||
public SkinManager getSkinManager() {
|
||||
return skinManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取生命值系统
|
||||
*
|
||||
* @return 生命值系统实例
|
||||
*/
|
||||
public LifeSystem getLifeSystem() {
|
||||
return lifeSystem;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取配置管理器
|
||||
*
|
||||
* @return 配置管理器实例
|
||||
*/
|
||||
public ConfigManager getConfigManager() {
|
||||
return configManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取消息管理器
|
||||
*
|
||||
* @return 消息管理器实例
|
||||
*/
|
||||
public MessageManager getMessageManager() {
|
||||
return messageManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取游戏状态管理器
|
||||
*
|
||||
* @return 游戏状态管理器实例
|
||||
*/
|
||||
public GameStateManager getGameStateManager() {
|
||||
return gameStateManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录信息级别日志
|
||||
*
|
||||
* @param message 日志消息
|
||||
*/
|
||||
public void logInfo(String message) {
|
||||
getLogger().info(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录警告级别日志
|
||||
*
|
||||
* @param message 日志消息
|
||||
*/
|
||||
public void logWarning(String message) {
|
||||
getLogger().warning(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录错误级别日志
|
||||
*
|
||||
* @param message 日志消息
|
||||
* @param throwable 异常对象
|
||||
*/
|
||||
public void logError(String message, Throwable throwable) {
|
||||
getLogger().log(Level.SEVERE, message, throwable);
|
||||
}
|
||||
|
||||
@@ -38,12 +38,11 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
* <li>支持异步皮肤加载,避免方块放置阻塞</li>
|
||||
* <li>提供皮肤加载状态检查,确保皮肤就绪后再放置方块</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* <p>使用并发安全的数据结构确保多线程环境下的数据一致性。</p>
|
||||
*
|
||||
* @author xiaobai
|
||||
* @version 2.1.0
|
||||
* @version 2.2.0
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class PlayerBlockManager {
|
||||
@@ -66,12 +65,10 @@ public class PlayerBlockManager {
|
||||
*/
|
||||
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();
|
||||
boolean requireOpenSky = true; // 使用默认值,因为配置已移除
|
||||
int maxAttempts = 50; // 使用默认值,因为配置已移除
|
||||
|
||||
return generateLifeBlocksForPlayer(player, blockAmount, spreadRange, requireOpenSky, maxAttempts);
|
||||
return generateLifeBlocksForPlayer(player, 5, 5, requireOpenSky, maxAttempts);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,9 +13,32 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* PlayerJoinListener - PlayerBlockLife游戏模式下的玩家加入和重生监听器
|
||||
*
|
||||
* <p>处理玩家加入服务器和重生时的逻辑,包括:
|
||||
* <ul>
|
||||
* <li>根据玩家权限设置游戏模式(OP玩家为观察者模式,非OP玩家为冒险模式)</li>
|
||||
* <li>为非OP玩家分配独特的生命方块颜色</li>
|
||||
* <li>给非OP玩家提供对应颜色的方块作为手持物品</li>
|
||||
* <li>根据当前游戏状态显示相应信息</li>
|
||||
* <li>处理玩家重生时的游戏状态恢复</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>此监听器与GameStateManager协作,确保玩家正确加入PlayerBlockLife游戏模式。</p>
|
||||
*
|
||||
* @author xiaobai
|
||||
* @version 2.2.0-1.20.4
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class PlayerJoinListener implements Listener {
|
||||
private final PlayerBlockLife plugin;
|
||||
|
||||
/**
|
||||
* 构造一个新的玩家加入监听器
|
||||
*
|
||||
* @param plugin 插件主类实例
|
||||
*/
|
||||
public PlayerJoinListener(PlayerBlockLife plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
@@ -25,70 +48,34 @@ public class PlayerJoinListener implements Listener {
|
||||
Player player = event.getPlayer();
|
||||
UUID playerId = player.getUniqueId();
|
||||
|
||||
// 异步加载玩家皮肤
|
||||
plugin.getSkinManager().loadPlayerSkinAsync(player);
|
||||
// 根据玩家权限设置游戏模式
|
||||
if (player.isOp()) {
|
||||
// OP玩家设置为观察者模式
|
||||
player.setGameMode(GameMode.SPECTATOR);
|
||||
player.sendMessage(org.bukkit.ChatColor.YELLOW + "您是管理员,已设置为观察者模式。");
|
||||
} else {
|
||||
// 非OP玩家设置为冒险模式
|
||||
player.setGameMode(GameMode.ADVENTURE);
|
||||
|
||||
// 分配颜色并给玩家对应颜色的方块
|
||||
plugin.getGameStateManager().assignPlayerColor(player);
|
||||
}
|
||||
|
||||
// 延迟执行,确保皮肤加载完成
|
||||
// 延迟执行,确保颜色分配完成
|
||||
Bukkit.getScheduler().runTaskLater(plugin, () -> {
|
||||
int remainingBlocks = plugin.getBlockManager().getRemainingBlocks(playerId);
|
||||
|
||||
// 检查是否启用自动生成
|
||||
if (plugin.getConfigManager().isAutoGenerationEnabled() && remainingBlocks == 0) {
|
||||
// 自动生成生命方块
|
||||
generateLifeBlocksForPlayer(player);
|
||||
} else if (remainingBlocks > 0) {
|
||||
// 已有方块,显示欢迎信息
|
||||
Map<String, String> variables = new HashMap<>();
|
||||
variables.put("player", player.getName());
|
||||
variables.put("remaining", String.valueOf(remainingBlocks));
|
||||
variables.put("total", String.valueOf(plugin.getConfigManager().getBlocksPerPlayer()));
|
||||
|
||||
String welcomeMsg = plugin.getMessageManager().getFormattedMessage("game.player.welcome_back",
|
||||
"&e欢迎回来,&6{player}&e!", variables);
|
||||
String blocksMsg = plugin.getMessageManager().getFormattedMessage("game.player.remaining_blocks",
|
||||
"&7你还有 &e{remaining} &7个生命方块", variables);
|
||||
String checkMsg = plugin.getMessageManager().getMessage("game.player.check_blocks_hint",
|
||||
"&7使用 &e/checklifeblocks &7查看方块位置");
|
||||
|
||||
player.sendMessage("§a========================================");
|
||||
player.sendMessage(welcomeMsg);
|
||||
player.sendMessage(blocksMsg);
|
||||
player.sendMessage(checkMsg);
|
||||
player.sendMessage("§a========================================");
|
||||
|
||||
if (remainingBlocks <= 2) {
|
||||
String warningMsg = plugin.getMessageManager().getMessage("game.block.warning_low_blocks",
|
||||
"&c⚠ 警告!你的生命方块即将耗尽!");
|
||||
player.sendMessage(warningMsg);
|
||||
player.playSound(player.getLocation(),
|
||||
org.bukkit.Sound.ENTITY_WITHER_SPAWN, 0.5f, 1.0f);
|
||||
}
|
||||
} else {
|
||||
// 没有方块且自动生成未启用
|
||||
String welcomeMsg = plugin.getMessageManager().getMessage("game.player.welcome_new",
|
||||
"&e欢迎加入游戏!");
|
||||
String setBlocksMsg = plugin.getMessageManager().getMessage("game.player.set_blocks_hint",
|
||||
"&7使用 &e/setlifeblocks &7来设置你的生命方块");
|
||||
String rulesTitle = plugin.getMessageManager().getMessage("game.player.rules_title",
|
||||
"&6游戏规则:");
|
||||
String rule1 = plugin.getMessageManager().getMessage("game.player.rule1",
|
||||
"&7- 每个玩家有5个生命方块");
|
||||
String rule2 = plugin.getMessageManager().getMessage("game.player.rule2",
|
||||
"&7- 方块被其他玩家挖光时,你将死亡");
|
||||
String rule3 = plugin.getMessageManager().getMessage("game.player.rule3",
|
||||
"&7- 方块使用你的皮肤作为材质");
|
||||
String rule4 = plugin.getMessageManager().getMessage("game.player.rule4",
|
||||
"&7- 你可以自由移动,但方块固定位置");
|
||||
|
||||
player.sendMessage(welcomeMsg);
|
||||
player.sendMessage(setBlocksMsg);
|
||||
player.sendMessage(rulesTitle);
|
||||
player.sendMessage(rule1);
|
||||
player.sendMessage(rule2);
|
||||
player.sendMessage(rule3);
|
||||
player.sendMessage(rule4);
|
||||
// 更新玩家的计分板
|
||||
plugin.getGameStateManager().updatePlayerScoreboard(player);
|
||||
|
||||
// 根据游戏状态显示不同信息
|
||||
GameStateManager.GameState currentState = plugin.getGameStateManager().getCurrentState();
|
||||
if (currentState == GameStateManager.GameState.WAITING) {
|
||||
player.sendMessage(org.bukkit.ChatColor.YELLOW + "游戏等待中,请等待管理员开始游戏。");
|
||||
} else if (currentState == GameStateManager.GameState.STARTED) {
|
||||
player.sendMessage(org.bukkit.ChatColor.GREEN + "游戏中,请小心保护你的生命方块!");
|
||||
} else if (currentState == GameStateManager.GameState.FINISHED) {
|
||||
player.sendMessage(org.bukkit.ChatColor.RED + "游戏已结束,请等待下一轮开始。");
|
||||
}
|
||||
}, 40L);
|
||||
}, 20L);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -98,14 +85,12 @@ public class PlayerJoinListener implements Listener {
|
||||
try {
|
||||
// 获取配置
|
||||
ConfigManager config = plugin.getConfigManager();
|
||||
int blockAmount = config.getBlocksPerPlayer();
|
||||
int spreadRange = config.getSpreadRange();
|
||||
boolean requireOpenSky = config.isRequireOpenSky();
|
||||
int maxAttempts = config.getMaxAttempts();
|
||||
boolean requireOpenSky = true; // 使用默认值,因为配置已移除
|
||||
int maxAttempts = 50; // 使用默认值,因为配置已移除
|
||||
|
||||
// 调用方块管理器生成方块
|
||||
boolean success = plugin.getBlockManager().generateLifeBlocksForPlayer(
|
||||
player, blockAmount, spreadRange, requireOpenSky, maxAttempts
|
||||
player, 5, 5, requireOpenSky, maxAttempts
|
||||
);
|
||||
|
||||
if (success) {
|
||||
@@ -113,7 +98,7 @@ public class PlayerJoinListener implements Listener {
|
||||
String message = plugin.getMessageManager().getMessage("console.blocks_generated",
|
||||
"&a[PlayerBlockLife] 已为玩家 {player} 生成 {amount} 个生命方块");
|
||||
message = message.replace("{player}", player.getName())
|
||||
.replace("{amount}", String.valueOf(blockAmount));
|
||||
.replace("{amount}", String.valueOf(5)); // 使用固定值5
|
||||
// 移除颜色代码用于日志
|
||||
String logMessage = message.replace("&", "");
|
||||
plugin.getLogger().info(logMessage);
|
||||
@@ -121,7 +106,7 @@ public class PlayerJoinListener implements Listener {
|
||||
// 发送给玩家
|
||||
String playerMsg = plugin.getMessageManager().getMessage("game.block.placed",
|
||||
"&a已为你生成 {amount} 个生命方块!");
|
||||
playerMsg = playerMsg.replace("{amount}", String.valueOf(blockAmount))
|
||||
playerMsg = playerMsg.replace("{amount}", String.valueOf(5)) // 使用固定值5
|
||||
.replace("&", "§");
|
||||
player.sendMessage(playerMsg);
|
||||
} else {
|
||||
@@ -134,7 +119,7 @@ public class PlayerJoinListener implements Listener {
|
||||
plugin.getLogger().warning(logFailureMsg);
|
||||
|
||||
// 根据配置处理失败
|
||||
String onFailure = config.getOnFailureAction();
|
||||
String onFailure = "notify"; // 使用默认值,因为配置已移除
|
||||
if (onFailure.equals("notify")) {
|
||||
String notifyMsg = plugin.getMessageManager().getMessage("game.errors.cannot_generate_blocks",
|
||||
"&c无法生成生命方块:{reason}");
|
||||
@@ -168,14 +153,28 @@ public class PlayerJoinListener implements Listener {
|
||||
Player player = event.getPlayer();
|
||||
UUID playerId = player.getUniqueId();
|
||||
|
||||
if (!plugin.getLifeSystem().isPlayerAlive(playerId)) {
|
||||
Bukkit.getScheduler().runTaskLater(plugin, () -> {
|
||||
if (player.isOnline()) {
|
||||
player.setGameMode(GameMode.SPECTATOR);
|
||||
player.sendMessage("§e你已被淘汰,可以观察其他玩家。");
|
||||
player.sendMessage("§7等待下一轮游戏开始...");
|
||||
}
|
||||
}, 20L);
|
||||
// 根据当前游戏状态处理玩家重生
|
||||
GameStateManager.GameState currentState = plugin.getGameStateManager().getCurrentState();
|
||||
if (currentState == GameStateManager.GameState.STARTED) {
|
||||
// 如果游戏中,检查玩家是否存活
|
||||
int remainingBlocks = plugin.getGameStateManager().getRemainingBlocks(playerId);
|
||||
if (remainingBlocks <= 0) {
|
||||
// 玩家已被淘汰,设置为观察者模式
|
||||
Bukkit.getScheduler().runTaskLater(plugin, () -> {
|
||||
if (player.isOnline()) {
|
||||
player.setGameMode(GameMode.SPECTATOR);
|
||||
player.sendMessage(org.bukkit.ChatColor.YELLOW + "你已被淘汰,可以观察其他玩家。");
|
||||
}
|
||||
}, 20L);
|
||||
}
|
||||
} else {
|
||||
// 如果游戏未开始或已结束,OP玩家保持观察者模式,非OP玩家设置为冒险模式
|
||||
if (player.isOp()) {
|
||||
player.setGameMode(GameMode.SPECTATOR);
|
||||
} else {
|
||||
player.setGameMode(GameMode.ADVENTURE);
|
||||
plugin.getGameStateManager().assignPlayerColor(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,16 @@ import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
|
||||
/**
|
||||
* PlayerQuitListener - PlayerBlockLife游戏模式下的玩家退出监听器
|
||||
*
|
||||
* <p>处理玩家退出服务器时的日志记录。在PlayerBlockLife游戏模式中,玩家退出不会影响
|
||||
* 其生命方块状态,因为方块位置信息仅在服务器运行期间维护。</p>
|
||||
*
|
||||
* @author xiaobai
|
||||
* @version 2.2.0-1.20.4
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class PlayerQuitListener implements Listener {
|
||||
private final PlayerBlockLife plugin;
|
||||
|
||||
|
||||
@@ -1,14 +1,22 @@
|
||||
package com.playerblocklife;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* SetLifeBlocksCommand - 在新模式下提示命令不可用
|
||||
*
|
||||
* <p>在PlayerBlockLife游戏模式下,此命令不再用于手动设置生命方块。
|
||||
* 生命方块将在游戏开始时自动为每个非OP玩家生成。
|
||||
* 玩家应等待管理员使用 /PlayerBlockLife start 命令开始游戏。</p>
|
||||
*
|
||||
* @author xiaobai
|
||||
* @version 2.2.0-1.20.4
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class SetLifeBlocksCommand implements CommandExecutor {
|
||||
private final PlayerBlockLife plugin;
|
||||
|
||||
@@ -18,167 +26,18 @@ public class SetLifeBlocksCommand implements CommandExecutor {
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
// 检查命令是否启用
|
||||
if (!plugin.getConfigManager().isCommandEnabled("setlifeblocks")) {
|
||||
String message = plugin.getMessageManager().getMessage("game.errors.command_disabled",
|
||||
"&c此命令已被禁用!");
|
||||
sender.sendMessage(message);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(sender instanceof Player)) {
|
||||
String message = plugin.getMessageManager().getMessage("game.errors.no_permission",
|
||||
"&c只有玩家可以使用此命令!");
|
||||
sender.sendMessage(message);
|
||||
sender.sendMessage(ChatColor.RED + "此命令只能由玩家执行!控制台无法执行此命令。");
|
||||
return true;
|
||||
}
|
||||
|
||||
Player player = (Player) sender;
|
||||
UUID playerId = player.getUniqueId();
|
||||
|
||||
if (args.length > 0 && args[0].equalsIgnoreCase("help")) {
|
||||
showHelp(player);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 检查管理员权限和配置
|
||||
if (args.length > 0 && args[0].equalsIgnoreCase("other")) {
|
||||
// 检查是否允许管理员使用
|
||||
if (!plugin.getConfigManager().isAdminUseAllowed("setlifeblocks") || !player.hasPermission("playerblocklife.admin")) {
|
||||
String message = plugin.getMessageManager().getMessage("game.errors.no_permission",
|
||||
"&c你没有权限使用此命令!");
|
||||
player.sendMessage(message);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.length < 2) {
|
||||
String usage = plugin.getMessageManager().getCommandMessage("setlifeblocks", "usage",
|
||||
"&c用法: /setlifeblocks [reset|other|help]");
|
||||
player.sendMessage(usage);
|
||||
return true;
|
||||
}
|
||||
|
||||
Player target = Bukkit.getPlayer(args[1]);
|
||||
if (target == null) {
|
||||
String message = plugin.getMessageManager().getMessage("game.errors.player_not_found",
|
||||
"&c找不到玩家: {player}");
|
||||
message = message.replace("{player}", args[1]);
|
||||
player.sendMessage(message);
|
||||
return true;
|
||||
}
|
||||
|
||||
setBlocksForPlayer(target, player);
|
||||
return true;
|
||||
}
|
||||
// 在新模式下,setlifeblocks命令不可用
|
||||
player.sendMessage(ChatColor.RED + "此命令在新模式下不可用。");
|
||||
player.sendMessage(ChatColor.YELLOW + "生命方块将在游戏开始时自动分配。");
|
||||
player.sendMessage(ChatColor.GRAY + "请等待管理员使用 /PlayerBlockLife start 开始游戏。");
|
||||
|
||||
// 检查是否允许玩家自己使用
|
||||
if (!plugin.getConfigManager().isSelfUseAllowed("setlifeblocks")) {
|
||||
String message = plugin.getMessageManager().getMessage("game.errors.no_permission",
|
||||
"&c你没有权限使用此命令!");
|
||||
player.sendMessage(message);
|
||||
return true;
|
||||
}
|
||||
|
||||
setBlocksForPlayer(player, null);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void setBlocksForPlayer(Player target, Player executor) {
|
||||
UUID targetId = target.getUniqueId();
|
||||
MessageManager msgManager = plugin.getMessageManager();
|
||||
|
||||
if (plugin.getBlockManager().hasLifeBlocks(targetId)) {
|
||||
if (executor != null && !targetId.equals(executor.getUniqueId())) {
|
||||
String message = msgManager.getMessage("game.errors.player_already_has_blocks",
|
||||
"&c玩家 {player} 已经有生命方块了!");
|
||||
message = message.replace("{player}", target.getName());
|
||||
executor.sendMessage(message);
|
||||
} else {
|
||||
String alreadyHas = msgManager.getCommandMessage("setlifeblocks", "already_has",
|
||||
"&c你已经有生命方块了!使用 /checklifeblocks 查看位置");
|
||||
target.sendMessage(alreadyHas);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!plugin.getSkinManager().isSkinLoaded(targetId)) {
|
||||
if (executor != null && !targetId.equals(executor.getUniqueId())) {
|
||||
String message = msgManager.getMessage("game.skin_loading",
|
||||
"&e玩家 {player} 的皮肤正在加载中,请稍候...");
|
||||
message = message.replace("{player}", target.getName());
|
||||
executor.sendMessage(message);
|
||||
} else {
|
||||
target.sendMessage("§e你的皮肤正在加载中,请稍候...");
|
||||
target.sendMessage("§7(如果长时间未加载完成,请重新加入服务器)");
|
||||
}
|
||||
|
||||
plugin.getSkinManager().loadPlayerSkinAsync(target);
|
||||
|
||||
Bukkit.getScheduler().runTaskLater(plugin, () -> {
|
||||
boolean success = plugin.getBlockManager().setLifeBlocks(target, target.getLocation());
|
||||
if (success) {
|
||||
if (executor != null && !targetId.equals(executor.getUniqueId())) {
|
||||
String message = msgManager.getMessage("game.blocks_generated_for_other",
|
||||
"&a已为玩家 {player} 生成生命方块!");
|
||||
message = message.replace("{player}", target.getName());
|
||||
executor.sendMessage(message);
|
||||
} else {
|
||||
String successMsg = msgManager.getCommandMessage("setlifeblocks", "success",
|
||||
"&a已为你生成 {blocks} 个生命方块!");
|
||||
ConfigManager config = plugin.getConfigManager();
|
||||
successMsg = successMsg.replace("{blocks}", String.valueOf(config.getBlocksPerPlayer()));
|
||||
target.sendMessage(successMsg);
|
||||
}
|
||||
} else {
|
||||
if (executor != null && !targetId.equals(executor.getUniqueId())) {
|
||||
String message = msgManager.getMessage("game.errors.failed_to_generate_blocks",
|
||||
"&c为玩家 {player} 生成生命方块失败!");
|
||||
message = message.replace("{player}", target.getName());
|
||||
executor.sendMessage(message);
|
||||
} else {
|
||||
target.sendMessage("§c生成失败,请稍后再试或联系管理员");
|
||||
}
|
||||
}
|
||||
}, 40L);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
boolean success = plugin.getBlockManager().setLifeBlocks(target, target.getLocation());
|
||||
|
||||
if (success) {
|
||||
if (executor != null && !targetId.equals(executor.getUniqueId())) {
|
||||
String message = msgManager.getMessage("game.blocks_generated_for_other",
|
||||
"&a已为玩家 {player} 生成生命方块!");
|
||||
message = message.replace("{player}", target.getName());
|
||||
executor.sendMessage(message);
|
||||
} else {
|
||||
String successMsg = msgManager.getCommandMessage("setlifeblocks", "success",
|
||||
"&a已为你生成 {blocks} 个生命方块!");
|
||||
ConfigManager config = plugin.getConfigManager();
|
||||
successMsg = successMsg.replace("{blocks}", String.valueOf(config.getBlocksPerPlayer()));
|
||||
target.sendMessage(successMsg);
|
||||
}
|
||||
} else {
|
||||
if (executor != null && !targetId.equals(executor.getUniqueId())) {
|
||||
String message = msgManager.getMessage("game.errors.failed_to_generate_blocks",
|
||||
"&c为玩家 {player} 生成生命方块失败!");
|
||||
message = message.replace("{player}", target.getName());
|
||||
executor.sendMessage(message);
|
||||
} else {
|
||||
target.sendMessage("§c生成失败,请确保周围有足够空间");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void showHelp(Player player) {
|
||||
String helpMessage = plugin.getMessageManager().getCommandMessage("setlifeblocks", "help",
|
||||
"&6=== PlayerBlockLife 帮助 ===\n" +
|
||||
"&e/setlifeblocks &7- 设置你的生命方块\n" +
|
||||
"&e/setlifeblocks reset &7- 重置生命方块位置\n" +
|
||||
"&e/setlifeblocks other <玩家> &7- 为其他玩家设置(管理员)\n" +
|
||||
"&e/setlifeblocks help &7- 显示此帮助");
|
||||
|
||||
player.sendMessage(helpMessage);
|
||||
}
|
||||
}
|
||||
@@ -52,7 +52,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
* <p>皮肤缓存默认保留7天,过期后自动重新获取。</p>
|
||||
*
|
||||
* @author xiaobai
|
||||
* @version 2.1.0
|
||||
* @version 2.2.0
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class SkinManager {
|
||||
@@ -97,7 +97,7 @@ public class SkinManager {
|
||||
plugin.logInfo("开始加载皮肤: " + player.getName());
|
||||
|
||||
String skinBase64 = null;
|
||||
String skinSource = plugin.getConfigManager().getSkinSource();
|
||||
String skinSource = "skinsrestorer"; // 使用默认值,因为配置已移除
|
||||
|
||||
// 根据配置的皮肤来源优先级获取皮肤
|
||||
if ("skinsrestorer".equalsIgnoreCase(skinSource)) {
|
||||
@@ -110,7 +110,7 @@ public class SkinManager {
|
||||
} else if ("player_profile".equalsIgnoreCase(skinSource)) {
|
||||
// 优先尝试PlayerProfile
|
||||
skinBase64 = getSkinFromPlayerProfile(player);
|
||||
if (skinBase64 == null && plugin.getConfigManager().useSkinsRestorer()) {
|
||||
if (skinBase64 == null && true) {
|
||||
plugin.logInfo("PlayerProfile获取失败,尝试SkinsRestorer: " + player.getName());
|
||||
skinBase64 = getSkinFromSkinsRestorer(player);
|
||||
}
|
||||
@@ -122,7 +122,7 @@ public class SkinManager {
|
||||
}
|
||||
// 缓存不存在,尝试其他来源
|
||||
skinBase64 = getSkinFromPlayerProfile(player);
|
||||
if (skinBase64 == null && plugin.getConfigManager().useSkinsRestorer()) {
|
||||
if (skinBase64 == null && true) {
|
||||
skinBase64 = getSkinFromSkinsRestorer(player);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user