完善并添加README.md

This commit is contained in:
yhy
2026-06-03 07:09:32 +08:00
parent 293a160856
commit 7cd89e59fd
47 changed files with 750 additions and 3726 deletions

508
README.md Normal file
View File

@@ -0,0 +1,508 @@
# AuPet 灵宠系统
AuPet 是一个面向 Minecraft Paper 1.21 系列服务端的灵宠插件。
插件围绕“宠物物品 + 玩家携带数据 + 宠物召唤实体 + GUI 管理”构建,适合用于服务器宠物养成、宠物技能展示、宠物出战和宠物经验成长玩法。
核心功能:
- 玩家通过 GUI 查看当前携带的灵宠、等级、经验、血量和资质
- 支持将手持宠物物品携带到玩家数据中
- 支持从玩家数据中取出宠物物品
- 支持召唤和召回宠物实体
- 支持 BetterModel 模型展示
- 支持 MythicMobs 技能触发
- 支持宠物喂养、经验增加和进化阶段控制
- 支持宠物改名
- 支持 MySQL 持久化玩家宠物数据
- 提供宠物召唤事件,方便其他插件监听
简单理解:
```yml
AuPet = 灵宠物品管理 + 宠物数据持久化 + 宠物 GUI + 宠物召唤 + 技能触发
玩家 = 携带、召唤、喂养、改名和培养宠物
管理员 = 配置宠物类型、模型、技能、等级经验和 GUI 展示
```
------
# 运行环境
## 服务端版本
```yml
Minecraft: 1.21.x
推荐服务端: Paper
Java: 21
```
当前构建配置使用:
```yml
Paper API: 1.21.8-R0.1-SNAPSHOT
plugin.yml api-version: 1.21
```
## 前置依赖
插件代码依赖以下组件:
```yml
DemonAPI
CraftDataManager
NBTAPI
MythicMobs
BetterModel
```
服务器启动前应确认这些插件或依赖已正确安装,并且版本与构建配置兼容。
------
# 安装方式
## 第一步:构建插件
在项目根目录执行:
```bash
mvn -q -DskipTests package
```
构建成功后会生成:
```yml
target/AuPet-1.0-SNAPSHOT.jar
```
## 第二步:放入服务器
将插件 jar 放入服务器插件目录:
```yml
plugins/AuPet.jar
```
同时放入前置依赖插件。
## 第三步:启动服务器
首次启动后会生成插件配置目录。
控制台应能看到宠物系统参数、等级经验配置和宠物类型加载日志。
------
# 目录结构
## 插件默认资源
```yml
config.yml
lang.yml
plugin.yml
```
## 插件数据目录
宠物类型配置读取自:
```yml
plugins/AuPet/PetData/
```
玩家携带宠物数据保存到 MySQL 表:
```yml
aupets_data
```
表字段:
```yml
PLAYERNAME: 玩家名
PETSTACK: 序列化后的宠物 ItemStack
```
------
# 玩家命令
## 打开主界面
```yml
/apet
/apet open
```
玩家未携带宠物时会打开携带界面;已携带宠物时会打开宠物主界面。
## 携带或取出宠物
```yml
/apet carry
```
当玩家未携带宠物时:
- 需要手持带有 `petKey` NBT 的宠物物品
- 执行后会将手持宠物写入玩家数据
- 手持栏会被清空
当玩家已携带宠物时:
- 会收回已召唤宠物
- 刷新宠物物品数据
- 将宠物物品返还给玩家
如果玩家背包已满,剩余物品会掉落在玩家当前位置。
## 召唤或召回宠物
```yml
/apet call
```
当宠物未出战时会召唤宠物;当宠物已出战时会召回宠物。
召唤时会:
- 生成狼实体作为宠物基础实体
- 应用宠物血量、攻击、移动速度属性
- 加载 BetterModel 模型
- 触发配置的出生技能
- 触发 `PetCallEvent`
------
# 管理员命令
管理员命令需要:
```yml
admin.use
```
## 查看帮助
```yml
/apet
```
## 重载配置
```yml
/apet reload
```
重载内容:
- 语言文件
- 默认配置
- 等级经验配置
- 宠物模板
- GUI 物品配置
- 品质显示配置
- 宠物类型配置
## 给予宠物
```yml
/apet give 宠物Key 玩家名
```
示例:
```yml
/apet give cat Steve
```
插件会读取 `PetData` 中对应宠物配置,生成默认宠物物品并发放给目标玩家。
## 增加宠物经验
```yml
/apet addexp 经验 玩家名
```
给玩家当前携带的宠物增加经验。
## 增加手持宠物经验
```yml
/apet addexp 经验 玩家名 true
```
给目标玩家手持的宠物物品增加经验。
------
# 宠物配置
宠物配置文件位于:
```yml
plugins/AuPet/PetData/宠物Key.yml
```
示例:
```yml
PetName: "§a森林灵宠"
MaxLevel: 30
ModelId: forest_pet
CustomIconModelId: 10001
AccessChannels:
- "活动获取"
- "商城购买"
SummonSkill: "pet_spawn_effect"
DeathSkill: "default"
Skills:
bite:
mmSkill: "pet_bite"
needLevel: 1
trigger: "onAttack"
coolDown: 5000
item:
name: "&a撕咬"
lore:
- "&7触发条件: 攻击时"
- "&7冷却: 5秒"
```
字段说明:
```yml
PetName: 宠物显示名
MaxLevel: 宠物最大等级
ModelId: BetterModel 模型 ID
CustomIconModelId: 宠物物品 CustomModelData
AccessChannels: 获取渠道展示
SummonSkill: 召唤时触发的 MythicMobs 技能default 表示不触发
DeathSkill: 死亡技能预留字段
Skills: 宠物技能列表
```
------
# 技能触发器
当前代码支持:
```yml
onAttack: 玩家攻击实体时触发
onDamaged: 玩家受到伤害时触发
onSpawn: 宠物召唤后触发 SummonSkill
```
技能由 MythicMobs 执行。
技能冷却使用玩家 UUID 和技能名作为冷却键。
------
# 等级经验配置
默认配置节点:
```yml
NeedExpSettings:
1: 10000
2: 10000
3: 10000
30: -1
```
说明:
```yml
正数: 当前等级升到下一级所需经验
-1: 终止等级,不再继续升级
```
宠物还会受到进化阶段限制:
```yml
进化 1: 最高 20 级
进化 2: 最高 40 级
进化 3: 最高 60 级
进化 4: 最高 70 级
进化 5: 最高 75 级
```
------
# GUI 功能
## 主界面
主界面展示:
- 宠物物品
- 宠物技能
- 宠物资质
- 召唤或召回按钮
- 喂养入口
- 改名入口
## 携带界面
用于携带或取出宠物。
玩家可以通过点击背包中的宠物物品,将其写入当前携带数据。
## 喂养界面
支持放入带有以下 NBT 的道具:
```yml
petAppleExp: 增加经验
petAppleUp: 进化阶段要求等级
```
关闭界面时,如果喂养槽仍有物品,会自动返还玩家。
背包满时,剩余物会掉落到玩家当前位置。
------
# 数据保存机制
玩家数据以序列化 ItemStack 形式保存到 MySQL。
主要保存时机:
- 携带宠物
- 取出宠物
- 喂养加经验
- 宠物改名
- 玩家退出
- 插件关闭
注意:
```yml
当前数据库读写仍是同步模型。
在线人数较多或数据库延迟较高时,建议后续改造为异步加载和保存队列。
```
------
# API 与事件
## 宠物经验 API
其他代码可以调用:
```java
PetExpAPI.addExp(player, 1000);
```
也可以对手持宠物物品增加经验:
```java
PetNbt petNbt = PetExpAPI.addExp(playerName, itemStack, 1000);
```
## 宠物召唤事件
宠物召唤成功后会触发:
```java
PetCallEvent
```
可读取:
```java
event.getOwner();
event.getCutomName();
event.getEntityUuid();
```
------
# 构建验证
本地验证命令:
```bash
mvn -q test
mvn -q -DskipTests package
```
当前验证结果:
```yml
mvn test: 通过
mvn package: 通过
```
------
# 常见问题
## 插件启动时报缺少类
检查前置依赖是否安装:
```yml
DemonAPI
CraftDataManager
NBTAPI
MythicMobs
BetterModel
```
同时检查这些依赖的版本是否与构建配置兼容。
## 执行 /apet give 找不到宠物
检查:
```yml
plugins/AuPet/PetData/宠物Key.yml 是否存在
文件名是否与命令中的宠物Key一致
配置是否能被 YAML 正常读取
```
## 召唤宠物失败
检查:
```yml
BetterModel 中是否存在对应 ModelId
宠物血量是否大于 0
MythicMobs 技能名是否正确
```
## 宠物无法升级
检查:
```yml
NeedExpSettings 当前等级是否为 -1
宠物是否已达到 MaxLevel
宠物是否已达到当前进化阶段上限
```
## GUI 按钮显示异常
检查:
```yml
config.yml 中 GuiItemData 是否完整
lang.yml 中语言键是否完整
```
------
# 维护建议
- 发布前同步检查 `plugin.yml` 版本号。
- 修改宠物配置后执行 `/apet reload`
- 新增宠物前先在测试服验证 BetterModel 模型和 MythicMobs 技能。
- 不建议在高峰期进行大量玩家数据迁移。
- 后续优先补充数据库异步队列和 MockBukkit 测试。
- 每次更新后至少完成一次登录、携带宠物、打开 GUI、召唤宠物、喂养、退出保存的冒烟测试。

