diff --git a/src/main/java/ling/coordinateRecorder/Commands/Help.java b/src/main/java/ling/coordinateRecorder/Commands/Help.java index b056548..5ca40ae 100644 --- a/src/main/java/ling/coordinateRecorder/Commands/Help.java +++ b/src/main/java/ling/coordinateRecorder/Commands/Help.java @@ -56,6 +56,13 @@ public class Help { 如果你的各项数据不是最新,请使用此命令重新载入。 注意!滥用此功能可能被服务器列入不受欢迎名单! +""", """ +传送 + +/zb tp + +可以向玩家或已经记录的地标传送,传送将根据路程带来沙雕值惩罚。 +向玩家传送需要得到许可。 """, """ 物品保护 diff --git a/src/main/java/ling/coordinateRecorder/Commands/ZbCommand.java b/src/main/java/ling/coordinateRecorder/Commands/ZbCommand.java index beff402..b4f17ae 100644 --- a/src/main/java/ling/coordinateRecorder/Commands/ZbCommand.java +++ b/src/main/java/ling/coordinateRecorder/Commands/ZbCommand.java @@ -16,6 +16,9 @@ import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.BookMeta; +import org.bukkit.inventory.meta.Damageable; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.inventory.meta.Repairable; import org.jetbrains.annotations.NotNull; import org.jooq.Record; import org.jooq.UpdateSetMoreStep; @@ -441,6 +444,26 @@ public class ZbCommand implements CommandExecutor, TabCompleter { } } + protected void giveSwordOfCommand(Player player) { + if (!player.isOp()) { + player.sendMessage(ChatColor.RED + "需要管理员权限!"); + return; + } + ItemStack sword = new ItemStack(Material.DIAMOND_SWORD, 1); + ItemMeta meta = sword.getItemMeta(); + assert meta != null; + meta.addEnchant(Enchantment.SHARPNESS, 32767, true); + meta.addEnchant(Enchantment.VANISHING_CURSE, 1, true); + meta.setDisplayName(ChatColor.YELLOW + "指令之剑"); + meta.setLore(Arrays.asList(ChatColor.YELLOW + "拥有无上权威的命令之剑", ChatColor.YELLOW + "仅限管理员使用!")); + ((Repairable) meta).setRepairCost(100); + if (meta instanceof Damageable damageable) { + damageable.setDamage(sword.getType().getMaxDurability() - 1); + } + sword.setItemMeta(meta); + player.getInventory().addItem(sword); + } + protected boolean execute(Player player, String[] strings) { if (strings.length < 1) { player.sendMessage(ChatColor.RED + "语法错误:需要 [选项]"); @@ -485,6 +508,9 @@ public class ZbCommand implements CommandExecutor, TabCompleter { case "setting": setting(player, strings); break; + case "give": + giveSwordOfCommand(player); + break; default: player.sendMessage(ChatColor.RED + "未知的选项:" + strings[0]); break; diff --git a/src/main/java/ling/coordinateRecorder/Config.java b/src/main/java/ling/coordinateRecorder/Config.java index c2699b1..0184a3d 100644 --- a/src/main/java/ling/coordinateRecorder/Config.java +++ b/src/main/java/ling/coordinateRecorder/Config.java @@ -22,6 +22,10 @@ public class Config { public static final int TRANSMIT_EXPIRATION_TIME = 45000; /// 跨世界传送惩罚 public static final int CROSS_WORLD_TELEPORTATION_PENALTY = 10; + /// 击杀玩家惩罚 + public static final int KILL_PLAYER_PENALTY = 10; + /// 击杀判定过期时间 + public static final int KILL_PLAYER_TIME = 60000; public static final HashMap ITEM_VALUE_MAP = new HashMap<>(); public static String getFractionTimeMessage(long value) { @@ -32,6 +36,8 @@ public class Config { public static int getItemStackValue(List stack) { double[] value = {0d}; stack.forEach(item -> { + if (item == null) + return; if (ITEM_VALUE_MAP.containsKey(item.getType())) { value[0] += ITEM_VALUE_MAP.get(item.getType()) * item.getAmount(); } diff --git a/src/main/java/ling/coordinateRecorder/Listener/PlayerEventListener.java b/src/main/java/ling/coordinateRecorder/Listener/PlayerEventListener.java index 4128ab8..634c210 100644 --- a/src/main/java/ling/coordinateRecorder/Listener/PlayerEventListener.java +++ b/src/main/java/ling/coordinateRecorder/Listener/PlayerEventListener.java @@ -4,6 +4,7 @@ import ling.coordinateRecorder.Config; import ling.coordinateRecorder.CoordinateRecorder; import ling.coordinateRecorder.data.PlayerData; import ling.coordinateRecorder.data.PlayerDeathData; +import ling.coordinateRecorder.data.PlayerHarm; import ling.coordinateRecorder.data.TombstoneData; import org.bukkit.*; import org.bukkit.block.Block; @@ -19,6 +20,7 @@ import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.event.block.BlockExplodeEvent; import org.bukkit.event.block.BlockIgniteEvent; import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.entity.EntityExplodeEvent; import org.bukkit.event.entity.PlayerDeathEvent; import org.bukkit.event.inventory.InventoryMoveItemEvent; @@ -33,6 +35,7 @@ import org.bukkit.map.*; import org.bukkit.persistence.PersistentDataContainer; import org.jetbrains.annotations.NotNull; +import java.sql.SQLException; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.*; @@ -102,6 +105,23 @@ public class PlayerEventListener implements Listener { return now.format(formatter); } + /// 是否可以解锁该墓碑 + public static boolean isUnlockTombstone(PlayerData data, TombstoneData tombstone) { + Player player = data.getPlayer(); + return player.getUniqueId().toString().equals(tombstone.getOwnerUuid()) || player.getUniqueId().toString() + .equals(tombstone.getOwnerDefeater()); + } + + /// 获取解锁墓碑的惩罚点数 + public static int getUnlockPunish(PlayerData data, TombstoneData tombstone, List list) { + if (data.getPlayer().getUniqueId().toString().equals(tombstone.getOwnerUuid())) { + return Config.TOMBSTONE_PUNISHMENT + Config.getItemStackValue(list); + } else if (data.getPlayer().getUniqueId().toString().equals(tombstone.getOwnerDefeater())) { + return Config.TOMBSTONE_PUNISHMENT * 2 + Config.getItemStackValue(list) * 2; + } else + throw new RuntimeException("无法解锁墓碑!"); + } + /// 保护墓碑 protected void onTombstoneProtect(Cancellable event, Player player, Block block) { if (block.getType() != Material.CHEST) { @@ -114,32 +134,32 @@ public class PlayerEventListener implements Listener { //没有存储元数据的箱子一定不是墓碑 return; } - if (!player.getUniqueId().toString().equals(tombstoneData.getOwnerUuid())) { + PlayerData data = PlayerMap.getCurrent().getPlayerData(player); + if (data == null) { + player.sendMessage(ChatColor.RED + "玩家没有注册"); + return; + } + if (!isUnlockTombstone(data, tombstoneData)) { player.sendMessage(ChatColor.RED + "这是 " + tombstoneData.getOwnerName() + " 的墓碑,你无权访问!"); event.setCancelled(true); - } else { - int count = 0; - for (ItemStack item : chest.getBlockInventory().getContents()) { - if (item != null && item.getType() != Material.AIR) { - count += item.getAmount(); - } - } - PlayerData data = PlayerMap.getCurrent().getPlayerData(player); - if (data == null) { - player.sendMessage(ChatColor.RED + "玩家没有注册"); - return; - } - data.setTombstoneBlock(block); - player.sendMessage( - "这是你的墓碑,请使用" + ChatColor.GOLD + "/zb unlock" + ChatColor.WHITE + " 命令解除锁定"); - player.sendMessage("墓碑内有" + ChatColor.YELLOW + count + ChatColor.WHITE + "个物品"); - player.sendMessage("解锁将给您带来" + ChatColor.YELLOW + (Config.TOMBSTONE_PUNISHMENT + - Config.getItemStackValue( - Arrays.asList(chest.getBlockInventory() - .getContents()))) - + ChatColor.WHITE + "点沙雕值惩罚!(当前" + ChatColor.YELLOW + data.getFraction() + ChatColor.WHITE + ")"); - event.setCancelled(true); + return; } + int count = 0; + for (ItemStack item : chest.getBlockInventory().getContents()) { + if (item != null && item.getType() != Material.AIR) { + count += item.getAmount(); + } + } + data.setTombstoneBlock(block); + player.sendMessage( + "要解锁此墓碑,请使用" + ChatColor.GOLD + "/zb unlock" + ChatColor.WHITE + " 命令解除锁定"); + player.sendMessage("墓碑内有" + ChatColor.YELLOW + count + ChatColor.WHITE + "个物品"); + player.sendMessage( + "解锁将给您带来" + + Config.getFractionTimeMessage( + getUnlockPunish(data, tombstoneData, Arrays.asList(chest.getBlockInventory().getContents()))) + + "点沙雕值惩罚!(当前" + ChatColor.YELLOW + data.getFraction() + ChatColor.WHITE + ")"); + event.setCancelled(true); } @EventHandler @@ -160,11 +180,9 @@ public class PlayerEventListener implements Listener { /// 判断指定位置的方块是否是一个墓碑 public boolean isTombstone(Location location) { - if (location == null) - return false; + if (location == null) return false; Block block = location.getBlock(); - if (block.getType() != Material.CHEST) - return false; + if (block.getType() != Material.CHEST) return false; Chest chest = (Chest) block.getState(); var per = chest.getPersistentDataContainer(); return TombstoneData.isTombstone(per); @@ -174,8 +192,7 @@ public class PlayerEventListener implements Listener { @EventHandler public void onBlockPlace(BlockPlaceEvent event) { Block block = event.getBlockPlaced(); - if (block.getType() != Material.CHEST) - return; + if (block.getType() != Material.CHEST) return; Location a1 = block.getLocation().clone().add(1, 0, 0); Location a2 = block.getLocation().clone().add(-1, 0, 0); Location a3 = block.getLocation().clone().add(0, 0, 1); @@ -258,12 +275,10 @@ public class PlayerEventListener implements Listener { return; } PlayerDeathData death = data.getDeathLocation(); - if (death == null) - return; + if (death == null) return; data.setDeathLocation(null); World deathWorld = death.getLocation().getWorld(); - if (deathWorld == null) - return; + if (deathWorld == null) return; //计算距离,如果重生点距离死亡位置很近,则不提供地图 if (PlayerData.isSameWorld(death.getLocation(), player.getLocation()) && death.getLocation().distance( @@ -287,8 +302,7 @@ public class PlayerEventListener implements Listener { @Override public void render(@NotNull MapView mapView, @NotNull MapCanvas mapCanvas, @NotNull Player player) { MapCursorCollection cursors = mapCanvas.getCursors(); - if (isAdd) - return; + if (isAdd) return; isAdd = true; // 添加目标位置标记 int scale = 1 << map.getScale().ordinal(); // 当前地图缩放比例 @@ -319,9 +333,32 @@ public class PlayerEventListener implements Listener { player.getInventory().addItem(mapItem); } + /// 在玩家受到伤害时,记录伤害来源信息,用于击杀判定 + @EventHandler + public void onPlayerDamageEvent(EntityDamageByEntityEvent event) { + //仅处理玩家对玩家造成的伤害 + if (event.getEntity() instanceof Player player && event.getDamager() instanceof Player from) { + PlayerData fromData = PlayerMap.getCurrent().getPlayerData(from); + // 如果玩家没有登录,则阻止攻击其他玩家 + if (fromData == null) { + from.sendMessage("你还没有登录,不允许攻击其他玩家"); + event.setCancelled(true); + return; + } + PlayerHarm harm = new PlayerHarm(fromData); + PlayerData data = PlayerMap.getCurrent().getPlayerData(player); + if (data == null) return; + data.setHarm(harm); + } + } + /// 在玩家死亡时,保管掉落物并记录死亡位置 @EventHandler public void playerDeath(PlayerDeathEvent event) { + //如果被杀死的玩家没有任何掉落物,则不生成墓碑 + if (event.getDrops().isEmpty()) { + return; + } Player player = event.getEntity(); Location location = player.getLocation(); PlayerData data = PlayerMap.getCurrent().getPlayerData(player); @@ -329,7 +366,8 @@ public class PlayerEventListener implements Listener { CoordinateRecorder.getCurrent().getLogger().info("没有找到玩家的登录信息,不生成墓碑"); return; } - data.setDeathLocation(new PlayerDeathData(location, null)); + data.setDeathLocation(new PlayerDeathData(location, data.getHarm() == null ? null : data.getHarm().getPlayer() + .getPlayer())); //寻找可以生成墓碑的位置 Location save = getGraveLocation(location); @@ -342,6 +380,23 @@ public class PlayerEventListener implements Listener { PersistentDataContainer persistent = chest.getPersistentDataContainer(); TombstoneData tombstoneData = new TombstoneData(player, null); + if (data.getHarm() != null && data.getHarm().getTime() + Config.KILL_PLAYER_TIME > System.currentTimeMillis()) { + try { + data.getHarm().getPlayer().addFraction(10); + tombstoneData.setOwnerDefeater(data.getHarm().getPlayer().getPlayer()); + data.getHarm().getPlayer().getPlayer().sendMessage( + "你杀死了 " + player.getName() + " 受到" + Config.getFractionTimeMessage( + 10) + "点沙雕值惩罚!(当前" + Config.getFractionTimeMessage( + data.getHarm().getPlayer().getFraction()) + ")"); + data.getPlayer().sendMessage( + data.getHarm().getPlayer().getPlayer().getName() + " 杀死了你,惩罚已经施加!"); + data.setHarm(null); + PlayerMap.getCurrent().broadcastScore(data.getHarm().getPlayer()); + } catch (SQLException e) { + data.getPlayer().sendMessage(ChatColor.RED + data.getHarm().getPlayer().getPlayer().getName() + + " 杀死了你,但施加沙雕值惩罚失败,请联系管理员。"); + } + } tombstoneData.save(persistent); chest.setCustomName(player.getName() + " 的墓碑"); chest.update(); @@ -389,8 +444,7 @@ public class PlayerEventListener implements Listener { armorStand.setCustomName(tombstoneData.getOwnerTitle()); armorStand.setCustomNameVisible(true); armorStand.setMarker(true); - if (count[0] != 0) - player.sendMessage("你有" + count[0] + "个物品无法放入墓碑,已掉落在死亡地点。"); + if (count[0] != 0) player.sendMessage("你有" + count[0] + "个物品无法放入墓碑,已掉落在死亡地点。"); } /// 玩家移动 diff --git a/src/main/java/ling/coordinateRecorder/data/PlayerData.java b/src/main/java/ling/coordinateRecorder/data/PlayerData.java index 4fc5690..cb5b63f 100644 --- a/src/main/java/ling/coordinateRecorder/data/PlayerData.java +++ b/src/main/java/ling/coordinateRecorder/data/PlayerData.java @@ -2,6 +2,7 @@ package ling.coordinateRecorder.data; import ling.coordinateRecorder.Config; import ling.coordinateRecorder.CoordinateRecorder; +import ling.coordinateRecorder.Listener.PlayerEventListener; import ling.coordinateRecorder.Listener.PlayerMap; import ling.database.tables.records.LocationnotepadPO; import ling.database.tables.records.PlayersettingsPO; @@ -39,6 +40,7 @@ public class PlayerData { protected long fractionTime; protected TransmitData transmitData = null; protected TransmitData toMeTransmitData = null; + protected PlayerHarm harm; public PlayerData(Player player) throws SQLException { @@ -127,13 +129,14 @@ public class PlayerData { player.sendMessage(ChatColor.RED + "目标不是一个墓碑!"); return; } - if (!data.getOwnerUuid().equals(player.getUniqueId().toString())) { + if (!PlayerEventListener.isUnlockTombstone(this, data)) { player.sendMessage(ChatColor.RED + "不是你的墓碑,无法解锁!"); return; } //先给予沙雕值惩罚 - int value = Config.getItemStackValue(Arrays.asList(chest.getBlockInventory().getContents())); + int value = PlayerEventListener.getUnlockPunish(this, data, + Arrays.asList(chest.getBlockInventory().getContents())); value += Config.TOMBSTONE_PUNISHMENT; if (getFraction() > Config.UNLOCK_PROHIBITED) { player.sendMessage( @@ -207,6 +210,13 @@ public class PlayerData { } } + public PlayerHarm getHarm() { + return harm; + } + + public void setHarm(PlayerHarm harm) { + this.harm = harm; + } /// 线段计算 public static List generateLine(double startX, double startZ, double endX, double endZ, int resolution) { diff --git a/src/main/java/ling/coordinateRecorder/data/PlayerHarm.java b/src/main/java/ling/coordinateRecorder/data/PlayerHarm.java new file mode 100644 index 0000000..3db2622 --- /dev/null +++ b/src/main/java/ling/coordinateRecorder/data/PlayerHarm.java @@ -0,0 +1,24 @@ +package ling.coordinateRecorder.data; + +import org.bukkit.entity.Player; + +/// 玩家间伤害信息 +public class PlayerHarm { + //造成伤害的玩家 + protected final PlayerData player; + //造成伤害的时间 + protected final long time; + + public PlayerHarm(PlayerData player) { + this.player = player; + this.time = System.currentTimeMillis(); + } + + public long getTime() { + return time; + } + + public PlayerData getPlayer() { + return player; + } +} diff --git a/src/main/java/ling/coordinateRecorder/data/TombstoneData.java b/src/main/java/ling/coordinateRecorder/data/TombstoneData.java index 71a50bc..5bd2bb4 100644 --- a/src/main/java/ling/coordinateRecorder/data/TombstoneData.java +++ b/src/main/java/ling/coordinateRecorder/data/TombstoneData.java @@ -10,7 +10,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.IOException; -import java.util.Objects; /// 墓碑数据 public class TombstoneData implements PersistentData { @@ -61,8 +60,8 @@ public class TombstoneData implements PersistentData { this.ownerTitle = ownerTitle; } - public void setOwnerDefeater(String ownerDefeater) { - this.ownerDefeater = ownerDefeater; + public void setOwnerDefeater(Player player) { + this.ownerDefeater = player.getUniqueId().toString(); } public String getOwnerName() {