fix: 修复自定义附魔只能单个附魔的问题

This commit is contained in:
ypsdf1
2026-06-05 18:17:12 +08:00
parent 7c7d7bd127
commit aea44ef92d
9 changed files with 2449 additions and 374 deletions
+28
View File
@@ -52,6 +52,34 @@ public class BondBridge {
return false;
}
}
/** 查询玩家当前债券余额 */
public int getBonds(String playerName) {
if (!isHooked()) return 0;
try {
org.bukkit.plugin.Plugin plug =
org.bukkit.Bukkit
.getPluginManager()
.getPlugin("Sdf1_login");
if (plug == null) return 0;
Object bondManager =
plug.getClass()
.getMethod("getBondManager")
.invoke(plug);
Object result =
bondManager.getClass()
.getMethod("getBonds",
String.class)
.invoke(bondManager,
playerName);
return (int) result;
} catch (Exception e) {
return 0;
}
}
private Object findManager(Plugin sdf1) {
Class<?> c = sdf1.getClass();
@@ -95,5 +95,22 @@ public class FileGenerator {
l.add("| M.C的全称是什么? | Mojang Studios | 10 |");
return l;
}
public static void ensureTreasureConfig(
File dir) {
dir.mkdirs();
File f = new File(dir, "设置.txt");
if (f.exists()) return;
try {
OutputStreamWriter w =
new OutputStreamWriter(
new FileOutputStream(f),
StandardCharsets.UTF_8);
w.write("# 寻宝全局设置\n");
w.write("保底债券: 是\n");
w.write("随机数量: 1~3\n");
w.close();
} catch (IOException ignored) {
}
}
}
+78 -5
View File
@@ -59,9 +59,9 @@ public class Main extends JavaPlugin
new TreasureListener(
this, treasureManager),
this);
spawnTask = new TreasureSpawnTask(
this, treasureManager)
.runTaskTimer(this, 600L, 600L);
new TreasureSpawnTask(this, treasureManager)
.runTaskTimer(this, 100L, 100L);
// ★ 安全注册 Tab 补全
if (getCommand("quiz") != null) {
@@ -81,6 +81,14 @@ public class Main extends JavaPlugin
.setTabCompleter(this);
}
// ★ 启动时清除残留宝箱
if (treasureManager != null) {
treasureManager.cleanResidualChests();
}
if (treasureManager != null) {
treasureManager.forceCleanClaims();
treasureManager.cleanResidualChests();
}
getLogger().info("=== Sdf1_game v1.0 ===");
getLogger().info("债券桥接: "
+ (bondBridge.isHooked()
@@ -118,6 +126,41 @@ public class Main extends JavaPlugin
public void onDisable() {
if (quizManager != null) quizManager.stopAuto();
if (spawnTask != null) spawnTask.cancel();
// ★ 卸载时清除所有宝箱
treasureManager.cleanResidualChests();
treasureManager.removeAllChests();
treasureManager.forceCleanClaims();
if (treasureManager != null) {
treasureManager.forceCleanClaims();
treasureManager.cleanResidualChests();
}
saveConfig();
getLogger().info("\n" +
" __ __ _ \n" +
" \\ \\ / / | | \n" +
" \\ \\ /\\ / /__ ___ ___ _ __ ___ ___ | |_ ___ \n" +
" \\ \\/ \\/ / _ \\/ __/ _ \\| '_ ` _ \\ / _ \\ | __/ _ \\ \n" +
" \\ /\\ / __/ (_| (_) | | | | | | __/ | || (_) | \n" +
" \\/ \\/ \\___|\\___\\___/|_| |_| |_|\\___| \\__\\___/ _ \n" +
" | | (_) \n" +
" ___ __ _ ___ _ _ _ _ __ _ _ __ | |_ __ _ _ __ __ ___ __ _ _ __ ___ ___ _ ____ _____ _ __ \n" +
" / __/ _` |/ _ \\ | | | | | | |/ _` | '_ \\ | __/ _` | '_ \\ \\ \\/ / |/ _` | '_ \\ / __|/ _ \\ '__\\ \\ / / _ \\ '__| \n" +
" | (_| (_| | (_) | | |_| | |_| | (_| | | | | | || (_| | | | | > <| | (_| | | | | \\__ \\ __/ | \\ V / __/ | \n" +
" \\___\\__,_|\\___/ \\__, |\\__,_|\\__,_|_| |_| \\__\\__,_|_| |_|_/_/\\_\\_|\\__,_|_| |_|_|___/\\___|_| _ \\_/ \\___|_| \n" +
" __/ | (_) _ |__ \\ | | (_) | (_)/ _| \n" +
" ___ ___ _ ____ |___/ _ __ _ _ __ (_) _ __ ___ ___ ) | _ _ _ __ ___| |__ _ __| |_| |_ _ _ ___ _ __ \n" +
" / __|/ _ \\ '__\\ \\ / / _ \\ '__| | '_ \\ | '_ ` _ \\ / __| / / | | | | '_ \\/ __| '_ \\| |/ _` | | _| | | | / __| '_ \\ \n" +
" \\__ \\ __/ | \\ V / __/ | | | |_) | _ | | | | | | (__ / /_ | |_| | |_) \\__ \\ | | | | (_| | | | | |_| || (__| | | |\n" +
" |___/\\___|_| \\_/ \\___|_| |_| .__/ (_) |_| |_| |_|\\___|____(_)__, | .__/|___/_| |_|_|\\__,_|_|_| \\__,_(_)___|_| |_|\n" +
" | | __/ | | \n" +
" _ ____ _|_| ________ ___ |___/|_| \n" +
" | | _|___ \\ / _ \\ / /____ / _ \\ \n" +
" _ __ ___ ___| |_(_) __) | | | |/ /_ / / (_) | \n" +
" | '_ \\ / _ \\/ __| __| |__ <| | | | '_ \\ / / \\__, | \n" +
" | |_) | (_) \\__ \\ |_ _ ___) | |_| | (_) / / / / \n" +
" | .__/ \\___/|___/\\__(_)____/ \\___/ \\___/_/ /_/ \n" +
" | | \n" +
" |_| ");
}
// ========== 命令 ==========
@@ -199,6 +242,10 @@ public class Main extends JavaPlugin
return false;
}
public TreasureManager getTreasureManager() {
return treasureManager;
}
// ========== 快问快答 ==========
private String[] shiftArgs(String[] a) {
@@ -342,8 +389,19 @@ public class Main extends JavaPlugin
sendTreasureList(s);
return true;
case "reload":
treasureManager.loadAll();
s.sendMessage("§a已重载");
case "重载":
treasureManager.forceCleanClaims();
for (org.bukkit.entity.Player p :
Bukkit.getOnlinePlayers()) {
treasureManager
.reclaimAllTreasureItems(p);
}
treasureManager.reload();
s.sendMessage(
"§a[寻宝] 重载完成,领取记录已清空");
s.sendMessage("§b§l\n欢迎游玩草原探险服务器");
s.sendMessage("§b§l服务器ip:mc2.ypshidifu.cn\n端口30679");
s.sendMessage("");
return true;
case "工具":
case "tool":
@@ -359,6 +417,9 @@ public class Main extends JavaPlugin
}
treasureManager.removeRegion(a[1]);
s.sendMessage("§a已删除: " + a[1]);
s.sendMessage("§b§l\n欢迎游玩草原探险服务器");
s.sendMessage("§b§l服务器ip:mc2.ypshidifu.cn\n端口30679");
s.sendMessage("");
return true;
// ★ on/off 代替 border
case "on":
@@ -394,6 +455,9 @@ public class Main extends JavaPlugin
.containsKey(regionName)) {
s.sendMessage("§c区域不存在: "
+ regionName);
s.sendMessage("§b§l\n欢迎游玩草原探险服务器");
s.sendMessage("§b§l服务器ip:mc2.ypshidifu.cn\n端口30679");
s.sendMessage("");
return true;
}
@@ -426,6 +490,9 @@ public class Main extends JavaPlugin
if (tr.getPos1() == null) {
s.sendMessage("§c请先获取工具并设置AB点");
s.sendMessage("§7或: /寻宝 on 区域名");
s.sendMessage("§b§l\n欢迎游玩草原探险服务器");
s.sendMessage("§b§l服务器ip:mc2.ypshidifu.cn\n端口30679");
s.sendMessage("");
return true;
}
@@ -456,6 +523,9 @@ public class Main extends JavaPlugin
TreasureRegion.giveTool((Player) s);
s.sendMessage("§a已获得圈地工具");
s.sendMessage("§7左键=A点 右键=B点");
s.sendMessage("§b§l\n欢迎游玩草原探险服务器");
s.sendMessage("§b§l服务器ip:mc2.ypshidifu.cn\n端口30679");
s.sendMessage("");
return true;
}
@@ -605,6 +675,9 @@ public class Main extends JavaPlugin
+ hMin + "~Y" + hMax);
s.sendMessage("§7使用 §e/寻宝 off "
+ name + " §7关闭边框");
s.sendMessage("§b§l\n欢迎游玩草原探险服务器");
s.sendMessage("§b§l服务器ip:mc2.ypshidifu.cn\n端口30679");
s.sendMessage("");
}
boolean o1k = treasureManager.saveRegion(
+38 -6
View File
@@ -1,5 +1,10 @@
package Sdf1_game;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
@@ -18,20 +23,47 @@ public class TreasureConfig {
public List<TreasureReward> rewards =
new ArrayList<>();
// ★ 开箱设置
public boolean guaranteeBond = true;
public int bondMin = 1;
public int bondMax = 3;
public int randomMin = 1;
public int randomMax = 3;
public long lastSpawnTime = 0;
// ★ 中文名映射:奖励索引→显示名
public java.util.Map<Integer, String>
rewardNames = new java.util.LinkedHashMap<>();
public TreasureConfig(String name) {
this.name = name;
}
public int getTotalWeight() {
int w = 0;
for (TreasureReward r : rewards) {
for (TreasureReward r : rewards)
w += r.getWeight();
}
return w;
}
public static void ensureTreasureConfig(
File dir) {
dir.mkdirs();
File f = new File(dir, "设置.txt");
if (f.exists()) return;
try {
OutputStreamWriter w =
new OutputStreamWriter(
new FileOutputStream(f),
StandardCharsets.UTF_8);
w.write("# 寻宝全局设置\n");
w.write("# 保底债券: 是/否\n");
w.write("保底债券: 是\n");
w.write("# 随机奖励数量范围\n");
w.write("随机数量: 1~3\n");
w.close();
} catch (IOException ignored) {
}
}
public String getBoundsStr() {
return "[" + minX + "," + minZ + "] ~ ["
+ maxX + "," + maxZ + "]";
}
}
+448 -123
View File
@@ -3,6 +3,8 @@ package Sdf1_game;
import net.kyori.adventure.text.Component;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.Inventory;
@@ -19,6 +21,9 @@ public class TreasureInventory {
// ★ 奖励索引标记(写在lore里用于精确匹配)
private static final String LORE_INDEX_PREFIX =
"§0§k";
// ★ 唯一的标记前缀
private static final String MARK = "§0§k";
public static class GuiHolder
implements InventoryHolder {
@@ -26,6 +31,8 @@ public class TreasureInventory {
private final Player player;
private Inventory inv;
public GuiHolder(TreasureConfig config,
Player player) {
this.config = config;
@@ -50,45 +57,455 @@ public class TreasureInventory {
}
}
public static void open(Player player,
TreasureConfig config) {
GuiHolder holder =
new GuiHolder(config, player);
Main plugin = (Main) Bukkit
.getPluginManager()
.getPlugin("Sdf1_game");
Inventory inv = Bukkit.createInventory(
holder, 27,
"§6§l寻宝宝箱 - " + config.name);
holder.setInventory(inv);
List<TreasureReward> selected =
rollRewards(config, 6);
int[] slots = {10, 12, 14, 16, 22, 24};
for (int i = 0;
i < selected.size()
&& i < slots.length; i++) {
TreasureReward r = selected.get(i);
inv.setItem(slots[i],
buildDisplayItem(r, i));
// 保底债券
int bondAmount = 0;
if (config.guaranteeBond) {
for (TreasureReward r : config.rewards) {
if (r.getType()
== TreasureReward.Type.BOND) {
bondAmount = r.rollBondAmount();
break;
}
}
}
// 边框
ItemStack glass = new ItemStack(
// 随机非债券奖励
List<TreasureReward> others =
new ArrayList<>();
for (TreasureReward r : config.rewards) {
if (r.getType()
!= TreasureReward.Type.BOND) {
others.add(r);
}
}
List<TreasureReward> rolled =
new ArrayList<>();
int count = ThreadLocalRandom.current()
.nextInt(
config.randomMin,
config.randomMax + 1);
int totalW = 0;
for (TreasureReward r : others)
totalW += r.getWeight();
if (totalW > 0 && !others.isEmpty()) {
List<TreasureReward> pool =
new ArrayList<>(others);
for (int i = 0;
i < count && !pool.isEmpty(); i++) {
int roll = ThreadLocalRandom
.current().nextInt(totalW);
int acc = 0;
TreasureReward pick = null;
for (TreasureReward r : pool) {
acc += r.getWeight();
if (roll < acc) {
pick = r;
break;
}
}
if (pick == null)
pick = pool.get(
pool.size() - 1);
rolled.add(pick);
pool.remove(pick);
totalW = 0;
for (TreasureReward r : pool)
totalW += r.getWeight();
}
// ★ 显示GUI让玩家确认
GuiHolder gh = new GuiHolder(
config, player);
Inventory inv = Bukkit.createInventory(
gh, 27,
"§6§l═══ 寻宝 ═══ " + config.name);
// 奖励预览
List<TreasureReward> preview =
new ArrayList<>();
preview.add(new TreasureReward(
TreasureReward.Type.BOND, null,
1, bondAmount, null, 0,
"§6债券+" + bondAmount));
preview.addAll(rolled);
for (int i = 0;
i < preview.size() && i < 9; i++) {
TreasureReward r = preview.get(i);
Material mat;
if (r.getType()
== TreasureReward.Type.BOND) {
mat = Material.PAPER;
} else {
mat = Material.matchMaterial(
r.getMaterialName());
if (mat == null)
mat = Material.PAPER;
}
ItemStack slot =
new ItemStack(mat,
r.getAmount());
ItemMeta sm = slot.getItemMeta();
if (sm != null) {
sm.displayName(
Component.text(
r.getDisplayName()));
slot.setItemMeta(sm);
}
inv.setItem(i, slot);
}
// 装饰边框
ItemStack filler = new ItemStack(
Material.BLACK_STAINED_GLASS_PANE);
ItemMeta gm = glass.getItemMeta();
if (gm != null) {
gm.displayName(Component.text(" "));
glass.setItemMeta(gm);
ItemMeta fm = filler.getItemMeta();
if (fm != null) {
fm.displayName(Component.text(""));
filler.setItemMeta(fm);
}
for (int i = 0; i < 27; i++) {
if (inv.getItem(i) == null) {
inv.setItem(i, glass);
if (inv.getItem(i) == null)
inv.setItem(i, filler);
}
gh.setInventory(inv);
player.openInventory(inv);
}
// ★ 直接发放+延迟显示结果
final int fBond = bondAmount;
final List<TreasureReward> fRolled =
rolled;
Bukkit.getScheduler().runTaskLater(plugin,
() -> giveRewards(player, config,
fBond, fRolled), 40L);
}
// ★ isCustomItem
public static boolean isCustomItem(
ItemStack item) {
if (item == null) return false;
if (!item.hasItemMeta()) return false;
ItemMeta meta = item.getItemMeta();
if (meta == null) return false;
if (!meta.hasLore()) return false;
List<Component> lore = meta.lore();
if (lore == null) return false;
for (Component lc : lore) {
String line = net.kyori.adventure.text
.serializer.legacy
.LegacyComponentSerializer
.legacySection().serialize(lc);
if (line.contains(MARK + "CUSTOM"))
return true;
}
return false;
}
// ===== buildCustomItem =====
private static ItemStack buildCustomItem(
TreasureReward r,
String playerName,
String regionName,
Main plugin) {
Material mat = Material.matchMaterial(
r.getMaterialName());
if (mat == null) {
mat = Material.PAPER;
}
ItemStack item =
new ItemStack(mat, r.getAmount());
ItemMeta meta = item.getItemMeta();
if (meta == null) {
return item;
}
meta.displayName(Component.text(
"§b" + r.getDisplayName()));
// 附魔
String enchStr = r.getEnchant();
if (enchStr != null
&& !enchStr.isEmpty()) {
String[] pairs = enchStr.split(";");
for (String pair : pairs) {
String[] ep = pair.split(",");
if (ep.length != 2) continue;
String id = ep[0].trim()
.toLowerCase();
int lvl = 1;
try {
lvl = Integer.parseInt(
ep[1].trim());
} catch (Exception ignore) {
}
Enchantment ench =
Enchantment.getByKey(
NamespacedKey
.minecraft(id));
if (ench != null) {
// ★ 每个附魔单独打,不共享meta
meta.addEnchant(ench, lvl, true);
}
}
// ★ 所有附魔加完后一次性设置
item.setItemMeta(meta);
}
String tag = "§0§kCUSTOM|"
+ playerName + "|"
+ regionName + "|"
+ r.getDisplayName() + "|"
+ r.getAttackUsesLimit() + "|0";
List<Component> lore = new ArrayList<>();
if (r.getDurationSec() > 0) {
lore.add(Component.text(
"§7时限: "
+ TreasureReward
.formatTime(
r.getDurationSec())));
lore.add(Component.text(
"§c§o到期自动收回"));
}
if (r.getAttackUsesLimit() > 0) {
lore.add(Component.text(
"§c攻击上限: "
+ r.getAttackUsesLimit()
+ "次"));
}
lore.add(Component.text(tag));
meta.lore(lore);
item.setItemMeta(meta);
return item;
}
// ===== giveRewards =====
private static void giveRewards(
Player player,
TreasureConfig config,
int bondAmount,
List<TreasureReward> rolled) {
Main plugin = (Main) Bukkit
.getPluginManager()
.getPlugin("Sdf1_game");
BondBridge bb = plugin.getBondBridge();
TreasureManager tm =
plugin.getTreasureManager();
StringBuilder msg = new StringBuilder();
msg.append("§6§l═══ 寻宝结果 ═══\n");
// ★ 控制台打印开箱结果
StringBuilder log = new StringBuilder();
log.append("[寻宝开箱] ");
log.append(player.getName());
log.append(" | 区域=" + config.name);
// 债券
if (bondAmount > 0) {
int oldBond = 0;
if (bb != null && bb.isHooked()) {
// ★ 先查原始余额
oldBond = bb.getBonds(
player.getName());
bb.addBonds(player.getName(),
bondAmount, "寻宝奖励");
}
int newBond = oldBond + bondAmount;
msg.append("§6保底债券: §e")
.append(oldBond)
.append(" §7→§a ")
.append(newBond)
.append(" §6(+" + bondAmount + ")\n");
msg.append("§6债券: §e+")
.append(bondAmount)
.append(" 张\n");
}
for (TreasureReward r : rolled) {
if (r.getType()
== TreasureReward.Type.ITEM) {
if (r.getDurationSec() > 0
&& tm.isClaimed(
player.getName(),
r.getDisplayName(),
config.name)) {
msg.append("§c")
.append(r.getDisplayName())
.append(" 已领过\n");
continue;
}
ItemStack give;
if (r.getDurationSec() > 0
|| r.getAttackUsesLimit()
> 0) {
give = buildCustomItem(r,
player.getName(),
config.name, plugin);
if (r.getDurationSec() > 0) {
tm.addClaim(
player.getName(),
r.getDisplayName(),
config.name);
}
} else {
Material m =
Material.matchMaterial(
r.getMaterialName());
if (m == null) {
m = Material.PAPER;
}
give = new ItemStack(m,
r.getAmount());
}
player.getInventory()
.addItem(give);
msg.append("§f")
.append(r.getDisplayName())
.append("\n");
if (r.getDurationSec() > 0) {
final int dur =
r.getDurationSec();
final String rn =
r.getDisplayName();
final String pn =
player.getName();
final String rc =
config.name;
Bukkit.getScheduler()
.runTaskLater(plugin,
() -> {
for (int i = 0;
i < player
.getInventory()
.getSize();
i++) {
ItemStack it =
player
.getInventory()
.getItem(i);
if (it != null
&& isCustomItem(
it)
&& getMarkName(
it)
.equals(
rn)) {
player
.getInventory()
.setItem(
i,
null);
break;
}
}
tm.removeClaim(
pn, rn, rc);
if (player
.isOnline()) {
player
.sendMessage(
"§c[寻宝] "
+ rn
+ " 已到期收回");
}
},
dur * 20L);
}
} else if (r.getType()
== TreasureReward.Type.COMMAND) {
String cmd = r.getCommand()
.replace("{player}",
player.getName());
Bukkit.dispatchCommand(
Bukkit.getConsoleSender(),
cmd);
msg.append("§a命令已执行\n");
}
}
msg.append("§6§l══════════════");
// ★ 打印到控制台
plugin.getLogger().info(log.toString());
player.sendMessage(msg.toString());
}
// ★ parseMark
public static String[] parseMark(
ItemStack item) {
if (!isCustomItem(item)) return null;
ItemMeta meta = item.getItemMeta();
if (meta == null) return null;
List<Component> lore = meta.lore();
if (lore == null) return null;
for (Component lc : lore) {
String line = net.kyori.adventure.text
.serializer.legacy
.LegacyComponentSerializer
.legacySection().serialize(lc);
if (line.contains(MARK + "CUSTOM")) {
String data = line.substring(
line.indexOf(MARK + "CUSTOM")
+ MARK.length());
String[] p = data.split("\\|");
if (p.length >= 6)
return new String[]{
p[1], p[2], p[3],
p[4], p[5]};
}
}
return null;
}
// ★ getMarkName
private static String getMarkName(
ItemStack item) {
String[] m = parseMark(item);
return m != null ? m[2] : "";
}
private static void expiryTimer(
Player player,
TreasureManager tm,
int dur,
String rName,
String pName,
String regionName) {
// 收回物品
for (int si = 0;
si < player.getInventory().getSize();
si++) {
ItemStack siItem =
player.getInventory()
.getItem(si);
if (siItem != null
&& isCustomItem(siItem)
&& getMarkName(siItem)
.equals(rName)) {
player.getInventory()
.setItem(si, null);
break;
}
}
// 清记录
tm.removeClaim(pName, rName, regionName);
if (player.isOnline()) {
player.sendMessage(
"§c[寻宝] " + rName
+ " 已到期收回");
}
}
player.openInventory(inv);
}
/**
* 构建GUI显示物品(含索引标记用于匹配)
@@ -240,7 +657,9 @@ public class TreasureInventory {
TreasureReward matched =
config.rewards.get(rewardIndex);
giveReward(player, matched, bb, plugin);
player.closeInventory();
open(player, config);
// 领取后删除GUI物品
event.getInventory()
@@ -251,101 +670,7 @@ public class TreasureInventory {
return true;
}
// ===== 发放奖励 =====
private static void giveReward(Player player,
TreasureReward r,
BondBridge bb,
Main plugin) {
switch (r.getType()) {
case BOND:
// ★ 直接发债券
if (bb != null && bb.isHooked()) {
bb.addBonds(player.getName(),
r.getBondAmount(),
"寻宝奖励");
}
player.sendMessage("§e获得了 §6"
+ r.getBondAmount()
+ " §e债券");
break;
case COMMAND:
String cmd = r.getCommand()
.replace("{player}",
player.getName());
Bukkit.dispatchCommand(
Bukkit.getConsoleSender(), cmd);
player.sendMessage(
"§e执行了命令奖励");
break;
case ITEM:
// ★ 给原版物品:不设自定义名称
Material mat =
Material.matchMaterial(
r.getMaterialName());
if (mat == null) mat = Material.PAPER;
ItemStack give =
new ItemStack(mat,
r.getAmount());
if (r.getDurationSec() > 0) {
// ★ 限时贴标物品
ItemMeta meta =
give.getItemMeta();
if (meta != null) {
meta.displayName(
Component.text(
"§b" + r.getDisplayName()));
List<Component> lore =
new ArrayList<>();
lore.add(Component.text(
"§7时限: "
+ TreasureReward
.formatTime(
r.getDurationSec())));
lore.add(Component.text(
"§c§o到期自动收回"));
meta.lore(lore);
TreasureReward.applyEnchant(
meta, r.getEnchant());
give.setItemMeta(meta);
}
player.getInventory()
.addItem(give);
// 定时收回
final ItemStack ref =
give.clone();
final int dur =
r.getDurationSec();
final String rName =
r.getDisplayName();
Bukkit.getScheduler()
.runTaskLater(plugin,
() -> {
player.getInventory()
.remove(ref);
if (player.isOnline()) {
player.sendMessage(
"§c[寻宝] " + rName
+ " §7已到期,自动收回");
}
},
dur * 20L);
} else {
// ★ 普通物品:纯原版,无自定义名称
player.getInventory()
.addItem(give);
}
player.sendMessage("§e获得了 §f"
+ mat.name()
+ " §ex" + r.getAmount());
break;
}
}
// ===== 权重随机 =====
@@ -1,5 +1,6 @@
package Sdf1_game;
import net.kyori.adventure.text.Component;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
@@ -11,7 +12,10 @@ import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import java.util.List;
import java.util.Map;
public class TreasureListener implements Listener {
@@ -24,6 +28,213 @@ public class TreasureListener implements Listener {
this.plugin = plugin;
this.manager = manager;
}
// ★ 玩家攻击玩家时计数
@org.bukkit.event.EventHandler
public void onDamage(
org.bukkit.event.entity
.EntityDamageByEntityEvent e) {
if (!(e.getDamager()
instanceof org.bukkit.entity.Player))
return;
if (!(e.getEntity()
instanceof org.bukkit.entity.Player))
return;
org.bukkit.entity.Player attacker =
(org.bukkit.entity.Player)
e.getDamager();
ItemStack hand =
attacker.getInventory()
.getItemInMainHand();
if (hand == null) return;
if (!TreasureInventory
.isCustomItem(hand)) return;
String[] mark =
TreasureInventory.parseMark(hand);
if (mark == null) return;
String owner = mark[0];
String region = mark[1];
String rName = mark[2];
int maxUse =
Integer.parseInt(mark[3]);
int useCount =
Integer.parseInt(mark[4]) + 1;
// ★ 只有攻击上限>0的物品才计数
if (maxUse <= 0) return;
// ★ 检查归属
if (!owner.equalsIgnoreCase(
attacker.getName())) return;
plugin.getLogger().info(
"[寻宝] ★攻击: "
+ attacker.getName()
+ " " + rName
+ " " + useCount
+ "/" + maxUse);
// ★ 更新lore里的次数
ItemMeta meta = hand.getItemMeta();
if (meta != null && meta.hasLore()) {
List<Component> lore = meta.lore();
for (int i = 0;
i < lore.size(); i++) {
String line =
net.kyori.adventure.text
.serializer.legacy
.LegacyComponentSerializer
.legacySection()
.serialize(lore.get(i));
if (line.contains("§0§kCUSTOM")) {
String newMark =
"§0§kCUSTOM|"
+ owner + "|"
+ region + "|"
+ rName + "|"
+ maxUse + "|"
+ useCount;
lore.set(i,
Component.text(newMark));
break;
}
}
meta.lore(lore);
hand.setItemMeta(meta);
}
// ★ 打印给玩家看
attacker.sendMessage(
"§e[寻宝] " + rName
+ " 攻击次数: "
+ useCount + "/" + maxUse);
// 达到上限 → 回收
if (useCount >= maxUse) {
attacker.getInventory()
.setItemInMainHand(null);
plugin.getTreasureManager()
.removeClaim(owner, rName,
region);
attacker.sendMessage(
"§c[寻宝] " + rName
+ " 攻击次数用完,已回收");
attacker.getWorld().dropItemNaturally(
attacker.getLocation(),
new ItemStack(Material.AIR));
}
}
// ★ 禁止丢弃寻宝物品
@org.bukkit.event.EventHandler
public void onDrop(
org.bukkit.event.player
.PlayerDropItemEvent e) {
if (TreasureInventory
.isCustomItem(
e.getItemDrop().getItemStack())) {
e.setCancelled(true);
e.getPlayer().sendMessage(
"§c[寻宝] 寻宝物品不可丢弃");
}
}
// ★ 死亡时回收寻宝物品(不掉落)
@org.bukkit.event.EventHandler
public void onDeath(
org.bukkit.event.entity
.PlayerDeathEvent e) {
org.bukkit.entity.Player player =
e.getEntity();
List<ItemStack> drops = e.getDrops();
// ★ 检查背包里所有物品
ItemStack[] contents =
player.getInventory().getContents();
for (int i = 0;
i < contents.length; i++) {
ItemStack item = contents[i];
if (item == null) continue;
if (!TreasureInventory
.isCustomItem(item)) continue;
String[] mark =
TreasureInventory.parseMark(item);
if (mark != null) {
// 从掉落列表移除
drops.remove(item);
// 清除记录
plugin.getTreasureManager()
.removeClaim(mark[0],
mark[2], mark[1]);
}
}
// 清空背包里所有寻宝物品
for (int i = 0;
i < contents.length; i++) {
ItemStack item = contents[i];
if (item == null) continue;
if (TreasureInventory
.isCustomItem(item)) {
player.getInventory()
.setItem(i, null);
}
}
}
// ★ 物品掉落到地上时检测(丢弃后立即清除)
@org.bukkit.event.EventHandler
public void onItemSpawn(
org.bukkit.event.entity
.ItemSpawnEvent e) {
ItemStack item =
e.getEntity().getItemStack();
if (!TreasureInventory
.isCustomItem(item)) return;
// ★ 延迟1秒后检查,如果还在地上就销毁
// (已捡起的不会被销毁)
final org.bukkit.Location loc =
e.getLocation();
final String itemKey =
item.getType().name()
+ "_" + item.getAmount();
org.bukkit.Bukkit.getScheduler()
.runTaskLater(plugin, () -> {
for (org.bukkit.entity.Entity ent :
loc.getChunk()
.getEntities()) {
if (ent instanceof
org.bukkit.entity.Item) {
org.bukkit.entity.Item di =
(org.bukkit.entity.Item)
ent;
if (TreasureInventory
.isCustomItem(
di.getItemStack())) {
String[] mark =
TreasureInventory
.parseMark(
di.getItemStack());
if (mark != null) {
// 清除领取记录
plugin
.getTreasureManager()
.removeClaim(
mark[0],
mark[2],
mark[1]);
}
di.remove();
break;
}
}
}
}, 20L);
}
// ===== 圈地工具选点 =====
@@ -113,6 +324,16 @@ public class TreasureListener implements Listener {
}
}
}
// ★ 玩家下线时标记待回收
@org.bukkit.event.EventHandler
public void onQuit(
org.bukkit.event.player.PlayerQuitEvent e) {
String name = e.getPlayer().getName();
// 标记该玩家有物品需要在上线时回收
plugin.getTreasureManager()
.getPendingReclaims().add(name);
}
// ===== 阻止破坏宝箱 =====
@@ -140,6 +361,132 @@ public class TreasureListener implements Listener {
}
}
@org.bukkit.event.EventHandler(priority =
org.bukkit.event.EventPriority.HIGH)
public void onClick(
org.bukkit.event.inventory
.InventoryClickEvent e) {
if (e.getCursor() == null) return;
if (!TreasureInventory
.isCustomItem(e.getCursor()))
return;
String type = e.getView()
.getTopInventory().getType().name();
if (!type.equals("PLAYER")
&& !type.equals("CRAFTING")
&& !type.equals("CREATIVE")) {
e.setCancelled(true);
if (e.getWhoClicked()
instanceof org.bukkit.entity.Player) {
((org.bukkit.entity.Player)
e.getWhoClicked())
.sendMessage(
"§c[寻宝] 不可放入容器");
}
}
}
@org.bukkit.event.EventHandler(priority =
org.bukkit.event.EventPriority.HIGH)
public void onDrag(
org.bukkit.event.inventory
.InventoryDragEvent e) {
for (ItemStack item :
e.getNewItems().values()) {
if (TreasureInventory
.isCustomItem(item)) {
String type = e.getView()
.getTopInventory()
.getType().name();
if (!type.equals("PLAYER")
&& !type.equals("CRAFTING")) {
e.setCancelled(true);
return;
}
}
}
}
@org.bukkit.event.EventHandler(priority =
org.bukkit.event.EventPriority.HIGH)
public void onAnvil(
org.bukkit.event.inventory
.PrepareAnvilEvent e) {
ItemStack first =
e.getInventory().getItem(0);
if (first != null
&& TreasureInventory
.isCustomItem(first)) {
e.setResult(null);
}
}
@org.bukkit.event.EventHandler(priority =
org.bukkit.event.EventPriority.HIGH)
public void onInteract(
org.bukkit.event.player
.PlayerInteractEvent e) {
if (e.getItem() == null) return;
if (!TreasureInventory
.isCustomItem(e.getItem())) return;
if (!e.getAction().name()
.contains("RIGHT_CLICK")) return;
if (e.getClickedBlock() == null) return;
String b = e.getClickedBlock()
.getType().name();
if (b.contains("SHULKER")
|| b.contains("CHEST")
|| b.equals("BARREL")
|| b.equals("ENDER_CHEST")
|| b.equals("HOPPER")
|| b.equals("DROPPER")
|| b.equals("DISPENSER")) {
e.setCancelled(true);
e.getPlayer().sendMessage(
"§c[寻宝] 不可放入容器");
}
}
// ★ 上线时扫描背包回收
@org.bukkit.event.EventHandler
public void onJoin(
org.bukkit.event.player
.PlayerJoinEvent e) {
plugin.getTreasureManager()
.reclaimPlayerItems(
e.getPlayer().getName());
}
// ★ 手持寻宝物品切换槽位时检查归属
@org.bukkit.event.EventHandler
public void onHeld(
org.bukkit.event.player
.PlayerItemHeldEvent e) {
ItemStack item =
e.getPlayer().getInventory()
.getItem(
e.getNewSlot());
if (item == null) return;
if (!TreasureInventory
.isCustomItem(item)) return;
String[] mark =
TreasureInventory.parseMark(item);
if (mark == null) return;
if (!mark[0].equalsIgnoreCase(
e.getPlayer().getName())) {
// 不是本人的→立即回收
e.getPlayer().getInventory()
.setItem(e.getNewSlot(), null);
plugin.getTreasureManager()
.removeClaim(mark[0], mark[2],
mark[1]);
e.getPlayer().sendMessage(
"§c[寻宝] " + mark[2]
+ " 不属于你,已回收");
}
}
// ===== 预览区域范围 =====
File diff suppressed because it is too large Load Diff
+74 -98
View File
@@ -1,32 +1,26 @@
package Sdf1_game;
import net.kyori.adventure.text.Component;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
public class TreasureReward {
public enum Type {
ITEM, BOND, COMMAND
BOND, COMMAND, ITEM
}
private final Type type;
private final String materialName;
private final int amount;
private final int bondAmount;
private final String command;
private final int weight;
private final String displayName;
private final int durationSec;
private final String enchant;
private final String rewardLore;
private Type type;
private String materialName;
private int amount;
private int bondAmount;
private int bondMin;
private int bondMax;
private String command;
private int weight;
private String displayName;
private int durationSec;
private String enchant;
private String rewardLore;
private int attackUsesLimit;
public TreasureReward(Type type,
String materialName,
@@ -37,112 +31,94 @@ public class TreasureReward {
String displayName,
int durationSec,
String enchant,
String rewardLore) {
String lore,
int attackUsesLimit) {
this.type = type;
this.materialName = materialName;
this.amount = Math.max(1, amount);
this.amount = amount;
this.bondAmount = bondAmount;
this.bondMin = bondAmount;
this.bondMax = bondAmount;
this.command = command;
this.weight = weight;
this.displayName = displayName;
this.durationSec = durationSec;
this.enchant = enchant;
this.rewardLore = rewardLore;
this.rewardLore = lore;
this.attackUsesLimit = attackUsesLimit;
}
public TreasureReward(Type type,
String materialName,
int amount,
int bondAmount,
String command,
int weight,
String displayName) {
this(type, materialName, amount, bondAmount,
command, weight, displayName,
0, null, null);
String mat, int amt,
int bond, String cmd,
int w, String name) {
this(type, mat, amt, bond, cmd, w, name,
0, null, null, 0);
}
public TreasureReward(Type type,
String mat, int amt,
int bond, String cmd,
int w, String name,
int dur, String ench,
String lore) {
this(type, mat, amt, bond, cmd, w, name,
dur, ench, lore, 0);
}
public Type getType() { return type; }
public String getMaterialName() { return materialName; }
public int getAmount() { return amount; }
public int getBondAmount() { return bondAmount; }
public int getBondMin() { return bondMin; }
public int getBondMax() { return bondMax; }
public String getCommand() { return command; }
public int getWeight() { return weight; }
public String getDisplayName() { return displayName; }
public int getDurationSec() { return durationSec; }
public String getEnchant() { return enchant; }
public String getLore() { return rewardLore; }
public int getAttackUsesLimit() { return attackUsesLimit; }
public ItemStack toItemStack() {
Material mat = Material.PAPER;
if (materialName != null
&& !materialName.isEmpty()) {
Material found =
Material.matchMaterial(materialName);
if (found != null) mat = found;
public void setBondRange(int min, int max) {
this.bondMin = min;
this.bondMax = max;
this.bondAmount = min;
}
ItemStack item = new ItemStack(mat, amount);
ItemMeta meta = item.getItemMeta();
if (meta != null) {
meta.displayName(
Component.text(displayName));
List<Component> lore = new ArrayList<>();
if (durationSec > 0) {
lore.add(Component.text(
"§7时限: " + formatTime(durationSec)));
}
if (rewardLore != null
&& !rewardLore.isEmpty()) {
lore.add(Component.text(rewardLore));
}
lore.add(Component.text("§7点击领取"));
meta.lore(lore);
applyEnchant(meta, enchant);
item.setItemMeta(meta);
}
return item;
public int rollBondAmount() {
if (bondMax <= bondMin) return bondMin;
return ThreadLocalRandom.current()
.nextInt(bondMin, bondMax + 1);
}
/** ★ 安全附魔:用 NamespacedKey */
static void applyEnchant(ItemMeta meta,
String enchant) {
if (enchant == null || enchant.isEmpty())
return;
String[] ep = enchant.split(",");
if (ep.length != 2) return;
try {
// 支持 "KNOCKBACK" 或 "minecraft:knockback"
String key = ep[0].trim();
if (!key.contains(":")) {
key = "minecraft:" + key.toLowerCase();
}
NamespacedKey nk =
NamespacedKey.fromString(key);
if (nk == null) return;
Enchantment ench =
Enchantment.getByKey(nk);
if (ench == null) return;
int lvl = Integer.parseInt(ep[1].trim());
meta.addEnchant(ench, lvl, true);
meta.addItemFlags(
ItemFlag.HIDE_ENCHANTS);
} catch (Exception ignored) {
}
public static String formatTime(int s) {
if (s <= 0) return "永久";
int h = s / 3600;
int m = (s % 3600) / 60;
int sec = s % 60;
if (h > 0) return h + "小时" + m + "分钟";
if (m > 0) return m + "分钟" + sec + "秒";
return sec + "秒";
}
static String formatTime(int sec) {
if (sec >= 3600) {
int h = sec / 3600;
int m = (sec % 3600) / 60;
return m > 0 ? h + "小时" + m + "分钟"
: h + "小时";
public static int parseDuration(String input) {
if (input == null || input.isEmpty()) return 0;
String s = input.trim().toLowerCase();
if (s.matches("\\d+")) return Integer.parseInt(s);
if (s.contains("小时") || s.contains("时")) {
return Integer.parseInt(s.replaceAll("[^\\d]", "")) * 3600;
}
int m = sec / 60;
int s = sec % 60;
return s > 0 ? m + "分钟" + s + "秒"
: m + "分钟";
if (s.contains("分钟") || s.contains("分")) {
return Integer.parseInt(s.replaceAll("[^\\d]", "")) * 60;
}
if (s.contains("秒")) {
return Integer.parseInt(s.replaceAll("[^\\d]", ""));
}
if (s.contains("天")) {
return Integer.parseInt(s.replaceAll("[^\\d]", "")) * 86400;
}
try { return Integer.parseInt(s); }
catch (Exception e) { return 0; }
}
}
+69 -3
View File
@@ -1,5 +1,6 @@
package Sdf1_game;
import org.bukkit.Bukkit;
import org.bukkit.scheduler.BukkitRunnable;
import java.util.concurrent.ThreadLocalRandom;
@@ -8,6 +9,8 @@ public class TreasureSpawnTask extends BukkitRunnable {
private final Main plugin;
private final TreasureManager manager;
private int offlineCount = 0;
private boolean paused = false;
public TreasureSpawnTask(Main plugin,
TreasureManager manager) {
@@ -17,12 +20,75 @@ public class TreasureSpawnTask extends BukkitRunnable {
@Override
public void run() {
int online =
Bukkit.getOnlinePlayers().size();
// 无人在线
if (online == 0) {
offlineCount++;
if (offlineCount == 1) {
plugin.getLogger().info(
"[寻宝] 无人在线,60秒后暂停");
}
if (offlineCount >= 12 && !paused) {
paused = true;
manager.removeAllChests();
plugin.getLogger().info(
"[寻宝] ★暂停+收回");
}
return;
}
if (paused) {
paused = false;
plugin.getLogger().info(
"[寻宝] ★玩家上线");
}
offlineCount = 0;
long now = System.currentTimeMillis();
for (TreasureConfig tc
: manager.getConfigs().values()) {
int roll = ThreadLocalRandom.current()
.nextInt(100);
if (roll >= tc.spawnChance) continue;
// ★ 首次生成
if (tc.lastSpawnTime == 0) {
manager.spawnChest(tc);
tc.lastSpawnTime = now;
// ★ 通知所有玩家
broadcastSpawn(tc);
continue;
}
// 正常计时
long elapsed = now - tc.lastSpawnTime;
long need = tc.spawnInterval * 1000L;
if (elapsed < need) continue;
int roll = ThreadLocalRandom.current()
.nextInt(1, 101);
if (roll > tc.spawnChance) {
tc.lastSpawnTime = now;
continue;
}
manager.spawnChest(tc);
tc.lastSpawnTime = now;
// ★ 通知所有玩家
broadcastSpawn(tc);
plugin.getLogger().info(
"[寻宝] ★生成: " + tc.name);
}
}
private void broadcastSpawn(TreasureConfig tc) {
for (org.bukkit.entity.Player p
: Bukkit.getOnlinePlayers()) {
p.sendMessage("§6§l[寻宝] §e"
+ tc.name + " §a区域刷新了宝箱!");
}
plugin.getLogger().info(
"[寻宝] " + tc.name
+ " 区域刷新了宝箱!");
}
}