View File

@@ -1,604 +0,0 @@
# 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、领取奖励、退出保存的冒烟测试。

51
pom.xml
View File

@@ -21,6 +21,10 @@
<id>papermc-repo</id> <id>papermc-repo</id>
<url>https://repo.papermc.io/repository/maven-public/</url> <url>https://repo.papermc.io/repository/maven-public/</url>
</repository> </repository>
<repository>
<id>public-survive</id>
<url>https://repo.aurora-pixels.com/repository/public-survive/</url>
</repository>
</repositories> </repositories>
@@ -32,46 +36,29 @@
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>local</groupId> <groupId>de.tr7zw.nbtapi.plugin</groupId>
<artifactId>demon-api</artifactId> <artifactId>NBTAPI</artifactId>
<version>local</version> <version>2.15.6</version>
<scope>system</scope>
<systemPath>${parent.project.dir}/lib/DemonAPI.jar</systemPath>
</dependency> </dependency>
<dependency> <dependency>
<groupId>local</groupId> <groupId>me.Demon.DemonPlugin</groupId>
<artifactId>nbt-api</artifactId> <artifactId>DemonAPI</artifactId>
<version>local</version> <version>2.1.2</version>
<scope>system</scope>
<systemPath>${parent.project.dir}/lib/item-nbt-api-plugin-2.15.1.jar</systemPath>
</dependency> </dependency>
<dependency> <dependency>
<groupId>local</groupId> <groupId>io.lumine.mythic.bukkit</groupId>
<artifactId>mythicmobs</artifactId> <artifactId>MythicMobs</artifactId>
<version>local</version> <version>5.9.1</version>
<scope>system</scope>
<systemPath>${parent.project.dir}/lib/MythicMobs-5.9.1.jar</systemPath>
</dependency> </dependency>
<dependency> <dependency>
<groupId>local</groupId> <groupId>kr.toxicity.model.paper</groupId>
<artifactId>bettermodel</artifactId> <artifactId>BetterModel</artifactId>
<version>local</version> <version>2.2.0</version>
<scope>system</scope>
<systemPath>${parent.project.dir}/lib/BetterModel-2.2.0-paper.jar</systemPath>
</dependency> </dependency>
<dependency> <dependency>
<groupId>local</groupId> <groupId>com.tianyu.datamanager</groupId>
<artifactId>craft-data-manager</artifactId> <artifactId>CraftDataManager</artifactId>
<version>local</version> <version>1.0.0</version>
<scope>system</scope>
<systemPath>${parent.project.dir}/lib/CraftDataManager.jar</systemPath>
</dependency>
<dependency>
<groupId>local</groupId>
<artifactId>paper-1.21.8-mapped-server</artifactId>
<version>1.21.8</version>
<scope>system</scope>
<systemPath>${parent.project.dir}/.gradle/caches/paperweight/taskCache/mappedServerJar.jar</systemPath>
</dependency> </dependency>
</dependencies> </dependencies>
</project> </project>

