commit 293a1608566f3c75f10661af25607e9393b49df9 Author: yhy <1763917516@qq.com> Date: Wed Jun 3 06:25:44 2026 +0800 初始化项目 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6d0414d --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +target/ +out/ +lib/ +libs/ +.vscode/ +.codex/ +.idea/ +*.iml +*.class +*.log +logs/ +*.db +*.sqlite +*.sqlite3 +.DS_Store +Thumbs.db \ No newline at end of file diff --git a/README2.md b/README2.md new file mode 100644 index 0000000..9827765 --- /dev/null +++ b/README2.md @@ -0,0 +1,604 @@ +# 1.AuOnlineReward 是什么? + +AuOnlineReward 是一个 Minecraft 1.12.2 Bukkit / Spigot 在线奖励插件。 + +它用于根据玩家在线时间发放阶段奖励,并根据阶段奖励领取次数解锁累积奖励。 + +核心功能: + +- 玩家通过 GUI 查看在线信息和奖励状态 +- 支持每日在线阶段奖励 +- 支持累积领取次数奖励 +- 支持每日在线数据刷新 +- 支持月度在线数据刷新 +- 支持配置奖励物品和奖励命令 +- 支持通过 API 获取玩家在线时间 +- 支持奖励领取事件,方便其他插件监听 + +简单理解: + +```yml +AuOnlineReward = 在线时长统计 + 在线奖励 GUI + 奖励命令执行 +玩家 = 在线积累时间并领取奖励 +管理员 = 配置奖励物品、奖励条件和发奖命令 +``` + +------ + +# 2.运行环境 + +## 服务端版本 + +```yml +Minecraft: 1.12.2 +服务端: Bukkit / Spigot / Paper +Java: 8 +``` + +## 前置插件 + +AuOnlineReward 依赖 DemonAPI。 + +`plugin.yml` 中已经声明: + +```yml +depend: + - DemonAPI +``` + +服务器启动前必须确保 DemonAPI 已放入插件目录。 + +------ + +# 3.安装方式 + +## 第一步:放入插件 + +将插件 jar 放入服务器插件目录: + +```yml +plugins/AuOnlineReward.jar +``` + +同时放入前置插件: + +```yml +plugins/DemonAPI.jar +``` + +## 第二步:启动服务器 + +首次启动后会生成插件配置文件。 + +## 第三步:检查加载 + +控制台应能看到插件正常启用,没有缺少 DemonAPI 的报错。 + +------ + +# 4.目录结构 + +## 插件配置目录 + +默认配置和语言文件位于: + +```yml +plugins/AuOnlineReward/config.yml +plugins/AuOnlineReward/language.yml +``` + +## 玩家数据目录 + +玩家在线数据会保存到: + +```yml +plugins/AuData/AuOnlineReward/玩家名.yml +``` + +玩家数据示例: + +```yml +OnlineData: + todayOnline: 0 + totalOnline: 0 + monthOnline: 0 + totalSign: 0 + signAmount: 0 + currentReward: MinuteReward_30 + collectionTime: 1717200000000 + offlineTime: 1717200000000 + receivedList: [] + permanentReceived: [] +``` + +字段说明: + +```yml +todayOnline: 今日在线秒数 +totalOnline: 累积在线秒数 +monthOnline: 本月在线秒数 +totalSign: 总计领取阶段奖励次数 +signAmount: 当前轮累积领取阶段奖励次数 +currentReward: 当前可领取的阶段奖励 +collectionTime: 上次阶段奖励领取时间 +offlineTime: 上次离线时间 +receivedList: 今日已领取阶段奖励 +permanentReceived: 当前轮已领取累积奖励 +``` + +------ + +# 5.玩家命令 + +## 打开在线奖励界面 + +```yml +/auonline open +``` + +玩家执行后会打开在线福利 GUI。 + +GUI 中会显示: + +- 累积在线时间 +- 本月在线时间 +- 今日在线时间 +- 总计领取奖励次数 +- 阶段奖励状态 +- 累积奖励状态 + +注意: + +```yml +每日 00:10 前无法打开 GUI +这是为了避免跨日数据刷新期间领取状态异常 +``` + +------ + +# 6.管理员命令 + +管理员命令需要 OP 权限。 + +## 查看命令帮助 + +```yml +/auonline +``` + +## 重载配置 + +```yml +/auonline reload +``` + +重载内容: + +- 奖励配置 +- 语言配置 +- GUI 标题 +- 奖励显示文本 + +## 刷新所有玩家每日数据 + +```yml +/auonline dayrefresh +``` + +作用: + +- 清空今日在线时间 +- 重置当前阶段奖励 +- 重置今日阶段奖励领取记录 +- 重新载入在线玩家数据 + +## 刷新指定玩家每日数据 + +```yml +/auonline dayrefresh 玩家名 +``` + +只刷新指定玩家的每日在线奖励数据。 + +## 查看玩家数据 + +```yml +/auonline info 玩家名 +/auonline look 玩家名 +``` + +不填写玩家名时,默认查看执行者自己的数据。 + +## 查看今日在线排行 + +```yml +/auonline topDay +/auonline top +``` + +显示当前已缓存玩家的今日在线排行。 + +------ + +# 7.奖励类型 + +AuOnlineReward 当前包含两类奖励。 + +## 阶段奖励 + +配置节点通常以 `MinuteReward_` 开头。 + +示例: + +```yml +RewardData: + MinuteReward_30: + slot: 10 + needOnline: 1800 + needOnlineTime: 1800 + itemStack: + ==: org.bukkit.inventory.ItemStack + type: IRON_INGOT + commands: + - eco give %player% 500 +``` + +字段说明: + +```yml +slot: GUI 中的格子位置 +needOnline: 距离上次阶段奖励领取需要等待的秒数 +needOnlineTime: 今日在线秒数要求 +itemStack: GUI 中展示的奖励物品 +commands: 领取后执行的命令 +``` + +阶段奖励领取成功后: + +- 增加当前轮阶段领取次数 +- 增加总计阶段领取次数 +- 写入今日已领取记录 +- 切换到下一个阶段奖励 +- 执行奖励命令 +- 保存玩家数据 + +## 累积奖励 + +配置节点通常以 `SignReward_` 开头。 + +示例: + +```yml +RewardData: + SignReward_15: + slot: 29 + needOnline: 25 + itemStack: + ==: org.bukkit.inventory.ItemStack + type: CHEST + commands: + - eco give %player% 10000 +``` + +字段说明: + +```yml +slot: GUI 中的格子位置 +needOnline: 需要累计领取多少次阶段奖励 +itemStack: GUI 中展示的奖励物品 +commands: 领取后执行的命令 +``` + +累积奖励领取成功后会写入 `permanentReceived`。 + +当领取 `SignReward_Max` 后: + +```yml +signAmount 会重置为 0 +permanentReceived 会清空 +进入下一轮累积奖励 +``` + +------ + +# 8.奖励命令怎么写? + +奖励命令写在 `commands` 列表中。 + +示例: + +```yml +commands: + - eco give %player% 500 + - points give %player% 10 + - bcm &a玩家 &e%player% &a领取了在线奖励 +``` + +支持占位符: + +```yml +%player% 玩家名 +%itemName% 奖励物品显示名 +``` + +如果命令包含 `msg:`,会向玩家发送消息,而不是由控制台执行命令。 + +示例: + +```yml +commands: + - "msg:&a你领取了在线奖励" +``` + +颜色代码支持: + +```yml +&a &b &c &e &f +``` + +插件执行时会转换为 Minecraft 颜色符号。 + +------ + +# 9.语言文件怎么写? + +语言文件为: + +```yml +plugins/AuOnlineReward/language.yml +``` + +主要节点: + +```yml +Online-Gui: + Title: "§r日常福利 - 累积在线豪礼相赠" + Received: "§a§l★ §7领取状态: §a已领取" + Not-Started: "§c§l★ §7领取状态: §c未开始" + Pending: "§b§l★ §7领取状态: §b待领取" + Total-Online: "§7累积在线: §a%time%" + Monthly-Online: "§7本月在线: §a%time%" + Daily-Online: "§7今日在线: §a%time%" + Total-Rewards: "§7总计领取奖励: §a%amount%次" +Message: + Reward-Progress: "你还需要获取§e[%amount%次]§a阶段奖励才能领取奖励。" + Wait-Time: "您还需要等待§e[%time%秒]§a才能领取该奖励。" + Daily-Requirement: "你需要今日在线§e[%time%秒]§a才能领取该奖励。" + Already-Claimed: "你已经领取过这个累积奖励。" + Not-Available: "这个阶段奖励尚未开启,暂时无法领取。" +``` + +修改语言文件后执行: + +```yml +/auonline reload +``` + +------ + +# 10.在线时间刷新机制 + +## 在线计时 + +插件会定时统计在线玩家的在线时长。 + +统计字段: + +```yml +todayOnline: 今日在线 +monthOnline: 本月在线 +totalOnline: 累积在线 +``` + +玩家退出时会补算最后一段在线时间,并保存玩家数据。 + +## 每日刷新 + +每日刷新会处理: + +```yml +todayOnline = 0 +currentReward = MinuteReward_30 +receivedList = [] +collectionTime = 当前时间 +``` + +## 月度刷新 + +月度刷新会处理: + +```yml +monthOnline = 0 +``` + +## 刷新性能说明 + +批量刷新玩家文件时,插件会将 YAML 文件读写放到异步任务中执行。 + +刷新完成后,再回到主线程重新载入在线玩家。 + +这样可以降低大量玩家数据文件刷新时对主线程的影响。 + +------ + +# 11.API 调用 + +其他插件可以通过 `OnlineAPI` 获取玩家在线时间。 + +## 获取今日在线分钟数 + +```java +int minutes = OnlineAPI.getTime(playerName); +``` + +## 获取本月在线分钟数 + +```java +int minutes = OnlineAPI.getTimeMonth(playerName); +``` + +## 获取累积在线分钟数 + +```java +int minutes = OnlineAPI.getTimeTotal(playerName); +``` + +注意: + +```yml +API 返回单位是分钟 +底层玩家数据保存单位是秒 +``` + +------ + +# 12.事件监听 + +玩家成功领取奖励后会触发: + +```java +OnlineRewardEvent +``` + +监听示例: + +```java +@EventHandler +public void onOnlineReward(OnlineRewardEvent event) { + Player player = event.getPlayer(); + String playerName = event.getPlayerName(); + String rewardKey = event.getOnlineData().getRewardKey(); +} +``` + +事件可用于: + +- 记录领奖日志 +- 触发额外奖励 +- 接入活动系统 +- 接入统计系统 + +------ + +# 13.配置示例 + +## 阶段奖励示例 + +```yml +RewardData: + MinuteReward_30: + slot: 10 + needOnline: 1800 + needOnlineTime: 1800 + itemStack: + ==: org.bukkit.inventory.ItemStack + type: IRON_INGOT + meta: + ==: ItemMeta + meta-type: UNSPECIFIC + display-name: §6§l阶段奖励 I + lore: + - §7还差 §c%time% §7才能领取 + - §e金币 §e*500 + commands: + - eco give %player% 500 +``` + +## 累积奖励示例 + +```yml +RewardData: + SignReward_15: + slot: 29 + needOnline: 25 + itemStack: + ==: org.bukkit.inventory.ItemStack + type: CHEST + meta: + ==: ItemMeta + meta-type: TILE_ENTITY + display-name: §6§l累积打卡奖励 I + lore: + - §7还需领取 §c%amounut% §7阶段奖励 + - §e金币 §e*10000 + commands: + - eco give %player% 10000 +``` + +注意: + +```yml +阶段奖励 lore 中需要包含 %time% +累积奖励 lore 中需要包含 %amounut% +``` + +------ + +# 14.常见问题 + +## 执行 /auonline open 没反应 + +检查: + +```yml +是否在每日 00:10 之前 +DemonAPI 是否正常加载 +language.yml 中 Title 是否存在 +config.yml 中 RewardData 是否存在 +``` + +## 奖励无法领取 + +检查: + +```yml +今日在线秒数是否达到 needOnlineTime +距离上次领取是否达到 needOnline +currentReward 是否等于当前点击的阶段奖励 +该奖励是否已经在 receivedList 中 +``` + +## 累积奖励无法领取 + +检查: + +```yml +signAmount 是否达到 needOnline +该奖励是否已经在 permanentReceived 中 +``` + +## 修改配置后没有变化 + +执行: + +```yml +/auonline reload +``` + +如果删除了奖励节点,重载后旧奖励缓存会被清理。 + +## 玩家数据异常 + +可以刷新指定玩家每日数据: + +```yml +/auonline dayrefresh 玩家名 +``` + +也可以检查玩家数据文件: + +```yml +plugins/AuData/AuOnlineReward/玩家名.yml +``` + +------ + +# 15.维护建议 + +- 修改奖励配置前,先备份 `config.yml`。 +- 大量玩家数据刷新建议避开服务器高峰期。 +- 奖励命令应先在测试服验证,避免命令拼写错误导致奖励无法发放。 +- 不建议把高耗时命令放入奖励命令列表。 +- 修改语言或奖励配置后,使用 `/auonline reload` 重载。 +- 发布新版本时,应同步检查 `plugin.yml` 的版本号。 +- 每次更新后建议至少完成一次登录、打开 GUI、领取奖励、退出保存的冒烟测试。 diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..c811293 --- /dev/null +++ b/pom.xml @@ -0,0 +1,77 @@ + + + 4.0.0 + + com.yaohun.main + AuPet + 1.0-SNAPSHOT + + + 21 + 21 + UTF-8 + ${project.basedir}/.. + + + + + + papermc-repo + https://repo.papermc.io/repository/maven-public/ + + + + + + + io.papermc.paper + paper-api + 1.21.8-R0.1-SNAPSHOT + provided + + + local + demon-api + local + system + ${parent.project.dir}/lib/DemonAPI.jar + + + local + nbt-api + local + system + ${parent.project.dir}/lib/item-nbt-api-plugin-2.15.1.jar + + + local + mythicmobs + local + system + ${parent.project.dir}/lib/MythicMobs-5.9.1.jar + + + local + bettermodel + local + system + ${parent.project.dir}/lib/BetterModel-2.2.0-paper.jar + + + local + craft-data-manager + local + system + ${parent.project.dir}/lib/CraftDataManager.jar + + + local + paper-1.21.8-mapped-server + 1.21.8 + system + ${parent.project.dir}/.gradle/caches/paperweight/taskCache/mappedServerJar.jar + + + diff --git a/src/main/java/com/yaohun/petsystem/PetMain.java b/src/main/java/com/yaohun/petsystem/PetMain.java new file mode 100644 index 0000000..d9e9ec9 --- /dev/null +++ b/src/main/java/com/yaohun/petsystem/PetMain.java @@ -0,0 +1,265 @@ +package com.yaohun.petsystem; + +import com.yaohun.petsystem.api.PetExpAPI; +import com.yaohun.petsystem.config.Config; +import com.yaohun.petsystem.data.PetData; +import com.yaohun.petsystem.data.PetSQL; +import com.yaohun.petsystem.data.PlayerData; +import com.yaohun.petsystem.data.SkillData; +import com.yaohun.petsystem.gui.CarryGui; +import com.yaohun.petsystem.gui.FeedingGui; +import com.yaohun.petsystem.gui.MainGui; +import com.yaohun.petsystem.listener.PetSkillsListener; +import com.yaohun.petsystem.listener.PlayerListener; +import com.yaohun.petsystem.listener.RePetNameListener; +import com.yaohun.petsystem.manage.PetManager; +import com.yaohun.petsystem.manage.PlayerManager; +import com.yaohun.petsystem.model.PetNbt; +import com.yaohun.petsystem.util.MessageUtil; +import com.yaohun.petsystem.util.PetUtil; +import de.tr7zw.nbtapi.NBTItem; +import io.lumine.mythic.bukkit.MythicBukkit; +import me.Demon.DemonPlugin.DemonAPI; +import me.Demon.DemonPlugin.Util.CDTimeAPI; +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.plugin.java.JavaPlugin; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; + +public class PetMain extends JavaPlugin { + + private static PetMain instance; + private static PlayerManager playerManager; + + + @Override + public void onEnable() { + instance = this; + saveDefaultConfig(); + + PetSQL.init(); + + MessageUtil.init(this); + Config.reloadConfig(this); + PetManager.reloadPetManager(); + playerManager = new PlayerManager(); + + getServer().getPluginManager().registerEvents(new MainGui(), this); + getServer().getPluginManager().registerEvents(new FeedingGui(), this); + getServer().getPluginManager().registerEvents(new CarryGui(), this); + + getServer().getPluginManager().registerEvents(new PlayerListener(), this); + getServer().getPluginManager().registerEvents(new RePetNameListener(), this); + + getServer().getPluginManager().registerEvents(new PetSkillsListener(), this); + } + + @Override + public void onDisable() { + getPlayerManager().closeSaveAllData(); + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (!sender.hasPermission("admin.use")) { + if(args.length == 0){ + if (sender instanceof Player player) { + MainGui.openGui(player); + } + return true; + } + return true; + } + String prefix = "§f[§c灵宠§f] §a"; + if (args.length == 0) { + sender.sendMessage("§r"); + sender.sendMessage("§e==----- ======= §6宠物系统 §e======= -----=="); + sender.sendMessage("§2/" + label + " call §f- §2召唤宠物"); + sender.sendMessage("§2/" + label + " carry §f- §2携带手持宠物"); + if (sender.isOp()) { + sender.sendMessage("§2/" + label + " addExp §e[经验] §b<玩家> §7true §f- §2给予玩家宠物"); + sender.sendMessage("§2/" + label + " give §e[宠物] §b<玩家> §f- §2给予玩家宠物"); + sender.sendMessage("§2/" + label + " reload §f- §2重载配置文件"); + } + sender.sendMessage("§e==----- ======= §6宠物系统 §e======= -----=="); + sender.sendMessage("§r"); + return true; + } + if ("reload".equalsIgnoreCase(args[0])) { + MessageUtil.init(this); + Config.reloadConfig(this); + PetManager.reloadPetManager(); + sender.sendMessage(prefix + "配置文件已重载完成."); + return true; + } + if ("open".equalsIgnoreCase(args[0])) { + if (sender instanceof Player player) { + MainGui.openGui(player); + } + return true; + } + if ("test".equalsIgnoreCase(args[0])) { + if (sender instanceof Player player) { + // 判断玩家是否携带宠物 + String playerName = player.getName(); + PlayerManager playerManager = PetMain.getPlayerManager(); + // 获取宠物相关数据 + PlayerData playerData = playerManager.getPlayerData(playerName); + if(playerData.isPetStackNull()){ + return true; + } + PetData petData = playerData.getPetNbt().petData; + LinkedHashMap skillDataMap = petData.getSkillDataMap(); + for(SkillData skillData : skillDataMap.values()){ + String cdKey = "skill_"+skillData.getSkillName(); + if ("onAttack".equalsIgnoreCase(skillData.getTrigger())) { + UUID uuid = player.getUniqueId(); + if(!CDTimeAPI.isCD(uuid,cdKey)) { + long cooldown = skillData.getCoolDown(); + CDTimeAPI.setPlayerCD(uuid, cdKey, cooldown); + String mmSkill = skillData.getMmSkill(); + // 执行技能 + MythicBukkit.inst().getAPIHelper().castSkill( + player, + mmSkill, + player.getLocation() + ); + player.sendMessage("[调试 - 宠物技能] 触发条件: onAttack 触发技能: "+mmSkill); + break; + } else { + sender.sendMessage("# 拦截 冷却中..."); + } + } + } + } + return true; + } + if ("carry".equalsIgnoreCase(args[0])) { + String playerName = sender.getName(); + if (args.length >= 2) { + playerName = args[1]; + } + Player player = Bukkit.getPlayer(playerName); + if (player == null || !player.isOnline()) { + sender.sendMessage(prefix + "玩家 " + playerName + " 不在线."); + return true; + } + getPlayerManager().carryPetStackData(player); + return true; + } + if ("call".equalsIgnoreCase(args[0])) { + String playerName = sender.getName(); + if (args.length >= 2) { + playerName = args[1]; + } + Player player = Bukkit.getPlayer(playerName); + if (player == null || !player.isOnline()) { + sender.sendMessage(prefix + "玩家 " + playerName + " 不在线."); + return true; + } + if (playerManager.isPetCallExit(playerName)) { + playerManager.removePetCall(playerName); + sender.sendMessage(prefix + "已收回 §6" + playerName + " §a的宠物."); + } else { + playerManager.callPet(player); + sender.sendMessage(prefix + "已召唤 §6" + playerName + " §a的宠物."); + } + return true; + } + if ("addexp".equalsIgnoreCase(args[0])) { + if (args.length == 1) { + sender.sendMessage(prefix + "缺少数值参数."); + return true; + } + int exp = Integer.parseInt(args[1]); + // 获取玩家数据 + String playerName = sender.getName(); + if (args.length >= 3) { + playerName = args[2]; + } + Player player = Bukkit.getPlayer(playerName); + if (player == null || !player.isOnline()) { + sender.sendMessage(prefix + "玩家 " + playerName + " 不在线."); + return true; + } + if (args.length >= 4) { + ItemStack stack = player.getInventory().getItemInMainHand(); + if (DemonAPI.itemIsNull(stack) || DemonAPI.itemIsLore(stack)) { + sender.sendMessage(prefix + "执行失败,你需要将灵宠拿在手中."); + return true; + } + NBTItem nbtItem = new NBTItem(stack); + if (!nbtItem.hasKey("petKey")) { + sender.sendMessage(prefix + "执行失败,你需要将灵宠拿在手中."); + return true; + } + PetNbt petNbt = PetExpAPI.addExp(playerName, stack, exp); + ItemStack petStack = PetUtil.refreshPetStackData(petNbt); + player.getInventory().setItemInMainHand(petNbt.saveIoItem(petStack)); + sender.sendMessage(prefix + "给予 §6手持灵宠 §a经验: §d+" + exp); + } else { + PetExpAPI.addExp(player, exp); + sender.sendMessage(prefix + "给予 §6" + playerName + " §a的灵宠经验: §d+" + exp); + } + return true; + } + if ("give".equalsIgnoreCase(args[0])) { + if (args.length == 1) { + sender.sendMessage(prefix + "缺少宠物参数."); + return true; + } + String petKey = args[1]; + PetData petData = PetManager.getPetData(petKey); + if (petData == null) { + sender.sendMessage(prefix + "未找到 " + petKey + " 的相关配置."); + return true; + } + String playerName = sender.getName(); + if (args.length >= 3) { + playerName = args[2]; + } + Player player = Bukkit.getPlayer(playerName); + if (player == null) { + sender.sendMessage(prefix + "未找到玩家 " + playerName); + return true; + } + PetNbt petNbt = new PetNbt(petData); + ItemStack petStack = PetUtil.getPetStackDefault(petNbt); + player.getInventory().addItem(petNbt.saveIoItem(petStack)); + DemonAPI.sendMessage(sender, "玩家: §e" + playerName + " §a宠物: §6" + petKey + " §r[背包]"); + return true; + } + return false; + } + + @Override + public List onTabComplete(CommandSender sender, Command command, String label, String[] args) { + if (sender.isOp()) { + if (args.length == 2 && "give".equalsIgnoreCase(args[0])) { + return PetManager.getPetDataMap().keySet().stream() + .filter(s -> s.toLowerCase().startsWith(args[1].toLowerCase())) + .collect(Collectors.toList()); + } + } + // 如果不符合上述条件,则返回空列表 + return Collections.emptyList(); + } + + public static PetMain inst() { + return instance; + } + + public static PlayerManager getPlayerManager() { + return playerManager; + } + +} \ No newline at end of file diff --git a/src/main/java/com/yaohun/petsystem/api/PetExpAPI.java b/src/main/java/com/yaohun/petsystem/api/PetExpAPI.java new file mode 100644 index 0000000..1126330 --- /dev/null +++ b/src/main/java/com/yaohun/petsystem/api/PetExpAPI.java @@ -0,0 +1,122 @@ +package com.yaohun.petsystem.api; + +import com.yaohun.petsystem.PetMain; +import com.yaohun.petsystem.data.PetData; +import com.yaohun.petsystem.data.PlayerData; +import com.yaohun.petsystem.manage.LevelExpManager; +import com.yaohun.petsystem.manage.PetManager; +import com.yaohun.petsystem.manage.PlayerManager; +import com.yaohun.petsystem.model.PetNbt; +import de.tr7zw.nbtapi.NBTItem; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +public class PetExpAPI { + + public static void addExp(Player player, int addExp) { + String playerName = player.getName(); + PlayerManager playerManager = PetMain.getPlayerManager(); + PlayerData playerData = playerManager.getPlayerData(playerName); + if (playerData.isPetStackNull()) { + System.out.println("[日志 - 灵宠] 玩家 " + playerName + " 尚未携带宠物."); + return; + } + PetNbt petNbt = playerData.getPetNbt(); + String petKey = petNbt.petData.getFileName(); + PetData petData = PetManager.getPetData(petKey); + if (petData == null) { + System.out.println("[日志 - 灵宠] 未添加 " + petKey + " 的宠物配置."); + return; + } + int petLevel = petNbt.level; + if (petLevel >= petData.getMaxLevel()) { + return; + } + // 进化阶段判断 + int evolution = petNbt.evolution; + if (evolution == 1) { + if (petLevel >= 20) { + return; + } + } else if (evolution == 2) { + if (petLevel >= 40) { + return; + } + } else if (evolution == 3) { + if (petLevel >= 60) { + return; + } + } else if (evolution == 4) { + if (petLevel >= 70) { + return; + } + } else if (evolution == 5) { + if (petLevel >= 75) { + return; + } + } + int needExp = LevelExpManager.getNeedExpValue(petLevel); + petNbt.setTotalExp(petNbt.totalExp + addExp); + + int nowExp = petNbt.exp; + addExp = nowExp + addExp; + while (addExp >= needExp) { + if (petLevel < 0) { + break; + } + addExp -= needExp; + petLevel++; + needExp = LevelExpManager.getNeedExpValue(petLevel); + bcmupLevelEvant(playerName, petLevel); + playerData.refreshPetStack(); + } + petNbt.setExp(addExp); + petNbt.setLevel(petLevel); + playerData.savePlayerData(); + } + + public static PetNbt addExp(String playerName, ItemStack stack, int addExp) { + NBTItem nbtItem = new NBTItem(stack); + String petKey = nbtItem.getString("petKey"); + PetData petData = PetManager.getPetData(petKey); + if (petData == null) { + System.out.println("[日志 - 灵宠] 未添加 " + petKey + " 的宠物配置."); + return null; + } + PetNbt petNbt = new PetNbt(); + petNbt.loadNbtData(nbtItem); + int petLevel = petNbt.level; + if (petLevel >= petData.getMaxLevel()) { + return petNbt; + } + int needExp = LevelExpManager.getNeedExpValue(petLevel); + petNbt.setTotalExp(petNbt.totalExp + addExp); + + int nowExp = petNbt.exp; + addExp = nowExp + addExp; + while (addExp >= needExp) { + if (petLevel < 0) { + break; + } + addExp -= needExp; + petLevel++; + needExp = LevelExpManager.getNeedExpValue(petLevel); + bcmupLevelEvant(playerName, petLevel); + } + petNbt.setExp(addExp); + petNbt.setLevel(petLevel); + return petNbt; + } + + public static void bcmupLevelEvant(String playerName, int level) { + boolean butt = false; + if (level == 10 || level == 20 || level == 30 || level == 40 || level == 50 || + level == 60 || level == 70 || level == 80 || level == 90 || level == 100 + || level == 105 || level == 110 || level == 115 || level == 120 || level == 125) { + butt = true; + } + if (butt) { + // Bukkit.broadcastMessage("§f[§c§l公告§f] §a玩家§e"+playName+"§a本月器师手礼达到了 §7[§e§lLv."+level+"§7]"); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/yaohun/petsystem/command/PetCommand.java b/src/main/java/com/yaohun/petsystem/command/PetCommand.java new file mode 100644 index 0000000..6572657 --- /dev/null +++ b/src/main/java/com/yaohun/petsystem/command/PetCommand.java @@ -0,0 +1,4 @@ +package com.yaohun.petsystem.command; + +public class PetCommand { +} diff --git a/src/main/java/com/yaohun/petsystem/config/Config.java b/src/main/java/com/yaohun/petsystem/config/Config.java new file mode 100644 index 0000000..5c44203 --- /dev/null +++ b/src/main/java/com/yaohun/petsystem/config/Config.java @@ -0,0 +1,141 @@ +package com.yaohun.petsystem.config; + +import com.yaohun.petsystem.PetMain; +import com.yaohun.petsystem.manage.LevelExpManager; +import com.yaohun.petsystem.util.MessageUtil; +import me.Demon.DemonPlugin.DemonAPI; +import me.Demon.DemonPlugin.Util.ColorUtil; +import me.Demon.DemonPlugin.data.GuiItemData; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +import java.util.*; + +public class Config { + + private static ItemStack petStackTemplate; + private static NavigableMap qualityShowMap = new TreeMap<>(); + private static HashMap guiItemDataMap = new HashMap<>(); + + public static void reloadConfig(PetMain plugin) { + plugin.reloadConfig(); + plugin.saveConfig(); + FileConfiguration config = plugin.getConfig(); + Bukkit.getConsoleSender().sendMessage("§6[宠物系统] §7参数设定:"); + LevelExpManager.reloadLevelManager(plugin); + loadPetStackTemplate(config); + loadGuiItemData(config); + loadQualityShowMap(config); + } + + private static void loadGuiItemData(FileConfiguration config) { + guiItemDataMap.clear(); + ConfigurationSection section = config.getConfigurationSection("GuiItemData"); + if (section == null) { + return; + } + for (String itemKey : section.getKeys(false)) { + guiItemDataMap.put(itemKey, new GuiItemData(itemKey, section)); + } + } + + private static void loadQualityShowMap(FileConfiguration config) { + ConfigurationSection section = config.getConfigurationSection("QualityShow"); + if (section == null) { + return; + } + for (String levelKey : section.getKeys(false)) { + int level = Integer.parseInt(levelKey); + String show = section.getString(levelKey).replace("&", "§"); + qualityShowMap.put(level, show); + } + Bukkit.getConsoleSender().sendMessage("- 资质稀有度: §f" + qualityShowMap.size() + "个"); + } + + private static void loadPetStackTemplate(FileConfiguration config) { + String stackType = config.getString("Pet_Stack_Template.type", "DIAMOND"); + Material material; + try { + material = Material.valueOf(stackType.toUpperCase()); + } catch (IllegalArgumentException e) { + Bukkit.getConsoleSender().sendMessage("- 宠物模板载入: §c失败"); + return; + } + String stackName = config.getString("Pet_Stack_Template.name", "§f宠物"); + List stringList = config.getStringList("Pet_Stack_Template.lore"); + List stackLore = ColorUtil.listcolor(stringList); + ItemStack itemStack = new ItemStack(material); + ItemMeta meta = itemStack.getItemMeta(); + meta.setDisplayName(stackName); + meta.setLore(stackLore); + itemStack.setItemMeta(meta); + petStackTemplate = itemStack; + + String itemName = DemonAPI.getItemName(itemStack.clone()); + Bukkit.getConsoleSender().sendMessage("- 宠物模板载入: §f" + itemName); + } + + public static ItemStack getPetStackTemplate() { + return petStackTemplate.clone(); + } + + public static String getLanguage(String key) { + HashMap hashMap = MessageUtil.getLanguageMap(); + if (hashMap.containsKey(key)) { + return hashMap.get(key); + } + return "缺少参数.§7" + key; + } + + public static ItemStack getItemStack(String itemKey) { + if (guiItemDataMap.containsKey(itemKey)) { + ItemStack itemStack = guiItemDataMap.get(itemKey).getItemStack().clone(); + if (itemStack.hasItemMeta()) { + ItemMeta meta = itemStack.getItemMeta(); + if (meta.hasLore()) { + List originalLore = meta.getLore(); + if (originalLore != null) { + // 创建新的lore列表进行处理 + List newLore = new ArrayList<>(); + for (String line : originalLore) { + // 应用颜色转换或其他处理逻辑 + newLore.add(DemonAPI.convertHexColor(line)); + } + // 设置处理后的新lore + meta.setLore(newLore); + itemStack.setItemMeta(meta); + } + } + } + return itemStack; + } + return DemonAPI.getErrItems(); + } + + + public static String getQualityShow(int value) { + Map.Entry entry = qualityShowMap.floorEntry(value); + return entry != null ? entry.getValue() : qualityShowMap.get(0); + } + + public static String getHealthChar(int percent) { + // 限制范围 [0,100] + percent = Math.max(0, Math.min(100, percent)); + // 取整到最近的 10 倍数,例如 87 → 80,93 → 90 + int step = (percent / 10) * 10; + // 处理 100 特殊情况 + if (percent == 100) { + step = 100; + } + return "§f"; + } + + public static String getLevelChar(int level) { + return "§f"; + } + +} \ No newline at end of file diff --git a/src/main/java/com/yaohun/petsystem/data/PetData.java b/src/main/java/com/yaohun/petsystem/data/PetData.java new file mode 100644 index 0000000..ae42937 --- /dev/null +++ b/src/main/java/com/yaohun/petsystem/data/PetData.java @@ -0,0 +1,86 @@ +package com.yaohun.petsystem.data; + +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.FileConfiguration; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; + +public class PetData { + private String fileName; + private String petName; + private int customIconModelId; + private int maxLevel; + private String modelId; + private List accessChannelsList; + private String summonSkill; + private String deathSkill; + private LinkedHashMap skillDataMap = new LinkedHashMap<>(); + + public PetData(String fileName, FileConfiguration config) { + this.fileName = fileName; + this.petName = config.getString("PetName"); + this.maxLevel = config.getInt("MaxLevel"); + this.modelId = config.getString("ModelId"); + this.customIconModelId = config.getInt("CustomIconModelId"); + this.accessChannelsList = config.getStringList("AccessChannels"); + this.summonSkill = config.getString("SummonSkill", "default"); + this.deathSkill = config.getString("DeathSkill", "default"); + loadSkillData(config); + } + + private void loadSkillData(FileConfiguration config) { + ConfigurationSection section = config.getConfigurationSection("Skills"); + if (section == null) { + return; + } + for (String key : section.getKeys(false)) { + SkillData skillData = new SkillData(key, section); + skillDataMap.put(key, skillData); + } + } + + public String getFileName() { + return fileName; + } + + public String getPetName() { + return petName; + } + + public int getMaxLevel() { + return maxLevel; + } + + public String getModelId() { + return modelId; + } + + public int getCustomIconModelId() { + return customIconModelId; + } + + public List getAccessChannelsList() { + return accessChannelsList; + } + + public String getSummonSkill() { + return summonSkill; + } + + public String getDeathSkill() { + return deathSkill; + } + + public LinkedHashMap getSkillDataMap() { + return skillDataMap; + } + + public SkillData getSkillData(String skillName) { + if (skillDataMap.containsKey(skillName)) { + return skillDataMap.get(skillName); + } + return null; + } +} \ No newline at end of file diff --git a/src/main/java/com/yaohun/petsystem/data/PetSQL.java b/src/main/java/com/yaohun/petsystem/data/PetSQL.java new file mode 100644 index 0000000..c6074b2 --- /dev/null +++ b/src/main/java/com/yaohun/petsystem/data/PetSQL.java @@ -0,0 +1,105 @@ +package com.yaohun.petsystem.data; + +import com.tianyu.datamanager.DataAPI; +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; +import org.bukkit.Bukkit; + +import java.sql.*; + +public class PetSQL { + + private static HikariDataSource dataSource; + private static String host; + private static String port; + private static String database; + private static String tables; + private static String username; + private static String password; + + // 初始化数据库连接和表结构 + public static void init() { + host = DataAPI.getMysql().getHost(); + port = DataAPI.getMysql().getPort(); + database = DataAPI.getMysql().getDatabase(); + tables = "aupets_data"; + username = DataAPI.getMysql().getUser(); + password = DataAPI.getMysql().getPassword(); + + setupDataSource(); // 初始化连接池 + initTable(); // 确保表结构已经创建 + } + + // 配置并初始化 HikariCP 连接池 + private static void setupDataSource() { + HikariConfig config = new HikariConfig(); + config.setDriverClassName("com.mysql.cj.jdbc.Driver"); + config.setJdbcUrl("jdbc:mysql://" + host + ":" + port + "/" + database + "?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&autoReconnect=true&useSSL=false"); + config.setUsername(username); + config.setPassword(password); + config.setMaximumPoolSize(3); + config.setMinimumIdle(1); + config.setIdleTimeout(300000L); + config.setMaxLifetime(0L); + dataSource = new HikariDataSource(config); + } + + // 初始化表结构(如果表不存在) + private static void initTable() { + String createTableSQL = "CREATE TABLE IF NOT EXISTS " + tables + " (" + + "PLAYERNAME VARCHAR(255) NOT NULL, " + + "PETSTACK TEXT NOT NULL, " + + "PRIMARY KEY (PLAYERNAME))"; + try (Connection conn = getConnection(); Statement stmt = conn.createStatement()) { + stmt.execute(createTableSQL); + } catch (SQLException e) { + Bukkit.getLogger().severe("初始化表结构失败: " + e.getMessage()); + } + } + + // 获取玩家宠物信息 + public static String getPetStack(String playerName) { + String query = "SELECT PETSTACK FROM " + tables + " WHERE PLAYERNAME = ? LIMIT 1"; + try (Connection conn = getConnection(); + PreparedStatement ps = conn.prepareStatement(query)) { + ps.setString(1, playerName); + try (ResultSet rs = ps.executeQuery()) { + if (rs.next()) { + return rs.getString("PETSTACK"); + } + } + } catch (SQLException e) { + Bukkit.getLogger().severe("获取字符串值失败: " + e.getMessage()); + } + return null; + } + + + // 保存数据 + public static void saveData(String playerName, String petStack) { + String sql = "INSERT INTO " + tables + " (PLAYERNAME, PETSTACK) " + + "VALUES (?, ?) " + "ON DUPLICATE KEY UPDATE PETSTACK = VALUES(PETSTACK)"; + try (Connection conn = getConnection(); + PreparedStatement ps = conn.prepareStatement(sql)) { + ps.setString(1, playerName); + ps.setString(2, petStack); + ps.executeUpdate(); + } catch (SQLException e) { + Bukkit.getLogger().severe("保存宠物数据失败: " + e.getMessage()); + } + } + + // 获取数据库连接 + private static Connection getConnection() throws SQLException { + return dataSource.getConnection(); + } + + // 关闭连接池 + public static void closeConnection() { + if (dataSource != null && !dataSource.isClosed()) { + dataSource.close(); + Bukkit.getConsoleSender().sendMessage("数据库连接已成功关闭。#1"); + } + } + +} \ No newline at end of file diff --git a/src/main/java/com/yaohun/petsystem/data/PlayerData.java b/src/main/java/com/yaohun/petsystem/data/PlayerData.java new file mode 100644 index 0000000..a6c999b --- /dev/null +++ b/src/main/java/com/yaohun/petsystem/data/PlayerData.java @@ -0,0 +1,136 @@ +package com.yaohun.petsystem.data; + +import com.yaohun.petsystem.model.PetNbt; +import com.yaohun.petsystem.util.PetUtil; +import de.tr7zw.nbtapi.NBTItem; +import me.Demon.DemonPlugin.DemonAPI; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextDecoration; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.util.io.BukkitObjectInputStream; +import org.bukkit.util.io.BukkitObjectOutputStream; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Base64; + +public class PlayerData { + + private String playerName; + private ItemStack petStack; + private PetNbt petNbt; + + public PlayerData(String playerName) { + this.playerName = playerName; + this.petStack = deserializeItemStack(PetSQL.getPetStack(playerName)); + if (isPetStackNull()) { + return; + } + PetNbt petNbt = new PetNbt(); + NBTItem nbtItem = new NBTItem(petStack); + petNbt.loadNbtData(nbtItem); + this.petNbt = petNbt; + } + + public String getPlayerName() { + return playerName; + } + + public PetNbt getPetNbt() { + return petNbt; + } + + public ItemStack getPetStack() { + return petStack; + } + + public void rePetNameStack(String newName) { + ItemStack stack = petStack.clone(); + ItemMeta meta = stack.getItemMeta(); + meta.displayName(Component.text(newName) + .color(NamedTextColor.GREEN) + .decoration(TextDecoration.ITALIC, false)); + stack.setItemMeta(meta); + setPetStack(stack); + } + + public void setPetStack(ItemStack petStack) { + this.petStack = petStack; + if (isPetStackNull()) { + return; + } + PetNbt petNbt = new PetNbt(); + NBTItem nbtItem = new NBTItem(petStack); + petNbt.loadNbtData(nbtItem); + this.petNbt = petNbt; + } + + public void refreshPetStack() { + ItemStack itemStack = PetUtil.getPetStackDefault(petNbt); + ItemMeta meta = itemStack.getItemMeta(); + meta.displayName(Component.text(petStack.getItemMeta().getDisplayName()) + .color(NamedTextColor.GREEN) + .decoration(TextDecoration.ITALIC, false)); + + petStack = petNbt.saveIoItem(itemStack); + } + + public void savePlayerData() { + if (DemonAPI.itemIsNull(petStack)) { + PetSQL.saveData(playerName, serializeItemStack(DemonAPI.getErrItems())); + } else { + PetSQL.saveData(playerName, serializeItemStack(petStack)); + } + } + + public boolean isPetStackNull() { + return petStack == null || petStack.getType().equals(Material.BARRIER); + } + + /** + * 将 ItemStack 序列化为 Base64 字符串 + * + * @param itemStack 要序列化的 ItemStack + * @return 序列化后的 Base64 字符串 + */ + private String serializeItemStack(ItemStack itemStack) { + if (itemStack == null) { + return ""; + } + try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + BukkitObjectOutputStream dataOutput = new BukkitObjectOutputStream(outputStream)) { + dataOutput.writeObject(itemStack); + return Base64.getEncoder().encodeToString(outputStream.toByteArray()); + } catch (IOException e) { + Bukkit.getLogger().warning("[日志 - 灵宠] 序列化 ItemStack 时出错: " + e.getMessage()); + return ""; + } + } + + /** + * 从 Base64 字符串反序列化为 ItemStack + * + * @param data Base64 字符串 + * @return 反序列化后的 ItemStack + */ + private ItemStack deserializeItemStack(String data) { + if (data == null || data.isEmpty()) { + return null; + } + try { + byte[] decodedData = Base64.getDecoder().decode(data); + try (ByteArrayInputStream inputStream = new ByteArrayInputStream(decodedData); + BukkitObjectInputStream dataInput = new BukkitObjectInputStream(inputStream)) { + return (ItemStack) dataInput.readObject(); + } + } catch (Exception e) { + Bukkit.getLogger().warning("[日志 - 灵宠] 反序列化 ItemStack 时出错: " + e.getMessage()); + return DemonAPI.getErrItems(); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/yaohun/petsystem/data/SkillData.java b/src/main/java/com/yaohun/petsystem/data/SkillData.java new file mode 100644 index 0000000..1588fea --- /dev/null +++ b/src/main/java/com/yaohun/petsystem/data/SkillData.java @@ -0,0 +1,60 @@ +package com.yaohun.petsystem.data; + +import me.Demon.DemonPlugin.DemonAPI; +import org.bukkit.configuration.ConfigurationSection; + +import java.util.ArrayList; +import java.util.List; + +public class SkillData { + + private String skillName; // 技能名 + private String mmSkill; // MM技能 + private int needLevel; // 触发要求等级 + private String trigger; // 触发器 + private long coolDown; // 冷却时间 + private String itemName; + private List itemLore = new ArrayList<>(); + + public SkillData(String skillName, ConfigurationSection section) { + this.skillName = skillName; + this.mmSkill = section.getString(skillName + ".mmSkill"); + this.needLevel = section.getInt(skillName + ".needLevel"); + this.trigger = section.getString(skillName + ".trigger"); + this.coolDown = section.getLong(skillName + ".coolDown"); + this.itemName = DemonAPI.convertHexColor(section.getString(skillName + ".item.name", "技能 I")); + List itemlore = section.getStringList(skillName + ".item.lore"); + // Bukkit.getConsoleSender().sendMessage("[调试] name: "+skillName+" itemlore = "+itemlore); + for (String line : itemlore) { + this.itemLore.add(DemonAPI.convertHexColor(line)); + } + } + + public String getSkillName() { + return skillName; + } + + public String getMmSkill() { + return mmSkill; + } + + public int getNeedLevel() { + return needLevel; + } + + public String getTrigger() { + return trigger; + } + + public long getCoolDown() { + return coolDown; + } + + public String getItemName() { + return itemName; + } + + public List getItemLore() { + return itemLore; + } +} \ No newline at end of file diff --git a/src/main/java/com/yaohun/petsystem/event/PetCallEvent.java b/src/main/java/com/yaohun/petsystem/event/PetCallEvent.java new file mode 100644 index 0000000..629dee4 --- /dev/null +++ b/src/main/java/com/yaohun/petsystem/event/PetCallEvent.java @@ -0,0 +1,47 @@ +package com.yaohun.petsystem.event; + +import org.bukkit.entity.Player; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; + +import java.util.UUID; + +public class PetCallEvent extends Event { + + private final Player owner; + private final String cutomName; + private final UUID entityUuid; + private static final HandlerList handlers = new HandlerList(); + + public PetCallEvent(Player owner, String cutomName, UUID entityUuid) { + this.owner = owner; + this.cutomName = cutomName; + this.entityUuid = entityUuid; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } + + public Player getOwner() { + return owner; + } + + public String getCutomName() { + return cutomName; + } + + public UUID getEntityUuid() { + return entityUuid; + } + + @Override + public String getEventName() { + return super.getEventName(); + } +} diff --git a/src/main/java/com/yaohun/petsystem/gui/CarryGui.java b/src/main/java/com/yaohun/petsystem/gui/CarryGui.java new file mode 100644 index 0000000..a3851ad --- /dev/null +++ b/src/main/java/com/yaohun/petsystem/gui/CarryGui.java @@ -0,0 +1,89 @@ +package com.yaohun.petsystem.gui; + +import com.yaohun.petsystem.PetMain; +import com.yaohun.petsystem.config.Config; +import com.yaohun.petsystem.data.PlayerData; +import com.yaohun.petsystem.gui.holder.CarryGuiHolder; +import com.yaohun.petsystem.manage.PlayerManager; +import com.yaohun.petsystem.util.MessageUtil; +import de.tr7zw.nbtapi.NBTItem; +import me.Demon.DemonPlugin.DemonAPI; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.Sound; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +public class CarryGui implements Listener { + + private static final int PET_SLOT = 4; + + public static void openGui(Player player) { + String playerName = player.getName(); + PlayerManager playerManager = PetMain.getPlayerManager(); + PlayerData playerData = playerManager.getPlayerData(playerName); + Inventory inv = Bukkit.createInventory(new CarryGuiHolder(), 9, Config.getLanguage("carry_gui_title")); + if (playerData.isPetStackNull()) { + inv.setItem(PET_SLOT, Config.getItemStack("carry_gui")); + } else { + inv.setItem(PET_SLOT, playerData.getPetStack()); + } + player.openInventory(inv); + } + + @EventHandler + public void onClick(InventoryClickEvent e) { + int rawSlot = e.getRawSlot(); + Player player = (Player) e.getWhoClicked(); + Inventory inv = e.getInventory(); + if ((e.getInventory().getHolder() instanceof CarryGuiHolder)) { + e.setCancelled(true); + ItemStack stack = e.getCurrentItem(); + if (DemonAPI.itemIsNull(stack)) { + return; + } + String playerName = player.getName(); + PlayerManager playerManager = PetMain.getPlayerManager(); + PlayerData playerData = playerManager.getPlayerData(playerName); + if (rawSlot == PET_SLOT) { + if (!playerData.isPetStackNull()) { + playerManager.removePetCall(playerName); + playerData.refreshPetStack(); + ItemStack petStack = playerData.getPetStack(); + player.getInventory().addItem(petStack); + playerData.setPetStack(DemonAPI.getErrItems()); + playerData.savePlayerData(); + inv.setItem(PET_SLOT, Config.getItemStack("carry_gui")); + } + return; + } + if (playerData.isPetStackNull()) { + NBTItem nbtItem = new NBTItem(stack); + if (!nbtItem.hasKey("petKey")) { + MessageUtil.sendMessageKey(player, "hand_not_pet_stack", Sound.ENTITY_VILLAGER_NO); + return; + } + e.setCurrentItem(new ItemStack(Material.AIR)); + playerData.setPetStack(stack); + playerData.savePlayerData(); + String itemName = DemonAPI.getItemName(stack); + String message = Config.getLanguage("pet_stack_carry_success"); + MessageUtil.sendMessage(player, message.replace("{petName}", itemName), Sound.ENTITY_PLAYER_LEVELUP); + MainGui.openGui(player); + } else { + playerManager.removePetCall(playerName); + playerData.refreshPetStack(); + ItemStack petStack = playerData.getPetStack(); + player.getInventory().addItem(petStack); + playerData.setPetStack(DemonAPI.getErrItems()); + playerData.savePlayerData(); + MessageUtil.sendMessageKey(player, "pet_stack_carry_takeOut", Sound.ENTITY_PLAYER_LEVELUP); + } + } + + } +} diff --git a/src/main/java/com/yaohun/petsystem/gui/FeedingGui.java b/src/main/java/com/yaohun/petsystem/gui/FeedingGui.java new file mode 100644 index 0000000..88c0211 --- /dev/null +++ b/src/main/java/com/yaohun/petsystem/gui/FeedingGui.java @@ -0,0 +1,206 @@ +package com.yaohun.petsystem.gui; + +import com.yaohun.petsystem.PetMain; +import com.yaohun.petsystem.api.PetExpAPI; +import com.yaohun.petsystem.config.Config; +import com.yaohun.petsystem.data.PlayerData; +import com.yaohun.petsystem.gui.holder.FeedingGuiHolder; +import com.yaohun.petsystem.manage.LevelExpManager; +import com.yaohun.petsystem.manage.PlayerManager; +import com.yaohun.petsystem.model.PetNbt; +import com.yaohun.petsystem.util.MessageUtil; +import com.yaohun.petsystem.util.PetUtil; +import de.tr7zw.nbtapi.NBTItem; +import me.Demon.DemonPlugin.DemonAPI; +import org.bukkit.Bukkit; +import org.bukkit.Sound; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +import java.util.List; + +public class FeedingGui implements Listener { + + private final static int FOOD_SLOT = 15; + + public static void openGui(Player player) { + String playerName = player.getName(); + PlayerManager playerManager = PetMain.getPlayerManager(); + PlayerData playerData = playerManager.getPlayerData(playerName); + if (playerData.isPetStackNull()) { + MessageUtil.sendMessageKey(player, "gui_no_pets_brought", Sound.ENTITY_VILLAGER_NO); + return; + } + String invTitle = Config.getLanguage("main_gui_title"); + if (playerManager.isPetCallExit(playerName)) { + invTitle = Config.getLanguage("main_gui_title") + Config.getLanguage("call_gui_title"); + } + Inventory inv = Bukkit.createInventory(new FeedingGuiHolder(), 36, invTitle + Config.getLanguage("feeding_gui_title")); + // 关闭界面 + inv.setItem(1, Config.getItemStack("close")); + //食物槽 + inv.setItem(FOOD_SLOT, DemonAPI.getPaperAirItems()); + checkFoodGuiPetState(inv, playerData); + player.openInventory(inv); + } + + private static void checkFoodGuiPetState(Inventory inv, PlayerData playerData) { + inv.setItem(23, getFoodButt(playerData.getPetNbt())); + inv.setItem(24, getFoodButt(playerData.getPetNbt())); + inv.setItem(25, getFoodButt(playerData.getPetNbt())); + inv.setItem(11, PetUtil.getPetGuiStack(playerData, "chongwu_feed")); + } + + private static ItemStack getFoodButt(PetNbt petNbt) { + ItemStack stack = Config.getItemStack("weishibutton"); + ItemMeta meta = stack.getItemMeta(); + List lore = meta.getLore(); + lore.replaceAll(s -> DemonAPI.convertHexColor(s + .replace("{petlevel}", String.valueOf(petNbt.level)) + .replace("{exp}", String.valueOf(petNbt.exp)) + .replace("{maxExp}", String.valueOf(LevelExpManager.getNeedExpValue(petNbt.level))))); + meta.setLore(lore); + stack.setItemMeta(meta); + return stack; + } + + private ItemStack getFooedStack(Inventory inv) { + ItemStack foodStack = inv.getItem(FOOD_SLOT); + if (DemonAPI.itemIsNull(foodStack) || DemonAPI.itemIsLore(foodStack)) { + return null; + } + return foodStack; + } + + @EventHandler + public void onClick(InventoryClickEvent e) { + int rawSlot = e.getRawSlot(); + Player player = (Player) e.getWhoClicked(); + String playerName = player.getName(); + Inventory inv = e.getInventory(); + if ((e.getInventory().getHolder() instanceof FeedingGuiHolder)) { + e.setCancelled(true); + if (rawSlot > 35) { + ItemStack clickStack = e.getCurrentItem(); + if (DemonAPI.itemIsNull(clickStack) || DemonAPI.itemIsLore(clickStack)) { + return; + } + NBTItem nbtItem = new NBTItem(clickStack); + if (nbtItem.hasKey("petAppleExp") || nbtItem.hasKey("petAppleUp")) { + // 判断槽位内是否已存在物品 + ItemStack foodStack = getFooedStack(inv); + if (foodStack != null) { + player.getInventory().addItem(foodStack); + } + e.setCurrentItem(null); + inv.setItem(FOOD_SLOT, clickStack); + player.playSound(player.getLocation(), Sound.UI_BUTTON_CLICK, 1, 1); + } else { + MessageUtil.sendMessageKey(player, "pet_feed_not_apple", Sound.ENTITY_VILLAGER_NO); + } + return; + } + if (rawSlot == FOOD_SLOT) { + // 判断槽位内是否已存在物品 + ItemStack foodStack = getFooedStack(inv); + if (foodStack != null) { + player.getInventory().addItem(foodStack); + inv.setItem(FOOD_SLOT, DemonAPI.getPaperAirItems()); + } + player.playSound(player.getLocation(), Sound.UI_BUTTON_CLICK, 1, 1); + } + if (rawSlot == 1) { + MainGui.openGui(player); + player.playSound(player.getLocation(), Sound.UI_BUTTON_CLICK, 1, 1); + } + if (rawSlot == 23 || rawSlot == 24 || rawSlot == 25) { + // 获取实物槽位中的物品 + ItemStack foodStack = inv.getItem(FOOD_SLOT); + if (DemonAPI.itemIsNull(foodStack) || DemonAPI.itemIsLore(foodStack)) { + return; + } + PlayerManager playerManager = PetMain.getPlayerManager(); + PlayerData playerData = playerManager.getPlayerData(playerName); + player.playSound(player.getLocation(), Sound.UI_BUTTON_CLICK, 1, 1); + String itemName = DemonAPI.getItemName(foodStack); + NBTItem nbtItem = new NBTItem(foodStack); + if (nbtItem.hasKey("petAppleExp")) { + // 扣除果实 + if (foodStack.getAmount() == 1) { + inv.setItem(FOOD_SLOT, DemonAPI.getPaperAirItems()); + } else { + foodStack.setAmount(foodStack.getAmount() - 1); + } + // 获取增加经验 + int addExp = Integer.parseInt(nbtItem.getString("petAppleExp")); + PetExpAPI.addExp(player, addExp); + String message = Config.getLanguage("pet_feed_exp_success"); + message = message.replace("{itemName}", itemName); + message = message.replace("{exp}", String.valueOf(addExp)); + MessageUtil.sendMessage(player, message, Sound.ENTITY_EXPERIENCE_ORB_PICKUP); + // 刷新宠物经验 + checkFoodGuiPetState(inv, playerData); + playerData.refreshPetStack(); + } else if (nbtItem.hasKey("petAppleUp")) { + PetNbt petNbt = playerData.getPetNbt(); + // 获取进化阶段 + int needLevel = Integer.parseInt(nbtItem.getString("petAppleUp")); + if (needLevel == 20) { + feedEvolutionEvent(player, needLevel, petNbt, 1, 2, itemName); + } else if (needLevel == 40) { + feedEvolutionEvent(player, needLevel, petNbt, 2, 3, itemName); + } else if (needLevel == 60) { + feedEvolutionEvent(player, needLevel, petNbt, 3, 4, itemName); + } else if (needLevel == 70) { + feedEvolutionEvent(player, needLevel, petNbt, 4, 5, itemName); + } else if (needLevel == 75) { + feedEvolutionEvent(player, needLevel, petNbt, 5, 6, itemName); + } + } else { + MessageUtil.sendMessageKey(player, "pet_feed_not_apple", Sound.ENTITY_VILLAGER_NO); + } + } + } + } + + private void feedEvolutionEvent(Player player, int appleLevel, PetNbt petNbt, int needEvolution, int nextEvolution, String itemName) { + int evolution = petNbt.evolution; + int petLevel = petNbt.level; + if (evolution == nextEvolution) { + MessageUtil.sendMessageKey(player, "pet_feed_evolution_already", Sound.ENTITY_VILLAGER_NO); + return; + } + if (evolution == needEvolution) { + // 判断宠物等级是否满足要求 + if (petLevel < appleLevel) { + String message = Config.getLanguage("pet_feed_evolution_need_level"); + MessageUtil.sendMessage(player, message.replace("{level}", Config.getLevelChar(appleLevel)), Sound.ENTITY_VILLAGER_NO); + return; + } + petNbt.setEvolution(nextEvolution); + String message = Config.getLanguage("pet_feed_evolution_success"); + MessageUtil.sendMessage(player, message.replace("{itemName}", itemName), Sound.ENTITY_EXPERIENCE_ORB_PICKUP); + } else { + MessageUtil.sendMessageKey(player, "pet_feed_evolution_request", Sound.ENTITY_VILLAGER_NO); + } + } + + @EventHandler + public void onClose(InventoryCloseEvent e) { + Inventory inv = e.getInventory(); + if ((e.getInventory().getHolder() instanceof FeedingGuiHolder)) { + Player player = (Player) e.getPlayer(); + ItemStack stack = getFooedStack(inv); + if (stack != null) { + inv.setItem(FOOD_SLOT, DemonAPI.getPaperAirItems()); + player.getInventory().addItem(stack); + } + } + } +} \ No newline at end of file diff --git a/src/main/java/com/yaohun/petsystem/gui/MainGui.java b/src/main/java/com/yaohun/petsystem/gui/MainGui.java new file mode 100644 index 0000000..70f50a8 --- /dev/null +++ b/src/main/java/com/yaohun/petsystem/gui/MainGui.java @@ -0,0 +1,185 @@ +package com.yaohun.petsystem.gui; + +import com.yaohun.petsystem.PetMain; +import com.yaohun.petsystem.config.Config; +import com.yaohun.petsystem.data.PetData; +import com.yaohun.petsystem.data.PlayerData; +import com.yaohun.petsystem.data.SkillData; +import com.yaohun.petsystem.gui.holder.MainGuiHolder; +import com.yaohun.petsystem.listener.RePetNameListener; +import com.yaohun.petsystem.manage.PlayerManager; +import com.yaohun.petsystem.model.PetNbt; +import com.yaohun.petsystem.util.MessageUtil; +import com.yaohun.petsystem.util.PetUtil; +import me.Demon.DemonPlugin.DemonAPI; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextDecoration; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.Sound; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +import java.util.List; + +public class MainGui implements Listener { + + public static void openGui(Player player) { + String playerName = player.getName(); + PlayerManager playerManager = PetMain.getPlayerManager(); + PlayerData playerData = playerManager.getPlayerData(playerName); + if (playerData.isPetStackNull()) { + CarryGui.openGui(player); + // MessageUtil.sendMessageKey(player, "gui_no_pets_brought", Sound.ENTITY_VILLAGER_NO); + return; + } + String invTitle = Config.getLanguage("main_gui_title"); + String buttonKey = "zhaohuan"; + if (playerManager.isPetCallExit(playerName)) { + invTitle = Config.getLanguage("main_gui_title") + Config.getLanguage("call_gui_title"); + buttonKey = "zhaohui"; + } + Inventory inv = Bukkit.createInventory(new MainGuiHolder(), 36, invTitle); + // 关闭界面 + inv.setItem(0, Config.getItemStack("close")); + // 宠物图鉴 + inv.setItem(1, Config.getItemStack("pettujian")); + // 宠物物品显示 + inv.setItem(11, PetUtil.getPetGuiStack(playerData, "chongwu")); + // 宠物技能显示 + PetData petData = playerData.getPetNbt().petData; + int slot = 13; + for (SkillData skillData : petData.getSkillDataMap().values()) { + inv.setItem(slot, getSkillGoldenApple(skillData)); + slot++; + } + // 宠物召唤按钮 + ItemStack zhaohuanStack = Config.getItemStack(buttonKey); + inv.setItem(29, zhaohuanStack); + inv.setItem(30, zhaohuanStack); + // 宠物喂养按钮 + inv.setItem(31, Config.getItemStack("weiyang")); + // 宠物工作按钮 + inv.setItem(32, Config.getItemStack("gongzuo")); + inv.setItem(33, Config.getItemStack("gongzuo")); + // 宠物品质数据查询 + inv.setItem(22, getQualificationStack(playerData, "fangs")); + inv.setItem(23, getQualificationStack(playerData, "survive")); + inv.setItem(24, getQualificationStack(playerData, "efficiency")); + // 仓库、Wiki按钮 + inv.setItem(7, Config.getItemStack("cangku")); + inv.setItem(8, Config.getItemStack("wiki")); + player.openInventory(inv); + } + + private static ItemStack getSkillGoldenApple(SkillData skillData) { + ItemStack stack = new ItemStack(Material.GOLDEN_APPLE); + ItemMeta meta = stack.getItemMeta(); + meta.displayName(Component.text(skillData.getItemName()) + .color(NamedTextColor.GREEN) + .decoration(TextDecoration.ITALIC, false)); + meta.setLore(skillData.getItemLore()); + stack.setItemMeta(meta); + return stack; + } + + private static ItemStack getQualificationStack(PlayerData playerData, String type) { + PetNbt petNbt = playerData.getPetNbt(); + int value = 0; + if ("fangs".equalsIgnoreCase(type)) { + value = petNbt.quality.getFangs(); + } else if ("survive".equalsIgnoreCase(type)) { + value = petNbt.quality.getSurvive(); + } else if ("efficiency".equalsIgnoreCase(type)) { + value = petNbt.quality.getEfficiency(); + } + ItemStack stack = Config.getItemStack(type); + ItemMeta meta = stack.getItemMeta(); + List lore = meta.getLore(); + for (int i = 0; i < lore.size(); i++) { + String line = lore.get(i); + if (line.contains("{qualityShow}")) { + lore.set(i, DemonAPI.convertHexColor(line).replace("{qualityShow}", Config.getQualityShow(value))); + break; + } + } + meta.setLore(lore); + stack.setItemMeta(meta); + return stack; + } + + @EventHandler + public void onClick(InventoryClickEvent e) { + int rawSlot = e.getRawSlot(); + Player player = (Player) e.getWhoClicked(); + if ((e.getInventory().getHolder() instanceof MainGuiHolder)) { + e.setCancelled(true); + // 关闭界面按钮 + if (rawSlot == 0) { + player.closeInventory(); + player.playSound(player.getLocation(), Sound.UI_BUTTON_CLICK, 1, 1); + } + if (rawSlot == 1) { + player.closeInventory(); + // 打开宠物图鉴操作 + player.playSound(player.getLocation(), Sound.UI_BUTTON_CLICK, 1, 1); + } + // Wiki按钮 + if (rawSlot == 8) { + player.closeInventory(); + MessageUtil.sendMessageKey(player, "gui_wiki_message", Sound.UI_BUTTON_CLICK); + } + // 仓库按钮 + if (rawSlot == 7) { + player.closeInventory(); + // 打开便捷仓库操作 + player.playSound(player.getLocation(), Sound.UI_BUTTON_CLICK, 1, 1); + } + PlayerManager playerManager = PetMain.getPlayerManager(); + if (rawSlot == 11) { + if (e.isShiftClick()) { + String playerName = player.getName(); + PlayerData playerData = playerManager.getPlayerData(playerName); + if (!playerData.isPetStackNull()) { + playerManager.removePetCall(playerName); + playerData.refreshPetStack(); + ItemStack petStack = playerData.getPetStack(); + player.getInventory().addItem(petStack); + playerData.setPetStack(DemonAPI.getErrItems()); + playerData.savePlayerData(); + player.closeInventory(); + Bukkit.getScheduler().runTaskLater(PetMain.inst(), player::updateInventory, 2L); + MessageUtil.sendMessageKey(player, "pet_stack_carry_takeOut", Sound.ENTITY_PLAYER_LEVELUP); + } + return; + } + if (e.isLeftClick()) { + player.closeInventory(); + RePetNameListener.addReNameUUID(player.getUniqueId()); + MessageUtil.sendMessageKey(player, "pet_rename_add", Sound.UI_BUTTON_CLICK); + } + } + // 召唤宠物操作按钮 + if (rawSlot == 29 || rawSlot == 30) { + player.closeInventory(); + playerManager.callPet(player); + } + // 打开投喂宠物界面 + if (rawSlot == 31) { + FeedingGui.openGui(player); + player.playSound(player.getLocation(), Sound.UI_BUTTON_CLICK, 1, 1); + } + // 打开打工系统 + if (rawSlot == 32 || rawSlot == 33) { + player.closeInventory(); + MessageUtil.sendMessageKey(player, "imperfect_function", Sound.ENTITY_VILLAGER_NO); + } + } + } +} \ No newline at end of file diff --git a/src/main/java/com/yaohun/petsystem/gui/holder/CarryGuiHolder.java b/src/main/java/com/yaohun/petsystem/gui/holder/CarryGuiHolder.java new file mode 100644 index 0000000..9ae4857 --- /dev/null +++ b/src/main/java/com/yaohun/petsystem/gui/holder/CarryGuiHolder.java @@ -0,0 +1,12 @@ +package com.yaohun.petsystem.gui.holder; + +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; + +public class CarryGuiHolder implements InventoryHolder { + + @Override + public Inventory getInventory() { + return null; + } +} diff --git a/src/main/java/com/yaohun/petsystem/gui/holder/FeedingGuiHolder.java b/src/main/java/com/yaohun/petsystem/gui/holder/FeedingGuiHolder.java new file mode 100644 index 0000000..a97e280 --- /dev/null +++ b/src/main/java/com/yaohun/petsystem/gui/holder/FeedingGuiHolder.java @@ -0,0 +1,12 @@ +package com.yaohun.petsystem.gui.holder; + +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; + +public class FeedingGuiHolder implements InventoryHolder { + + @Override + public Inventory getInventory() { + return null; + } +} diff --git a/src/main/java/com/yaohun/petsystem/gui/holder/MainGuiHolder.java b/src/main/java/com/yaohun/petsystem/gui/holder/MainGuiHolder.java new file mode 100644 index 0000000..496777d --- /dev/null +++ b/src/main/java/com/yaohun/petsystem/gui/holder/MainGuiHolder.java @@ -0,0 +1,12 @@ +package com.yaohun.petsystem.gui.holder; + +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; + +public class MainGuiHolder implements InventoryHolder { + + @Override + public Inventory getInventory() { + return null; + } +} diff --git a/src/main/java/com/yaohun/petsystem/listener/PetSkillsListener.java b/src/main/java/com/yaohun/petsystem/listener/PetSkillsListener.java new file mode 100644 index 0000000..3e89c8d --- /dev/null +++ b/src/main/java/com/yaohun/petsystem/listener/PetSkillsListener.java @@ -0,0 +1,94 @@ +package com.yaohun.petsystem.listener; + +import com.yaohun.petsystem.PetMain; +import com.yaohun.petsystem.data.PetData; +import com.yaohun.petsystem.data.PlayerData; +import com.yaohun.petsystem.data.SkillData; +import com.yaohun.petsystem.manage.PlayerManager; +import io.lumine.mythic.bukkit.MythicBukkit; +import me.Demon.DemonPlugin.Util.CDTimeAPI; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityDamageByEntityEvent; + +import java.util.LinkedHashMap; +import java.util.UUID; + +public class PetSkillsListener implements Listener { + + @EventHandler + public void onDamager(EntityDamageByEntityEvent e){ + if(e.getDamager() instanceof Player player){ + // 判断玩家是否携带宠物 + String playerName = player.getName(); + PlayerManager playerManager = PetMain.getPlayerManager(); + // 获取宠物相关数据 + PlayerData playerData = playerManager.getPlayerData(playerName); + if(playerData.isPetStackNull()){ + return; + } + PetData petData = playerData.getPetNbt().petData; + int petLevel = playerData.getPetNbt().level; + LinkedHashMap skillDataMap = petData.getSkillDataMap(); + for(SkillData skillData : skillDataMap.values()){ + if(petLevel >= skillData.getNeedLevel()) { + String cdKey = "skill_"+skillData.getSkillName(); + if ("onAttack".equalsIgnoreCase(skillData.getTrigger())) { + UUID uuid = player.getUniqueId(); + if(!CDTimeAPI.isCD(uuid,cdKey)) { + long cooldown = skillData.getCoolDown(); + CDTimeAPI.setPlayerCD(uuid, cdKey, cooldown); + String mmSkill = skillData.getMmSkill(); + // 执行技能 + MythicBukkit.inst().getAPIHelper().castSkill( + player, + mmSkill, + player.getLocation() + ); + player.sendMessage("[调试 - 宠物技能] 触发条件: onAttack 触发技能: "+skillData.getSkillName()); + } + } + } + } + } + if(e.getEntity() instanceof Player player){ + String playerName = player.getName(); + PlayerManager playerManager = PetMain.getPlayerManager(); + + PlayerData playerData = playerManager.getPlayerData(playerName); + if(playerData.isPetStackNull()){ + return; + } + PetData petData = playerData.getPetNbt().petData; + int petLevel = playerData.getPetNbt().level; + LinkedHashMap skillDataMap = petData.getSkillDataMap(); + for(SkillData skillData : skillDataMap.values()){ + if(petLevel >= skillData.getNeedLevel()) { + String cdKey = "skill_"+skillData.getSkillName(); + if ("onDamaged".equalsIgnoreCase(skillData.getTrigger())) { + UUID uuid = player.getUniqueId(); + if(!CDTimeAPI.isCD(uuid,cdKey)) { + long cooldown = skillData.getCoolDown(); + CDTimeAPI.setPlayerCD(uuid, cdKey, cooldown); + String mmSkill = skillData.getMmSkill(); + // 执行技能 + MythicBukkit.inst().getAPIHelper().castSkill(player, mmSkill, player.getLocation()); + player.sendMessage("[调试 - 宠物技能] 触发条件: onDamaged 触发技能: "+skillData.getSkillName()); + } + } + } + } + } + } + + /*public boolean runSkills(SkillCaster caster, SkillTrigger cause, AbstractLocation origin, AbstractEntity trigger, Consumer transformer) { + return new TriggeredSkill(cause, caster, origin, trigger, this.artifact.getMechanics(cause), true, meta -> { + transformer.getVariables().putString("equip-slot", this.equippedSlot); + transformer.getVariables().putObject("equip-item", this.itemStack); + if (r5 != null) { + r5.accept(transformer); + } + }, new Pair[0]).getCancelled(); + }*/ +} diff --git a/src/main/java/com/yaohun/petsystem/listener/PlayerListener.java b/src/main/java/com/yaohun/petsystem/listener/PlayerListener.java new file mode 100644 index 0000000..90a5b68 --- /dev/null +++ b/src/main/java/com/yaohun/petsystem/listener/PlayerListener.java @@ -0,0 +1,79 @@ +package com.yaohun.petsystem.listener; + +import com.yaohun.petsystem.PetMain; +import com.yaohun.petsystem.data.PlayerData; +import com.yaohun.petsystem.manage.PlayerManager; +import com.yaohun.petsystem.model.PetNbt; +import com.yaohun.petsystem.util.PetUtil; +import org.bukkit.Bukkit; +import org.bukkit.entity.Entity; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.entity.Wolf; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.entity.EntityDeathEvent; +import org.bukkit.event.player.PlayerQuitEvent; + +import java.util.UUID; + +public class PlayerListener implements Listener { + + @EventHandler + public void onDamage(EntityDamageByEntityEvent e) { + if (e.getEntity() instanceof Player) { + return; + } + if (!(e.getEntity() instanceof LivingEntity target)) { + return; + } + if (e.getDamager() instanceof Player player) { + String playerName = player.getName(); + PlayerManager playerManager = PetMain.getPlayerManager(); + if (!playerManager.isPetCallExit(playerName)) { + return; + } + UUID petUUID = playerManager.getPetUUID(playerName); + if (petUUID == null) { + return; + } + Entity entity = Bukkit.getEntity(petUUID); + if (entity instanceof Wolf wolf) { + wolf.setTarget(target); + } + } + } + + // 检查伤害 狼造成的伤害 + @EventHandler + public void onDamageCheck(EntityDamageByEntityEvent e) { + if (e.getDamager() instanceof Wolf wolf) { + PlayerData playerData = PetUtil.updatePetNameTag(wolf); + if (playerData != null) { + double health = wolf.getHealth(); + PetNbt petNbt = playerData.getPetNbt(); + petNbt.setHealth((int) health); + } + } + } + + @EventHandler + public void onDeath(EntityDeathEvent e) { + String ownerName = PetMain.getPlayerManager().getPetOwnerName(e.getEntity().getUniqueId()); + if (ownerName == null) { + return; + } + PlayerData playerData = PetMain.getPlayerManager().getPlayerData(ownerName); + playerData.getPetNbt().setHealth(0); + PetMain.getPlayerManager().removePetCall(ownerName); + } + + @EventHandler + public void onQuit(PlayerQuitEvent e){ + Player player = e.getPlayer(); + String playerName = player.getName(); + PlayerManager playerManager = PetMain.getPlayerManager(); + playerManager.removePetCall(playerName); + } +} diff --git a/src/main/java/com/yaohun/petsystem/listener/RePetNameListener.java b/src/main/java/com/yaohun/petsystem/listener/RePetNameListener.java new file mode 100644 index 0000000..de4e730 --- /dev/null +++ b/src/main/java/com/yaohun/petsystem/listener/RePetNameListener.java @@ -0,0 +1,62 @@ +package com.yaohun.petsystem.listener; + +import com.yaohun.petsystem.PetMain; +import com.yaohun.petsystem.config.Config; +import com.yaohun.petsystem.data.PlayerData; +import com.yaohun.petsystem.manage.PlayerManager; +import com.yaohun.petsystem.util.MessageUtil; +import org.bukkit.Sound; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.AsyncPlayerChatEvent; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +public class RePetNameListener implements Listener { + + private static List reNameList = new ArrayList<>(); + + public static void addReNameUUID(UUID uuid) { + if (!reNameList.contains(uuid)) { + reNameList.add(uuid); + } + } + + @EventHandler + public void onChat(AsyncPlayerChatEvent e) { + Player player = e.getPlayer(); + UUID uuid = player.getUniqueId(); + if (reNameList.isEmpty()) { + return; + } + if (!reNameList.contains(uuid)) { + return; + } + e.setCancelled(true); + reNameList.remove(uuid); + String playerName = player.getName(); + PlayerManager playerManager = PetMain.getPlayerManager(); + PlayerData playerData = playerManager.getPlayerData(playerName); + if (playerData.isPetStackNull()) { + MessageUtil.sendMessageKey(player, "pet_rename_no_pets", Sound.ENTITY_VILLAGER_NO); + return; + } + String reName = e.getMessage(); + if (reName.isEmpty()) { + MessageUtil.sendMessageKey(player, "pet_rename_empty", Sound.ENTITY_VILLAGER_NO); + return; + } + if (reName.length() < 3) { + MessageUtil.sendMessageKey(player, "pet_rename_length", Sound.ENTITY_VILLAGER_NO); + return; + } + String newName = reName.replace("&", "§"); + playerData.rePetNameStack(newName); + playerData.savePlayerData(); + String message = Config.getLanguage("pet_rename_success"); + MessageUtil.sendMessage(player, message.replace("{petName}", newName), Sound.ENTITY_PLAYER_LEVELUP); + } +} \ No newline at end of file diff --git a/src/main/java/com/yaohun/petsystem/manage/LevelExpManager.java b/src/main/java/com/yaohun/petsystem/manage/LevelExpManager.java new file mode 100644 index 0000000..42133c8 --- /dev/null +++ b/src/main/java/com/yaohun/petsystem/manage/LevelExpManager.java @@ -0,0 +1,35 @@ +package com.yaohun.petsystem.manage; + +import com.yaohun.petsystem.PetMain; +import org.bukkit.Bukkit; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.io.File; +import java.util.HashMap; + +public class LevelExpManager { + + private static HashMap needExpMap = new HashMap<>(); + + public static void reloadLevelManager(PetMain plugin) { + File file = new File(plugin.getDataFolder() + "/Settings", "needExp.yml"); + if (file.getParentFile().exists()) { + file.getParentFile().mkdirs(); + } + FileConfiguration config = YamlConfiguration.loadConfiguration(file); + for (String levelKey : config.getKeys(false)) { + int level = Integer.parseInt(levelKey); + int needExp = config.getInt(levelKey); + needExpMap.put(level, needExp); + } + Bukkit.getConsoleSender().sendMessage("§7- 等级经验配置: §f" + needExpMap.size() + "条"); + } + + public static int getNeedExpValue(int level) { + if (needExpMap.containsKey(level)) { + return needExpMap.get(level); + } + return 1000000; + } +} \ No newline at end of file diff --git a/src/main/java/com/yaohun/petsystem/manage/PetCallSession.java b/src/main/java/com/yaohun/petsystem/manage/PetCallSession.java new file mode 100644 index 0000000..df1db1e --- /dev/null +++ b/src/main/java/com/yaohun/petsystem/manage/PetCallSession.java @@ -0,0 +1,48 @@ +package com.yaohun.petsystem.manage; + +import kr.toxicity.model.api.tracker.Tracker; +import org.bukkit.Bukkit; +import org.bukkit.entity.Entity; + +import java.util.UUID; + +public class PetCallSession { + + private final UUID ownerUuid; + private final UUID entityUuid; + private final Tracker tracker; + + public PetCallSession(UUID ownerUuid, UUID entityUuid, Tracker tracker) { + this.ownerUuid = ownerUuid; + this.entityUuid = entityUuid; + this.tracker = tracker; + } + + public UUID getOwnerUuid() { + return ownerUuid; + } + + public UUID getEntityUuid() { + return entityUuid; + } + + public Entity getEntity() { + return Bukkit.getEntity(entityUuid); + } + + public boolean isAlive() { + Entity entity = getEntity(); + return entity != null && !entity.isDead(); + } + + public void close() { + if (tracker != null && !tracker.isClosed()) { + tracker.despawn(); + tracker.close(); + } + Entity entity = getEntity(); + if (entity != null && !entity.isDead()) { + entity.remove(); + } + } +} diff --git a/src/main/java/com/yaohun/petsystem/manage/PetManager.java b/src/main/java/com/yaohun/petsystem/manage/PetManager.java new file mode 100644 index 0000000..5996459 --- /dev/null +++ b/src/main/java/com/yaohun/petsystem/manage/PetManager.java @@ -0,0 +1,71 @@ +package com.yaohun.petsystem.manage; + +import com.yaohun.petsystem.PetMain; +import com.yaohun.petsystem.data.PetData; +import me.Demon.DemonPlugin.DemonAPI; +import org.bukkit.Bukkit; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +public class PetManager { + + private static HashMap petDataMap = new HashMap<>(); + + public static void reloadPetManager() { + petDataMap.clear(); + Bukkit.getConsoleSender().sendMessage("§6[宠物系统] §7宠物类型:"); + String folderPath = PetMain.inst().getDataFolder().getPath() + "/PetData"; + File folder = new File(folderPath); + if (!folder.exists() || !folder.isDirectory()) { + System.out.println("[日志 - 灵宠] 未检测到任何宠物配置: " + folderPath); + return; + } + List ymlFiles = new ArrayList<>(); + DemonAPI.collectYmlFiles(folder, ymlFiles); + if (ymlFiles.isEmpty()) { + System.out.println("[日志 - 灵宠] 未检测到任何宠物配置: " + folderPath); + return; + } + for (File file : ymlFiles) { + String fileName = file.getName().replace(".yml", ""); + FileConfiguration config = YamlConfiguration.loadConfiguration(file); + PetData petData = new PetData(fileName, config); + petDataMap.put(fileName, petData); + Bukkit.getConsoleSender().sendMessage("- 宠物: §f" + petData.getPetName() + " §7MaxLevel: §f" + petData.getMaxLevel()); + } + } + + public static HashMap getPetDataMap() { + return petDataMap; + } + + public static PetData getItemNameToPetData(String itemName) { + for (PetData petData : petDataMap.values()) { + if (petData.getPetName().contains(itemName)) { + return petData; + } + } + return null; + } + + public static PetData getPetData(String petKey) { + if (petDataMap.containsKey(petKey)) { + return petDataMap.get(petKey); + } + return null; + } + + // 获取宠物获取渠道 + public static List getPetAccessChannels(String petKey) { + PetData petData = PetManager.getPetData(petKey); + if (petData == null) { + return new ArrayList<>(); + } + return petData.getAccessChannelsList(); + } +} \ No newline at end of file diff --git a/src/main/java/com/yaohun/petsystem/manage/PlayerManager.java b/src/main/java/com/yaohun/petsystem/manage/PlayerManager.java new file mode 100644 index 0000000..5e5e432 --- /dev/null +++ b/src/main/java/com/yaohun/petsystem/manage/PlayerManager.java @@ -0,0 +1,253 @@ +package com.yaohun.petsystem.manage; + +import com.yaohun.petsystem.PetMain; +import com.yaohun.petsystem.config.Config; +import com.yaohun.petsystem.data.PetData; +import com.yaohun.petsystem.data.PetSQL; +import com.yaohun.petsystem.data.PlayerData; +import com.yaohun.petsystem.event.PetCallEvent; +import com.yaohun.petsystem.util.MessageUtil; +import com.yaohun.petsystem.util.model.BetterModelUtil; +import com.yaohun.petsystem.util.wolf.CustomWolf; +import de.tr7zw.nbtapi.NBTItem; +import io.lumine.mythic.bukkit.MythicBukkit; +import kr.toxicity.model.api.tracker.Tracker; +import me.Demon.DemonPlugin.DemonAPI; +import me.Demon.DemonPlugin.Util.CDTimeAPI; +import org.bukkit.Bukkit; +import org.bukkit.Sound; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.util.HashMap; +import java.util.UUID; + +public class PlayerManager { + + private HashMap playerDataMap = new HashMap<>(); + private HashMap petCallMap = new HashMap<>(); + // private HashMap petEntityModelMap = new HashMap<>(); + + public PlayerData getPlayerData(String name) { + if (!playerDataMap.containsKey(name)) { + playerDataMap.put(name, new PlayerData(name)); + } + return playerDataMap.get(name); + } + + public void saveAllData() { + for (PlayerData playerData : playerDataMap.values()) { + playerData.savePlayerData(); + } + } + + public void closeSaveData(String playerName) { + PlayerData playerData = getPlayerData(playerName); + playerData.savePlayerData(); + playerDataMap.remove(playerName); + + removePetCall(playerName); + } + + public void closeSaveAllData() { + for (PlayerData playerData : playerDataMap.values()) { + playerData.savePlayerData(); + } + playerDataMap.clear(); + for (PetCallSession session : petCallMap.values()) { + session.close(); + } + PetSQL.closeConnection(); + petCallMap.clear(); + } + + public void setPetCall(String name, UUID ownerUuid, UUID entityUuid, Tracker tracker) { + petCallMap.put(name, new PetCallSession(ownerUuid, entityUuid, tracker)); + } + + public void removePetCall(String name) { + PetCallSession session = petCallMap.remove(name); + if (session != null) { + session.close(); + } + } + + public boolean isPetCallExit(String name) { + PetCallSession session = petCallMap.get(name); + if (session == null) { + return false; + } + if (!session.isAlive()) { + petCallMap.remove(name); + session.close(); + return false; + } + return true; + } + + public String getPetOwnerName(UUID entityUuid) { + for (String name : petCallMap.keySet()) { + PetCallSession session = petCallMap.get(name); + if (session.getEntityUuid().equals(entityUuid)) { + return name; + } + } + return null; + } + + public UUID getPetUUID(String name) { + if (isPetCallExit(name)) { + return petCallMap.get(name).getEntityUuid(); + } + return null; + } +/* + public ModeledEntity getPetEntityModel(UUID entityUuid) { + for (ModeledEntity entity : petEntityModelMap.values()) { + if (entity.getBase().getUUID().equals(entityUuid)) { + return entity; + } + } + return null; + } + + public void setPetEntityModelMap(UUID entityUUID, ModeledEntity entityModel) { + petEntityModelMap.put(entityUUID, entityModel); + }*/ + + public void callPet(Player player) { + String carryCdKey = "callPetStackCD"; + UUID uuid = player.getUniqueId(); + if (CDTimeAPI.isCD(uuid, carryCdKey)) { + MessageUtil.sendMessageKey(player, "system_coolingDown", Sound.ENTITY_VILLAGER_NO); + return; + } + CDTimeAPI.setPlayerCD(uuid, carryCdKey, 1000 * 5); + String playerName = player.getName(); + PlayerData playerData = getPlayerData(playerName); + if (playerData == null) { + return; + } + if (playerData.isPetStackNull()) { + MessageUtil.sendMessageKey(player, "no_summons_pets_brought", Sound.ENTITY_VILLAGER_NO); + return; + } + // 判断玩家是否已经召唤宠物 + if (isPetCallExit(playerName)) { + removePetCall(playerName); + MessageUtil.sendMessageKey(player, "pet_summons_recovers", Sound.UI_BUTTON_CLICK); + return; + } + int health = playerData.getPetNbt().health; + if (health < 1) { + MessageUtil.sendMessageKey(player, "no_summons_pets_health", Sound.ENTITY_VILLAGER_NO); + return; + } + String petStackName = DemonAPI.getItemName(playerData.getPetStack()); + CustomWolf wolf = CustomWolf.spawnCustomWolf(player, playerData.getPetNbt()); + PetData petData = playerData.getPetNbt().petData; + String modelID = petData.getModelId(); + Tracker tracker = BetterModelUtil.spawnModel(player, wolf.getBukkitEntity(), modelID); + if (tracker == null) { + wolf.getBukkitEntity().remove(); + MessageUtil.sendMessage(player, "§c宠物模型载入失败,召唤已取消。", Sound.ENTITY_VILLAGER_NO); + return; + } + setPetCall(playerName, player.getUniqueId(), wolf.getUUID(), tracker); + /*ActiveModel model = ModelEngineAPI.createActiveModel(modelID); + if (model != null) { + ModeledEntity modeledEntity = ModelEngineAPI.createModeledEntity(wolf.getBukkitEntity()); + setPetEntityModelMap(wolf.getUUID(), modeledEntity); + modeledEntity.addModel(model, true); + modeledEntity.setBaseEntityVisible(false); + model.setHitboxVisible(true); + model.setShadowVisible(true); + String text = Config.getLanguage("pet_name_tag") + .replace("{name}", playerName) + .replace("{petName}", petStackName); + + wolf.setCustomName(Component.literal(text)); + wolf.setCustomNameVisible(true); + + NameTag tag = PetUtil.getNameBone(modeledEntity, "name"); + if (tag != null) { + tag.setString(text); + tag.setVisible(true); + } + int petLevel = playerData.getPetNbt().level; + double healthNow = wolf.getHealth(); + double healthMax = wolf.getMaxHealth(); + // 计算获得血量百分比 + int percent = (int) Math.round((healthNow / healthMax) * 100.0); + String levelText = Config.getLanguage("pet_level_tag") + .replace("{level}", Config.getLevelChar(petLevel)) + .replace("{health}", Config.getHealthChar(percent)); + NameTag levelTag = PetUtil.getNameBone(modeledEntity, "level"); + if (levelTag != null) { + levelTag.setString(levelText); + levelTag.setVisible(true); + } + Bukkit.getConsoleSender().sendMessage("[日志 - 宠物] 模型资源 " + modelID + " 载入成功!"); + } else { + Bukkit.getConsoleSender().sendMessage("[日志 - 宠物] 模型资源 " + modelID + " 载入失败!"); + }*/ + String summonSkill = petData.getSummonSkill(); + if (!"default".equalsIgnoreCase(summonSkill)) { + Bukkit.getScheduler().runTaskLater(PetMain.inst(), () -> { + if (wolf.isRemoved() || wolf.getBukkitLivingEntity().isDead()) { + return; + } + MythicBukkit.inst().getAPIHelper().castSkill(wolf.getBukkitLivingEntity(), summonSkill, wolf.getBukkitLivingEntity().getLocation()); + player.sendMessage("[调试 - 宠物技能] 触发条件: onSpawn 触发技能: "+summonSkill); + }, 2L); + } + // 设置宠物召唤状态 + MessageUtil.sendMessageKey(player, "pet_summons_success", Sound.UI_BUTTON_CLICK); + // 发布事件 + PetCallEvent event = new PetCallEvent(player, wolf.getBukkitEntity().getCustomName(), wolf.getUUID()); + Bukkit.getPluginManager().callEvent(event); + } + + public void carryPetStackData(Player player) { + String carryCdKey = "carryPetStackCD"; + UUID uuid = player.getUniqueId(); + if (CDTimeAPI.isCD(uuid, carryCdKey)) { + MessageUtil.sendMessageKey(player, "system_coolingDown", Sound.ENTITY_VILLAGER_NO); + return; + } + CDTimeAPI.setPlayerCD(uuid, carryCdKey, 1000 * 5); + String playerName = player.getName(); + PlayerData playerData = getPlayerData(playerName); + if (playerData.isPetStackNull()) { + ItemStack stack = player.getInventory().getItemInMainHand(); + if (DemonAPI.itemIsNull(stack)) { + MessageUtil.sendMessageKey(player, "error_empty_hand", Sound.ENTITY_VILLAGER_NO); + return; + } + NBTItem nbtItem = new NBTItem(stack); + if (!nbtItem.hasKey("petKey")) { + MessageUtil.sendMessageKey(player, "hand_not_pet_stack", Sound.ENTITY_VILLAGER_NO); + return; + } + player.getInventory().setItemInMainHand(null); + playerData.setPetStack(stack); + playerData.savePlayerData(); + String itemName = DemonAPI.getItemName(stack); + String message = Config.getLanguage("pet_stack_carry_success"); + MessageUtil.sendMessage(player, message.replace("{petName}", itemName), Sound.ENTITY_PLAYER_LEVELUP); + } else { + removePetCall(playerName); + playerData.refreshPetStack(); + ItemStack petStack = playerData.getPetStack(); + NBTItem nbtItem = new NBTItem(petStack); + if (!nbtItem.hasKey("petKey")) { + Bukkit.getConsoleSender().sendMessage("[日志 - 宠物] 未检测到宠物petKey的Nbt标识."); + } + player.getInventory().addItem(petStack); + playerData.setPetStack(DemonAPI.getErrItems()); + playerData.savePlayerData(); + MessageUtil.sendMessageKey(player, "pet_stack_carry_takeOut", Sound.ENTITY_PLAYER_LEVELUP); + } + } +} diff --git a/src/main/java/com/yaohun/petsystem/model/PetNbt.java b/src/main/java/com/yaohun/petsystem/model/PetNbt.java new file mode 100644 index 0000000..b156afa --- /dev/null +++ b/src/main/java/com/yaohun/petsystem/model/PetNbt.java @@ -0,0 +1,111 @@ +package com.yaohun.petsystem.model; + +import com.yaohun.petsystem.data.PetData; +import com.yaohun.petsystem.manage.LevelExpManager; +import com.yaohun.petsystem.manage.PetManager; +import de.tr7zw.nbtapi.NBTItem; +import org.bukkit.inventory.ItemStack; + +public class PetNbt { + + private String petKey; + public PetData petData; + public int level; + public int exp; + public int maxExp; + public int totalExp; + public int damage; + public int health; + public int maxHealth; + public int evolution; + public PetQuality quality; + + public PetNbt(PetData petData) { + this.petKey = petData.getFileName(); + this.petData = petData; + this.level = 1; + this.maxExp = LevelExpManager.getNeedExpValue(level); + this.exp = 0; + this.totalExp = 0; + this.damage = 7; + this.health = 20; + this.maxHealth = 20; + this.evolution = 1; + this.quality = PetQuality.random(); + } + + public PetNbt() { + } + + public void loadNbtData(NBTItem nbtItem) { + petKey = nbtItem.getString("petKey"); + petData = PetManager.getPetData(petKey); + level = nbtItem.getInteger("level"); + exp = nbtItem.getInteger("exp"); + totalExp = nbtItem.getInteger("totalExp"); + maxExp = LevelExpManager.getNeedExpValue(level); + damage = nbtItem.getInteger("damage"); + health = nbtItem.getInteger("health"); + maxHealth = nbtItem.getInteger("maxHealth"); + evolution = nbtItem.getInteger("evolution"); + int qualityFangs = nbtItem.getInteger("quality_fangs"); + int qualitySurvive = nbtItem.getInteger("quality_survive"); + int qualityEfficiency = nbtItem.getInteger("quality_efficiency"); + quality = new PetQuality(qualityFangs, qualitySurvive, qualityEfficiency); + } + + public ItemStack saveIoItem(ItemStack stack) { + NBTItem nbtItem = new NBTItem(stack); + nbtItem.setString("petKey", petKey); + nbtItem.setInteger("level", level); + nbtItem.setInteger("exp", exp); + nbtItem.setInteger("totalExp", totalExp); + nbtItem.setInteger("maxExp", LevelExpManager.getNeedExpValue(level)); + nbtItem.setInteger("damage", damage); + nbtItem.setInteger("health", health); + nbtItem.setInteger("maxHealth", maxHealth); + nbtItem.setInteger("evolution", evolution); + nbtItem.setInteger("quality_fangs", quality.getFangs()); + nbtItem.setInteger("quality_survive", quality.getSurvive()); + nbtItem.setInteger("quality_efficiency", quality.getEfficiency()); + return nbtItem.getItem(); + } + + public void setLevel(int level) { + this.level = level; + } + + public void setExp(int exp) { + this.exp = exp; + } + + public void setTotalExp(int totalExp) { + this.totalExp = totalExp; + } + + public void setDamage(int damage) { + this.damage = damage; + } + + public void setHealth(int health) { + this.health = health; + } + + public void setMaxHealth(int maxHealth) { + this.maxHealth = maxHealth; + } + + public void setEvolution(int evolution) { + this.evolution = evolution; + } + + public void setQuality(String qualityType, int value) { + if ("quality_fangs".equals(qualityType)) { + this.quality.setFangs(value); + } else if ("quality_survive".equals(qualityType)) { + this.quality.setSurvive(value); + } else if ("quality_efficiency".equals(qualityType)) { + this.quality.setEfficiency(value); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/yaohun/petsystem/model/PetQuality.java b/src/main/java/com/yaohun/petsystem/model/PetQuality.java new file mode 100644 index 0000000..5abfcf6 --- /dev/null +++ b/src/main/java/com/yaohun/petsystem/model/PetQuality.java @@ -0,0 +1,49 @@ +package com.yaohun.petsystem.model; + +import me.Demon.DemonPlugin.Util.RandomUtil; + +public class PetQuality { + + private int fangs; + + private int survive; + + private int efficiency; + + public PetQuality(int fangs, int survive, int efficiency) { + this.fangs = fangs; + this.survive = survive; + this.efficiency = efficiency; + } + + public static PetQuality random() { + int fangs = RandomUtil.getRandomInt(1, 100); + int survive = RandomUtil.getRandomInt(1, 100); + int efficiency = RandomUtil.getRandomInt(1, 100); + return new PetQuality(fangs, survive, efficiency); + } + + public int getFangs() { + return fangs; + } + + public void setFangs(int fangs) { + this.fangs = fangs; + } + + public int getSurvive() { + return survive; + } + + public void setSurvive(int survive) { + this.survive = survive; + } + + public int getEfficiency() { + return efficiency; + } + + public void setEfficiency(int efficiency) { + this.efficiency = efficiency; + } +} \ No newline at end of file diff --git a/src/main/java/com/yaohun/petsystem/util/MessageUtil.java b/src/main/java/com/yaohun/petsystem/util/MessageUtil.java new file mode 100644 index 0000000..2d67c53 --- /dev/null +++ b/src/main/java/com/yaohun/petsystem/util/MessageUtil.java @@ -0,0 +1,78 @@ +package com.yaohun.petsystem.util; + +import com.yaohun.petsystem.PetMain; +import com.yaohun.petsystem.config.Config; +import org.bukkit.Sound; +import org.bukkit.command.CommandSender; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Player; + +import java.io.File; +import java.util.HashMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class MessageUtil { + + private static HashMap languageMap = new HashMap<>(); + + public static HashMap getLanguageMap() { + return languageMap; + } + + public static void init(PetMain plugin) { + File file = new File(plugin.getDataFolder(), "lang.yml"); + if (!file.exists()) { + plugin.saveResource("lang.yml", false); + } + FileConfiguration config = YamlConfiguration.loadConfiguration(file); + for (String key : config.getKeys(false)) { + ConfigurationSection section = config.getConfigurationSection(key); + for (String key2 : section.getKeys(false)) { + String message = section.getString(key2).replace("&", "§"); + languageMap.put(key2, message); + } + } + } + + public static void sendMessageKey(CommandSender sender, String key, Sound sound) { + String message = Config.getLanguage(key); + sender.sendMessage(convertHexColor(message)); + if (sender instanceof Player) { + Player player = (Player) sender; + player.playSound(player.getLocation(), sound, 0.8f, 1.2f); + } + } + + public static void sendMessage(CommandSender sender, String message, Sound sound) { + message = convertHexColor(message); + sender.sendMessage(message); + if (sender instanceof Player) { + Player player = (Player) sender; + player.playSound(player.getLocation(), sound, 0.8f, 1.2f); + } + } + + public static String convertHexColor(String text) { + if (text == null) return ""; + + // 匹配形如 #FFFFFF 的十六进制颜色代码 + Pattern pattern = Pattern.compile("#[A-Fa-f0-9]{6}"); + Matcher matcher = pattern.matcher(text); + StringBuffer buffer = new StringBuffer(); + // 去掉# + while (matcher.find()) { + String hex = matcher.group().substring(1); + StringBuilder colorBuilder = new StringBuilder("§x"); + for (char c : hex.toCharArray()) { + colorBuilder.append('§').append(c); + } + matcher.appendReplacement(buffer, colorBuilder.toString()); + } + matcher.appendTail(buffer); + return buffer.toString(); + } + +} diff --git a/src/main/java/com/yaohun/petsystem/util/PetUtil.java b/src/main/java/com/yaohun/petsystem/util/PetUtil.java new file mode 100644 index 0000000..025cf8c --- /dev/null +++ b/src/main/java/com/yaohun/petsystem/util/PetUtil.java @@ -0,0 +1,178 @@ +package com.yaohun.petsystem.util; + +import com.yaohun.petsystem.PetMain; +import com.yaohun.petsystem.config.Config; +import com.yaohun.petsystem.data.PlayerData; +import com.yaohun.petsystem.manage.LevelExpManager; +import com.yaohun.petsystem.manage.PlayerManager; +import com.yaohun.petsystem.model.PetNbt; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextDecoration; +import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; +import org.bukkit.entity.Wolf; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +public class PetUtil { + + public static ItemStack refreshPetStackData(PetNbt petNbt) { + return getPetStackDefault(petNbt); + } + + public static ItemStack getPetStackDefault(PetNbt petNbt) { + // 获取相关默认参数 + String petName = petNbt.petData.getPetName(); + ItemStack stack = Config.getPetStackTemplate(); + ItemMeta meta = stack.getItemMeta(); + // 获取当前显示名 + String templateName = "{name}"; + if (meta.hasDisplayName()) { + Component comp = meta.displayName(); + if (comp != null) { + // 将组件转换为普通字符串 + templateName = PlainTextComponentSerializer.plainText().serialize(comp); + } + } + // 替换占位符 + String replaced = templateName.replace("{name}", petName); + // 正确方式:直接用 Adventure Component 设置显示名 + // 设置新的显示名(新版 Adventure API) + meta.displayName(Component.text(replaced) + .color(NamedTextColor.GREEN) + .decoration(TextDecoration.ITALIC, false)); + + List newLore = new ArrayList<>(); + List rawLore = meta.getLore(); + if (rawLore == null) { + rawLore = new ArrayList<>(); + } + + for (String line : rawLore) { + String string = line + .replace("{name}", petName) + .replace("{fangs}", String.valueOf(petNbt.quality.getFangs())) + .replace("{survive}", String.valueOf(petNbt.quality.getSurvive())) + .replace("{efficiency}", String.valueOf(petNbt.quality.getEfficiency())) + .replace("{level}", String.valueOf(petNbt.level)) + .replace("{exp}", String.valueOf(petNbt.exp)) + .replace("{maxExp}", String.valueOf(LevelExpManager.getNeedExpValue(petNbt.level))) + .replace("{damage}", String.valueOf(petNbt.damage)) + .replace("{health}", String.valueOf(petNbt.health)) + .replace("{maxHealth}", String.valueOf(petNbt.maxHealth)); + newLore.add(Component.text(MessageUtil.convertHexColor(string)).decoration(TextDecoration.ITALIC, false)); + } + meta.lore(newLore); + meta.setCustomModelData(petNbt.petData.getCustomIconModelId()); + stack.setItemMeta(meta); + return stack; + } + + public static ItemStack getPetGuiStack(PlayerData playerData, String guiStackID) { + // 1.获取玩家数据 + PetNbt petNbt = playerData.getPetNbt(); + ItemStack petStack = playerData.getPetStack(); + + ItemStack stack = Config.getItemStack(guiStackID); + ItemMeta meta = stack.getItemMeta(); + // 设置新的显示名(新版 Adventure API) + meta.displayName(Component.text(petStack.getItemMeta().getDisplayName()) + .color(NamedTextColor.GREEN) + .decoration(TextDecoration.ITALIC, false)); + + List newLore = new ArrayList<>(); + List rawLore = meta.getLore(); + if (rawLore == null) { + rawLore = new ArrayList<>(); + } + + String statsName = "休息中"; + PlayerManager playerManager = PetMain.getPlayerManager(); + if (playerManager.isPetCallExit(playerData.getPlayerName())) { + statsName = "出战中"; + } + for (String line : rawLore) { + String string = line + .replace("{stats}", statsName) + .replace("{fangs}", String.valueOf(petNbt.quality.getFangs())) + .replace("{survive}", String.valueOf(petNbt.quality.getSurvive())) + .replace("{efficiency}", String.valueOf(petNbt.quality.getEfficiency())) + .replace("{level}", String.valueOf(petNbt.level)) + .replace("{exp}", String.valueOf(petNbt.exp)) + .replace("{maxExp}", String.valueOf(LevelExpManager.getNeedExpValue(petNbt.level))) + .replace("{damage}", String.valueOf(petNbt.damage)) + .replace("{health}", String.valueOf(petNbt.health)) + .replace("{maxHealth}", String.valueOf(petNbt.maxHealth)); + newLore.add(Component.text(MessageUtil.convertHexColor(string)).decoration(TextDecoration.ITALIC, false)); + } + meta.lore(newLore); + meta.setCustomModelData(petNbt.petData.getCustomIconModelId()); + stack.setItemMeta(meta); + return stack; + } + + /*public static NameTag getNameBone(ModeledEntity model, String stringType) { + if (model == null) { + return null; + } + if (model.getModels().size() == 0) { + return null; + } + Optional opt = model.getModels().values().stream().findFirst(); + ActiveModel activeModel = null; + if (opt.isPresent()) { + activeModel = opt.get(); + } else { + return null; + } + ModelBone bone = activeModel.getBone(stringType) + .stream() + .filter(modelBone -> modelBone.getBoneBehavior(BoneBehaviorTypes.NAMETAG).orElse(null) != null) + .findFirst().orElse(null); + if (bone == null) { + return null; + } + NameTag nameTag = bone.getBoneBehavior(BoneBehaviorTypes.NAMETAG).orElse(null); + return nameTag; + }*/ + public static PlayerData updatePetNameTag(Wolf wolf) { + PlayerManager playerManager = PetMain.getPlayerManager(); + if (wolf == null || wolf.isDead()) { + return null; + } + UUID petUuid = wolf.getUniqueId(); + String ownerName = playerManager.getPetOwnerName(petUuid); + if (ownerName == null) { + return null; + } + PlayerData playerData = playerManager.getPlayerData(ownerName); + // 判断玩家是否召唤宠物 + if (playerManager.isPetCallExit(ownerName)) { + /*// 获取宠物的的模型文件 + ModeledEntity modeledEntity = playerManager.getPetEntityModel(petUuid); + if (modeledEntity == null) { + return null; + } + int petLevel = playerData.getPetNbt().level; + double healthNow = wolf.getHealth(); + double healthMax = wolf.getMaxHealth(); + // 计算获得血量百分比 + int percent = (int) Math.round((healthNow / healthMax) * 100.0); + String levelText = Config.getLanguage("pet_level_tag") + .replace("{level}", Config.getLevelChar(petLevel)) + .replace("{health}", Config.getHealthChar(percent)); + NameTag levelTag = PetUtil.getNameBone(modeledEntity, "level"); + if (levelTag != null) { + levelTag.setString(levelText); + levelTag.setVisible(true); + }*/ + return playerData; + } + return null; + } +} diff --git a/src/main/java/com/yaohun/petsystem/util/model/BetterModelUtil.java b/src/main/java/com/yaohun/petsystem/util/model/BetterModelUtil.java new file mode 100644 index 0000000..716e9d0 --- /dev/null +++ b/src/main/java/com/yaohun/petsystem/util/model/BetterModelUtil.java @@ -0,0 +1,81 @@ +package com.yaohun.petsystem.util.model; + +import kr.toxicity.model.api.BetterModel; +import kr.toxicity.model.api.bukkit.platform.BukkitAdapter; +import kr.toxicity.model.api.data.renderer.ModelRenderer; +import kr.toxicity.model.api.tracker.EntityTracker; +import kr.toxicity.model.api.tracker.Tracker; +import kr.toxicity.model.api.tracker.TrackerModifier; +import org.bukkit.Bukkit; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; + +public class BetterModelUtil { + + public static Tracker spawnModel(Player owner, Entity entity, String modelId) { + if (entity == null || entity.isDead()) { + Bukkit.getLogger().warning("宠物模型载入失败: 实体不存在"); + return null; + } + if (modelId == null || modelId.isBlank()) { + Bukkit.getLogger().warning("宠物模型载入失败: 模型ID为空"); + return null; + } + + ModelRenderer renderer = BetterModel.modelOrNull(modelId); + if (renderer == null) { + Bukkit.getLogger().warning("宠物模型不存在: " + modelId); + return null; + } + + EntityTracker tracker = renderer.create( + BukkitAdapter.adapt(entity), + getPetTrackerModifier(), + BetterModelUtil::refreshModelTracker + ); + + refreshModelTracker(tracker); + spawnVisiblePlayers(tracker, entity); + Bukkit.getLogger().info("宠物模型载入成功: 玩家=" + owner.getName() + ", 模型=" + modelId + ", 实体=" + entity.getUniqueId()); + + return tracker; + } + + public static void spawnVisiblePlayers(Tracker tracker, Entity entity) { + if (tracker == null || tracker.isClosed() || entity == null) { + return; + } + for (Player viewer : Bukkit.getOnlinePlayers()) { + if (!viewer.isOnline()) { + continue; + } + if (!viewer.getWorld().equals(entity.getWorld())) { + continue; + } + if (tracker instanceof EntityTracker entityTracker) { + entityTracker.registry().spawnIfNotSpawned(BukkitAdapter.adapt(viewer)); + } else { + tracker.show(BukkitAdapter.adapt(viewer)); + } + } + } + + public static void refreshModelTracker(Tracker tracker) { + if (tracker == null || tracker.isClosed()) { + return; + } + if (tracker instanceof EntityTracker entityTracker) { + entityTracker.updateBaseEntity(); + entityTracker.refresh(); + } + tracker.forceUpdate(true); + } + + private static TrackerModifier getPetTrackerModifier() { + return TrackerModifier.DEFAULT.toBuilder() + .sightTrace(false) + .damageAnimation(false) + .damageTint(false) + .build(); + } +} diff --git a/src/main/java/com/yaohun/petsystem/util/wolf/CustomWolf.java b/src/main/java/com/yaohun/petsystem/util/wolf/CustomWolf.java new file mode 100644 index 0000000..8e70336 --- /dev/null +++ b/src/main/java/com/yaohun/petsystem/util/wolf/CustomWolf.java @@ -0,0 +1,202 @@ +package com.yaohun.petsystem.util.wolf; + +import com.yaohun.petsystem.model.PetNbt; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.animal.wolf.Wolf; +import net.minecraft.world.level.Level; +import net.minecraft.world.phys.Vec3; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.attribute.Attribute; +import org.bukkit.attribute.AttributeInstance; +import org.bukkit.block.Block; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; + +import java.util.UUID; + +/** + * 自定义的狼实体类,用于实现“跟随玩家”的宠物功能。 + * 继承原版 NMS Wolf 实体,通过 tick() 方法控制移动与传送。 + */ +public class CustomWolf extends Wolf { + + private static final double STOP_DISTANCE_SQ = 9.0D; + private static final double TELEPORT_DISTANCE_SQ = 144.0D; + private static final double LOOK_DISTANCE_SQ = 256.0D; + private static final double FOLLOW_SPEED = 1.2D; + private static final double DEFAULT_MOVEMENT_SPEED = 0.4D; + private static final int PATH_RECALC_TICKS = 10; + + private final UUID ownerUuid; + + private int timeToRecalcPath = 0; + + /** + * 构造方法:初始化宠物实体 + * + * @param world NMS 世界对象 + * @param owner 宠物主人(Bukkit Player) + */ + public CustomWolf(Level world, Player owner) { + super(EntityType.WOLF, world); + this.ownerUuid = owner.getUniqueId(); + } + + /** + * 每 tick 执行的逻辑,控制宠物的移动与状态 + */ + @Override + public void tick() { + super.tick(); + + Player owner = Bukkit.getPlayer(ownerUuid); + if (owner == null || !owner.isOnline()) { + this.discard(); + return; + } + if (owner.isDead()) { + this.getNavigation().stop(); + return; + } + if (!owner.getWorld().equals(this.getBukkitEntity().getWorld())) { + this.getNavigation().stop(); + teleportToOwnerLocation(owner); + return; + } + + net.minecraft.world.entity.player.Player nmsOwner = ((CraftPlayer) owner).getHandle(); + + double distanceSq = this.distanceToSqr(nmsOwner); + + if (distanceSq < STOP_DISTANCE_SQ) { + this.getNavigation().stop(); + return; + } + + boolean shouldTeleport = distanceSq > TELEPORT_DISTANCE_SQ; + + if (!shouldTeleport && distanceSq <= LOOK_DISTANCE_SQ) { + this.getLookControl().setLookAt(nmsOwner, 10.0F, this.getMaxHeadXRot()); + } + + if (--this.timeToRecalcPath <= 0) { + this.timeToRecalcPath = PATH_RECALC_TICKS; + + if (shouldTeleport) { + if (!tryTeleportNearOwner(nmsOwner)) { + this.getNavigation().moveTo(nmsOwner, FOLLOW_SPEED); + } + } else { + this.getNavigation().moveTo(nmsOwner, FOLLOW_SPEED); + } + } + } + + /** + * 当宠物距离主人太远时,尝试在主人周围随机传送 + * + * @param nmsOwner 主人 NMS 实体 + */ + private boolean tryTeleportNearOwner(Entity nmsOwner) { + Vec3 pos = nmsOwner.position(); + + for (int i = 0; i < 10; i++) { + int x = (int) Math.floor(pos.x) + (random.nextInt(7) - 3); + int y = (int) Math.floor(pos.y); + int z = (int) Math.floor(pos.z) + (random.nextInt(7) - 3); + + Location location = new Location(getBukkitEntity().getWorld(), x + 0.5D, y, z + 0.5D); + if (isSafeTeleportLocation(location)) { + this.teleportTo(location.getX(), location.getY(), location.getZ()); + this.getNavigation().stop(); + return true; + } + } + return false; + } + + private void teleportToOwnerLocation(Player owner) { + Location location = owner.getLocation(); + if (isSafeTeleportLocation(location)) { + this.getBukkitEntity().teleport(location); + } + } + + private boolean isSafeTeleportLocation(Location location) { + Block feet = location.getBlock(); + Block head = feet.getRelative(0, 1, 0); + Block ground = feet.getRelative(0, -1, 0); + if (!feet.isPassable() || !head.isPassable() || !ground.getType().isSolid()) { + return false; + } + if (!location.getWorld().equals(getBukkitEntity().getWorld())) { + return true; + } + return this.level().noCollision(this, this.getBoundingBox().move( + location.getX() - this.getX(), + location.getY() - this.getY(), + location.getZ() - this.getZ() + )); + } + + /** + * 重写受伤方法,防止主人伤害到自己的宠物 + * + * @param source 伤害来源 + * @param amount 伤害数值 + * @return 是否成功造成伤害 + */ + @Override + public boolean hurtServer(ServerLevel level, DamageSource source, float amount) { + Entity entity = source.getEntity(); + + if (entity != null && entity.getUUID().equals(ownerUuid)) { + return false; + } + return super.hurtServer(level, source, amount); + } + + /** + * 静态方法:用于在 Bukkit 世界中生成并注册一只 CustomWolf + * + * @param owner 宠物主人 + * @return 新生成的 CustomWolf 实例 + */ + public static CustomWolf spawnCustomWolf(Player owner, PetNbt petNbt) { + CraftWorld world = (CraftWorld) owner.getWorld(); + Level nmsWorld = world.getHandle(); + + CustomWolf wolf = new CustomWolf(nmsWorld, owner); + + wolf.setSilent(true); + wolf.setOrderedToSit(false); + wolf.setPos(owner.getLocation().getX(), owner.getLocation().getY(), owner.getLocation().getZ()); + + LivingEntity livingEntity = wolf.getBukkitLivingEntity(); + double maxHealth = Math.max(1.0D, petNbt.maxHealth); + double health = Math.max(1.0D, Math.min(petNbt.health, maxHealth)); + double damage = Math.max(0.0D, petNbt.damage); + + setAttribute(livingEntity, Attribute.MAX_HEALTH, maxHealth); + setAttribute(livingEntity, Attribute.MOVEMENT_SPEED, DEFAULT_MOVEMENT_SPEED); + setAttribute(livingEntity, Attribute.ATTACK_DAMAGE, damage); + livingEntity.setHealth(health); + + nmsWorld.addFreshEntity(wolf); + + return wolf; + } + + private static void setAttribute(LivingEntity entity, Attribute attribute, double value) { + AttributeInstance instance = entity.getAttribute(attribute); + if (instance != null) { + instance.setBaseValue(value); + } + } +} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml new file mode 100644 index 0000000..75039dc --- /dev/null +++ b/src/main/resources/config.yml @@ -0,0 +1,31 @@ +NeedExpSettings: + 1: 10000 + 2: 10000 + 3: 10000 + 4: 10000 + 5: 10000 + 6: 10000 + 7: 10000 + 8: 10000 + 9: 10000 + 10: 10000 + 11: 10000 + 12: 10000 + 13: 10000 + 14: 10000 + 15: 10000 + 16: 10000 + 17: 10000 + 18: 10000 + 19: 10000 + 20: 10000 + 21: 10000 + 22: 10000 + 23: 10000 + 24: 10000 + 25: 10000 + 26: 10000 + 27: 10000 + 28: 10000 + 29: 10000 + 30: -1 \ No newline at end of file diff --git a/src/main/resources/lang.yml b/src/main/resources/lang.yml new file mode 100644 index 0000000..e69de29 diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml new file mode 100644 index 0000000..66b3172 --- /dev/null +++ b/src/main/resources/plugin.yml @@ -0,0 +1,6 @@ +name: AuPet +main: com.yaohun.petsystem.PetMain +version: 1.0.1 +api-version: 1.21 +commands: + apet: diff --git a/src/src/main/java/com/yaohun/petsystem/PetMain.java b/src/src/main/java/com/yaohun/petsystem/PetMain.java new file mode 100644 index 0000000..d9e9ec9 --- /dev/null +++ b/src/src/main/java/com/yaohun/petsystem/PetMain.java @@ -0,0 +1,265 @@ +package com.yaohun.petsystem; + +import com.yaohun.petsystem.api.PetExpAPI; +import com.yaohun.petsystem.config.Config; +import com.yaohun.petsystem.data.PetData; +import com.yaohun.petsystem.data.PetSQL; +import com.yaohun.petsystem.data.PlayerData; +import com.yaohun.petsystem.data.SkillData; +import com.yaohun.petsystem.gui.CarryGui; +import com.yaohun.petsystem.gui.FeedingGui; +import com.yaohun.petsystem.gui.MainGui; +import com.yaohun.petsystem.listener.PetSkillsListener; +import com.yaohun.petsystem.listener.PlayerListener; +import com.yaohun.petsystem.listener.RePetNameListener; +import com.yaohun.petsystem.manage.PetManager; +import com.yaohun.petsystem.manage.PlayerManager; +import com.yaohun.petsystem.model.PetNbt; +import com.yaohun.petsystem.util.MessageUtil; +import com.yaohun.petsystem.util.PetUtil; +import de.tr7zw.nbtapi.NBTItem; +import io.lumine.mythic.bukkit.MythicBukkit; +import me.Demon.DemonPlugin.DemonAPI; +import me.Demon.DemonPlugin.Util.CDTimeAPI; +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.plugin.java.JavaPlugin; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; + +public class PetMain extends JavaPlugin { + + private static PetMain instance; + private static PlayerManager playerManager; + + + @Override + public void onEnable() { + instance = this; + saveDefaultConfig(); + + PetSQL.init(); + + MessageUtil.init(this); + Config.reloadConfig(this); + PetManager.reloadPetManager(); + playerManager = new PlayerManager(); + + getServer().getPluginManager().registerEvents(new MainGui(), this); + getServer().getPluginManager().registerEvents(new FeedingGui(), this); + getServer().getPluginManager().registerEvents(new CarryGui(), this); + + getServer().getPluginManager().registerEvents(new PlayerListener(), this); + getServer().getPluginManager().registerEvents(new RePetNameListener(), this); + + getServer().getPluginManager().registerEvents(new PetSkillsListener(), this); + } + + @Override + public void onDisable() { + getPlayerManager().closeSaveAllData(); + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (!sender.hasPermission("admin.use")) { + if(args.length == 0){ + if (sender instanceof Player player) { + MainGui.openGui(player); + } + return true; + } + return true; + } + String prefix = "§f[§c灵宠§f] §a"; + if (args.length == 0) { + sender.sendMessage("§r"); + sender.sendMessage("§e==----- ======= §6宠物系统 §e======= -----=="); + sender.sendMessage("§2/" + label + " call §f- §2召唤宠物"); + sender.sendMessage("§2/" + label + " carry §f- §2携带手持宠物"); + if (sender.isOp()) { + sender.sendMessage("§2/" + label + " addExp §e[经验] §b<玩家> §7true §f- §2给予玩家宠物"); + sender.sendMessage("§2/" + label + " give §e[宠物] §b<玩家> §f- §2给予玩家宠物"); + sender.sendMessage("§2/" + label + " reload §f- §2重载配置文件"); + } + sender.sendMessage("§e==----- ======= §6宠物系统 §e======= -----=="); + sender.sendMessage("§r"); + return true; + } + if ("reload".equalsIgnoreCase(args[0])) { + MessageUtil.init(this); + Config.reloadConfig(this); + PetManager.reloadPetManager(); + sender.sendMessage(prefix + "配置文件已重载完成."); + return true; + } + if ("open".equalsIgnoreCase(args[0])) { + if (sender instanceof Player player) { + MainGui.openGui(player); + } + return true; + } + if ("test".equalsIgnoreCase(args[0])) { + if (sender instanceof Player player) { + // 判断玩家是否携带宠物 + String playerName = player.getName(); + PlayerManager playerManager = PetMain.getPlayerManager(); + // 获取宠物相关数据 + PlayerData playerData = playerManager.getPlayerData(playerName); + if(playerData.isPetStackNull()){ + return true; + } + PetData petData = playerData.getPetNbt().petData; + LinkedHashMap skillDataMap = petData.getSkillDataMap(); + for(SkillData skillData : skillDataMap.values()){ + String cdKey = "skill_"+skillData.getSkillName(); + if ("onAttack".equalsIgnoreCase(skillData.getTrigger())) { + UUID uuid = player.getUniqueId(); + if(!CDTimeAPI.isCD(uuid,cdKey)) { + long cooldown = skillData.getCoolDown(); + CDTimeAPI.setPlayerCD(uuid, cdKey, cooldown); + String mmSkill = skillData.getMmSkill(); + // 执行技能 + MythicBukkit.inst().getAPIHelper().castSkill( + player, + mmSkill, + player.getLocation() + ); + player.sendMessage("[调试 - 宠物技能] 触发条件: onAttack 触发技能: "+mmSkill); + break; + } else { + sender.sendMessage("# 拦截 冷却中..."); + } + } + } + } + return true; + } + if ("carry".equalsIgnoreCase(args[0])) { + String playerName = sender.getName(); + if (args.length >= 2) { + playerName = args[1]; + } + Player player = Bukkit.getPlayer(playerName); + if (player == null || !player.isOnline()) { + sender.sendMessage(prefix + "玩家 " + playerName + " 不在线."); + return true; + } + getPlayerManager().carryPetStackData(player); + return true; + } + if ("call".equalsIgnoreCase(args[0])) { + String playerName = sender.getName(); + if (args.length >= 2) { + playerName = args[1]; + } + Player player = Bukkit.getPlayer(playerName); + if (player == null || !player.isOnline()) { + sender.sendMessage(prefix + "玩家 " + playerName + " 不在线."); + return true; + } + if (playerManager.isPetCallExit(playerName)) { + playerManager.removePetCall(playerName); + sender.sendMessage(prefix + "已收回 §6" + playerName + " §a的宠物."); + } else { + playerManager.callPet(player); + sender.sendMessage(prefix + "已召唤 §6" + playerName + " §a的宠物."); + } + return true; + } + if ("addexp".equalsIgnoreCase(args[0])) { + if (args.length == 1) { + sender.sendMessage(prefix + "缺少数值参数."); + return true; + } + int exp = Integer.parseInt(args[1]); + // 获取玩家数据 + String playerName = sender.getName(); + if (args.length >= 3) { + playerName = args[2]; + } + Player player = Bukkit.getPlayer(playerName); + if (player == null || !player.isOnline()) { + sender.sendMessage(prefix + "玩家 " + playerName + " 不在线."); + return true; + } + if (args.length >= 4) { + ItemStack stack = player.getInventory().getItemInMainHand(); + if (DemonAPI.itemIsNull(stack) || DemonAPI.itemIsLore(stack)) { + sender.sendMessage(prefix + "执行失败,你需要将灵宠拿在手中."); + return true; + } + NBTItem nbtItem = new NBTItem(stack); + if (!nbtItem.hasKey("petKey")) { + sender.sendMessage(prefix + "执行失败,你需要将灵宠拿在手中."); + return true; + } + PetNbt petNbt = PetExpAPI.addExp(playerName, stack, exp); + ItemStack petStack = PetUtil.refreshPetStackData(petNbt); + player.getInventory().setItemInMainHand(petNbt.saveIoItem(petStack)); + sender.sendMessage(prefix + "给予 §6手持灵宠 §a经验: §d+" + exp); + } else { + PetExpAPI.addExp(player, exp); + sender.sendMessage(prefix + "给予 §6" + playerName + " §a的灵宠经验: §d+" + exp); + } + return true; + } + if ("give".equalsIgnoreCase(args[0])) { + if (args.length == 1) { + sender.sendMessage(prefix + "缺少宠物参数."); + return true; + } + String petKey = args[1]; + PetData petData = PetManager.getPetData(petKey); + if (petData == null) { + sender.sendMessage(prefix + "未找到 " + petKey + " 的相关配置."); + return true; + } + String playerName = sender.getName(); + if (args.length >= 3) { + playerName = args[2]; + } + Player player = Bukkit.getPlayer(playerName); + if (player == null) { + sender.sendMessage(prefix + "未找到玩家 " + playerName); + return true; + } + PetNbt petNbt = new PetNbt(petData); + ItemStack petStack = PetUtil.getPetStackDefault(petNbt); + player.getInventory().addItem(petNbt.saveIoItem(petStack)); + DemonAPI.sendMessage(sender, "玩家: §e" + playerName + " §a宠物: §6" + petKey + " §r[背包]"); + return true; + } + return false; + } + + @Override + public List onTabComplete(CommandSender sender, Command command, String label, String[] args) { + if (sender.isOp()) { + if (args.length == 2 && "give".equalsIgnoreCase(args[0])) { + return PetManager.getPetDataMap().keySet().stream() + .filter(s -> s.toLowerCase().startsWith(args[1].toLowerCase())) + .collect(Collectors.toList()); + } + } + // 如果不符合上述条件,则返回空列表 + return Collections.emptyList(); + } + + public static PetMain inst() { + return instance; + } + + public static PlayerManager getPlayerManager() { + return playerManager; + } + +} \ No newline at end of file diff --git a/src/src/main/java/com/yaohun/petsystem/api/PetExpAPI.java b/src/src/main/java/com/yaohun/petsystem/api/PetExpAPI.java new file mode 100644 index 0000000..1126330 --- /dev/null +++ b/src/src/main/java/com/yaohun/petsystem/api/PetExpAPI.java @@ -0,0 +1,122 @@ +package com.yaohun.petsystem.api; + +import com.yaohun.petsystem.PetMain; +import com.yaohun.petsystem.data.PetData; +import com.yaohun.petsystem.data.PlayerData; +import com.yaohun.petsystem.manage.LevelExpManager; +import com.yaohun.petsystem.manage.PetManager; +import com.yaohun.petsystem.manage.PlayerManager; +import com.yaohun.petsystem.model.PetNbt; +import de.tr7zw.nbtapi.NBTItem; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +public class PetExpAPI { + + public static void addExp(Player player, int addExp) { + String playerName = player.getName(); + PlayerManager playerManager = PetMain.getPlayerManager(); + PlayerData playerData = playerManager.getPlayerData(playerName); + if (playerData.isPetStackNull()) { + System.out.println("[日志 - 灵宠] 玩家 " + playerName + " 尚未携带宠物."); + return; + } + PetNbt petNbt = playerData.getPetNbt(); + String petKey = petNbt.petData.getFileName(); + PetData petData = PetManager.getPetData(petKey); + if (petData == null) { + System.out.println("[日志 - 灵宠] 未添加 " + petKey + " 的宠物配置."); + return; + } + int petLevel = petNbt.level; + if (petLevel >= petData.getMaxLevel()) { + return; + } + // 进化阶段判断 + int evolution = petNbt.evolution; + if (evolution == 1) { + if (petLevel >= 20) { + return; + } + } else if (evolution == 2) { + if (petLevel >= 40) { + return; + } + } else if (evolution == 3) { + if (petLevel >= 60) { + return; + } + } else if (evolution == 4) { + if (petLevel >= 70) { + return; + } + } else if (evolution == 5) { + if (petLevel >= 75) { + return; + } + } + int needExp = LevelExpManager.getNeedExpValue(petLevel); + petNbt.setTotalExp(petNbt.totalExp + addExp); + + int nowExp = petNbt.exp; + addExp = nowExp + addExp; + while (addExp >= needExp) { + if (petLevel < 0) { + break; + } + addExp -= needExp; + petLevel++; + needExp = LevelExpManager.getNeedExpValue(petLevel); + bcmupLevelEvant(playerName, petLevel); + playerData.refreshPetStack(); + } + petNbt.setExp(addExp); + petNbt.setLevel(petLevel); + playerData.savePlayerData(); + } + + public static PetNbt addExp(String playerName, ItemStack stack, int addExp) { + NBTItem nbtItem = new NBTItem(stack); + String petKey = nbtItem.getString("petKey"); + PetData petData = PetManager.getPetData(petKey); + if (petData == null) { + System.out.println("[日志 - 灵宠] 未添加 " + petKey + " 的宠物配置."); + return null; + } + PetNbt petNbt = new PetNbt(); + petNbt.loadNbtData(nbtItem); + int petLevel = petNbt.level; + if (petLevel >= petData.getMaxLevel()) { + return petNbt; + } + int needExp = LevelExpManager.getNeedExpValue(petLevel); + petNbt.setTotalExp(petNbt.totalExp + addExp); + + int nowExp = petNbt.exp; + addExp = nowExp + addExp; + while (addExp >= needExp) { + if (petLevel < 0) { + break; + } + addExp -= needExp; + petLevel++; + needExp = LevelExpManager.getNeedExpValue(petLevel); + bcmupLevelEvant(playerName, petLevel); + } + petNbt.setExp(addExp); + petNbt.setLevel(petLevel); + return petNbt; + } + + public static void bcmupLevelEvant(String playerName, int level) { + boolean butt = false; + if (level == 10 || level == 20 || level == 30 || level == 40 || level == 50 || + level == 60 || level == 70 || level == 80 || level == 90 || level == 100 + || level == 105 || level == 110 || level == 115 || level == 120 || level == 125) { + butt = true; + } + if (butt) { + // Bukkit.broadcastMessage("§f[§c§l公告§f] §a玩家§e"+playName+"§a本月器师手礼达到了 §7[§e§lLv."+level+"§7]"); + } + } +} \ No newline at end of file diff --git a/src/src/main/java/com/yaohun/petsystem/command/PetCommand.java b/src/src/main/java/com/yaohun/petsystem/command/PetCommand.java new file mode 100644 index 0000000..6572657 --- /dev/null +++ b/src/src/main/java/com/yaohun/petsystem/command/PetCommand.java @@ -0,0 +1,4 @@ +package com.yaohun.petsystem.command; + +public class PetCommand { +} diff --git a/src/src/main/java/com/yaohun/petsystem/config/Config.java b/src/src/main/java/com/yaohun/petsystem/config/Config.java new file mode 100644 index 0000000..5c44203 --- /dev/null +++ b/src/src/main/java/com/yaohun/petsystem/config/Config.java @@ -0,0 +1,141 @@ +package com.yaohun.petsystem.config; + +import com.yaohun.petsystem.PetMain; +import com.yaohun.petsystem.manage.LevelExpManager; +import com.yaohun.petsystem.util.MessageUtil; +import me.Demon.DemonPlugin.DemonAPI; +import me.Demon.DemonPlugin.Util.ColorUtil; +import me.Demon.DemonPlugin.data.GuiItemData; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +import java.util.*; + +public class Config { + + private static ItemStack petStackTemplate; + private static NavigableMap qualityShowMap = new TreeMap<>(); + private static HashMap guiItemDataMap = new HashMap<>(); + + public static void reloadConfig(PetMain plugin) { + plugin.reloadConfig(); + plugin.saveConfig(); + FileConfiguration config = plugin.getConfig(); + Bukkit.getConsoleSender().sendMessage("§6[宠物系统] §7参数设定:"); + LevelExpManager.reloadLevelManager(plugin); + loadPetStackTemplate(config); + loadGuiItemData(config); + loadQualityShowMap(config); + } + + private static void loadGuiItemData(FileConfiguration config) { + guiItemDataMap.clear(); + ConfigurationSection section = config.getConfigurationSection("GuiItemData"); + if (section == null) { + return; + } + for (String itemKey : section.getKeys(false)) { + guiItemDataMap.put(itemKey, new GuiItemData(itemKey, section)); + } + } + + private static void loadQualityShowMap(FileConfiguration config) { + ConfigurationSection section = config.getConfigurationSection("QualityShow"); + if (section == null) { + return; + } + for (String levelKey : section.getKeys(false)) { + int level = Integer.parseInt(levelKey); + String show = section.getString(levelKey).replace("&", "§"); + qualityShowMap.put(level, show); + } + Bukkit.getConsoleSender().sendMessage("- 资质稀有度: §f" + qualityShowMap.size() + "个"); + } + + private static void loadPetStackTemplate(FileConfiguration config) { + String stackType = config.getString("Pet_Stack_Template.type", "DIAMOND"); + Material material; + try { + material = Material.valueOf(stackType.toUpperCase()); + } catch (IllegalArgumentException e) { + Bukkit.getConsoleSender().sendMessage("- 宠物模板载入: §c失败"); + return; + } + String stackName = config.getString("Pet_Stack_Template.name", "§f宠物"); + List stringList = config.getStringList("Pet_Stack_Template.lore"); + List stackLore = ColorUtil.listcolor(stringList); + ItemStack itemStack = new ItemStack(material); + ItemMeta meta = itemStack.getItemMeta(); + meta.setDisplayName(stackName); + meta.setLore(stackLore); + itemStack.setItemMeta(meta); + petStackTemplate = itemStack; + + String itemName = DemonAPI.getItemName(itemStack.clone()); + Bukkit.getConsoleSender().sendMessage("- 宠物模板载入: §f" + itemName); + } + + public static ItemStack getPetStackTemplate() { + return petStackTemplate.clone(); + } + + public static String getLanguage(String key) { + HashMap hashMap = MessageUtil.getLanguageMap(); + if (hashMap.containsKey(key)) { + return hashMap.get(key); + } + return "缺少参数.§7" + key; + } + + public static ItemStack getItemStack(String itemKey) { + if (guiItemDataMap.containsKey(itemKey)) { + ItemStack itemStack = guiItemDataMap.get(itemKey).getItemStack().clone(); + if (itemStack.hasItemMeta()) { + ItemMeta meta = itemStack.getItemMeta(); + if (meta.hasLore()) { + List originalLore = meta.getLore(); + if (originalLore != null) { + // 创建新的lore列表进行处理 + List newLore = new ArrayList<>(); + for (String line : originalLore) { + // 应用颜色转换或其他处理逻辑 + newLore.add(DemonAPI.convertHexColor(line)); + } + // 设置处理后的新lore + meta.setLore(newLore); + itemStack.setItemMeta(meta); + } + } + } + return itemStack; + } + return DemonAPI.getErrItems(); + } + + + public static String getQualityShow(int value) { + Map.Entry entry = qualityShowMap.floorEntry(value); + return entry != null ? entry.getValue() : qualityShowMap.get(0); + } + + public static String getHealthChar(int percent) { + // 限制范围 [0,100] + percent = Math.max(0, Math.min(100, percent)); + // 取整到最近的 10 倍数,例如 87 → 80,93 → 90 + int step = (percent / 10) * 10; + // 处理 100 特殊情况 + if (percent == 100) { + step = 100; + } + return "§f"; + } + + public static String getLevelChar(int level) { + return "§f"; + } + +} \ No newline at end of file diff --git a/src/src/main/java/com/yaohun/petsystem/data/PetData.java b/src/src/main/java/com/yaohun/petsystem/data/PetData.java new file mode 100644 index 0000000..ae42937 --- /dev/null +++ b/src/src/main/java/com/yaohun/petsystem/data/PetData.java @@ -0,0 +1,86 @@ +package com.yaohun.petsystem.data; + +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.FileConfiguration; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; + +public class PetData { + private String fileName; + private String petName; + private int customIconModelId; + private int maxLevel; + private String modelId; + private List accessChannelsList; + private String summonSkill; + private String deathSkill; + private LinkedHashMap skillDataMap = new LinkedHashMap<>(); + + public PetData(String fileName, FileConfiguration config) { + this.fileName = fileName; + this.petName = config.getString("PetName"); + this.maxLevel = config.getInt("MaxLevel"); + this.modelId = config.getString("ModelId"); + this.customIconModelId = config.getInt("CustomIconModelId"); + this.accessChannelsList = config.getStringList("AccessChannels"); + this.summonSkill = config.getString("SummonSkill", "default"); + this.deathSkill = config.getString("DeathSkill", "default"); + loadSkillData(config); + } + + private void loadSkillData(FileConfiguration config) { + ConfigurationSection section = config.getConfigurationSection("Skills"); + if (section == null) { + return; + } + for (String key : section.getKeys(false)) { + SkillData skillData = new SkillData(key, section); + skillDataMap.put(key, skillData); + } + } + + public String getFileName() { + return fileName; + } + + public String getPetName() { + return petName; + } + + public int getMaxLevel() { + return maxLevel; + } + + public String getModelId() { + return modelId; + } + + public int getCustomIconModelId() { + return customIconModelId; + } + + public List getAccessChannelsList() { + return accessChannelsList; + } + + public String getSummonSkill() { + return summonSkill; + } + + public String getDeathSkill() { + return deathSkill; + } + + public LinkedHashMap getSkillDataMap() { + return skillDataMap; + } + + public SkillData getSkillData(String skillName) { + if (skillDataMap.containsKey(skillName)) { + return skillDataMap.get(skillName); + } + return null; + } +} \ No newline at end of file diff --git a/src/src/main/java/com/yaohun/petsystem/data/PetSQL.java b/src/src/main/java/com/yaohun/petsystem/data/PetSQL.java new file mode 100644 index 0000000..c6074b2 --- /dev/null +++ b/src/src/main/java/com/yaohun/petsystem/data/PetSQL.java @@ -0,0 +1,105 @@ +package com.yaohun.petsystem.data; + +import com.tianyu.datamanager.DataAPI; +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; +import org.bukkit.Bukkit; + +import java.sql.*; + +public class PetSQL { + + private static HikariDataSource dataSource; + private static String host; + private static String port; + private static String database; + private static String tables; + private static String username; + private static String password; + + // 初始化数据库连接和表结构 + public static void init() { + host = DataAPI.getMysql().getHost(); + port = DataAPI.getMysql().getPort(); + database = DataAPI.getMysql().getDatabase(); + tables = "aupets_data"; + username = DataAPI.getMysql().getUser(); + password = DataAPI.getMysql().getPassword(); + + setupDataSource(); // 初始化连接池 + initTable(); // 确保表结构已经创建 + } + + // 配置并初始化 HikariCP 连接池 + private static void setupDataSource() { + HikariConfig config = new HikariConfig(); + config.setDriverClassName("com.mysql.cj.jdbc.Driver"); + config.setJdbcUrl("jdbc:mysql://" + host + ":" + port + "/" + database + "?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&autoReconnect=true&useSSL=false"); + config.setUsername(username); + config.setPassword(password); + config.setMaximumPoolSize(3); + config.setMinimumIdle(1); + config.setIdleTimeout(300000L); + config.setMaxLifetime(0L); + dataSource = new HikariDataSource(config); + } + + // 初始化表结构(如果表不存在) + private static void initTable() { + String createTableSQL = "CREATE TABLE IF NOT EXISTS " + tables + " (" + + "PLAYERNAME VARCHAR(255) NOT NULL, " + + "PETSTACK TEXT NOT NULL, " + + "PRIMARY KEY (PLAYERNAME))"; + try (Connection conn = getConnection(); Statement stmt = conn.createStatement()) { + stmt.execute(createTableSQL); + } catch (SQLException e) { + Bukkit.getLogger().severe("初始化表结构失败: " + e.getMessage()); + } + } + + // 获取玩家宠物信息 + public static String getPetStack(String playerName) { + String query = "SELECT PETSTACK FROM " + tables + " WHERE PLAYERNAME = ? LIMIT 1"; + try (Connection conn = getConnection(); + PreparedStatement ps = conn.prepareStatement(query)) { + ps.setString(1, playerName); + try (ResultSet rs = ps.executeQuery()) { + if (rs.next()) { + return rs.getString("PETSTACK"); + } + } + } catch (SQLException e) { + Bukkit.getLogger().severe("获取字符串值失败: " + e.getMessage()); + } + return null; + } + + + // 保存数据 + public static void saveData(String playerName, String petStack) { + String sql = "INSERT INTO " + tables + " (PLAYERNAME, PETSTACK) " + + "VALUES (?, ?) " + "ON DUPLICATE KEY UPDATE PETSTACK = VALUES(PETSTACK)"; + try (Connection conn = getConnection(); + PreparedStatement ps = conn.prepareStatement(sql)) { + ps.setString(1, playerName); + ps.setString(2, petStack); + ps.executeUpdate(); + } catch (SQLException e) { + Bukkit.getLogger().severe("保存宠物数据失败: " + e.getMessage()); + } + } + + // 获取数据库连接 + private static Connection getConnection() throws SQLException { + return dataSource.getConnection(); + } + + // 关闭连接池 + public static void closeConnection() { + if (dataSource != null && !dataSource.isClosed()) { + dataSource.close(); + Bukkit.getConsoleSender().sendMessage("数据库连接已成功关闭。#1"); + } + } + +} \ No newline at end of file diff --git a/src/src/main/java/com/yaohun/petsystem/data/PlayerData.java b/src/src/main/java/com/yaohun/petsystem/data/PlayerData.java new file mode 100644 index 0000000..a6c999b --- /dev/null +++ b/src/src/main/java/com/yaohun/petsystem/data/PlayerData.java @@ -0,0 +1,136 @@ +package com.yaohun.petsystem.data; + +import com.yaohun.petsystem.model.PetNbt; +import com.yaohun.petsystem.util.PetUtil; +import de.tr7zw.nbtapi.NBTItem; +import me.Demon.DemonPlugin.DemonAPI; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextDecoration; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.util.io.BukkitObjectInputStream; +import org.bukkit.util.io.BukkitObjectOutputStream; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Base64; + +public class PlayerData { + + private String playerName; + private ItemStack petStack; + private PetNbt petNbt; + + public PlayerData(String playerName) { + this.playerName = playerName; + this.petStack = deserializeItemStack(PetSQL.getPetStack(playerName)); + if (isPetStackNull()) { + return; + } + PetNbt petNbt = new PetNbt(); + NBTItem nbtItem = new NBTItem(petStack); + petNbt.loadNbtData(nbtItem); + this.petNbt = petNbt; + } + + public String getPlayerName() { + return playerName; + } + + public PetNbt getPetNbt() { + return petNbt; + } + + public ItemStack getPetStack() { + return petStack; + } + + public void rePetNameStack(String newName) { + ItemStack stack = petStack.clone(); + ItemMeta meta = stack.getItemMeta(); + meta.displayName(Component.text(newName) + .color(NamedTextColor.GREEN) + .decoration(TextDecoration.ITALIC, false)); + stack.setItemMeta(meta); + setPetStack(stack); + } + + public void setPetStack(ItemStack petStack) { + this.petStack = petStack; + if (isPetStackNull()) { + return; + } + PetNbt petNbt = new PetNbt(); + NBTItem nbtItem = new NBTItem(petStack); + petNbt.loadNbtData(nbtItem); + this.petNbt = petNbt; + } + + public void refreshPetStack() { + ItemStack itemStack = PetUtil.getPetStackDefault(petNbt); + ItemMeta meta = itemStack.getItemMeta(); + meta.displayName(Component.text(petStack.getItemMeta().getDisplayName()) + .color(NamedTextColor.GREEN) + .decoration(TextDecoration.ITALIC, false)); + + petStack = petNbt.saveIoItem(itemStack); + } + + public void savePlayerData() { + if (DemonAPI.itemIsNull(petStack)) { + PetSQL.saveData(playerName, serializeItemStack(DemonAPI.getErrItems())); + } else { + PetSQL.saveData(playerName, serializeItemStack(petStack)); + } + } + + public boolean isPetStackNull() { + return petStack == null || petStack.getType().equals(Material.BARRIER); + } + + /** + * 将 ItemStack 序列化为 Base64 字符串 + * + * @param itemStack 要序列化的 ItemStack + * @return 序列化后的 Base64 字符串 + */ + private String serializeItemStack(ItemStack itemStack) { + if (itemStack == null) { + return ""; + } + try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + BukkitObjectOutputStream dataOutput = new BukkitObjectOutputStream(outputStream)) { + dataOutput.writeObject(itemStack); + return Base64.getEncoder().encodeToString(outputStream.toByteArray()); + } catch (IOException e) { + Bukkit.getLogger().warning("[日志 - 灵宠] 序列化 ItemStack 时出错: " + e.getMessage()); + return ""; + } + } + + /** + * 从 Base64 字符串反序列化为 ItemStack + * + * @param data Base64 字符串 + * @return 反序列化后的 ItemStack + */ + private ItemStack deserializeItemStack(String data) { + if (data == null || data.isEmpty()) { + return null; + } + try { + byte[] decodedData = Base64.getDecoder().decode(data); + try (ByteArrayInputStream inputStream = new ByteArrayInputStream(decodedData); + BukkitObjectInputStream dataInput = new BukkitObjectInputStream(inputStream)) { + return (ItemStack) dataInput.readObject(); + } + } catch (Exception e) { + Bukkit.getLogger().warning("[日志 - 灵宠] 反序列化 ItemStack 时出错: " + e.getMessage()); + return DemonAPI.getErrItems(); + } + } +} \ No newline at end of file diff --git a/src/src/main/java/com/yaohun/petsystem/data/SkillData.java b/src/src/main/java/com/yaohun/petsystem/data/SkillData.java new file mode 100644 index 0000000..1588fea --- /dev/null +++ b/src/src/main/java/com/yaohun/petsystem/data/SkillData.java @@ -0,0 +1,60 @@ +package com.yaohun.petsystem.data; + +import me.Demon.DemonPlugin.DemonAPI; +import org.bukkit.configuration.ConfigurationSection; + +import java.util.ArrayList; +import java.util.List; + +public class SkillData { + + private String skillName; // 技能名 + private String mmSkill; // MM技能 + private int needLevel; // 触发要求等级 + private String trigger; // 触发器 + private long coolDown; // 冷却时间 + private String itemName; + private List itemLore = new ArrayList<>(); + + public SkillData(String skillName, ConfigurationSection section) { + this.skillName = skillName; + this.mmSkill = section.getString(skillName + ".mmSkill"); + this.needLevel = section.getInt(skillName + ".needLevel"); + this.trigger = section.getString(skillName + ".trigger"); + this.coolDown = section.getLong(skillName + ".coolDown"); + this.itemName = DemonAPI.convertHexColor(section.getString(skillName + ".item.name", "技能 I")); + List itemlore = section.getStringList(skillName + ".item.lore"); + // Bukkit.getConsoleSender().sendMessage("[调试] name: "+skillName+" itemlore = "+itemlore); + for (String line : itemlore) { + this.itemLore.add(DemonAPI.convertHexColor(line)); + } + } + + public String getSkillName() { + return skillName; + } + + public String getMmSkill() { + return mmSkill; + } + + public int getNeedLevel() { + return needLevel; + } + + public String getTrigger() { + return trigger; + } + + public long getCoolDown() { + return coolDown; + } + + public String getItemName() { + return itemName; + } + + public List getItemLore() { + return itemLore; + } +} \ No newline at end of file diff --git a/src/src/main/java/com/yaohun/petsystem/event/PetCallEvent.java b/src/src/main/java/com/yaohun/petsystem/event/PetCallEvent.java new file mode 100644 index 0000000..629dee4 --- /dev/null +++ b/src/src/main/java/com/yaohun/petsystem/event/PetCallEvent.java @@ -0,0 +1,47 @@ +package com.yaohun.petsystem.event; + +import org.bukkit.entity.Player; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; + +import java.util.UUID; + +public class PetCallEvent extends Event { + + private final Player owner; + private final String cutomName; + private final UUID entityUuid; + private static final HandlerList handlers = new HandlerList(); + + public PetCallEvent(Player owner, String cutomName, UUID entityUuid) { + this.owner = owner; + this.cutomName = cutomName; + this.entityUuid = entityUuid; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } + + public Player getOwner() { + return owner; + } + + public String getCutomName() { + return cutomName; + } + + public UUID getEntityUuid() { + return entityUuid; + } + + @Override + public String getEventName() { + return super.getEventName(); + } +} diff --git a/src/src/main/java/com/yaohun/petsystem/gui/CarryGui.java b/src/src/main/java/com/yaohun/petsystem/gui/CarryGui.java new file mode 100644 index 0000000..a3851ad --- /dev/null +++ b/src/src/main/java/com/yaohun/petsystem/gui/CarryGui.java @@ -0,0 +1,89 @@ +package com.yaohun.petsystem.gui; + +import com.yaohun.petsystem.PetMain; +import com.yaohun.petsystem.config.Config; +import com.yaohun.petsystem.data.PlayerData; +import com.yaohun.petsystem.gui.holder.CarryGuiHolder; +import com.yaohun.petsystem.manage.PlayerManager; +import com.yaohun.petsystem.util.MessageUtil; +import de.tr7zw.nbtapi.NBTItem; +import me.Demon.DemonPlugin.DemonAPI; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.Sound; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +public class CarryGui implements Listener { + + private static final int PET_SLOT = 4; + + public static void openGui(Player player) { + String playerName = player.getName(); + PlayerManager playerManager = PetMain.getPlayerManager(); + PlayerData playerData = playerManager.getPlayerData(playerName); + Inventory inv = Bukkit.createInventory(new CarryGuiHolder(), 9, Config.getLanguage("carry_gui_title")); + if (playerData.isPetStackNull()) { + inv.setItem(PET_SLOT, Config.getItemStack("carry_gui")); + } else { + inv.setItem(PET_SLOT, playerData.getPetStack()); + } + player.openInventory(inv); + } + + @EventHandler + public void onClick(InventoryClickEvent e) { + int rawSlot = e.getRawSlot(); + Player player = (Player) e.getWhoClicked(); + Inventory inv = e.getInventory(); + if ((e.getInventory().getHolder() instanceof CarryGuiHolder)) { + e.setCancelled(true); + ItemStack stack = e.getCurrentItem(); + if (DemonAPI.itemIsNull(stack)) { + return; + } + String playerName = player.getName(); + PlayerManager playerManager = PetMain.getPlayerManager(); + PlayerData playerData = playerManager.getPlayerData(playerName); + if (rawSlot == PET_SLOT) { + if (!playerData.isPetStackNull()) { + playerManager.removePetCall(playerName); + playerData.refreshPetStack(); + ItemStack petStack = playerData.getPetStack(); + player.getInventory().addItem(petStack); + playerData.setPetStack(DemonAPI.getErrItems()); + playerData.savePlayerData(); + inv.setItem(PET_SLOT, Config.getItemStack("carry_gui")); + } + return; + } + if (playerData.isPetStackNull()) { + NBTItem nbtItem = new NBTItem(stack); + if (!nbtItem.hasKey("petKey")) { + MessageUtil.sendMessageKey(player, "hand_not_pet_stack", Sound.ENTITY_VILLAGER_NO); + return; + } + e.setCurrentItem(new ItemStack(Material.AIR)); + playerData.setPetStack(stack); + playerData.savePlayerData(); + String itemName = DemonAPI.getItemName(stack); + String message = Config.getLanguage("pet_stack_carry_success"); + MessageUtil.sendMessage(player, message.replace("{petName}", itemName), Sound.ENTITY_PLAYER_LEVELUP); + MainGui.openGui(player); + } else { + playerManager.removePetCall(playerName); + playerData.refreshPetStack(); + ItemStack petStack = playerData.getPetStack(); + player.getInventory().addItem(petStack); + playerData.setPetStack(DemonAPI.getErrItems()); + playerData.savePlayerData(); + MessageUtil.sendMessageKey(player, "pet_stack_carry_takeOut", Sound.ENTITY_PLAYER_LEVELUP); + } + } + + } +} diff --git a/src/src/main/java/com/yaohun/petsystem/gui/FeedingGui.java b/src/src/main/java/com/yaohun/petsystem/gui/FeedingGui.java new file mode 100644 index 0000000..88c0211 --- /dev/null +++ b/src/src/main/java/com/yaohun/petsystem/gui/FeedingGui.java @@ -0,0 +1,206 @@ +package com.yaohun.petsystem.gui; + +import com.yaohun.petsystem.PetMain; +import com.yaohun.petsystem.api.PetExpAPI; +import com.yaohun.petsystem.config.Config; +import com.yaohun.petsystem.data.PlayerData; +import com.yaohun.petsystem.gui.holder.FeedingGuiHolder; +import com.yaohun.petsystem.manage.LevelExpManager; +import com.yaohun.petsystem.manage.PlayerManager; +import com.yaohun.petsystem.model.PetNbt; +import com.yaohun.petsystem.util.MessageUtil; +import com.yaohun.petsystem.util.PetUtil; +import de.tr7zw.nbtapi.NBTItem; +import me.Demon.DemonPlugin.DemonAPI; +import org.bukkit.Bukkit; +import org.bukkit.Sound; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +import java.util.List; + +public class FeedingGui implements Listener { + + private final static int FOOD_SLOT = 15; + + public static void openGui(Player player) { + String playerName = player.getName(); + PlayerManager playerManager = PetMain.getPlayerManager(); + PlayerData playerData = playerManager.getPlayerData(playerName); + if (playerData.isPetStackNull()) { + MessageUtil.sendMessageKey(player, "gui_no_pets_brought", Sound.ENTITY_VILLAGER_NO); + return; + } + String invTitle = Config.getLanguage("main_gui_title"); + if (playerManager.isPetCallExit(playerName)) { + invTitle = Config.getLanguage("main_gui_title") + Config.getLanguage("call_gui_title"); + } + Inventory inv = Bukkit.createInventory(new FeedingGuiHolder(), 36, invTitle + Config.getLanguage("feeding_gui_title")); + // 关闭界面 + inv.setItem(1, Config.getItemStack("close")); + //食物槽 + inv.setItem(FOOD_SLOT, DemonAPI.getPaperAirItems()); + checkFoodGuiPetState(inv, playerData); + player.openInventory(inv); + } + + private static void checkFoodGuiPetState(Inventory inv, PlayerData playerData) { + inv.setItem(23, getFoodButt(playerData.getPetNbt())); + inv.setItem(24, getFoodButt(playerData.getPetNbt())); + inv.setItem(25, getFoodButt(playerData.getPetNbt())); + inv.setItem(11, PetUtil.getPetGuiStack(playerData, "chongwu_feed")); + } + + private static ItemStack getFoodButt(PetNbt petNbt) { + ItemStack stack = Config.getItemStack("weishibutton"); + ItemMeta meta = stack.getItemMeta(); + List lore = meta.getLore(); + lore.replaceAll(s -> DemonAPI.convertHexColor(s + .replace("{petlevel}", String.valueOf(petNbt.level)) + .replace("{exp}", String.valueOf(petNbt.exp)) + .replace("{maxExp}", String.valueOf(LevelExpManager.getNeedExpValue(petNbt.level))))); + meta.setLore(lore); + stack.setItemMeta(meta); + return stack; + } + + private ItemStack getFooedStack(Inventory inv) { + ItemStack foodStack = inv.getItem(FOOD_SLOT); + if (DemonAPI.itemIsNull(foodStack) || DemonAPI.itemIsLore(foodStack)) { + return null; + } + return foodStack; + } + + @EventHandler + public void onClick(InventoryClickEvent e) { + int rawSlot = e.getRawSlot(); + Player player = (Player) e.getWhoClicked(); + String playerName = player.getName(); + Inventory inv = e.getInventory(); + if ((e.getInventory().getHolder() instanceof FeedingGuiHolder)) { + e.setCancelled(true); + if (rawSlot > 35) { + ItemStack clickStack = e.getCurrentItem(); + if (DemonAPI.itemIsNull(clickStack) || DemonAPI.itemIsLore(clickStack)) { + return; + } + NBTItem nbtItem = new NBTItem(clickStack); + if (nbtItem.hasKey("petAppleExp") || nbtItem.hasKey("petAppleUp")) { + // 判断槽位内是否已存在物品 + ItemStack foodStack = getFooedStack(inv); + if (foodStack != null) { + player.getInventory().addItem(foodStack); + } + e.setCurrentItem(null); + inv.setItem(FOOD_SLOT, clickStack); + player.playSound(player.getLocation(), Sound.UI_BUTTON_CLICK, 1, 1); + } else { + MessageUtil.sendMessageKey(player, "pet_feed_not_apple", Sound.ENTITY_VILLAGER_NO); + } + return; + } + if (rawSlot == FOOD_SLOT) { + // 判断槽位内是否已存在物品 + ItemStack foodStack = getFooedStack(inv); + if (foodStack != null) { + player.getInventory().addItem(foodStack); + inv.setItem(FOOD_SLOT, DemonAPI.getPaperAirItems()); + } + player.playSound(player.getLocation(), Sound.UI_BUTTON_CLICK, 1, 1); + } + if (rawSlot == 1) { + MainGui.openGui(player); + player.playSound(player.getLocation(), Sound.UI_BUTTON_CLICK, 1, 1); + } + if (rawSlot == 23 || rawSlot == 24 || rawSlot == 25) { + // 获取实物槽位中的物品 + ItemStack foodStack = inv.getItem(FOOD_SLOT); + if (DemonAPI.itemIsNull(foodStack) || DemonAPI.itemIsLore(foodStack)) { + return; + } + PlayerManager playerManager = PetMain.getPlayerManager(); + PlayerData playerData = playerManager.getPlayerData(playerName); + player.playSound(player.getLocation(), Sound.UI_BUTTON_CLICK, 1, 1); + String itemName = DemonAPI.getItemName(foodStack); + NBTItem nbtItem = new NBTItem(foodStack); + if (nbtItem.hasKey("petAppleExp")) { + // 扣除果实 + if (foodStack.getAmount() == 1) { + inv.setItem(FOOD_SLOT, DemonAPI.getPaperAirItems()); + } else { + foodStack.setAmount(foodStack.getAmount() - 1); + } + // 获取增加经验 + int addExp = Integer.parseInt(nbtItem.getString("petAppleExp")); + PetExpAPI.addExp(player, addExp); + String message = Config.getLanguage("pet_feed_exp_success"); + message = message.replace("{itemName}", itemName); + message = message.replace("{exp}", String.valueOf(addExp)); + MessageUtil.sendMessage(player, message, Sound.ENTITY_EXPERIENCE_ORB_PICKUP); + // 刷新宠物经验 + checkFoodGuiPetState(inv, playerData); + playerData.refreshPetStack(); + } else if (nbtItem.hasKey("petAppleUp")) { + PetNbt petNbt = playerData.getPetNbt(); + // 获取进化阶段 + int needLevel = Integer.parseInt(nbtItem.getString("petAppleUp")); + if (needLevel == 20) { + feedEvolutionEvent(player, needLevel, petNbt, 1, 2, itemName); + } else if (needLevel == 40) { + feedEvolutionEvent(player, needLevel, petNbt, 2, 3, itemName); + } else if (needLevel == 60) { + feedEvolutionEvent(player, needLevel, petNbt, 3, 4, itemName); + } else if (needLevel == 70) { + feedEvolutionEvent(player, needLevel, petNbt, 4, 5, itemName); + } else if (needLevel == 75) { + feedEvolutionEvent(player, needLevel, petNbt, 5, 6, itemName); + } + } else { + MessageUtil.sendMessageKey(player, "pet_feed_not_apple", Sound.ENTITY_VILLAGER_NO); + } + } + } + } + + private void feedEvolutionEvent(Player player, int appleLevel, PetNbt petNbt, int needEvolution, int nextEvolution, String itemName) { + int evolution = petNbt.evolution; + int petLevel = petNbt.level; + if (evolution == nextEvolution) { + MessageUtil.sendMessageKey(player, "pet_feed_evolution_already", Sound.ENTITY_VILLAGER_NO); + return; + } + if (evolution == needEvolution) { + // 判断宠物等级是否满足要求 + if (petLevel < appleLevel) { + String message = Config.getLanguage("pet_feed_evolution_need_level"); + MessageUtil.sendMessage(player, message.replace("{level}", Config.getLevelChar(appleLevel)), Sound.ENTITY_VILLAGER_NO); + return; + } + petNbt.setEvolution(nextEvolution); + String message = Config.getLanguage("pet_feed_evolution_success"); + MessageUtil.sendMessage(player, message.replace("{itemName}", itemName), Sound.ENTITY_EXPERIENCE_ORB_PICKUP); + } else { + MessageUtil.sendMessageKey(player, "pet_feed_evolution_request", Sound.ENTITY_VILLAGER_NO); + } + } + + @EventHandler + public void onClose(InventoryCloseEvent e) { + Inventory inv = e.getInventory(); + if ((e.getInventory().getHolder() instanceof FeedingGuiHolder)) { + Player player = (Player) e.getPlayer(); + ItemStack stack = getFooedStack(inv); + if (stack != null) { + inv.setItem(FOOD_SLOT, DemonAPI.getPaperAirItems()); + player.getInventory().addItem(stack); + } + } + } +} \ No newline at end of file diff --git a/src/src/main/java/com/yaohun/petsystem/gui/MainGui.java b/src/src/main/java/com/yaohun/petsystem/gui/MainGui.java new file mode 100644 index 0000000..70f50a8 --- /dev/null +++ b/src/src/main/java/com/yaohun/petsystem/gui/MainGui.java @@ -0,0 +1,185 @@ +package com.yaohun.petsystem.gui; + +import com.yaohun.petsystem.PetMain; +import com.yaohun.petsystem.config.Config; +import com.yaohun.petsystem.data.PetData; +import com.yaohun.petsystem.data.PlayerData; +import com.yaohun.petsystem.data.SkillData; +import com.yaohun.petsystem.gui.holder.MainGuiHolder; +import com.yaohun.petsystem.listener.RePetNameListener; +import com.yaohun.petsystem.manage.PlayerManager; +import com.yaohun.petsystem.model.PetNbt; +import com.yaohun.petsystem.util.MessageUtil; +import com.yaohun.petsystem.util.PetUtil; +import me.Demon.DemonPlugin.DemonAPI; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextDecoration; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.Sound; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +import java.util.List; + +public class MainGui implements Listener { + + public static void openGui(Player player) { + String playerName = player.getName(); + PlayerManager playerManager = PetMain.getPlayerManager(); + PlayerData playerData = playerManager.getPlayerData(playerName); + if (playerData.isPetStackNull()) { + CarryGui.openGui(player); + // MessageUtil.sendMessageKey(player, "gui_no_pets_brought", Sound.ENTITY_VILLAGER_NO); + return; + } + String invTitle = Config.getLanguage("main_gui_title"); + String buttonKey = "zhaohuan"; + if (playerManager.isPetCallExit(playerName)) { + invTitle = Config.getLanguage("main_gui_title") + Config.getLanguage("call_gui_title"); + buttonKey = "zhaohui"; + } + Inventory inv = Bukkit.createInventory(new MainGuiHolder(), 36, invTitle); + // 关闭界面 + inv.setItem(0, Config.getItemStack("close")); + // 宠物图鉴 + inv.setItem(1, Config.getItemStack("pettujian")); + // 宠物物品显示 + inv.setItem(11, PetUtil.getPetGuiStack(playerData, "chongwu")); + // 宠物技能显示 + PetData petData = playerData.getPetNbt().petData; + int slot = 13; + for (SkillData skillData : petData.getSkillDataMap().values()) { + inv.setItem(slot, getSkillGoldenApple(skillData)); + slot++; + } + // 宠物召唤按钮 + ItemStack zhaohuanStack = Config.getItemStack(buttonKey); + inv.setItem(29, zhaohuanStack); + inv.setItem(30, zhaohuanStack); + // 宠物喂养按钮 + inv.setItem(31, Config.getItemStack("weiyang")); + // 宠物工作按钮 + inv.setItem(32, Config.getItemStack("gongzuo")); + inv.setItem(33, Config.getItemStack("gongzuo")); + // 宠物品质数据查询 + inv.setItem(22, getQualificationStack(playerData, "fangs")); + inv.setItem(23, getQualificationStack(playerData, "survive")); + inv.setItem(24, getQualificationStack(playerData, "efficiency")); + // 仓库、Wiki按钮 + inv.setItem(7, Config.getItemStack("cangku")); + inv.setItem(8, Config.getItemStack("wiki")); + player.openInventory(inv); + } + + private static ItemStack getSkillGoldenApple(SkillData skillData) { + ItemStack stack = new ItemStack(Material.GOLDEN_APPLE); + ItemMeta meta = stack.getItemMeta(); + meta.displayName(Component.text(skillData.getItemName()) + .color(NamedTextColor.GREEN) + .decoration(TextDecoration.ITALIC, false)); + meta.setLore(skillData.getItemLore()); + stack.setItemMeta(meta); + return stack; + } + + private static ItemStack getQualificationStack(PlayerData playerData, String type) { + PetNbt petNbt = playerData.getPetNbt(); + int value = 0; + if ("fangs".equalsIgnoreCase(type)) { + value = petNbt.quality.getFangs(); + } else if ("survive".equalsIgnoreCase(type)) { + value = petNbt.quality.getSurvive(); + } else if ("efficiency".equalsIgnoreCase(type)) { + value = petNbt.quality.getEfficiency(); + } + ItemStack stack = Config.getItemStack(type); + ItemMeta meta = stack.getItemMeta(); + List lore = meta.getLore(); + for (int i = 0; i < lore.size(); i++) { + String line = lore.get(i); + if (line.contains("{qualityShow}")) { + lore.set(i, DemonAPI.convertHexColor(line).replace("{qualityShow}", Config.getQualityShow(value))); + break; + } + } + meta.setLore(lore); + stack.setItemMeta(meta); + return stack; + } + + @EventHandler + public void onClick(InventoryClickEvent e) { + int rawSlot = e.getRawSlot(); + Player player = (Player) e.getWhoClicked(); + if ((e.getInventory().getHolder() instanceof MainGuiHolder)) { + e.setCancelled(true); + // 关闭界面按钮 + if (rawSlot == 0) { + player.closeInventory(); + player.playSound(player.getLocation(), Sound.UI_BUTTON_CLICK, 1, 1); + } + if (rawSlot == 1) { + player.closeInventory(); + // 打开宠物图鉴操作 + player.playSound(player.getLocation(), Sound.UI_BUTTON_CLICK, 1, 1); + } + // Wiki按钮 + if (rawSlot == 8) { + player.closeInventory(); + MessageUtil.sendMessageKey(player, "gui_wiki_message", Sound.UI_BUTTON_CLICK); + } + // 仓库按钮 + if (rawSlot == 7) { + player.closeInventory(); + // 打开便捷仓库操作 + player.playSound(player.getLocation(), Sound.UI_BUTTON_CLICK, 1, 1); + } + PlayerManager playerManager = PetMain.getPlayerManager(); + if (rawSlot == 11) { + if (e.isShiftClick()) { + String playerName = player.getName(); + PlayerData playerData = playerManager.getPlayerData(playerName); + if (!playerData.isPetStackNull()) { + playerManager.removePetCall(playerName); + playerData.refreshPetStack(); + ItemStack petStack = playerData.getPetStack(); + player.getInventory().addItem(petStack); + playerData.setPetStack(DemonAPI.getErrItems()); + playerData.savePlayerData(); + player.closeInventory(); + Bukkit.getScheduler().runTaskLater(PetMain.inst(), player::updateInventory, 2L); + MessageUtil.sendMessageKey(player, "pet_stack_carry_takeOut", Sound.ENTITY_PLAYER_LEVELUP); + } + return; + } + if (e.isLeftClick()) { + player.closeInventory(); + RePetNameListener.addReNameUUID(player.getUniqueId()); + MessageUtil.sendMessageKey(player, "pet_rename_add", Sound.UI_BUTTON_CLICK); + } + } + // 召唤宠物操作按钮 + if (rawSlot == 29 || rawSlot == 30) { + player.closeInventory(); + playerManager.callPet(player); + } + // 打开投喂宠物界面 + if (rawSlot == 31) { + FeedingGui.openGui(player); + player.playSound(player.getLocation(), Sound.UI_BUTTON_CLICK, 1, 1); + } + // 打开打工系统 + if (rawSlot == 32 || rawSlot == 33) { + player.closeInventory(); + MessageUtil.sendMessageKey(player, "imperfect_function", Sound.ENTITY_VILLAGER_NO); + } + } + } +} \ No newline at end of file diff --git a/src/src/main/java/com/yaohun/petsystem/gui/holder/CarryGuiHolder.java b/src/src/main/java/com/yaohun/petsystem/gui/holder/CarryGuiHolder.java new file mode 100644 index 0000000..9ae4857 --- /dev/null +++ b/src/src/main/java/com/yaohun/petsystem/gui/holder/CarryGuiHolder.java @@ -0,0 +1,12 @@ +package com.yaohun.petsystem.gui.holder; + +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; + +public class CarryGuiHolder implements InventoryHolder { + + @Override + public Inventory getInventory() { + return null; + } +} diff --git a/src/src/main/java/com/yaohun/petsystem/gui/holder/FeedingGuiHolder.java b/src/src/main/java/com/yaohun/petsystem/gui/holder/FeedingGuiHolder.java new file mode 100644 index 0000000..a97e280 --- /dev/null +++ b/src/src/main/java/com/yaohun/petsystem/gui/holder/FeedingGuiHolder.java @@ -0,0 +1,12 @@ +package com.yaohun.petsystem.gui.holder; + +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; + +public class FeedingGuiHolder implements InventoryHolder { + + @Override + public Inventory getInventory() { + return null; + } +} diff --git a/src/src/main/java/com/yaohun/petsystem/gui/holder/MainGuiHolder.java b/src/src/main/java/com/yaohun/petsystem/gui/holder/MainGuiHolder.java new file mode 100644 index 0000000..496777d --- /dev/null +++ b/src/src/main/java/com/yaohun/petsystem/gui/holder/MainGuiHolder.java @@ -0,0 +1,12 @@ +package com.yaohun.petsystem.gui.holder; + +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; + +public class MainGuiHolder implements InventoryHolder { + + @Override + public Inventory getInventory() { + return null; + } +} diff --git a/src/src/main/java/com/yaohun/petsystem/listener/PetSkillsListener.java b/src/src/main/java/com/yaohun/petsystem/listener/PetSkillsListener.java new file mode 100644 index 0000000..3e89c8d --- /dev/null +++ b/src/src/main/java/com/yaohun/petsystem/listener/PetSkillsListener.java @@ -0,0 +1,94 @@ +package com.yaohun.petsystem.listener; + +import com.yaohun.petsystem.PetMain; +import com.yaohun.petsystem.data.PetData; +import com.yaohun.petsystem.data.PlayerData; +import com.yaohun.petsystem.data.SkillData; +import com.yaohun.petsystem.manage.PlayerManager; +import io.lumine.mythic.bukkit.MythicBukkit; +import me.Demon.DemonPlugin.Util.CDTimeAPI; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityDamageByEntityEvent; + +import java.util.LinkedHashMap; +import java.util.UUID; + +public class PetSkillsListener implements Listener { + + @EventHandler + public void onDamager(EntityDamageByEntityEvent e){ + if(e.getDamager() instanceof Player player){ + // 判断玩家是否携带宠物 + String playerName = player.getName(); + PlayerManager playerManager = PetMain.getPlayerManager(); + // 获取宠物相关数据 + PlayerData playerData = playerManager.getPlayerData(playerName); + if(playerData.isPetStackNull()){ + return; + } + PetData petData = playerData.getPetNbt().petData; + int petLevel = playerData.getPetNbt().level; + LinkedHashMap skillDataMap = petData.getSkillDataMap(); + for(SkillData skillData : skillDataMap.values()){ + if(petLevel >= skillData.getNeedLevel()) { + String cdKey = "skill_"+skillData.getSkillName(); + if ("onAttack".equalsIgnoreCase(skillData.getTrigger())) { + UUID uuid = player.getUniqueId(); + if(!CDTimeAPI.isCD(uuid,cdKey)) { + long cooldown = skillData.getCoolDown(); + CDTimeAPI.setPlayerCD(uuid, cdKey, cooldown); + String mmSkill = skillData.getMmSkill(); + // 执行技能 + MythicBukkit.inst().getAPIHelper().castSkill( + player, + mmSkill, + player.getLocation() + ); + player.sendMessage("[调试 - 宠物技能] 触发条件: onAttack 触发技能: "+skillData.getSkillName()); + } + } + } + } + } + if(e.getEntity() instanceof Player player){ + String playerName = player.getName(); + PlayerManager playerManager = PetMain.getPlayerManager(); + + PlayerData playerData = playerManager.getPlayerData(playerName); + if(playerData.isPetStackNull()){ + return; + } + PetData petData = playerData.getPetNbt().petData; + int petLevel = playerData.getPetNbt().level; + LinkedHashMap skillDataMap = petData.getSkillDataMap(); + for(SkillData skillData : skillDataMap.values()){ + if(petLevel >= skillData.getNeedLevel()) { + String cdKey = "skill_"+skillData.getSkillName(); + if ("onDamaged".equalsIgnoreCase(skillData.getTrigger())) { + UUID uuid = player.getUniqueId(); + if(!CDTimeAPI.isCD(uuid,cdKey)) { + long cooldown = skillData.getCoolDown(); + CDTimeAPI.setPlayerCD(uuid, cdKey, cooldown); + String mmSkill = skillData.getMmSkill(); + // 执行技能 + MythicBukkit.inst().getAPIHelper().castSkill(player, mmSkill, player.getLocation()); + player.sendMessage("[调试 - 宠物技能] 触发条件: onDamaged 触发技能: "+skillData.getSkillName()); + } + } + } + } + } + } + + /*public boolean runSkills(SkillCaster caster, SkillTrigger cause, AbstractLocation origin, AbstractEntity trigger, Consumer transformer) { + return new TriggeredSkill(cause, caster, origin, trigger, this.artifact.getMechanics(cause), true, meta -> { + transformer.getVariables().putString("equip-slot", this.equippedSlot); + transformer.getVariables().putObject("equip-item", this.itemStack); + if (r5 != null) { + r5.accept(transformer); + } + }, new Pair[0]).getCancelled(); + }*/ +} diff --git a/src/src/main/java/com/yaohun/petsystem/listener/PlayerListener.java b/src/src/main/java/com/yaohun/petsystem/listener/PlayerListener.java new file mode 100644 index 0000000..90a5b68 --- /dev/null +++ b/src/src/main/java/com/yaohun/petsystem/listener/PlayerListener.java @@ -0,0 +1,79 @@ +package com.yaohun.petsystem.listener; + +import com.yaohun.petsystem.PetMain; +import com.yaohun.petsystem.data.PlayerData; +import com.yaohun.petsystem.manage.PlayerManager; +import com.yaohun.petsystem.model.PetNbt; +import com.yaohun.petsystem.util.PetUtil; +import org.bukkit.Bukkit; +import org.bukkit.entity.Entity; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.entity.Wolf; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.entity.EntityDeathEvent; +import org.bukkit.event.player.PlayerQuitEvent; + +import java.util.UUID; + +public class PlayerListener implements Listener { + + @EventHandler + public void onDamage(EntityDamageByEntityEvent e) { + if (e.getEntity() instanceof Player) { + return; + } + if (!(e.getEntity() instanceof LivingEntity target)) { + return; + } + if (e.getDamager() instanceof Player player) { + String playerName = player.getName(); + PlayerManager playerManager = PetMain.getPlayerManager(); + if (!playerManager.isPetCallExit(playerName)) { + return; + } + UUID petUUID = playerManager.getPetUUID(playerName); + if (petUUID == null) { + return; + } + Entity entity = Bukkit.getEntity(petUUID); + if (entity instanceof Wolf wolf) { + wolf.setTarget(target); + } + } + } + + // 检查伤害 狼造成的伤害 + @EventHandler + public void onDamageCheck(EntityDamageByEntityEvent e) { + if (e.getDamager() instanceof Wolf wolf) { + PlayerData playerData = PetUtil.updatePetNameTag(wolf); + if (playerData != null) { + double health = wolf.getHealth(); + PetNbt petNbt = playerData.getPetNbt(); + petNbt.setHealth((int) health); + } + } + } + + @EventHandler + public void onDeath(EntityDeathEvent e) { + String ownerName = PetMain.getPlayerManager().getPetOwnerName(e.getEntity().getUniqueId()); + if (ownerName == null) { + return; + } + PlayerData playerData = PetMain.getPlayerManager().getPlayerData(ownerName); + playerData.getPetNbt().setHealth(0); + PetMain.getPlayerManager().removePetCall(ownerName); + } + + @EventHandler + public void onQuit(PlayerQuitEvent e){ + Player player = e.getPlayer(); + String playerName = player.getName(); + PlayerManager playerManager = PetMain.getPlayerManager(); + playerManager.removePetCall(playerName); + } +} diff --git a/src/src/main/java/com/yaohun/petsystem/listener/RePetNameListener.java b/src/src/main/java/com/yaohun/petsystem/listener/RePetNameListener.java new file mode 100644 index 0000000..de4e730 --- /dev/null +++ b/src/src/main/java/com/yaohun/petsystem/listener/RePetNameListener.java @@ -0,0 +1,62 @@ +package com.yaohun.petsystem.listener; + +import com.yaohun.petsystem.PetMain; +import com.yaohun.petsystem.config.Config; +import com.yaohun.petsystem.data.PlayerData; +import com.yaohun.petsystem.manage.PlayerManager; +import com.yaohun.petsystem.util.MessageUtil; +import org.bukkit.Sound; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.AsyncPlayerChatEvent; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +public class RePetNameListener implements Listener { + + private static List reNameList = new ArrayList<>(); + + public static void addReNameUUID(UUID uuid) { + if (!reNameList.contains(uuid)) { + reNameList.add(uuid); + } + } + + @EventHandler + public void onChat(AsyncPlayerChatEvent e) { + Player player = e.getPlayer(); + UUID uuid = player.getUniqueId(); + if (reNameList.isEmpty()) { + return; + } + if (!reNameList.contains(uuid)) { + return; + } + e.setCancelled(true); + reNameList.remove(uuid); + String playerName = player.getName(); + PlayerManager playerManager = PetMain.getPlayerManager(); + PlayerData playerData = playerManager.getPlayerData(playerName); + if (playerData.isPetStackNull()) { + MessageUtil.sendMessageKey(player, "pet_rename_no_pets", Sound.ENTITY_VILLAGER_NO); + return; + } + String reName = e.getMessage(); + if (reName.isEmpty()) { + MessageUtil.sendMessageKey(player, "pet_rename_empty", Sound.ENTITY_VILLAGER_NO); + return; + } + if (reName.length() < 3) { + MessageUtil.sendMessageKey(player, "pet_rename_length", Sound.ENTITY_VILLAGER_NO); + return; + } + String newName = reName.replace("&", "§"); + playerData.rePetNameStack(newName); + playerData.savePlayerData(); + String message = Config.getLanguage("pet_rename_success"); + MessageUtil.sendMessage(player, message.replace("{petName}", newName), Sound.ENTITY_PLAYER_LEVELUP); + } +} \ No newline at end of file diff --git a/src/src/main/java/com/yaohun/petsystem/manage/LevelExpManager.java b/src/src/main/java/com/yaohun/petsystem/manage/LevelExpManager.java new file mode 100644 index 0000000..42133c8 --- /dev/null +++ b/src/src/main/java/com/yaohun/petsystem/manage/LevelExpManager.java @@ -0,0 +1,35 @@ +package com.yaohun.petsystem.manage; + +import com.yaohun.petsystem.PetMain; +import org.bukkit.Bukkit; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.io.File; +import java.util.HashMap; + +public class LevelExpManager { + + private static HashMap needExpMap = new HashMap<>(); + + public static void reloadLevelManager(PetMain plugin) { + File file = new File(plugin.getDataFolder() + "/Settings", "needExp.yml"); + if (file.getParentFile().exists()) { + file.getParentFile().mkdirs(); + } + FileConfiguration config = YamlConfiguration.loadConfiguration(file); + for (String levelKey : config.getKeys(false)) { + int level = Integer.parseInt(levelKey); + int needExp = config.getInt(levelKey); + needExpMap.put(level, needExp); + } + Bukkit.getConsoleSender().sendMessage("§7- 等级经验配置: §f" + needExpMap.size() + "条"); + } + + public static int getNeedExpValue(int level) { + if (needExpMap.containsKey(level)) { + return needExpMap.get(level); + } + return 1000000; + } +} \ No newline at end of file diff --git a/src/src/main/java/com/yaohun/petsystem/manage/PetCallSession.java b/src/src/main/java/com/yaohun/petsystem/manage/PetCallSession.java new file mode 100644 index 0000000..df1db1e --- /dev/null +++ b/src/src/main/java/com/yaohun/petsystem/manage/PetCallSession.java @@ -0,0 +1,48 @@ +package com.yaohun.petsystem.manage; + +import kr.toxicity.model.api.tracker.Tracker; +import org.bukkit.Bukkit; +import org.bukkit.entity.Entity; + +import java.util.UUID; + +public class PetCallSession { + + private final UUID ownerUuid; + private final UUID entityUuid; + private final Tracker tracker; + + public PetCallSession(UUID ownerUuid, UUID entityUuid, Tracker tracker) { + this.ownerUuid = ownerUuid; + this.entityUuid = entityUuid; + this.tracker = tracker; + } + + public UUID getOwnerUuid() { + return ownerUuid; + } + + public UUID getEntityUuid() { + return entityUuid; + } + + public Entity getEntity() { + return Bukkit.getEntity(entityUuid); + } + + public boolean isAlive() { + Entity entity = getEntity(); + return entity != null && !entity.isDead(); + } + + public void close() { + if (tracker != null && !tracker.isClosed()) { + tracker.despawn(); + tracker.close(); + } + Entity entity = getEntity(); + if (entity != null && !entity.isDead()) { + entity.remove(); + } + } +} diff --git a/src/src/main/java/com/yaohun/petsystem/manage/PetManager.java b/src/src/main/java/com/yaohun/petsystem/manage/PetManager.java new file mode 100644 index 0000000..5996459 --- /dev/null +++ b/src/src/main/java/com/yaohun/petsystem/manage/PetManager.java @@ -0,0 +1,71 @@ +package com.yaohun.petsystem.manage; + +import com.yaohun.petsystem.PetMain; +import com.yaohun.petsystem.data.PetData; +import me.Demon.DemonPlugin.DemonAPI; +import org.bukkit.Bukkit; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +public class PetManager { + + private static HashMap petDataMap = new HashMap<>(); + + public static void reloadPetManager() { + petDataMap.clear(); + Bukkit.getConsoleSender().sendMessage("§6[宠物系统] §7宠物类型:"); + String folderPath = PetMain.inst().getDataFolder().getPath() + "/PetData"; + File folder = new File(folderPath); + if (!folder.exists() || !folder.isDirectory()) { + System.out.println("[日志 - 灵宠] 未检测到任何宠物配置: " + folderPath); + return; + } + List ymlFiles = new ArrayList<>(); + DemonAPI.collectYmlFiles(folder, ymlFiles); + if (ymlFiles.isEmpty()) { + System.out.println("[日志 - 灵宠] 未检测到任何宠物配置: " + folderPath); + return; + } + for (File file : ymlFiles) { + String fileName = file.getName().replace(".yml", ""); + FileConfiguration config = YamlConfiguration.loadConfiguration(file); + PetData petData = new PetData(fileName, config); + petDataMap.put(fileName, petData); + Bukkit.getConsoleSender().sendMessage("- 宠物: §f" + petData.getPetName() + " §7MaxLevel: §f" + petData.getMaxLevel()); + } + } + + public static HashMap getPetDataMap() { + return petDataMap; + } + + public static PetData getItemNameToPetData(String itemName) { + for (PetData petData : petDataMap.values()) { + if (petData.getPetName().contains(itemName)) { + return petData; + } + } + return null; + } + + public static PetData getPetData(String petKey) { + if (petDataMap.containsKey(petKey)) { + return petDataMap.get(petKey); + } + return null; + } + + // 获取宠物获取渠道 + public static List getPetAccessChannels(String petKey) { + PetData petData = PetManager.getPetData(petKey); + if (petData == null) { + return new ArrayList<>(); + } + return petData.getAccessChannelsList(); + } +} \ No newline at end of file diff --git a/src/src/main/java/com/yaohun/petsystem/manage/PlayerManager.java b/src/src/main/java/com/yaohun/petsystem/manage/PlayerManager.java new file mode 100644 index 0000000..5e5e432 --- /dev/null +++ b/src/src/main/java/com/yaohun/petsystem/manage/PlayerManager.java @@ -0,0 +1,253 @@ +package com.yaohun.petsystem.manage; + +import com.yaohun.petsystem.PetMain; +import com.yaohun.petsystem.config.Config; +import com.yaohun.petsystem.data.PetData; +import com.yaohun.petsystem.data.PetSQL; +import com.yaohun.petsystem.data.PlayerData; +import com.yaohun.petsystem.event.PetCallEvent; +import com.yaohun.petsystem.util.MessageUtil; +import com.yaohun.petsystem.util.model.BetterModelUtil; +import com.yaohun.petsystem.util.wolf.CustomWolf; +import de.tr7zw.nbtapi.NBTItem; +import io.lumine.mythic.bukkit.MythicBukkit; +import kr.toxicity.model.api.tracker.Tracker; +import me.Demon.DemonPlugin.DemonAPI; +import me.Demon.DemonPlugin.Util.CDTimeAPI; +import org.bukkit.Bukkit; +import org.bukkit.Sound; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.util.HashMap; +import java.util.UUID; + +public class PlayerManager { + + private HashMap playerDataMap = new HashMap<>(); + private HashMap petCallMap = new HashMap<>(); + // private HashMap petEntityModelMap = new HashMap<>(); + + public PlayerData getPlayerData(String name) { + if (!playerDataMap.containsKey(name)) { + playerDataMap.put(name, new PlayerData(name)); + } + return playerDataMap.get(name); + } + + public void saveAllData() { + for (PlayerData playerData : playerDataMap.values()) { + playerData.savePlayerData(); + } + } + + public void closeSaveData(String playerName) { + PlayerData playerData = getPlayerData(playerName); + playerData.savePlayerData(); + playerDataMap.remove(playerName); + + removePetCall(playerName); + } + + public void closeSaveAllData() { + for (PlayerData playerData : playerDataMap.values()) { + playerData.savePlayerData(); + } + playerDataMap.clear(); + for (PetCallSession session : petCallMap.values()) { + session.close(); + } + PetSQL.closeConnection(); + petCallMap.clear(); + } + + public void setPetCall(String name, UUID ownerUuid, UUID entityUuid, Tracker tracker) { + petCallMap.put(name, new PetCallSession(ownerUuid, entityUuid, tracker)); + } + + public void removePetCall(String name) { + PetCallSession session = petCallMap.remove(name); + if (session != null) { + session.close(); + } + } + + public boolean isPetCallExit(String name) { + PetCallSession session = petCallMap.get(name); + if (session == null) { + return false; + } + if (!session.isAlive()) { + petCallMap.remove(name); + session.close(); + return false; + } + return true; + } + + public String getPetOwnerName(UUID entityUuid) { + for (String name : petCallMap.keySet()) { + PetCallSession session = petCallMap.get(name); + if (session.getEntityUuid().equals(entityUuid)) { + return name; + } + } + return null; + } + + public UUID getPetUUID(String name) { + if (isPetCallExit(name)) { + return petCallMap.get(name).getEntityUuid(); + } + return null; + } +/* + public ModeledEntity getPetEntityModel(UUID entityUuid) { + for (ModeledEntity entity : petEntityModelMap.values()) { + if (entity.getBase().getUUID().equals(entityUuid)) { + return entity; + } + } + return null; + } + + public void setPetEntityModelMap(UUID entityUUID, ModeledEntity entityModel) { + petEntityModelMap.put(entityUUID, entityModel); + }*/ + + public void callPet(Player player) { + String carryCdKey = "callPetStackCD"; + UUID uuid = player.getUniqueId(); + if (CDTimeAPI.isCD(uuid, carryCdKey)) { + MessageUtil.sendMessageKey(player, "system_coolingDown", Sound.ENTITY_VILLAGER_NO); + return; + } + CDTimeAPI.setPlayerCD(uuid, carryCdKey, 1000 * 5); + String playerName = player.getName(); + PlayerData playerData = getPlayerData(playerName); + if (playerData == null) { + return; + } + if (playerData.isPetStackNull()) { + MessageUtil.sendMessageKey(player, "no_summons_pets_brought", Sound.ENTITY_VILLAGER_NO); + return; + } + // 判断玩家是否已经召唤宠物 + if (isPetCallExit(playerName)) { + removePetCall(playerName); + MessageUtil.sendMessageKey(player, "pet_summons_recovers", Sound.UI_BUTTON_CLICK); + return; + } + int health = playerData.getPetNbt().health; + if (health < 1) { + MessageUtil.sendMessageKey(player, "no_summons_pets_health", Sound.ENTITY_VILLAGER_NO); + return; + } + String petStackName = DemonAPI.getItemName(playerData.getPetStack()); + CustomWolf wolf = CustomWolf.spawnCustomWolf(player, playerData.getPetNbt()); + PetData petData = playerData.getPetNbt().petData; + String modelID = petData.getModelId(); + Tracker tracker = BetterModelUtil.spawnModel(player, wolf.getBukkitEntity(), modelID); + if (tracker == null) { + wolf.getBukkitEntity().remove(); + MessageUtil.sendMessage(player, "§c宠物模型载入失败,召唤已取消。", Sound.ENTITY_VILLAGER_NO); + return; + } + setPetCall(playerName, player.getUniqueId(), wolf.getUUID(), tracker); + /*ActiveModel model = ModelEngineAPI.createActiveModel(modelID); + if (model != null) { + ModeledEntity modeledEntity = ModelEngineAPI.createModeledEntity(wolf.getBukkitEntity()); + setPetEntityModelMap(wolf.getUUID(), modeledEntity); + modeledEntity.addModel(model, true); + modeledEntity.setBaseEntityVisible(false); + model.setHitboxVisible(true); + model.setShadowVisible(true); + String text = Config.getLanguage("pet_name_tag") + .replace("{name}", playerName) + .replace("{petName}", petStackName); + + wolf.setCustomName(Component.literal(text)); + wolf.setCustomNameVisible(true); + + NameTag tag = PetUtil.getNameBone(modeledEntity, "name"); + if (tag != null) { + tag.setString(text); + tag.setVisible(true); + } + int petLevel = playerData.getPetNbt().level; + double healthNow = wolf.getHealth(); + double healthMax = wolf.getMaxHealth(); + // 计算获得血量百分比 + int percent = (int) Math.round((healthNow / healthMax) * 100.0); + String levelText = Config.getLanguage("pet_level_tag") + .replace("{level}", Config.getLevelChar(petLevel)) + .replace("{health}", Config.getHealthChar(percent)); + NameTag levelTag = PetUtil.getNameBone(modeledEntity, "level"); + if (levelTag != null) { + levelTag.setString(levelText); + levelTag.setVisible(true); + } + Bukkit.getConsoleSender().sendMessage("[日志 - 宠物] 模型资源 " + modelID + " 载入成功!"); + } else { + Bukkit.getConsoleSender().sendMessage("[日志 - 宠物] 模型资源 " + modelID + " 载入失败!"); + }*/ + String summonSkill = petData.getSummonSkill(); + if (!"default".equalsIgnoreCase(summonSkill)) { + Bukkit.getScheduler().runTaskLater(PetMain.inst(), () -> { + if (wolf.isRemoved() || wolf.getBukkitLivingEntity().isDead()) { + return; + } + MythicBukkit.inst().getAPIHelper().castSkill(wolf.getBukkitLivingEntity(), summonSkill, wolf.getBukkitLivingEntity().getLocation()); + player.sendMessage("[调试 - 宠物技能] 触发条件: onSpawn 触发技能: "+summonSkill); + }, 2L); + } + // 设置宠物召唤状态 + MessageUtil.sendMessageKey(player, "pet_summons_success", Sound.UI_BUTTON_CLICK); + // 发布事件 + PetCallEvent event = new PetCallEvent(player, wolf.getBukkitEntity().getCustomName(), wolf.getUUID()); + Bukkit.getPluginManager().callEvent(event); + } + + public void carryPetStackData(Player player) { + String carryCdKey = "carryPetStackCD"; + UUID uuid = player.getUniqueId(); + if (CDTimeAPI.isCD(uuid, carryCdKey)) { + MessageUtil.sendMessageKey(player, "system_coolingDown", Sound.ENTITY_VILLAGER_NO); + return; + } + CDTimeAPI.setPlayerCD(uuid, carryCdKey, 1000 * 5); + String playerName = player.getName(); + PlayerData playerData = getPlayerData(playerName); + if (playerData.isPetStackNull()) { + ItemStack stack = player.getInventory().getItemInMainHand(); + if (DemonAPI.itemIsNull(stack)) { + MessageUtil.sendMessageKey(player, "error_empty_hand", Sound.ENTITY_VILLAGER_NO); + return; + } + NBTItem nbtItem = new NBTItem(stack); + if (!nbtItem.hasKey("petKey")) { + MessageUtil.sendMessageKey(player, "hand_not_pet_stack", Sound.ENTITY_VILLAGER_NO); + return; + } + player.getInventory().setItemInMainHand(null); + playerData.setPetStack(stack); + playerData.savePlayerData(); + String itemName = DemonAPI.getItemName(stack); + String message = Config.getLanguage("pet_stack_carry_success"); + MessageUtil.sendMessage(player, message.replace("{petName}", itemName), Sound.ENTITY_PLAYER_LEVELUP); + } else { + removePetCall(playerName); + playerData.refreshPetStack(); + ItemStack petStack = playerData.getPetStack(); + NBTItem nbtItem = new NBTItem(petStack); + if (!nbtItem.hasKey("petKey")) { + Bukkit.getConsoleSender().sendMessage("[日志 - 宠物] 未检测到宠物petKey的Nbt标识."); + } + player.getInventory().addItem(petStack); + playerData.setPetStack(DemonAPI.getErrItems()); + playerData.savePlayerData(); + MessageUtil.sendMessageKey(player, "pet_stack_carry_takeOut", Sound.ENTITY_PLAYER_LEVELUP); + } + } +} diff --git a/src/src/main/java/com/yaohun/petsystem/model/PetNbt.java b/src/src/main/java/com/yaohun/petsystem/model/PetNbt.java new file mode 100644 index 0000000..b156afa --- /dev/null +++ b/src/src/main/java/com/yaohun/petsystem/model/PetNbt.java @@ -0,0 +1,111 @@ +package com.yaohun.petsystem.model; + +import com.yaohun.petsystem.data.PetData; +import com.yaohun.petsystem.manage.LevelExpManager; +import com.yaohun.petsystem.manage.PetManager; +import de.tr7zw.nbtapi.NBTItem; +import org.bukkit.inventory.ItemStack; + +public class PetNbt { + + private String petKey; + public PetData petData; + public int level; + public int exp; + public int maxExp; + public int totalExp; + public int damage; + public int health; + public int maxHealth; + public int evolution; + public PetQuality quality; + + public PetNbt(PetData petData) { + this.petKey = petData.getFileName(); + this.petData = petData; + this.level = 1; + this.maxExp = LevelExpManager.getNeedExpValue(level); + this.exp = 0; + this.totalExp = 0; + this.damage = 7; + this.health = 20; + this.maxHealth = 20; + this.evolution = 1; + this.quality = PetQuality.random(); + } + + public PetNbt() { + } + + public void loadNbtData(NBTItem nbtItem) { + petKey = nbtItem.getString("petKey"); + petData = PetManager.getPetData(petKey); + level = nbtItem.getInteger("level"); + exp = nbtItem.getInteger("exp"); + totalExp = nbtItem.getInteger("totalExp"); + maxExp = LevelExpManager.getNeedExpValue(level); + damage = nbtItem.getInteger("damage"); + health = nbtItem.getInteger("health"); + maxHealth = nbtItem.getInteger("maxHealth"); + evolution = nbtItem.getInteger("evolution"); + int qualityFangs = nbtItem.getInteger("quality_fangs"); + int qualitySurvive = nbtItem.getInteger("quality_survive"); + int qualityEfficiency = nbtItem.getInteger("quality_efficiency"); + quality = new PetQuality(qualityFangs, qualitySurvive, qualityEfficiency); + } + + public ItemStack saveIoItem(ItemStack stack) { + NBTItem nbtItem = new NBTItem(stack); + nbtItem.setString("petKey", petKey); + nbtItem.setInteger("level", level); + nbtItem.setInteger("exp", exp); + nbtItem.setInteger("totalExp", totalExp); + nbtItem.setInteger("maxExp", LevelExpManager.getNeedExpValue(level)); + nbtItem.setInteger("damage", damage); + nbtItem.setInteger("health", health); + nbtItem.setInteger("maxHealth", maxHealth); + nbtItem.setInteger("evolution", evolution); + nbtItem.setInteger("quality_fangs", quality.getFangs()); + nbtItem.setInteger("quality_survive", quality.getSurvive()); + nbtItem.setInteger("quality_efficiency", quality.getEfficiency()); + return nbtItem.getItem(); + } + + public void setLevel(int level) { + this.level = level; + } + + public void setExp(int exp) { + this.exp = exp; + } + + public void setTotalExp(int totalExp) { + this.totalExp = totalExp; + } + + public void setDamage(int damage) { + this.damage = damage; + } + + public void setHealth(int health) { + this.health = health; + } + + public void setMaxHealth(int maxHealth) { + this.maxHealth = maxHealth; + } + + public void setEvolution(int evolution) { + this.evolution = evolution; + } + + public void setQuality(String qualityType, int value) { + if ("quality_fangs".equals(qualityType)) { + this.quality.setFangs(value); + } else if ("quality_survive".equals(qualityType)) { + this.quality.setSurvive(value); + } else if ("quality_efficiency".equals(qualityType)) { + this.quality.setEfficiency(value); + } + } +} \ No newline at end of file diff --git a/src/src/main/java/com/yaohun/petsystem/model/PetQuality.java b/src/src/main/java/com/yaohun/petsystem/model/PetQuality.java new file mode 100644 index 0000000..5abfcf6 --- /dev/null +++ b/src/src/main/java/com/yaohun/petsystem/model/PetQuality.java @@ -0,0 +1,49 @@ +package com.yaohun.petsystem.model; + +import me.Demon.DemonPlugin.Util.RandomUtil; + +public class PetQuality { + + private int fangs; + + private int survive; + + private int efficiency; + + public PetQuality(int fangs, int survive, int efficiency) { + this.fangs = fangs; + this.survive = survive; + this.efficiency = efficiency; + } + + public static PetQuality random() { + int fangs = RandomUtil.getRandomInt(1, 100); + int survive = RandomUtil.getRandomInt(1, 100); + int efficiency = RandomUtil.getRandomInt(1, 100); + return new PetQuality(fangs, survive, efficiency); + } + + public int getFangs() { + return fangs; + } + + public void setFangs(int fangs) { + this.fangs = fangs; + } + + public int getSurvive() { + return survive; + } + + public void setSurvive(int survive) { + this.survive = survive; + } + + public int getEfficiency() { + return efficiency; + } + + public void setEfficiency(int efficiency) { + this.efficiency = efficiency; + } +} \ No newline at end of file diff --git a/src/src/main/java/com/yaohun/petsystem/util/MessageUtil.java b/src/src/main/java/com/yaohun/petsystem/util/MessageUtil.java new file mode 100644 index 0000000..2d67c53 --- /dev/null +++ b/src/src/main/java/com/yaohun/petsystem/util/MessageUtil.java @@ -0,0 +1,78 @@ +package com.yaohun.petsystem.util; + +import com.yaohun.petsystem.PetMain; +import com.yaohun.petsystem.config.Config; +import org.bukkit.Sound; +import org.bukkit.command.CommandSender; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Player; + +import java.io.File; +import java.util.HashMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class MessageUtil { + + private static HashMap languageMap = new HashMap<>(); + + public static HashMap getLanguageMap() { + return languageMap; + } + + public static void init(PetMain plugin) { + File file = new File(plugin.getDataFolder(), "lang.yml"); + if (!file.exists()) { + plugin.saveResource("lang.yml", false); + } + FileConfiguration config = YamlConfiguration.loadConfiguration(file); + for (String key : config.getKeys(false)) { + ConfigurationSection section = config.getConfigurationSection(key); + for (String key2 : section.getKeys(false)) { + String message = section.getString(key2).replace("&", "§"); + languageMap.put(key2, message); + } + } + } + + public static void sendMessageKey(CommandSender sender, String key, Sound sound) { + String message = Config.getLanguage(key); + sender.sendMessage(convertHexColor(message)); + if (sender instanceof Player) { + Player player = (Player) sender; + player.playSound(player.getLocation(), sound, 0.8f, 1.2f); + } + } + + public static void sendMessage(CommandSender sender, String message, Sound sound) { + message = convertHexColor(message); + sender.sendMessage(message); + if (sender instanceof Player) { + Player player = (Player) sender; + player.playSound(player.getLocation(), sound, 0.8f, 1.2f); + } + } + + public static String convertHexColor(String text) { + if (text == null) return ""; + + // 匹配形如 #FFFFFF 的十六进制颜色代码 + Pattern pattern = Pattern.compile("#[A-Fa-f0-9]{6}"); + Matcher matcher = pattern.matcher(text); + StringBuffer buffer = new StringBuffer(); + // 去掉# + while (matcher.find()) { + String hex = matcher.group().substring(1); + StringBuilder colorBuilder = new StringBuilder("§x"); + for (char c : hex.toCharArray()) { + colorBuilder.append('§').append(c); + } + matcher.appendReplacement(buffer, colorBuilder.toString()); + } + matcher.appendTail(buffer); + return buffer.toString(); + } + +} diff --git a/src/src/main/java/com/yaohun/petsystem/util/PetUtil.java b/src/src/main/java/com/yaohun/petsystem/util/PetUtil.java new file mode 100644 index 0000000..025cf8c --- /dev/null +++ b/src/src/main/java/com/yaohun/petsystem/util/PetUtil.java @@ -0,0 +1,178 @@ +package com.yaohun.petsystem.util; + +import com.yaohun.petsystem.PetMain; +import com.yaohun.petsystem.config.Config; +import com.yaohun.petsystem.data.PlayerData; +import com.yaohun.petsystem.manage.LevelExpManager; +import com.yaohun.petsystem.manage.PlayerManager; +import com.yaohun.petsystem.model.PetNbt; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextDecoration; +import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; +import org.bukkit.entity.Wolf; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +public class PetUtil { + + public static ItemStack refreshPetStackData(PetNbt petNbt) { + return getPetStackDefault(petNbt); + } + + public static ItemStack getPetStackDefault(PetNbt petNbt) { + // 获取相关默认参数 + String petName = petNbt.petData.getPetName(); + ItemStack stack = Config.getPetStackTemplate(); + ItemMeta meta = stack.getItemMeta(); + // 获取当前显示名 + String templateName = "{name}"; + if (meta.hasDisplayName()) { + Component comp = meta.displayName(); + if (comp != null) { + // 将组件转换为普通字符串 + templateName = PlainTextComponentSerializer.plainText().serialize(comp); + } + } + // 替换占位符 + String replaced = templateName.replace("{name}", petName); + // 正确方式:直接用 Adventure Component 设置显示名 + // 设置新的显示名(新版 Adventure API) + meta.displayName(Component.text(replaced) + .color(NamedTextColor.GREEN) + .decoration(TextDecoration.ITALIC, false)); + + List newLore = new ArrayList<>(); + List rawLore = meta.getLore(); + if (rawLore == null) { + rawLore = new ArrayList<>(); + } + + for (String line : rawLore) { + String string = line + .replace("{name}", petName) + .replace("{fangs}", String.valueOf(petNbt.quality.getFangs())) + .replace("{survive}", String.valueOf(petNbt.quality.getSurvive())) + .replace("{efficiency}", String.valueOf(petNbt.quality.getEfficiency())) + .replace("{level}", String.valueOf(petNbt.level)) + .replace("{exp}", String.valueOf(petNbt.exp)) + .replace("{maxExp}", String.valueOf(LevelExpManager.getNeedExpValue(petNbt.level))) + .replace("{damage}", String.valueOf(petNbt.damage)) + .replace("{health}", String.valueOf(petNbt.health)) + .replace("{maxHealth}", String.valueOf(petNbt.maxHealth)); + newLore.add(Component.text(MessageUtil.convertHexColor(string)).decoration(TextDecoration.ITALIC, false)); + } + meta.lore(newLore); + meta.setCustomModelData(petNbt.petData.getCustomIconModelId()); + stack.setItemMeta(meta); + return stack; + } + + public static ItemStack getPetGuiStack(PlayerData playerData, String guiStackID) { + // 1.获取玩家数据 + PetNbt petNbt = playerData.getPetNbt(); + ItemStack petStack = playerData.getPetStack(); + + ItemStack stack = Config.getItemStack(guiStackID); + ItemMeta meta = stack.getItemMeta(); + // 设置新的显示名(新版 Adventure API) + meta.displayName(Component.text(petStack.getItemMeta().getDisplayName()) + .color(NamedTextColor.GREEN) + .decoration(TextDecoration.ITALIC, false)); + + List newLore = new ArrayList<>(); + List rawLore = meta.getLore(); + if (rawLore == null) { + rawLore = new ArrayList<>(); + } + + String statsName = "休息中"; + PlayerManager playerManager = PetMain.getPlayerManager(); + if (playerManager.isPetCallExit(playerData.getPlayerName())) { + statsName = "出战中"; + } + for (String line : rawLore) { + String string = line + .replace("{stats}", statsName) + .replace("{fangs}", String.valueOf(petNbt.quality.getFangs())) + .replace("{survive}", String.valueOf(petNbt.quality.getSurvive())) + .replace("{efficiency}", String.valueOf(petNbt.quality.getEfficiency())) + .replace("{level}", String.valueOf(petNbt.level)) + .replace("{exp}", String.valueOf(petNbt.exp)) + .replace("{maxExp}", String.valueOf(LevelExpManager.getNeedExpValue(petNbt.level))) + .replace("{damage}", String.valueOf(petNbt.damage)) + .replace("{health}", String.valueOf(petNbt.health)) + .replace("{maxHealth}", String.valueOf(petNbt.maxHealth)); + newLore.add(Component.text(MessageUtil.convertHexColor(string)).decoration(TextDecoration.ITALIC, false)); + } + meta.lore(newLore); + meta.setCustomModelData(petNbt.petData.getCustomIconModelId()); + stack.setItemMeta(meta); + return stack; + } + + /*public static NameTag getNameBone(ModeledEntity model, String stringType) { + if (model == null) { + return null; + } + if (model.getModels().size() == 0) { + return null; + } + Optional opt = model.getModels().values().stream().findFirst(); + ActiveModel activeModel = null; + if (opt.isPresent()) { + activeModel = opt.get(); + } else { + return null; + } + ModelBone bone = activeModel.getBone(stringType) + .stream() + .filter(modelBone -> modelBone.getBoneBehavior(BoneBehaviorTypes.NAMETAG).orElse(null) != null) + .findFirst().orElse(null); + if (bone == null) { + return null; + } + NameTag nameTag = bone.getBoneBehavior(BoneBehaviorTypes.NAMETAG).orElse(null); + return nameTag; + }*/ + public static PlayerData updatePetNameTag(Wolf wolf) { + PlayerManager playerManager = PetMain.getPlayerManager(); + if (wolf == null || wolf.isDead()) { + return null; + } + UUID petUuid = wolf.getUniqueId(); + String ownerName = playerManager.getPetOwnerName(petUuid); + if (ownerName == null) { + return null; + } + PlayerData playerData = playerManager.getPlayerData(ownerName); + // 判断玩家是否召唤宠物 + if (playerManager.isPetCallExit(ownerName)) { + /*// 获取宠物的的模型文件 + ModeledEntity modeledEntity = playerManager.getPetEntityModel(petUuid); + if (modeledEntity == null) { + return null; + } + int petLevel = playerData.getPetNbt().level; + double healthNow = wolf.getHealth(); + double healthMax = wolf.getMaxHealth(); + // 计算获得血量百分比 + int percent = (int) Math.round((healthNow / healthMax) * 100.0); + String levelText = Config.getLanguage("pet_level_tag") + .replace("{level}", Config.getLevelChar(petLevel)) + .replace("{health}", Config.getHealthChar(percent)); + NameTag levelTag = PetUtil.getNameBone(modeledEntity, "level"); + if (levelTag != null) { + levelTag.setString(levelText); + levelTag.setVisible(true); + }*/ + return playerData; + } + return null; + } +} diff --git a/src/src/main/java/com/yaohun/petsystem/util/model/BetterModelUtil.java b/src/src/main/java/com/yaohun/petsystem/util/model/BetterModelUtil.java new file mode 100644 index 0000000..716e9d0 --- /dev/null +++ b/src/src/main/java/com/yaohun/petsystem/util/model/BetterModelUtil.java @@ -0,0 +1,81 @@ +package com.yaohun.petsystem.util.model; + +import kr.toxicity.model.api.BetterModel; +import kr.toxicity.model.api.bukkit.platform.BukkitAdapter; +import kr.toxicity.model.api.data.renderer.ModelRenderer; +import kr.toxicity.model.api.tracker.EntityTracker; +import kr.toxicity.model.api.tracker.Tracker; +import kr.toxicity.model.api.tracker.TrackerModifier; +import org.bukkit.Bukkit; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; + +public class BetterModelUtil { + + public static Tracker spawnModel(Player owner, Entity entity, String modelId) { + if (entity == null || entity.isDead()) { + Bukkit.getLogger().warning("宠物模型载入失败: 实体不存在"); + return null; + } + if (modelId == null || modelId.isBlank()) { + Bukkit.getLogger().warning("宠物模型载入失败: 模型ID为空"); + return null; + } + + ModelRenderer renderer = BetterModel.modelOrNull(modelId); + if (renderer == null) { + Bukkit.getLogger().warning("宠物模型不存在: " + modelId); + return null; + } + + EntityTracker tracker = renderer.create( + BukkitAdapter.adapt(entity), + getPetTrackerModifier(), + BetterModelUtil::refreshModelTracker + ); + + refreshModelTracker(tracker); + spawnVisiblePlayers(tracker, entity); + Bukkit.getLogger().info("宠物模型载入成功: 玩家=" + owner.getName() + ", 模型=" + modelId + ", 实体=" + entity.getUniqueId()); + + return tracker; + } + + public static void spawnVisiblePlayers(Tracker tracker, Entity entity) { + if (tracker == null || tracker.isClosed() || entity == null) { + return; + } + for (Player viewer : Bukkit.getOnlinePlayers()) { + if (!viewer.isOnline()) { + continue; + } + if (!viewer.getWorld().equals(entity.getWorld())) { + continue; + } + if (tracker instanceof EntityTracker entityTracker) { + entityTracker.registry().spawnIfNotSpawned(BukkitAdapter.adapt(viewer)); + } else { + tracker.show(BukkitAdapter.adapt(viewer)); + } + } + } + + public static void refreshModelTracker(Tracker tracker) { + if (tracker == null || tracker.isClosed()) { + return; + } + if (tracker instanceof EntityTracker entityTracker) { + entityTracker.updateBaseEntity(); + entityTracker.refresh(); + } + tracker.forceUpdate(true); + } + + private static TrackerModifier getPetTrackerModifier() { + return TrackerModifier.DEFAULT.toBuilder() + .sightTrace(false) + .damageAnimation(false) + .damageTint(false) + .build(); + } +} diff --git a/src/src/main/java/com/yaohun/petsystem/util/wolf/CustomWolf.java b/src/src/main/java/com/yaohun/petsystem/util/wolf/CustomWolf.java new file mode 100644 index 0000000..8e70336 --- /dev/null +++ b/src/src/main/java/com/yaohun/petsystem/util/wolf/CustomWolf.java @@ -0,0 +1,202 @@ +package com.yaohun.petsystem.util.wolf; + +import com.yaohun.petsystem.model.PetNbt; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.animal.wolf.Wolf; +import net.minecraft.world.level.Level; +import net.minecraft.world.phys.Vec3; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.attribute.Attribute; +import org.bukkit.attribute.AttributeInstance; +import org.bukkit.block.Block; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; + +import java.util.UUID; + +/** + * 自定义的狼实体类,用于实现“跟随玩家”的宠物功能。 + * 继承原版 NMS Wolf 实体,通过 tick() 方法控制移动与传送。 + */ +public class CustomWolf extends Wolf { + + private static final double STOP_DISTANCE_SQ = 9.0D; + private static final double TELEPORT_DISTANCE_SQ = 144.0D; + private static final double LOOK_DISTANCE_SQ = 256.0D; + private static final double FOLLOW_SPEED = 1.2D; + private static final double DEFAULT_MOVEMENT_SPEED = 0.4D; + private static final int PATH_RECALC_TICKS = 10; + + private final UUID ownerUuid; + + private int timeToRecalcPath = 0; + + /** + * 构造方法:初始化宠物实体 + * + * @param world NMS 世界对象 + * @param owner 宠物主人(Bukkit Player) + */ + public CustomWolf(Level world, Player owner) { + super(EntityType.WOLF, world); + this.ownerUuid = owner.getUniqueId(); + } + + /** + * 每 tick 执行的逻辑,控制宠物的移动与状态 + */ + @Override + public void tick() { + super.tick(); + + Player owner = Bukkit.getPlayer(ownerUuid); + if (owner == null || !owner.isOnline()) { + this.discard(); + return; + } + if (owner.isDead()) { + this.getNavigation().stop(); + return; + } + if (!owner.getWorld().equals(this.getBukkitEntity().getWorld())) { + this.getNavigation().stop(); + teleportToOwnerLocation(owner); + return; + } + + net.minecraft.world.entity.player.Player nmsOwner = ((CraftPlayer) owner).getHandle(); + + double distanceSq = this.distanceToSqr(nmsOwner); + + if (distanceSq < STOP_DISTANCE_SQ) { + this.getNavigation().stop(); + return; + } + + boolean shouldTeleport = distanceSq > TELEPORT_DISTANCE_SQ; + + if (!shouldTeleport && distanceSq <= LOOK_DISTANCE_SQ) { + this.getLookControl().setLookAt(nmsOwner, 10.0F, this.getMaxHeadXRot()); + } + + if (--this.timeToRecalcPath <= 0) { + this.timeToRecalcPath = PATH_RECALC_TICKS; + + if (shouldTeleport) { + if (!tryTeleportNearOwner(nmsOwner)) { + this.getNavigation().moveTo(nmsOwner, FOLLOW_SPEED); + } + } else { + this.getNavigation().moveTo(nmsOwner, FOLLOW_SPEED); + } + } + } + + /** + * 当宠物距离主人太远时,尝试在主人周围随机传送 + * + * @param nmsOwner 主人 NMS 实体 + */ + private boolean tryTeleportNearOwner(Entity nmsOwner) { + Vec3 pos = nmsOwner.position(); + + for (int i = 0; i < 10; i++) { + int x = (int) Math.floor(pos.x) + (random.nextInt(7) - 3); + int y = (int) Math.floor(pos.y); + int z = (int) Math.floor(pos.z) + (random.nextInt(7) - 3); + + Location location = new Location(getBukkitEntity().getWorld(), x + 0.5D, y, z + 0.5D); + if (isSafeTeleportLocation(location)) { + this.teleportTo(location.getX(), location.getY(), location.getZ()); + this.getNavigation().stop(); + return true; + } + } + return false; + } + + private void teleportToOwnerLocation(Player owner) { + Location location = owner.getLocation(); + if (isSafeTeleportLocation(location)) { + this.getBukkitEntity().teleport(location); + } + } + + private boolean isSafeTeleportLocation(Location location) { + Block feet = location.getBlock(); + Block head = feet.getRelative(0, 1, 0); + Block ground = feet.getRelative(0, -1, 0); + if (!feet.isPassable() || !head.isPassable() || !ground.getType().isSolid()) { + return false; + } + if (!location.getWorld().equals(getBukkitEntity().getWorld())) { + return true; + } + return this.level().noCollision(this, this.getBoundingBox().move( + location.getX() - this.getX(), + location.getY() - this.getY(), + location.getZ() - this.getZ() + )); + } + + /** + * 重写受伤方法,防止主人伤害到自己的宠物 + * + * @param source 伤害来源 + * @param amount 伤害数值 + * @return 是否成功造成伤害 + */ + @Override + public boolean hurtServer(ServerLevel level, DamageSource source, float amount) { + Entity entity = source.getEntity(); + + if (entity != null && entity.getUUID().equals(ownerUuid)) { + return false; + } + return super.hurtServer(level, source, amount); + } + + /** + * 静态方法:用于在 Bukkit 世界中生成并注册一只 CustomWolf + * + * @param owner 宠物主人 + * @return 新生成的 CustomWolf 实例 + */ + public static CustomWolf spawnCustomWolf(Player owner, PetNbt petNbt) { + CraftWorld world = (CraftWorld) owner.getWorld(); + Level nmsWorld = world.getHandle(); + + CustomWolf wolf = new CustomWolf(nmsWorld, owner); + + wolf.setSilent(true); + wolf.setOrderedToSit(false); + wolf.setPos(owner.getLocation().getX(), owner.getLocation().getY(), owner.getLocation().getZ()); + + LivingEntity livingEntity = wolf.getBukkitLivingEntity(); + double maxHealth = Math.max(1.0D, petNbt.maxHealth); + double health = Math.max(1.0D, Math.min(petNbt.health, maxHealth)); + double damage = Math.max(0.0D, petNbt.damage); + + setAttribute(livingEntity, Attribute.MAX_HEALTH, maxHealth); + setAttribute(livingEntity, Attribute.MOVEMENT_SPEED, DEFAULT_MOVEMENT_SPEED); + setAttribute(livingEntity, Attribute.ATTACK_DAMAGE, damage); + livingEntity.setHealth(health); + + nmsWorld.addFreshEntity(wolf); + + return wolf; + } + + private static void setAttribute(LivingEntity entity, Attribute attribute, double value) { + AttributeInstance instance = entity.getAttribute(attribute); + if (instance != null) { + instance.setBaseValue(value); + } + } +} diff --git a/src/src/main/resources/config.yml b/src/src/main/resources/config.yml new file mode 100644 index 0000000..75039dc --- /dev/null +++ b/src/src/main/resources/config.yml @@ -0,0 +1,31 @@ +NeedExpSettings: + 1: 10000 + 2: 10000 + 3: 10000 + 4: 10000 + 5: 10000 + 6: 10000 + 7: 10000 + 8: 10000 + 9: 10000 + 10: 10000 + 11: 10000 + 12: 10000 + 13: 10000 + 14: 10000 + 15: 10000 + 16: 10000 + 17: 10000 + 18: 10000 + 19: 10000 + 20: 10000 + 21: 10000 + 22: 10000 + 23: 10000 + 24: 10000 + 25: 10000 + 26: 10000 + 27: 10000 + 28: 10000 + 29: 10000 + 30: -1 \ No newline at end of file diff --git a/src/src/main/resources/lang.yml b/src/src/main/resources/lang.yml new file mode 100644 index 0000000..e69de29 diff --git a/src/src/main/resources/plugin.yml b/src/src/main/resources/plugin.yml new file mode 100644 index 0000000..66b3172 --- /dev/null +++ b/src/src/main/resources/plugin.yml @@ -0,0 +1,6 @@ +name: AuPet +main: com.yaohun.petsystem.PetMain +version: 1.0.1 +api-version: 1.21 +commands: + apet: