强化墓碑保护,解锁墓碑扣除沙雕值

保护墓碑免受爆炸的破坏,阻止玩家通过漏斗、以及连接大箱子的方法盗取墓碑内的物品。
解锁墓碑将扣除沙雕值
This commit is contained in:
2024-12-21 08:39:24 +08:00
parent 237e7824b0
commit 42bf3d1a80
7 changed files with 238 additions and 19 deletions

View File

@@ -71,6 +71,7 @@ public class Help {
沙雕值
一种具备惩罚性质的数据沙雕值过高将获得一系列debuff。
沙雕值每隔5分钟恢复一点。
使用此命令检查玩家沙雕值:
/zb sb <Name>
""",

View File

@@ -227,6 +227,8 @@ public class ZbCommand implements CommandExecutor, TabCompleter {
return;
}
data.locationListUpdate();
data.loadPlayerSettings();
PlayerMap.getCurrent().broadcastScore(data);
player.sendMessage("已重新载入数据");
}

View File

@@ -0,0 +1,82 @@
package ling.coordinateRecorder;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import java.util.HashMap;
import java.util.List;
public class Config {
/// 奖励得分间隔
public static final int REWARD_INTERVAL = 60 * 5;
/// 生成墓碑的基准沙雕值惩罚
public static final int TOMBSTONE_PUNISHMENT = 3;
/// 禁止解锁墓碑阈值
public static final int UNLOCK_PROHIBITED = 24;
public static final HashMap<Material, Double> ITEM_VALUE_MAP = new HashMap<>();
/// 计算物品堆的价值
public static int getItemStackValue(List<ItemStack> stack) {
double[] value = {0d};
stack.forEach(item -> {
if (ITEM_VALUE_MAP.containsKey(item.getType())) {
value[0] += ITEM_VALUE_MAP.get(item.getType()) * item.getAmount();
}
});
if (value[0] > 30)
value[0] = 30;
return (int) value[0];
}
static {
// 钻石
ITEM_VALUE_MAP.put(Material.DIAMOND, 0.2d);
ITEM_VALUE_MAP.put(Material.DIAMOND_ORE, 0.2d);
ITEM_VALUE_MAP.put(Material.DIAMOND_BLOCK, 1.8d);
// 铁
ITEM_VALUE_MAP.put(Material.IRON_ORE, 0.01);
ITEM_VALUE_MAP.put(Material.IRON_INGOT, 0.01);
ITEM_VALUE_MAP.put(Material.IRON_BLOCK, 0.09);
// 金
ITEM_VALUE_MAP.put(Material.GOLD_ORE, 0.02);
ITEM_VALUE_MAP.put(Material.GOLD_INGOT, 0.02);
ITEM_VALUE_MAP.put(Material.GOLD_BLOCK, 0.18);
// 绿宝石
ITEM_VALUE_MAP.put(Material.EMERALD, 0.2d);
ITEM_VALUE_MAP.put(Material.EMERALD_BLOCK, 1.8d);
ITEM_VALUE_MAP.put(Material.EMERALD_ORE, 0.2d);
// 末影之眼
ITEM_VALUE_MAP.put(Material.ENDER_EYE, 0.8d);
//龙蛋
ITEM_VALUE_MAP.put(Material.DRAGON_EGG, 10d);
//龙息
ITEM_VALUE_MAP.put(Material.DRAGON_BREATH, 2d);
//附魔书
ITEM_VALUE_MAP.put(Material.ENCHANTED_BOOK, 1d);
//下界合金
ITEM_VALUE_MAP.put(Material.NETHERITE_INGOT, 1d);
ITEM_VALUE_MAP.put(Material.NETHERITE_BLOCK, 9d);
//钻石装备
ITEM_VALUE_MAP.put(Material.DIAMOND_SWORD, 1d);
ITEM_VALUE_MAP.put(Material.DIAMOND_AXE, 1d);
ITEM_VALUE_MAP.put(Material.DIAMOND_SHOVEL, 1d);
ITEM_VALUE_MAP.put(Material.DIAMOND_PICKAXE, 1d);
ITEM_VALUE_MAP.put(Material.DIAMOND_BOOTS, 1d);
ITEM_VALUE_MAP.put(Material.DIAMOND_LEGGINGS, 1d);
ITEM_VALUE_MAP.put(Material.DIAMOND_CHESTPLATE, 1d);
ITEM_VALUE_MAP.put(Material.DIAMOND_HELMET, 1d);
//下界合金装备
ITEM_VALUE_MAP.put(Material.NETHERITE_SWORD, 1d);
ITEM_VALUE_MAP.put(Material.NETHERITE_AXE, 1d);
ITEM_VALUE_MAP.put(Material.NETHERITE_SHOVEL, 1d);
ITEM_VALUE_MAP.put(Material.NETHERITE_PICKAXE, 1d);
ITEM_VALUE_MAP.put(Material.NETHERITE_BOOTS, 1d);
ITEM_VALUE_MAP.put(Material.NETHERITE_LEGGINGS, 1d);
ITEM_VALUE_MAP.put(Material.NETHERITE_CHESTPLATE, 1d);
ITEM_VALUE_MAP.put(Material.NETHERITE_HELMET, 1d);
}
}

View File

@@ -1,5 +1,6 @@
package ling.coordinateRecorder.Listener;
import ling.coordinateRecorder.Config;
import ling.coordinateRecorder.CoordinateRecorder;
import ling.coordinateRecorder.data.PlayerData;
import ling.coordinateRecorder.data.PlayerDeathData;
@@ -8,14 +9,19 @@ import org.bukkit.*;
import org.bukkit.block.Block;
import org.bukkit.block.Chest;
import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
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.EntityExplodeEvent;
import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.event.inventory.InventoryMoveItemEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerQuitEvent;
@@ -29,7 +35,7 @@ import org.jetbrains.annotations.NotNull;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.*;
/// 玩家事件监听器
public class PlayerEventListener implements Listener {
@@ -124,8 +130,14 @@ public class PlayerEventListener implements Listener {
return;
}
data.setTombstoneBlock(block);
player.sendMessage("这是你的墓碑,请使用/zb unlock 命令解除锁定");
player.sendMessage("墓碑内有" + count + "个物品");
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);
}
}
@@ -146,6 +158,82 @@ public class PlayerEventListener implements Listener {
onTombstoneProtect(event, player, block);
}
/// 判断指定位置的方块是否是一个墓碑
public boolean isTombstone(Location location) {
if (location == null)
return false;
Block block = location.getBlock();
if (block.getType() != Material.CHEST)
return false;
Chest chest = (Chest) block.getState();
var per = chest.getPersistentDataContainer();
return TombstoneData.isTombstone(per);
}
/// 阻止玩家在墓碑附近摆放箱子,防止偷窃物品
@EventHandler
public void onBlockPlace(BlockPlaceEvent event) {
Block block = event.getBlockPlaced();
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);
Location a4 = block.getLocation().clone().add(0, 0, -1);
if (isTombstone(a1) || isTombstone(a2) || isTombstone(a3) || isTombstone(a4)) {
event.getPlayer().sendMessage(ChatColor.RED + "这是一个墓碑,你无法创建和它相连的大箱子!");
event.setCancelled(true);
}
}
/// 爆炸保护
@EventHandler
public void onExplosion(EntityExplodeEvent event) {
World world = event.getLocation().getWorld();
assert world != null;
// 获取爆炸位置附近的方块
for (Block block : event.blockList()) {
// 检查方块是否是墓碑
if (isTombstone(block.getLocation())) {
// 阻止墓碑被摧毁
event.setCancelled(true);
//通知附近玩家
Collection<Entity> entities = world.getNearbyEntities(event.getLocation(), 30, 30, 30);
entities.forEach(player -> {
player.sendMessage("爆炸波及到墓碑,为了保护墓碑,爆炸已取消!");
});
return;
}
}
}
/// 爆炸保护
@EventHandler
public void onBlockExplode(BlockExplodeEvent event) {
// 获取爆炸影响的方块
List<Block> affectedBlocks = event.blockList();
// 遍历爆炸影响的方块
for (Block block : affectedBlocks) {
if (isTombstone(block.getLocation())) {
// 如果爆炸影响了墓碑,取消该方块的摧毁
event.blockList().remove(block);
}
}
}
/// 阻止通过漏斗从墓碑中取出物品
@EventHandler
public void onInventoryMoveItem(InventoryMoveItemEvent event) {
Inventory source = event.getSource();
if (isTombstone(source.getLocation())) {
System.out.println("阻止漏斗");
event.setCancelled(true);
}
}
/// 阻止墓碑被火焰点燃
@EventHandler
public void onBlockIgnite(BlockIgniteEvent event) {
@@ -260,10 +348,36 @@ public class PlayerEventListener implements Listener {
//将玩家掉落物存入箱子
Inventory chestInventory = chest.getBlockInventory();
for (ItemStack item : event.getDrops()) {
if (item != null) chestInventory.addItem(item);
}
List<ItemStack> items = event.getDrops();
//有价值物品
List<ItemStack> valuable = new ArrayList<>();
//无价值物品
List<ItemStack> worthless = new ArrayList<>();
items.forEach(item -> {
if (Config.ITEM_VALUE_MAP.containsKey(item.getType())) {
valuable.add(item);
} else {
worthless.add(item);
}
});
//先将有价值的物品存入箱子
HashMap<Integer, ItemStack> other = chestInventory.addItem(valuable.toArray(new ItemStack[0]));
valuable.clear();
other.forEach((k, v) -> {
valuable.add(v);
});
//再将无价值物品存入箱子,确保优先保护有价值的物品。
other = chestInventory.addItem(worthless.toArray(new ItemStack[0]));
other.forEach((k, v) -> {
valuable.add(v);
});
int[] count = {0};
valuable.forEach(v -> {
count[0] += v.getAmount();
});
// 将无法放入墓碑的物品丢在地上。
event.getDrops().clear();
event.getDrops().addAll(valuable);
//在墓碑上方生成悬浮文字
Location textLocation = location.clone().add(0, 1.5, 0);
@@ -275,7 +389,8 @@ public class PlayerEventListener implements Listener {
armorStand.setCustomName(tombstoneData.getOwnerTitle());
armorStand.setCustomNameVisible(true);
armorStand.setMarker(true);
if (count[0] != 0)
player.sendMessage("你有" + count[0] + "个物品无法放入墓碑,已掉落在死亡地点。");
}
/// 玩家移动

View File

@@ -1,5 +1,6 @@
package ling.coordinateRecorder.Listener;
import ling.coordinateRecorder.Config;
import ling.coordinateRecorder.CoordinateRecorder;
import ling.coordinateRecorder.data.PlayerData;
import org.bukkit.Bukkit;
@@ -17,24 +18,23 @@ public class PlayerMap {
protected HashMap<UUID, PlayerData> playerList = new HashMap<>();
protected static final CoordinateRecorder plugin = CoordinateRecorder.getCurrent();
protected static final PlayerMap current = new PlayerMap();
//奖励得分间隔
protected final static int REWARD_INTERVAL = 60 * 5;
private PlayerMap() {
Bukkit.getScheduler().runTaskTimer(CoordinateRecorder.getCurrent(), this::flashTime, 0L, 200L);
Bukkit.getScheduler().runTaskTimer(CoordinateRecorder.getCurrent(), this::awardFraction, 0L,
20 * REWARD_INTERVAL);
20 * Config.REWARD_INTERVAL);
}
/// 广播玩家分数
private void broadcastScore(PlayerData data) {
public void broadcastScore(PlayerData data) {
for (Map.Entry<UUID, PlayerData> enter : playerList.entrySet()) {
enter.getValue().updatePlayerFraction(data);
}
}
/// 提供玩家分数
private void initScore(PlayerData data) {
public void initScore(PlayerData data) {
for (Map.Entry<UUID, PlayerData> enter : playerList.entrySet()) {
data.updatePlayerFraction(enter.getValue());
}
@@ -45,7 +45,7 @@ public class PlayerMap {
for (Map.Entry<UUID, PlayerData> enter : playerList.entrySet()) {
try {
// 跳过加入时间不足的玩家
if (enter.getValue().getFractionTime() > System.currentTimeMillis() - REWARD_INTERVAL * 800)
if (enter.getValue().getFractionTime() > System.currentTimeMillis() - Config.REWARD_INTERVAL * 800)
continue;
if (enter.getValue().addFraction(-1)) {
broadcastScore(enter.getValue());

View File

@@ -1,6 +1,8 @@
package ling.coordinateRecorder.data;
import ling.coordinateRecorder.Config;
import ling.coordinateRecorder.CoordinateRecorder;
import ling.coordinateRecorder.Listener.PlayerMap;
import ling.database.tables.records.LocationnotepadPO;
import ling.database.tables.records.PlayersettingsPO;
import org.bukkit.*;
@@ -16,10 +18,7 @@ import org.jooq.impl.DSL;
import java.math.BigDecimal;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.*;
import static ling.database.Tables.PLAYERSETTINGS;
import static ling.database.tables.LocationnotepadTB.LOCATIONNOTEPAD;
@@ -101,6 +100,23 @@ public class PlayerData {
return;
}
//先给予沙雕值惩罚
int value = Config.getItemStackValue(Arrays.asList(chest.getBlockInventory().getContents()));
value += Config.TOMBSTONE_PUNISHMENT;
if (getFraction() > Config.UNLOCK_PROHIBITED) {
player.sendMessage(
"您的沙雕值过高,禁止解锁墓碑!( >" + ChatColor.RED + Config.UNLOCK_PROHIBITED + ChatColor.WHITE + " )");
return;
}
try {
addFraction(value);
PlayerMap.getCurrent().broadcastScore(this);
} catch (SQLException e) {
player.sendMessage(ChatColor.RED + "解锁墓碑失败:服务器内部错误");
CoordinateRecorder.getCurrent().getLogger().severe("添加沙雕值出错:" + e.getMessage());
return;
}
Location location = tombstoneBlock.getLocation();
World world = location.getWorld();
assert world != null;
@@ -118,7 +134,8 @@ public class PlayerData {
//播放雷击特效
world.strikeLightningEffect(location);
//world.playSound(location, Sound.ENTITY_LIGHTNING_BOLT_THUNDER, 1.0f, 1.0f);
player.sendMessage("墓碑已解锁!");
player.sendMessage(
"墓碑已解锁!您受到了" + ChatColor.YELLOW + value + ChatColor.WHITE + "点沙雕值惩罚!(当前" + ChatColor.RED + getFraction() + ChatColor.WHITE + "");
}
/// 在给定位置绘制六芒星

View File

@@ -10,6 +10,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.util.Objects;
/// 墓碑数据
public class TombstoneData implements PersistentData {
@@ -81,7 +82,8 @@ public class TombstoneData implements PersistentData {
}
public static boolean isTombstone(PersistentDataContainer data) {
return data.has(OWNER_NAME) && data.has(OWNER_UUID) && data.has(OWNER_TITLE) && data.has(OWNER_DEFEATER);
return data.has(OWNER_NAME) && data.has(OWNER_UUID) && data.has(OWNER_TITLE) && data.has(
OWNER_DEFEATER);
}
@Override