View File

@@ -16,6 +16,7 @@ import com.yaohun.petsystem.manage.PetManager;
import com.yaohun.petsystem.manage.PlayerManager; import com.yaohun.petsystem.manage.PlayerManager;
import com.yaohun.petsystem.model.PetNbt; import com.yaohun.petsystem.model.PetNbt;
import com.yaohun.petsystem.util.MessageUtil; import com.yaohun.petsystem.util.MessageUtil;
import com.yaohun.petsystem.util.InventoryUtil;
import com.yaohun.petsystem.util.PetUtil; import com.yaohun.petsystem.util.PetUtil;
import de.tr7zw.nbtapi.NBTItem; import de.tr7zw.nbtapi.NBTItem;
import io.lumine.mythic.bukkit.MythicBukkit; import io.lumine.mythic.bukkit.MythicBukkit;
@@ -65,8 +66,10 @@ public class PetMain extends JavaPlugin {
@Override @Override
public void onDisable() { public void onDisable() {
if (getPlayerManager() != null) {
getPlayerManager().closeSaveAllData(); getPlayerManager().closeSaveAllData();
} }
}
@Override @Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
@@ -203,6 +206,10 @@ public class PetMain extends JavaPlugin {
return true; return true;
} }
PetNbt petNbt = PetExpAPI.addExp(playerName, stack, exp); PetNbt petNbt = PetExpAPI.addExp(playerName, stack, exp);
if (petNbt == null) {
sender.sendMessage(prefix + "执行失败,未找到宠物配置.");
return true;
}
ItemStack petStack = PetUtil.refreshPetStackData(petNbt); ItemStack petStack = PetUtil.refreshPetStackData(petNbt);
player.getInventory().setItemInMainHand(petNbt.saveIoItem(petStack)); player.getInventory().setItemInMainHand(petNbt.saveIoItem(petStack));
sender.sendMessage(prefix + "给予 §6手持灵宠 §a经验: §d+" + exp); sender.sendMessage(prefix + "给予 §6手持灵宠 §a经验: §d+" + exp);
@@ -234,7 +241,7 @@ public class PetMain extends JavaPlugin {
} }
PetNbt petNbt = new PetNbt(petData); PetNbt petNbt = new PetNbt(petData);
ItemStack petStack = PetUtil.getPetStackDefault(petNbt); ItemStack petStack = PetUtil.getPetStackDefault(petNbt);
player.getInventory().addItem(petNbt.saveIoItem(petStack)); InventoryUtil.giveOrDrop(player, petNbt.saveIoItem(petStack));
DemonAPI.sendMessage(sender, "玩家: §e" + playerName + " §a宠物: §6" + petKey + " §r[背包]"); DemonAPI.sendMessage(sender, "玩家: §e" + playerName + " §a宠物: §6" + petKey + " §r[背包]");
return true; return true;
} }

View File

@@ -29,49 +29,24 @@ public class PetExpAPI {
return; return;
} }
int petLevel = petNbt.level; int petLevel = petNbt.level;
if (petLevel >= petData.getMaxLevel()) { int levelCap = getLevelCap(petData, petNbt.evolution);
if (petLevel >= levelCap) {
return; 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); int needExp = LevelExpManager.getNeedExpValue(petLevel);
petNbt.setTotalExp(petNbt.totalExp + addExp); petNbt.setTotalExp(petNbt.totalExp + addExp);
int nowExp = petNbt.exp; int nowExp = petNbt.exp;
addExp = nowExp + addExp; addExp = nowExp + addExp;
while (addExp >= needExp) { while (needExp > 0 && petLevel < levelCap && addExp >= needExp) {
if (petLevel < 0) {
break;
}
addExp -= needExp; addExp -= needExp;
petLevel++; petLevel++;
needExp = LevelExpManager.getNeedExpValue(petLevel); needExp = LevelExpManager.getNeedExpValue(petLevel);
bcmupLevelEvant(playerName, petLevel); bcmupLevelEvant(playerName, petLevel);
playerData.refreshPetStack();
} }
petNbt.setExp(addExp); petNbt.setExp(addExp);
petNbt.setLevel(petLevel); petNbt.setLevel(petLevel);
playerData.refreshPetStack();
playerData.savePlayerData(); playerData.savePlayerData();
} }
@@ -86,7 +61,8 @@ public class PetExpAPI {
PetNbt petNbt = new PetNbt(); PetNbt petNbt = new PetNbt();
petNbt.loadNbtData(nbtItem); petNbt.loadNbtData(nbtItem);
int petLevel = petNbt.level; int petLevel = petNbt.level;
if (petLevel >= petData.getMaxLevel()) { int levelCap = getLevelCap(petData, petNbt.evolution);
if (petLevel >= levelCap) {
return petNbt; return petNbt;
} }
int needExp = LevelExpManager.getNeedExpValue(petLevel); int needExp = LevelExpManager.getNeedExpValue(petLevel);
@@ -94,10 +70,7 @@ public class PetExpAPI {
int nowExp = petNbt.exp; int nowExp = petNbt.exp;
addExp = nowExp + addExp; addExp = nowExp + addExp;
while (addExp >= needExp) { while (needExp > 0 && petLevel < levelCap && addExp >= needExp) {
if (petLevel < 0) {
break;
}
addExp -= needExp; addExp -= needExp;
petLevel++; petLevel++;
needExp = LevelExpManager.getNeedExpValue(petLevel); needExp = LevelExpManager.getNeedExpValue(petLevel);
@@ -108,6 +81,22 @@ public class PetExpAPI {
return petNbt; return petNbt;
} }
private static int getLevelCap(PetData petData, int evolution) {
int maxLevel = petData.getMaxLevel();
int evolutionLimit = switch (evolution) {
case 1 -> 20;
case 2 -> 40;
case 3 -> 60;
case 4 -> 70;
case 5 -> 75;
default -> maxLevel;
};
if (maxLevel <= 0) {
return evolutionLimit;
}
return Math.min(maxLevel, evolutionLimit);
}
public static void bcmupLevelEvant(String playerName, int level) { public static void bcmupLevelEvant(String playerName, int level) {
boolean butt = false; boolean butt = false;
if (level == 10 || level == 20 || level == 30 || level == 40 || level == 50 || if (level == 10 || level == 20 || level == 30 || level == 40 || level == 50 ||

View File

@@ -44,14 +44,21 @@ public class Config {
} }
private static void loadQualityShowMap(FileConfiguration config) { private static void loadQualityShowMap(FileConfiguration config) {
qualityShowMap.clear();
qualityShowMap.put(0, "§f普通");
ConfigurationSection section = config.getConfigurationSection("QualityShow"); ConfigurationSection section = config.getConfigurationSection("QualityShow");
if (section == null) { if (section == null) {
Bukkit.getConsoleSender().sendMessage("- 资质稀有度: §f" + qualityShowMap.size() + "");
return; return;
} }
for (String levelKey : section.getKeys(false)) { for (String levelKey : section.getKeys(false)) {
try {
int level = Integer.parseInt(levelKey); int level = Integer.parseInt(levelKey);
String show = section.getString(levelKey).replace("&", "§"); String show = section.getString(levelKey, "§f普通").replace("&", "§");
qualityShowMap.put(level, show); qualityShowMap.put(level, show);
} catch (NumberFormatException e) {
Bukkit.getConsoleSender().sendMessage("§c[宠物系统] 资质显示配置无效: " + levelKey);
}
} }
Bukkit.getConsoleSender().sendMessage("- 资质稀有度: §f" + qualityShowMap.size() + ""); Bukkit.getConsoleSender().sendMessage("- 资质稀有度: §f" + qualityShowMap.size() + "");
} }
@@ -62,8 +69,8 @@ public class Config {
try { try {
material = Material.valueOf(stackType.toUpperCase()); material = Material.valueOf(stackType.toUpperCase());
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
Bukkit.getConsoleSender().sendMessage("- 宠物模板载入: §c失败"); material = Material.DIAMOND;
return; Bukkit.getConsoleSender().sendMessage("- 宠物模板载入: §c材质无效已使用默认材质");
} }
String stackName = config.getString("Pet_Stack_Template.name", "§f宠物"); String stackName = config.getString("Pet_Stack_Template.name", "§f宠物");
List<String> stringList = config.getStringList("Pet_Stack_Template.lore"); List<String> stringList = config.getStringList("Pet_Stack_Template.lore");
@@ -80,6 +87,9 @@ public class Config {
} }
public static ItemStack getPetStackTemplate() { public static ItemStack getPetStackTemplate() {
if (petStackTemplate == null) {
petStackTemplate = new ItemStack(Material.DIAMOND);
}
return petStackTemplate.clone(); return petStackTemplate.clone();
} }
@@ -119,7 +129,7 @@ public class Config {
public static String getQualityShow(int value) { public static String getQualityShow(int value) {
Map.Entry<Integer, String> entry = qualityShowMap.floorEntry(value); Map.Entry<Integer, String> entry = qualityShowMap.floorEntry(value);
return entry != null ? entry.getValue() : qualityShowMap.get(0); return entry != null ? entry.getValue() : "§f普通";
} }
public static String getHealthChar(int percent) { public static String getHealthChar(int percent) {

View File

@@ -5,6 +5,7 @@ import com.yaohun.petsystem.config.Config;
import com.yaohun.petsystem.data.PlayerData; import com.yaohun.petsystem.data.PlayerData;
import com.yaohun.petsystem.gui.holder.CarryGuiHolder; import com.yaohun.petsystem.gui.holder.CarryGuiHolder;
import com.yaohun.petsystem.manage.PlayerManager; import com.yaohun.petsystem.manage.PlayerManager;
import com.yaohun.petsystem.util.InventoryUtil;
import com.yaohun.petsystem.util.MessageUtil; import com.yaohun.petsystem.util.MessageUtil;
import de.tr7zw.nbtapi.NBTItem; import de.tr7zw.nbtapi.NBTItem;
import me.Demon.DemonPlugin.DemonAPI; import me.Demon.DemonPlugin.DemonAPI;
@@ -54,7 +55,7 @@ public class CarryGui implements Listener {
playerManager.removePetCall(playerName); playerManager.removePetCall(playerName);
playerData.refreshPetStack(); playerData.refreshPetStack();
ItemStack petStack = playerData.getPetStack(); ItemStack petStack = playerData.getPetStack();
player.getInventory().addItem(petStack); InventoryUtil.giveOrDrop(player, petStack);
playerData.setPetStack(DemonAPI.getErrItems()); playerData.setPetStack(DemonAPI.getErrItems());
playerData.savePlayerData(); playerData.savePlayerData();
inv.setItem(PET_SLOT, Config.getItemStack("carry_gui")); inv.setItem(PET_SLOT, Config.getItemStack("carry_gui"));
@@ -78,7 +79,7 @@ public class CarryGui implements Listener {
playerManager.removePetCall(playerName); playerManager.removePetCall(playerName);
playerData.refreshPetStack(); playerData.refreshPetStack();
ItemStack petStack = playerData.getPetStack(); ItemStack petStack = playerData.getPetStack();
player.getInventory().addItem(petStack); InventoryUtil.giveOrDrop(player, petStack);
playerData.setPetStack(DemonAPI.getErrItems()); playerData.setPetStack(DemonAPI.getErrItems());
playerData.savePlayerData(); playerData.savePlayerData();
MessageUtil.sendMessageKey(player, "pet_stack_carry_takeOut", Sound.ENTITY_PLAYER_LEVELUP); MessageUtil.sendMessageKey(player, "pet_stack_carry_takeOut", Sound.ENTITY_PLAYER_LEVELUP);

View File

@@ -8,6 +8,7 @@ import com.yaohun.petsystem.gui.holder.FeedingGuiHolder;
import com.yaohun.petsystem.manage.LevelExpManager; import com.yaohun.petsystem.manage.LevelExpManager;
import com.yaohun.petsystem.manage.PlayerManager; import com.yaohun.petsystem.manage.PlayerManager;
import com.yaohun.petsystem.model.PetNbt; import com.yaohun.petsystem.model.PetNbt;
import com.yaohun.petsystem.util.InventoryUtil;
import com.yaohun.petsystem.util.MessageUtil; import com.yaohun.petsystem.util.MessageUtil;
import com.yaohun.petsystem.util.PetUtil; import com.yaohun.petsystem.util.PetUtil;
import de.tr7zw.nbtapi.NBTItem; import de.tr7zw.nbtapi.NBTItem;
@@ -96,7 +97,7 @@ public class FeedingGui implements Listener {
// 判断槽位内是否已存在物品 // 判断槽位内是否已存在物品
ItemStack foodStack = getFooedStack(inv); ItemStack foodStack = getFooedStack(inv);
if (foodStack != null) { if (foodStack != null) {
player.getInventory().addItem(foodStack); InventoryUtil.giveOrDrop(player, foodStack);
} }
e.setCurrentItem(null); e.setCurrentItem(null);
inv.setItem(FOOD_SLOT, clickStack); inv.setItem(FOOD_SLOT, clickStack);
@@ -110,7 +111,7 @@ public class FeedingGui implements Listener {
// 判断槽位内是否已存在物品 // 判断槽位内是否已存在物品
ItemStack foodStack = getFooedStack(inv); ItemStack foodStack = getFooedStack(inv);
if (foodStack != null) { if (foodStack != null) {
player.getInventory().addItem(foodStack); InventoryUtil.giveOrDrop(player, foodStack);
inv.setItem(FOOD_SLOT, DemonAPI.getPaperAirItems()); inv.setItem(FOOD_SLOT, DemonAPI.getPaperAirItems());
} }
player.playSound(player.getLocation(), Sound.UI_BUTTON_CLICK, 1, 1); player.playSound(player.getLocation(), Sound.UI_BUTTON_CLICK, 1, 1);
@@ -199,7 +200,7 @@ public class FeedingGui implements Listener {
ItemStack stack = getFooedStack(inv); ItemStack stack = getFooedStack(inv);
if (stack != null) { if (stack != null) {
inv.setItem(FOOD_SLOT, DemonAPI.getPaperAirItems()); inv.setItem(FOOD_SLOT, DemonAPI.getPaperAirItems());
player.getInventory().addItem(stack); InventoryUtil.giveOrDrop(player, stack);
} }
} }
} }

View File

@@ -9,6 +9,7 @@ import com.yaohun.petsystem.gui.holder.MainGuiHolder;
import com.yaohun.petsystem.listener.RePetNameListener; import com.yaohun.petsystem.listener.RePetNameListener;
import com.yaohun.petsystem.manage.PlayerManager; import com.yaohun.petsystem.manage.PlayerManager;
import com.yaohun.petsystem.model.PetNbt; import com.yaohun.petsystem.model.PetNbt;
import com.yaohun.petsystem.util.InventoryUtil;
import com.yaohun.petsystem.util.MessageUtil; import com.yaohun.petsystem.util.MessageUtil;
import com.yaohun.petsystem.util.PetUtil; import com.yaohun.petsystem.util.PetUtil;
import me.Demon.DemonPlugin.DemonAPI; import me.Demon.DemonPlugin.DemonAPI;
@@ -150,7 +151,7 @@ public class MainGui implements Listener {
playerManager.removePetCall(playerName); playerManager.removePetCall(playerName);
playerData.refreshPetStack(); playerData.refreshPetStack();
ItemStack petStack = playerData.getPetStack(); ItemStack petStack = playerData.getPetStack();
player.getInventory().addItem(petStack); InventoryUtil.giveOrDrop(player, petStack);
playerData.setPetStack(DemonAPI.getErrItems()); playerData.setPetStack(DemonAPI.getErrItems());
playerData.savePlayerData(); playerData.savePlayerData();
player.closeInventory(); player.closeInventory();

View File

@@ -38,6 +38,10 @@ public class PlayerListener implements Listener {
if (petUUID == null) { if (petUUID == null) {
return; return;
} }
if (petUUID.equals(e.getEntity().getUniqueId())) {
e.setCancelled(true);
return;
}
Entity entity = Bukkit.getEntity(petUUID); Entity entity = Bukkit.getEntity(petUUID);
if (entity instanceof Wolf wolf) { if (entity instanceof Wolf wolf) {
wolf.setTarget(target); wolf.setTarget(target);
@@ -74,6 +78,6 @@ public class PlayerListener implements Listener {
Player player = e.getPlayer(); Player player = e.getPlayer();
String playerName = player.getName(); String playerName = player.getName();
PlayerManager playerManager = PetMain.getPlayerManager(); PlayerManager playerManager = PetMain.getPlayerManager();
playerManager.removePetCall(playerName); playerManager.closeSaveData(playerName);
} }
} }

View File

@@ -5,38 +5,39 @@ import com.yaohun.petsystem.config.Config;
import com.yaohun.petsystem.data.PlayerData; import com.yaohun.petsystem.data.PlayerData;
import com.yaohun.petsystem.manage.PlayerManager; import com.yaohun.petsystem.manage.PlayerManager;
import com.yaohun.petsystem.util.MessageUtil; import com.yaohun.petsystem.util.MessageUtil;
import org.bukkit.Bukkit;
import org.bukkit.Sound; import org.bukkit.Sound;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.player.AsyncPlayerChatEvent; import org.bukkit.event.player.AsyncPlayerChatEvent;
import java.util.ArrayList; import java.util.Collections;
import java.util.List; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.UUID; import java.util.UUID;
public class RePetNameListener implements Listener { public class RePetNameListener implements Listener {
private static List<UUID> reNameList = new ArrayList<>(); private static final Set<UUID> reNameList = Collections.newSetFromMap(new ConcurrentHashMap<>());
public static void addReNameUUID(UUID uuid) { public static void addReNameUUID(UUID uuid) {
if (!reNameList.contains(uuid)) {
reNameList.add(uuid); reNameList.add(uuid);
} }
}
@EventHandler @EventHandler
public void onChat(AsyncPlayerChatEvent e) { public void onChat(AsyncPlayerChatEvent e) {
Player player = e.getPlayer(); Player player = e.getPlayer();
UUID uuid = player.getUniqueId(); UUID uuid = player.getUniqueId();
if (reNameList.isEmpty()) { if (!reNameList.remove(uuid)) {
return;
}
if (!reNameList.contains(uuid)) {
return; return;
} }
e.setCancelled(true); e.setCancelled(true);
reNameList.remove(uuid); String reName = e.getMessage();
Bukkit.getScheduler().runTask(PetMain.inst(), () -> handleRename(player, reName));
}
private void handleRename(Player player, String reName) {
String playerName = player.getName(); String playerName = player.getName();
PlayerManager playerManager = PetMain.getPlayerManager(); PlayerManager playerManager = PetMain.getPlayerManager();
PlayerData playerData = playerManager.getPlayerData(playerName); PlayerData playerData = playerManager.getPlayerData(playerName);
@@ -44,7 +45,6 @@ public class RePetNameListener implements Listener {
MessageUtil.sendMessageKey(player, "pet_rename_no_pets", Sound.ENTITY_VILLAGER_NO); MessageUtil.sendMessageKey(player, "pet_rename_no_pets", Sound.ENTITY_VILLAGER_NO);
return; return;
} }
String reName = e.getMessage();
if (reName.isEmpty()) { if (reName.isEmpty()) {
MessageUtil.sendMessageKey(player, "pet_rename_empty", Sound.ENTITY_VILLAGER_NO); MessageUtil.sendMessageKey(player, "pet_rename_empty", Sound.ENTITY_VILLAGER_NO);
return; return;

View File

@@ -2,6 +2,7 @@ package com.yaohun.petsystem.manage;
import com.yaohun.petsystem.PetMain; import com.yaohun.petsystem.PetMain;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.configuration.file.YamlConfiguration;
@@ -13,17 +14,43 @@ public class LevelExpManager {
private static HashMap<Integer, Integer> needExpMap = new HashMap<>(); private static HashMap<Integer, Integer> needExpMap = new HashMap<>();
public static void reloadLevelManager(PetMain plugin) { public static void reloadLevelManager(PetMain plugin) {
needExpMap.clear();
ConfigurationSection section = plugin.getConfig().getConfigurationSection("NeedExpSettings");
if (section != null) {
loadNeedExpSection(section);
} else {
loadLegacyNeedExpFile(plugin);
}
Bukkit.getConsoleSender().sendMessage("§7- 等级经验配置: §f" + needExpMap.size() + "");
}
private static void loadNeedExpSection(ConfigurationSection section) {
for (String levelKey : section.getKeys(false)) {
try {
int level = Integer.parseInt(levelKey);
int needExp = section.getInt(levelKey);
needExpMap.put(level, needExp);
} catch (NumberFormatException e) {
Bukkit.getConsoleSender().sendMessage("§c[宠物系统] 等级经验配置无效: " + levelKey);
}
}
}
private static void loadLegacyNeedExpFile(PetMain plugin) {
File file = new File(plugin.getDataFolder() + "/Settings", "needExp.yml"); File file = new File(plugin.getDataFolder() + "/Settings", "needExp.yml");
if (file.getParentFile().exists()) { if (!file.exists()) {
file.getParentFile().mkdirs(); return;
} }
FileConfiguration config = YamlConfiguration.loadConfiguration(file); FileConfiguration config = YamlConfiguration.loadConfiguration(file);
for (String levelKey : config.getKeys(false)) { for (String levelKey : config.getKeys(false)) {
try {
int level = Integer.parseInt(levelKey); int level = Integer.parseInt(levelKey);
int needExp = config.getInt(levelKey); int needExp = config.getInt(levelKey);
needExpMap.put(level, needExp); needExpMap.put(level, needExp);
} catch (NumberFormatException e) {
Bukkit.getConsoleSender().sendMessage("§c[宠物系统] 旧等级经验配置无效: " + levelKey);
}
} }
Bukkit.getConsoleSender().sendMessage("§7- 等级经验配置: §f" + needExpMap.size() + "");
} }
public static int getNeedExpValue(int level) { public static int getNeedExpValue(int level) {

View File

@@ -6,6 +6,7 @@ import com.yaohun.petsystem.data.PetData;
import com.yaohun.petsystem.data.PetSQL; import com.yaohun.petsystem.data.PetSQL;
import com.yaohun.petsystem.data.PlayerData; import com.yaohun.petsystem.data.PlayerData;
import com.yaohun.petsystem.event.PetCallEvent; import com.yaohun.petsystem.event.PetCallEvent;
import com.yaohun.petsystem.util.InventoryUtil;
import com.yaohun.petsystem.util.MessageUtil; import com.yaohun.petsystem.util.MessageUtil;
import com.yaohun.petsystem.util.model.BetterModelUtil; import com.yaohun.petsystem.util.model.BetterModelUtil;
import com.yaohun.petsystem.util.wolf.CustomWolf; import com.yaohun.petsystem.util.wolf.CustomWolf;
@@ -43,10 +44,10 @@ public class PlayerManager {
} }
public void closeSaveData(String playerName) { public void closeSaveData(String playerName) {
PlayerData playerData = getPlayerData(playerName); PlayerData playerData = playerDataMap.remove(playerName);
if (playerData != null) {
playerData.savePlayerData(); playerData.savePlayerData();
playerDataMap.remove(playerName); }
removePetCall(playerName); removePetCall(playerName);
} }
@@ -244,7 +245,7 @@ public class PlayerManager {
if (!nbtItem.hasKey("petKey")) { if (!nbtItem.hasKey("petKey")) {
Bukkit.getConsoleSender().sendMessage("[日志 - 宠物] 未检测到宠物petKey的Nbt标识."); Bukkit.getConsoleSender().sendMessage("[日志 - 宠物] 未检测到宠物petKey的Nbt标识.");
} }
player.getInventory().addItem(petStack); InventoryUtil.giveOrDrop(player, petStack);
playerData.setPetStack(DemonAPI.getErrItems()); playerData.setPetStack(DemonAPI.getErrItems());
playerData.savePlayerData(); playerData.savePlayerData();
MessageUtil.sendMessageKey(player, "pet_stack_carry_takeOut", Sound.ENTITY_PLAYER_LEVELUP); MessageUtil.sendMessageKey(player, "pet_stack_carry_takeOut", Sound.ENTITY_PLAYER_LEVELUP);

View File

@@ -0,0 +1,24 @@
package com.yaohun.petsystem.util;
import org.bukkit.Sound;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import java.util.HashMap;
public class InventoryUtil {
public static void giveOrDrop(Player player, ItemStack itemStack) {
if (player == null || itemStack == null) {
return;
}
HashMap<Integer, ItemStack> leftoverMap = player.getInventory().addItem(itemStack);
if (leftoverMap.isEmpty()) {
return;
}
for (ItemStack leftover : leftoverMap.values()) {
player.getWorld().dropItemNaturally(player.getLocation(), leftover);
}
MessageUtil.sendMessage(player, "§e背包空间不足剩余物品已掉落在当前位置。", Sound.ENTITY_ITEM_PICKUP);
}
}

View File

@@ -1,130 +1,110 @@
package com.yaohun.petsystem.util.wolf; package com.yaohun.petsystem.util.wolf;
import com.yaohun.petsystem.PetMain;
import com.yaohun.petsystem.model.PetNbt; 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.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.attribute.Attribute; import org.bukkit.attribute.Attribute;
import org.bukkit.attribute.AttributeInstance; import org.bukkit.attribute.AttributeInstance;
import org.bukkit.block.Block; 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.LivingEntity;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.entity.Wolf;
import org.bukkit.scheduler.BukkitRunnable;
import java.util.UUID; import java.util.UUID;
/** public class CustomWolf {
* 自定义的狼实体类,用于实现“跟随玩家”的宠物功能。
* 继承原版 NMS Wolf 实体,通过 tick() 方法控制移动与传送。
*/
public class CustomWolf extends Wolf {
private static final double STOP_DISTANCE_SQ = 9.0D; private static final double STOP_DISTANCE_SQ = 9.0D;
private static final double TELEPORT_DISTANCE_SQ = 144.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 FOLLOW_SPEED = 1.2D;
private static final double DEFAULT_MOVEMENT_SPEED = 0.4D; private static final double DEFAULT_MOVEMENT_SPEED = 0.4D;
private static final int PATH_RECALC_TICKS = 10; private static final long FOLLOW_INTERVAL_TICKS = 10L;
private final UUID ownerUuid; private final UUID ownerUuid;
private final Wolf wolf;
private int timeToRecalcPath = 0; private CustomWolf(UUID ownerUuid, Wolf wolf) {
this.ownerUuid = ownerUuid;
/** this.wolf = wolf;
* 构造方法:初始化宠物实体 startFollowTask();
*
* @param world NMS 世界对象
* @param owner 宠物主人Bukkit Player
*/
public CustomWolf(Level world, Player owner) {
super(EntityType.WOLF, world);
this.ownerUuid = owner.getUniqueId();
} }
/** public Wolf getBukkitEntity() {
* 每 tick 执行的逻辑,控制宠物的移动与状态 return wolf;
*/ }
@Override
public void tick() {
super.tick();
public LivingEntity getBukkitLivingEntity() {
return wolf;
}
public UUID getUUID() {
return wolf.getUniqueId();
}
public boolean isRemoved() {
return !wolf.isValid();
}
public static CustomWolf spawnCustomWolf(Player owner, PetNbt petNbt) {
Wolf wolf = owner.getWorld().spawn(owner.getLocation(), Wolf.class, entity -> {
entity.setSilent(true);
entity.setTamed(true);
entity.setOwner(owner);
entity.setSitting(false);
entity.setRemoveWhenFarAway(false);
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(entity, Attribute.MAX_HEALTH, maxHealth);
setAttribute(entity, Attribute.MOVEMENT_SPEED, DEFAULT_MOVEMENT_SPEED);
setAttribute(entity, Attribute.ATTACK_DAMAGE, damage);
entity.setHealth(health);
});
return new CustomWolf(owner.getUniqueId(), wolf);
}
private void startFollowTask() {
new BukkitRunnable() {
@Override
public void run() {
if (!wolf.isValid() || wolf.isDead()) {
cancel();
return;
}
Player owner = Bukkit.getPlayer(ownerUuid); Player owner = Bukkit.getPlayer(ownerUuid);
if (owner == null || !owner.isOnline()) { if (owner == null || !owner.isOnline()) {
this.discard(); wolf.remove();
cancel();
return; return;
} }
if (owner.isDead()) { if (owner.isDead()) {
this.getNavigation().stop(); wolf.setTarget(null);
return; return;
} }
if (!owner.getWorld().equals(this.getBukkitEntity().getWorld())) { if (!owner.getWorld().equals(wolf.getWorld())) {
this.getNavigation().stop(); teleportToOwner(owner);
teleportToOwnerLocation(owner);
return; return;
} }
double distanceSq = wolf.getLocation().distanceSquared(owner.getLocation());
net.minecraft.world.entity.player.Player nmsOwner = ((CraftPlayer) owner).getHandle();
double distanceSq = this.distanceToSqr(nmsOwner);
if (distanceSq < STOP_DISTANCE_SQ) { if (distanceSq < STOP_DISTANCE_SQ) {
this.getNavigation().stop(); wolf.setTarget(null);
return; return;
} }
if (distanceSq > TELEPORT_DISTANCE_SQ) {
boolean shouldTeleport = distanceSq > TELEPORT_DISTANCE_SQ; teleportToOwner(owner);
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);
} }
} }
}.runTaskTimer(PetMain.inst(), FOLLOW_INTERVAL_TICKS, FOLLOW_INTERVAL_TICKS);
} }
/** private void teleportToOwner(Player owner) {
* 当宠物距离主人太远时,尝试在主人周围随机传送
*
* @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(); Location location = owner.getLocation();
if (isSafeTeleportLocation(location)) { if (isSafeTeleportLocation(location)) {
this.getBukkitEntity().teleport(location); wolf.teleport(location);
} }
} }
@@ -132,65 +112,7 @@ public class CustomWolf extends Wolf {
Block feet = location.getBlock(); Block feet = location.getBlock();
Block head = feet.getRelative(0, 1, 0); Block head = feet.getRelative(0, 1, 0);
Block ground = feet.getRelative(0, -1, 0); Block ground = feet.getRelative(0, -1, 0);
if (!feet.isPassable() || !head.isPassable() || !ground.getType().isSolid()) { return 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) { private static void setAttribute(LivingEntity entity, Attribute attribute, double value) {

View File

@@ -1,6 +1,12 @@
name: AuPet name: AuPet
main: com.yaohun.petsystem.PetMain main: com.yaohun.petsystem.PetMain
version: 1.0.1 version: 1.0.2
api-version: 1.21 api-version: 1.21
softdepend:
- DemonAPI
- NBTAPI
- MythicMobs
- BetterModel
- CraftDataManager
commands: commands:
apet: apet:

View File

@@ -1,265 +0,0 @@
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<String , SkillData> 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<String> 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;
}
}

View File

@@ -1,122 +0,0 @@
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]");
}
}
}

View File

@@ -1,4 +0,0 @@
package com.yaohun.petsystem.command;
public class PetCommand {
}

View File

@@ -1,141 +0,0 @@
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<Integer, String> qualityShowMap = new TreeMap<>();
private static HashMap<String, GuiItemData> 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<String> stringList = config.getStringList("Pet_Stack_Template.lore");
List<String> 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<String, String> 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<String> originalLore = meta.getLore();
if (originalLore != null) {
// 创建新的lore列表进行处理
List<String> 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<Integer, String> 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 → 8093 → 90
int step = (percent / 10) * 10;
// 处理 100 特殊情况
if (percent == 100) {
step = 100;
}
return "§f<image:default:hp_" + step + ">";
}
public static String getLevelChar(int level) {
return "§f<image:default:level_tag_" + level + ">";
}
}

View File

@@ -1,86 +0,0 @@
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<String> accessChannelsList;
private String summonSkill;
private String deathSkill;
private LinkedHashMap<String, SkillData> 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<String> getAccessChannelsList() {
return accessChannelsList;
}
public String getSummonSkill() {
return summonSkill;
}
public String getDeathSkill() {
return deathSkill;
}
public LinkedHashMap<String, SkillData> getSkillDataMap() {
return skillDataMap;
}
public SkillData getSkillData(String skillName) {
if (skillDataMap.containsKey(skillName)) {
return skillDataMap.get(skillName);
}
return null;
}
}

View File

@@ -1,105 +0,0 @@
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");
}
}
}

View File

@@ -1,136 +0,0 @@
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();
}
}
}

View File

@@ -1,60 +0,0 @@
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<String> 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<String> 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<String> getItemLore() {
return itemLore;
}
}

View File

@@ -1,47 +0,0 @@
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();
}
}

View File

@@ -1,89 +0,0 @@
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);
}
}
}
}

View File

@@ -1,206 +0,0 @@
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<String> 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);
}
}
}
}

View File

@@ -1,185 +0,0 @@
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<String> 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);
}
}
}
}

View File

@@ -1,12 +0,0 @@
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;
}
}

View File

@@ -1,12 +0,0 @@
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;
}
}

View File

@@ -1,12 +0,0 @@
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;
}
}

View File

@@ -1,94 +0,0 @@
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<String , SkillData> 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<String , SkillData> 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<SkillMetadata> 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();
}*/
}

View File

@@ -1,79 +0,0 @@
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);
}
}

View File

@@ -1,62 +0,0 @@
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<UUID> 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);
}
}

View File

@@ -1,35 +0,0 @@
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<Integer, Integer> 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;
}
}

View File

@@ -1,48 +0,0 @@
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();
}
}
}

View File

@@ -1,71 +0,0 @@
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<String, PetData> 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<File> 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<String, PetData> 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<String> getPetAccessChannels(String petKey) {
PetData petData = PetManager.getPetData(petKey);
if (petData == null) {
return new ArrayList<>();
}
return petData.getAccessChannelsList();
}
}

View File

@@ -1,253 +0,0 @@
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<String, PlayerData> playerDataMap = new HashMap<>();
private HashMap<String, PetCallSession> petCallMap = new HashMap<>();
// private HashMap<UUID, ModeledEntity> 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);
}
}
}

View File

@@ -1,111 +0,0 @@
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);
}
}
}

View File

@@ -1,49 +0,0 @@
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;
}
}

View File

@@ -1,78 +0,0 @@
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<String, String> languageMap = new HashMap<>();
public static HashMap<String, String> 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();
}
}

View File

@@ -1,178 +0,0 @@
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<Component> newLore = new ArrayList<>();
List<String> 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<Component> newLore = new ArrayList<>();
List<String> 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<ActiveModel> 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;
}
}

View File

@@ -1,81 +0,0 @@
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();
}
}

View File

@@ -1,202 +0,0 @@
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);
}
}
}

View File

@@ -1,31 +0,0 @@
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

View File

@@ -1,6 +0,0 @@
name: AuPet
main: com.yaohun.petsystem.PetMain
version: 1.0.1
api-version: 1.21
commands:
apet: