From 721fb5a9921ab86398a7292092e728c2da854e7f Mon Sep 17 00:00:00 2001 From: Lenheart <947330670@qq.com> Date: Thu, 16 Apr 2026 16:27:53 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E7=A4=BA=E4=BE=8B=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AGENTS.md | 22 + 示例项目/DPS启动器/DPS启动器.nut | 169 ++++ 示例项目/DPS启动器/Proj.ifo | 11 + 示例项目/DPS启动器/登录器配置.json | 51 + 示例项目/GM便捷操作台/GM便捷操作台.nut | 145 +++ .../GM便捷操作台/GM便捷操作台_Lenheart.json | 16 + 示例项目/GM便捷操作台/Proj.ifo | 11 + 示例项目/NPC商店限购/NPC商店限购.nut | 290 ++++++ .../NPC商店限购/NPC商店限购配置_Nangua.json | 20 + 示例项目/NPC商店限购/Proj.ifo | 11 + 示例项目/SP_TP属性石返还/Proj.ifo | 11 + 示例项目/SP_TP属性石返还/SP_TP属性石返还.nut | 309 ++++++ .../SP_TP属性石返还/SP_TP属性石返还_Nangua.json | 7 + 示例项目/一键分解卷/Proj.ifo | 11 + 示例项目/一键分解卷/一键分解卷.nut | 92 ++ 示例项目/一键分解卷/一键分解卷_Lenheart.json | 9 + 示例项目/一键存入个人金库/Proj.ifo | 11 + 示例项目/一键存入个人金库/一键入库.nut | 116 +++ .../一键存入个人金库_Lenheart.json | 4 + 示例项目/上线自动完成任务/Proj.ifo | 11 + .../上线自动完成任务/上线自动完成任务.nut | 9 + .../上线自动完成任务_Lenheart.json | 4 + 示例项目/交易邮件播报/Proj.ifo | 11 + 示例项目/交易邮件播报/交易邮件播报.json | 4 + 示例项目/交易邮件播报/交易邮件播报.nut | 126 +++ 示例项目/任务清除卷/Proj.ifo | 12 + 示例项目/任务清除卷/任务清除卷.nut | 231 +++++ 示例项目/任务清除卷/任务相关配置_南瓜.json | 12 + 示例项目/任务清除卷2/Proj.ifo | 12 + 示例项目/任务清除卷2/任务清除卷.nut | 260 +++++ 示例项目/任务清除卷2/任务相关配置_2.json | 12 + 示例项目/修复卡NPC商店道具/Proj.ifo | 11 + .../修复卡NPC商店道具/修复卡NPC商店道具.nut | 12 + 示例项目/全职业通用转职书/Proj.ifo | 12 + .../全职业通用转职书/全职业通用转职书.nut | 50 + .../全职业通用转职书_Lenheart.json | 8 + 示例项目/副本使用道具奖励/Proj.ifo | 11 + .../副本使用道具奖励/副本使用道具奖励.json | 7 + .../副本使用道具奖励/副本使用道具奖励.nut | 32 + 示例项目/副本内禁止丢弃物品/Proj.ifo | 11 + .../副本内禁止丢弃物品/副本内禁止丢弃物品.nut | 27 + 示例项目/副本播报/Proj.ifo | 11 + 示例项目/副本播报/副本播报.nut | 227 +++++ 示例项目/副本播报/副本播报配置_Nangua.json | 18 + 示例项目/副本难度解锁券/Proj.ifo | 11 + .../副本难度解锁券/副本难度解锁_Nangua.json | 4 + 示例项目/副本难度解锁券/副本难度解锁券.nut | 12 + 示例项目/副本需要持有道具进入/Proj.ifo | 11 + .../副本需要持有道具进入/副本需要持有道具进入.nut | 38 + .../副本需要持有道具进入_Lenheart.json | 7 + 示例项目/史诗免确认/Proj.ifo | 11 + 示例项目/史诗免确认/史诗免确认.nut | 7 + 示例项目/史诗掉落奖励/Proj.ifo | 11 + 示例项目/史诗掉落奖励/史诗掉落奖励.nut | 333 ++++++ .../史诗掉落奖励/史诗掉落奖励配置_南瓜.json | 94 ++ 示例项目/史诗药剂/Proj.ifo | 11 + 示例项目/史诗药剂/史诗药剂.nut | 47 + 示例项目/史诗药剂/史诗药剂配置文件.json | 14 + 示例项目/在线泡点/Proj.ifo | 11 + 示例项目/在线泡点/在线泡点.nut | 97 ++ 示例项目/在线泡点/在线泡点配置_Nangua.json | 17 + 示例项目/多彩蜜蜡改跨界石/Proj.ifo | 11 + .../多彩蜜蜡改跨界石/多彩蜜蜡改跨界石.nut | 108 ++ .../多彩蜜蜡改跨界石/多彩蜜蜡改跨界石_Nangua.json | 15 + 示例项目/宠物附魔/Proj.ifo | 11 + 示例项目/宠物附魔/宠物附魔.nut | 84 ++ 示例项目/异界重置/Proj.ifo | 11 + 示例项目/异界重置/异界重置.nut | 39 + 示例项目/异界重置/异界重置_Lenheart.json | 4 + 示例项目/强化增幅药剂/Proj.ifo | 11 + 示例项目/强化增幅药剂/强化增幅药剂.nut | 63 ++ 示例项目/强化增幅药剂/强化增幅药剂_Pluto.json | 11 + 示例项目/强化增幅锻造药剂/Proj.ifo | 11 + .../强化增幅锻造药剂/强化增幅锻造药剂.nut | 64 ++ .../强化增幅锻造药剂配置_nangua.json | 35 + 示例项目/强化相关概率提升/Proj.ifo | 9 + 示例项目/强化相关概率提升/强化增幅锻造.nut | 134 +++ .../强化相关概率提升/强化相关概率配置.json | 71 ++ 示例项目/心悦播报/Proj.ifo | 11 + 示例项目/心悦播报/心悦播报.nut | 45 + 示例项目/心悦播报/心悦播报配置_Nangua.json | 36 + 示例项目/怪物攻城/Proj.ifo | 12 + 示例项目/怪物攻城/怪物攻城.nut | 397 ++++++++ 示例项目/怪物攻城/怪物攻城_南瓜.json | 63 ++ 示例项目/战力榜/Proj.ifo | 11 + 示例项目/战力榜/战力榜.nut | 326 ++++++ 示例项目/战力榜/战力榜配置_南瓜.json | 80 ++ 示例项目/技能拓展14键/Proj.ifo | 11 + 示例项目/技能拓展14键/技能拓展14键.nut | 7 + 示例项目/抗魔值进入副本/Proj.ifo | 11 + 示例项目/抗魔值进入副本/抗魔值进入副本.nut | 123 +++ .../抗魔值进入副本/抗魔值进入副本配置_Nangua.json | 23 + 示例项目/拓展瞬间移动药剂/Proj.ifo | 11 + .../拓展瞬间移动药剂/拓展瞬间移动药剂.nut | 4 + 示例项目/指定用户强化必定成功/Proj.ifo | 11 + .../指定用户强化必定成功/指定用户强化必定成功.nut | 52 + .../指定用户强化必定成功_Pluto.json | 4 + 示例项目/整点在线奖励/Proj.ifo | 11 + 示例项目/整点在线奖励/整点在线奖励.nut | 103 ++ .../整点在线奖励/整点在线奖励_Lenheart.json | 19 + 示例项目/时装与宠物清除卷/Proj.ifo | 11 + .../时装与宠物清除卷/时装与宠物清除卷.nut | 75 ++ .../时装与宠物清除卷_Lenheart.json | 8 + 示例项目/时装潜能/Proj.ifo | 11 + 示例项目/时装潜能/时装潜能.nut | 9 + 示例项目/是否允许创建缔造者/Proj.ifo | 11 + .../是否允许创建缔造者/是否允许创建缔造者.nut | 16 + .../是否允许创建缔造者_Lenheart.json | 3 + 示例项目/更加优雅的跨界石/Proj.ifo | 11 + .../更加优雅的跨界石/更加优雅的跨界石.nut | 76 ++ .../更加优雅的跨界石/更加优雅的跨界石_Pluto.json | 9 + 示例项目/服务器防入侵/Proj.ifo | 12 + 示例项目/服务器防入侵/服务器防入侵.sut | Bin 0 -> 232 bytes 示例项目/杂项功能/Proj.ifo | 11 + 示例项目/杂项功能/杂项功能.nut | 251 +++++ 示例项目/杂项功能/杂项功能开关_Nangua.json | 30 + 示例项目/武器锻造券/Proj.ifo | 11 + 示例项目/武器锻造券/武器锻造券.nut | 99 ++ .../武器锻造券/武器锻造券配置_Nangua.json | 41 + 示例项目/深渊模式称号/Proj.ifo | 11 + 示例项目/深渊模式称号/深渊模式功能_Pluto.json | 5 + 示例项目/深渊模式称号/深渊模式称号.nut | 47 + 示例项目/深渊活动/Proj.ifo | 11 + 示例项目/深渊活动/深渊活动.nut | 954 ++++++++++++++++++ 示例项目/深渊活动/深渊活动配置_nangua.json | 59 ++ 示例项目/深渊爆率实时控制/Proj.ifo | 11 + .../深渊爆率实时控制/深渊爆率实时控制.nut | 25 + .../深渊爆率实时控制/深渊爆率实时控制_Nangua.json | 6 + 示例项目/清理点券付费错误/Proj.ifo | 11 + 示例项目/清理点券付费错误/清理付费错误.json | 11 + .../清理点券付费错误/清理点券付费错误.nut | 43 + 示例项目/点券充值卡/Proj.ifo | 11 + 示例项目/点券充值卡/点券充值卡.nut | 38 + .../点券充值卡/点券充值卡配置_Nangua.json | 12 + 示例项目/物品回收UI/Proj.ifo | 11 + 示例项目/物品回收UI/物品回收UI.nut | 330 ++++++ .../物品回收UI/物品回收UI配置_Nangua.json | 84 ++ 示例项目/独立掉落/Proj.ifo | 11 + 示例项目/独立掉落/独立掉落.nut | 170 ++++ .../红字自动取消和道具自动打上全身红字/Proj.ifo | 11 + .../红字自动取消和道具自动打上全身红字.json | 9 + .../红字自动取消和道具自动打上全身红字.nut | 85 ++ 示例项目/自动刷新GM工具的邮件/Proj.ifo | 11 + .../自动刷新GM工具的邮件/自动刷新GM工具的邮件.nut | 88 ++ 示例项目/装备回收/Proj.ifo | 11 + 示例项目/装备回收/装备回收.nut | 340 +++++++ 示例项目/装备回收/装备回收配置_Nangua.json | 81 ++ 示例项目/装备继承券/Proj.ifo | 11 + 示例项目/装备继承券/装备继承券.nut | 166 +++ 示例项目/装备继承券/装备继承券_Lenheart.json | 13 + .../装备继承券/装备继承券配置_Nangua.json | 30 + 示例项目/装备镶嵌与时装镶嵌/Proj.ifo | 11 + .../装备镶嵌与时装镶嵌/装备镶嵌与时装镶嵌.nut | 590 +++++++++++ .../装备镶嵌与时装镶嵌_Lenheart.json | 6 + 示例项目/觉醒券/Proj.ifo | 11 + 示例项目/觉醒券/觉醒券.json | 55 + 示例项目/觉醒券/觉醒券.nut | 57 ++ 示例项目/角色初始武器修改/Proj.ifo | 11 + .../角色初始武器修改/角色初始武器修改.nut | 13 + .../角色初始武器修改_Lenheart.json | 4 + 示例项目/设置服务器等级上限/Proj.ifo | 11 + .../设置服务器等级上限/设置服务器等级上限.nut | 12 + .../设置服务器等级上限_Lenheart.json | 3 + 示例项目/设置装备解锁时间/Proj.ifo | 11 + .../设置装备解锁时间/设置装备解锁时间.nut | 12 + .../设置装备解锁时间_Lenheart.json | 3 + 示例项目/跨界石/Proj.ifo | 11 + 示例项目/跨界石/跨界石.nut | 92 ++ 示例项目/跨界石/跨界石_Lenheart.json | 9 + 示例项目/转职券/Proj.ifo | 11 + 示例项目/转职券/转职券.json | 53 + 示例项目/转职券/转职券.nut | 59 ++ 示例项目/进入副本自动修理装备/Proj.ifo | 11 + .../自动修理装备配置_nangua.json | 12 + .../进入副本自动修理装备/进入副本自动修理装备.nut | 156 +++ 示例项目/道具接取任务/Proj.ifo | 11 + 示例项目/道具接取任务/道具接取任务.json | 9 + 示例项目/道具接取任务/道具接取任务.nut | 16 + 示例项目/邮件金币去除验证/Proj.ifo | 11 + .../邮件金币去除验证/邮件金币去除验证.nut | 7 + 示例项目/重铸券/Proj.ifo | 11 + 示例项目/重铸券/重铸券.json | 4 + 示例项目/重铸券/重铸券.nut | 30 + 示例项目/门票进入副本/Proj.ifo | 11 + 示例项目/门票进入副本/门票进入副本.nut | 50 + .../门票进入副本/门票进入副本配置_Maomi.json | 29 + 示例项目/防脱机制裁/Proj.ifo | 11 + 示例项目/防脱机制裁/防脱机制裁.sut | Bin 0 -> 12226 bytes 示例项目/防脱机制裁/防脱机制裁_Nangua.json | 12 + 示例项目/黄金品级调整箱/Proj.ifo | 11 + 示例项目/黄金品级调整箱/黄金品级调整箱.nut | 29 + .../黄金品级调整箱/黄金品级调整箱_Lenheart.json | 4 + 192 files changed, 10148 insertions(+) create mode 100644 AGENTS.md create mode 100644 示例项目/DPS启动器/DPS启动器.nut create mode 100644 示例项目/DPS启动器/Proj.ifo create mode 100644 示例项目/DPS启动器/登录器配置.json create mode 100644 示例项目/GM便捷操作台/GM便捷操作台.nut create mode 100644 示例项目/GM便捷操作台/GM便捷操作台_Lenheart.json create mode 100644 示例项目/GM便捷操作台/Proj.ifo create mode 100644 示例项目/NPC商店限购/NPC商店限购.nut create mode 100644 示例项目/NPC商店限购/NPC商店限购配置_Nangua.json create mode 100644 示例项目/NPC商店限购/Proj.ifo create mode 100644 示例项目/SP_TP属性石返还/Proj.ifo create mode 100644 示例项目/SP_TP属性石返还/SP_TP属性石返还.nut create mode 100644 示例项目/SP_TP属性石返还/SP_TP属性石返还_Nangua.json create mode 100644 示例项目/一键分解卷/Proj.ifo create mode 100644 示例项目/一键分解卷/一键分解卷.nut create mode 100644 示例项目/一键分解卷/一键分解卷_Lenheart.json create mode 100644 示例项目/一键存入个人金库/Proj.ifo create mode 100644 示例项目/一键存入个人金库/一键入库.nut create mode 100644 示例项目/一键存入个人金库/一键存入个人金库_Lenheart.json create mode 100644 示例项目/上线自动完成任务/Proj.ifo create mode 100644 示例项目/上线自动完成任务/上线自动完成任务.nut create mode 100644 示例项目/上线自动完成任务/上线自动完成任务_Lenheart.json create mode 100644 示例项目/交易邮件播报/Proj.ifo create mode 100644 示例项目/交易邮件播报/交易邮件播报.json create mode 100644 示例项目/交易邮件播报/交易邮件播报.nut create mode 100644 示例项目/任务清除卷/Proj.ifo create mode 100644 示例项目/任务清除卷/任务清除卷.nut create mode 100644 示例项目/任务清除卷/任务相关配置_南瓜.json create mode 100644 示例项目/任务清除卷2/Proj.ifo create mode 100644 示例项目/任务清除卷2/任务清除卷.nut create mode 100644 示例项目/任务清除卷2/任务相关配置_2.json create mode 100644 示例项目/修复卡NPC商店道具/Proj.ifo create mode 100644 示例项目/修复卡NPC商店道具/修复卡NPC商店道具.nut create mode 100644 示例项目/全职业通用转职书/Proj.ifo create mode 100644 示例项目/全职业通用转职书/全职业通用转职书.nut create mode 100644 示例项目/全职业通用转职书/全职业通用转职书_Lenheart.json create mode 100644 示例项目/副本使用道具奖励/Proj.ifo create mode 100644 示例项目/副本使用道具奖励/副本使用道具奖励.json create mode 100644 示例项目/副本使用道具奖励/副本使用道具奖励.nut create mode 100644 示例项目/副本内禁止丢弃物品/Proj.ifo create mode 100644 示例项目/副本内禁止丢弃物品/副本内禁止丢弃物品.nut create mode 100644 示例项目/副本播报/Proj.ifo create mode 100644 示例项目/副本播报/副本播报.nut create mode 100644 示例项目/副本播报/副本播报配置_Nangua.json create mode 100644 示例项目/副本难度解锁券/Proj.ifo create mode 100644 示例项目/副本难度解锁券/副本难度解锁_Nangua.json create mode 100644 示例项目/副本难度解锁券/副本难度解锁券.nut create mode 100644 示例项目/副本需要持有道具进入/Proj.ifo create mode 100644 示例项目/副本需要持有道具进入/副本需要持有道具进入.nut create mode 100644 示例项目/副本需要持有道具进入/副本需要持有道具进入_Lenheart.json create mode 100644 示例项目/史诗免确认/Proj.ifo create mode 100644 示例项目/史诗免确认/史诗免确认.nut create mode 100644 示例项目/史诗掉落奖励/Proj.ifo create mode 100644 示例项目/史诗掉落奖励/史诗掉落奖励.nut create mode 100644 示例项目/史诗掉落奖励/史诗掉落奖励配置_南瓜.json create mode 100644 示例项目/史诗药剂/Proj.ifo create mode 100644 示例项目/史诗药剂/史诗药剂.nut create mode 100644 示例项目/史诗药剂/史诗药剂配置文件.json create mode 100644 示例项目/在线泡点/Proj.ifo create mode 100644 示例项目/在线泡点/在线泡点.nut create mode 100644 示例项目/在线泡点/在线泡点配置_Nangua.json create mode 100644 示例项目/多彩蜜蜡改跨界石/Proj.ifo create mode 100644 示例项目/多彩蜜蜡改跨界石/多彩蜜蜡改跨界石.nut create mode 100644 示例项目/多彩蜜蜡改跨界石/多彩蜜蜡改跨界石_Nangua.json create mode 100644 示例项目/宠物附魔/Proj.ifo create mode 100644 示例项目/宠物附魔/宠物附魔.nut create mode 100644 示例项目/异界重置/Proj.ifo create mode 100644 示例项目/异界重置/异界重置.nut create mode 100644 示例项目/异界重置/异界重置_Lenheart.json create mode 100644 示例项目/强化增幅药剂/Proj.ifo create mode 100644 示例项目/强化增幅药剂/强化增幅药剂.nut create mode 100644 示例项目/强化增幅药剂/强化增幅药剂_Pluto.json create mode 100644 示例项目/强化增幅锻造药剂/Proj.ifo create mode 100644 示例项目/强化增幅锻造药剂/强化增幅锻造药剂.nut create mode 100644 示例项目/强化增幅锻造药剂/强化增幅锻造药剂配置_nangua.json create mode 100644 示例项目/强化相关概率提升/Proj.ifo create mode 100644 示例项目/强化相关概率提升/强化增幅锻造.nut create mode 100644 示例项目/强化相关概率提升/强化相关概率配置.json create mode 100644 示例项目/心悦播报/Proj.ifo create mode 100644 示例项目/心悦播报/心悦播报.nut create mode 100644 示例项目/心悦播报/心悦播报配置_Nangua.json create mode 100644 示例项目/怪物攻城/Proj.ifo create mode 100644 示例项目/怪物攻城/怪物攻城.nut create mode 100644 示例项目/怪物攻城/怪物攻城_南瓜.json create mode 100644 示例项目/战力榜/Proj.ifo create mode 100644 示例项目/战力榜/战力榜.nut create mode 100644 示例项目/战力榜/战力榜配置_南瓜.json create mode 100644 示例项目/技能拓展14键/Proj.ifo create mode 100644 示例项目/技能拓展14键/技能拓展14键.nut create mode 100644 示例项目/抗魔值进入副本/Proj.ifo create mode 100644 示例项目/抗魔值进入副本/抗魔值进入副本.nut create mode 100644 示例项目/抗魔值进入副本/抗魔值进入副本配置_Nangua.json create mode 100644 示例项目/拓展瞬间移动药剂/Proj.ifo create mode 100644 示例项目/拓展瞬间移动药剂/拓展瞬间移动药剂.nut create mode 100644 示例项目/指定用户强化必定成功/Proj.ifo create mode 100644 示例项目/指定用户强化必定成功/指定用户强化必定成功.nut create mode 100644 示例项目/指定用户强化必定成功/指定用户强化必定成功_Pluto.json create mode 100644 示例项目/整点在线奖励/Proj.ifo create mode 100644 示例项目/整点在线奖励/整点在线奖励.nut create mode 100644 示例项目/整点在线奖励/整点在线奖励_Lenheart.json create mode 100644 示例项目/时装与宠物清除卷/Proj.ifo create mode 100644 示例项目/时装与宠物清除卷/时装与宠物清除卷.nut create mode 100644 示例项目/时装与宠物清除卷/时装与宠物清除卷_Lenheart.json create mode 100644 示例项目/时装潜能/Proj.ifo create mode 100644 示例项目/时装潜能/时装潜能.nut create mode 100644 示例项目/是否允许创建缔造者/Proj.ifo create mode 100644 示例项目/是否允许创建缔造者/是否允许创建缔造者.nut create mode 100644 示例项目/是否允许创建缔造者/是否允许创建缔造者_Lenheart.json create mode 100644 示例项目/更加优雅的跨界石/Proj.ifo create mode 100644 示例项目/更加优雅的跨界石/更加优雅的跨界石.nut create mode 100644 示例项目/更加优雅的跨界石/更加优雅的跨界石_Pluto.json create mode 100644 示例项目/服务器防入侵/Proj.ifo create mode 100644 示例项目/服务器防入侵/服务器防入侵.sut create mode 100644 示例项目/杂项功能/Proj.ifo create mode 100644 示例项目/杂项功能/杂项功能.nut create mode 100644 示例项目/杂项功能/杂项功能开关_Nangua.json create mode 100644 示例项目/武器锻造券/Proj.ifo create mode 100644 示例项目/武器锻造券/武器锻造券.nut create mode 100644 示例项目/武器锻造券/武器锻造券配置_Nangua.json create mode 100644 示例项目/深渊模式称号/Proj.ifo create mode 100644 示例项目/深渊模式称号/深渊模式功能_Pluto.json create mode 100644 示例项目/深渊模式称号/深渊模式称号.nut create mode 100644 示例项目/深渊活动/Proj.ifo create mode 100644 示例项目/深渊活动/深渊活动.nut create mode 100644 示例项目/深渊活动/深渊活动配置_nangua.json create mode 100644 示例项目/深渊爆率实时控制/Proj.ifo create mode 100644 示例项目/深渊爆率实时控制/深渊爆率实时控制.nut create mode 100644 示例项目/深渊爆率实时控制/深渊爆率实时控制_Nangua.json create mode 100644 示例项目/清理点券付费错误/Proj.ifo create mode 100644 示例项目/清理点券付费错误/清理付费错误.json create mode 100644 示例项目/清理点券付费错误/清理点券付费错误.nut create mode 100644 示例项目/点券充值卡/Proj.ifo create mode 100644 示例项目/点券充值卡/点券充值卡.nut create mode 100644 示例项目/点券充值卡/点券充值卡配置_Nangua.json create mode 100644 示例项目/物品回收UI/Proj.ifo create mode 100644 示例项目/物品回收UI/物品回收UI.nut create mode 100644 示例项目/物品回收UI/物品回收UI配置_Nangua.json create mode 100644 示例项目/独立掉落/Proj.ifo create mode 100644 示例项目/独立掉落/独立掉落.nut create mode 100644 示例项目/红字自动取消和道具自动打上全身红字/Proj.ifo create mode 100644 示例项目/红字自动取消和道具自动打上全身红字/红字自动取消和道具自动打上全身红字.json create mode 100644 示例项目/红字自动取消和道具自动打上全身红字/红字自动取消和道具自动打上全身红字.nut create mode 100644 示例项目/自动刷新GM工具的邮件/Proj.ifo create mode 100644 示例项目/自动刷新GM工具的邮件/自动刷新GM工具的邮件.nut create mode 100644 示例项目/装备回收/Proj.ifo create mode 100644 示例项目/装备回收/装备回收.nut create mode 100644 示例项目/装备回收/装备回收配置_Nangua.json create mode 100644 示例项目/装备继承券/Proj.ifo create mode 100644 示例项目/装备继承券/装备继承券.nut create mode 100644 示例项目/装备继承券/装备继承券_Lenheart.json create mode 100644 示例项目/装备继承券/装备继承券配置_Nangua.json create mode 100644 示例项目/装备镶嵌与时装镶嵌/Proj.ifo create mode 100644 示例项目/装备镶嵌与时装镶嵌/装备镶嵌与时装镶嵌.nut create mode 100644 示例项目/装备镶嵌与时装镶嵌/装备镶嵌与时装镶嵌_Lenheart.json create mode 100644 示例项目/觉醒券/Proj.ifo create mode 100644 示例项目/觉醒券/觉醒券.json create mode 100644 示例项目/觉醒券/觉醒券.nut create mode 100644 示例项目/角色初始武器修改/Proj.ifo create mode 100644 示例项目/角色初始武器修改/角色初始武器修改.nut create mode 100644 示例项目/角色初始武器修改/角色初始武器修改_Lenheart.json create mode 100644 示例项目/设置服务器等级上限/Proj.ifo create mode 100644 示例项目/设置服务器等级上限/设置服务器等级上限.nut create mode 100644 示例项目/设置服务器等级上限/设置服务器等级上限_Lenheart.json create mode 100644 示例项目/设置装备解锁时间/Proj.ifo create mode 100644 示例项目/设置装备解锁时间/设置装备解锁时间.nut create mode 100644 示例项目/设置装备解锁时间/设置装备解锁时间_Lenheart.json create mode 100644 示例项目/跨界石/Proj.ifo create mode 100644 示例项目/跨界石/跨界石.nut create mode 100644 示例项目/跨界石/跨界石_Lenheart.json create mode 100644 示例项目/转职券/Proj.ifo create mode 100644 示例项目/转职券/转职券.json create mode 100644 示例项目/转职券/转职券.nut create mode 100644 示例项目/进入副本自动修理装备/Proj.ifo create mode 100644 示例项目/进入副本自动修理装备/自动修理装备配置_nangua.json create mode 100644 示例项目/进入副本自动修理装备/进入副本自动修理装备.nut create mode 100644 示例项目/道具接取任务/Proj.ifo create mode 100644 示例项目/道具接取任务/道具接取任务.json create mode 100644 示例项目/道具接取任务/道具接取任务.nut create mode 100644 示例项目/邮件金币去除验证/Proj.ifo create mode 100644 示例项目/邮件金币去除验证/邮件金币去除验证.nut create mode 100644 示例项目/重铸券/Proj.ifo create mode 100644 示例项目/重铸券/重铸券.json create mode 100644 示例项目/重铸券/重铸券.nut create mode 100644 示例项目/门票进入副本/Proj.ifo create mode 100644 示例项目/门票进入副本/门票进入副本.nut create mode 100644 示例项目/门票进入副本/门票进入副本配置_Maomi.json create mode 100644 示例项目/防脱机制裁/Proj.ifo create mode 100644 示例项目/防脱机制裁/防脱机制裁.sut create mode 100644 示例项目/防脱机制裁/防脱机制裁_Nangua.json create mode 100644 示例项目/黄金品级调整箱/Proj.ifo create mode 100644 示例项目/黄金品级调整箱/黄金品级调整箱.nut create mode 100644 示例项目/黄金品级调整箱/黄金品级调整箱_Lenheart.json diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..a739fa1 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,22 @@ +# Repository Guidelines + +## Project Structure & Module Organization +This repository is a DP-S game-server hotfix tree. `Main.nut` is the entry point and hot-loads five roots: `OfficialConfig/`, `OfficialProject/`, `_DPS_/_Core/`, `_DPS_/_BuiltProject/`, and `MyProject/`. Put shared framework code, hooks, and callback glue in `_DPS_/_Core/`. Treat `_DPS_/_BuiltProject/` as bundled baseline features. Add official feature modules under `OfficialProject/<>/`, usually with a matching `<>.nut` and optional `Proj.ifo`; keep module JSON beside it in `OfficialConfig/`. Use `MyProject/` for local or experimental scripts. Runtime outputs belong in `log/`; native binaries and maintenance scripts live in `lib/`. + +## Build, Test, and Development Commands +There is no standalone compile step for `.nut` scripts; the server reloads them through `GameManager.OpenHotFix(...)` in `Main.nut`. + +- `git status` - check the workspace before editing or submitting. +- `git diff --stat` - review the size and scope of your change. +- `Get-Content Main.nut` - confirm hotfix load order and active roots. +- `Get-ChildItem OfficialProject, OfficialConfig, MyProject` - inspect feature and config layout. +- `bash lib/strip_other_funcs_unix.sh lib/libAurora.so` - maintenance-only helper for the Aurora binary; do not run unless you are intentionally updating symbols. + +## Coding Style & Naming Conventions +Use 4-space indentation and keep lines readable. Follow existing Squirrel patterns: `PascalCase` for helper functions, callback maps like `Cb_*`, and descriptive feature names for folders and main scripts. Keep one primary feature per folder/file pair, for example `OfficialProject/ս/ս.nut`. Name configs by feature, optionally with a server suffix such as `ս_Ϲ.json`. Prefer small, focused hooks over large unrelated edits. + +## Testing Guidelines +This repo has no automated test suite yet. Validate changes on a development server by reloading the affected hotfix path, exercising the related GM command, callback, or dungeon flow, and checking `log/` for regressions. When a module reads JSON, test both the script change and the matching config file together. + +## Commit & Pull Request Guidelines +Recent history favors Conventional Commit prefixes such as `feat`, `fix`, `refactor`, and `docs`, often with a scope: `feat(͵ϵͳ): ...`. Keep new commits in that style even though older messages are mixed. PRs should explain gameplay impact, list touched paths, call out config or data migrations, and include screenshots or log snippets when the change affects announcements, UI text, or player-visible flows. diff --git a/示例项目/DPS启动器/DPS启动器.nut b/示例项目/DPS启动器/DPS启动器.nut new file mode 100644 index 0000000..7d0a450 --- /dev/null +++ b/示例项目/DPS启动器/DPS启动器.nut @@ -0,0 +1,169 @@ +class _DPS_Login_Gateway_ { + + //数据库连接 + MysqlObject = null; + + PackHandleMap = null; + + + constructor() { + PackHandleMap = {}; + + MysqlObject = Mysql(Str_Ptr("127.0.0.1"), 3306, Str_Ptr("taiwan_cain"), Str_Ptr("game"), Str_Ptr("uu5!^%jg")); + MysqlObject.Exec_Sql(format("SET NAMES %s", "latin1")); + + //创建一个Http Server + try { + local HS = HttpServer("0.0.0.0", "41817"); + HS.Listen(function(SocketObject, Header, Msg) { + getroottable()._DPS_Login_Gateway_Object_._HttpServer_Event_DPS_(SocketObject, Header, Msg); + }); + } catch (exception) { + + } + + PackHandleMap["/GetConfig"] <- function(SocketObject, Header, Jso) { + local Config = sq_ReadJsonFile("/dp_s/OfficialConfig/登录器配置.json"); + //读取DPS登录器的配置并发送 + SocketObject.Write(Config); + } + + + PackHandleMap["/Login"] <- function(SocketObject, Header, Jso) { + if (!Jso.rawin("account")) return; + if (!Jso.rawin("passwd")) return; + local account = Jso.account; + local passwd = Jso.passwd; + local passwd_hash = MD5_Hash(passwd); + + + //正则表达式 只允许字母和数字 + local regex = regexp("^[A-Za-z0-9]{1,19}$"); + if (!regex.match(account)) { + SocketObject.Write({ + error = "账号只能包含字母和数字且长度小于20" + }); + return; + } + + local sql = format("select UID from d_taiwan.accounts where accountname='%s' and password='%s';", account, passwd_hash); + local Ret = MysqlObject.Select(sql, ["int"]); + if (Ret && Ret.len() > 0) { + local Uid = Ret[0][0]; + local str = format("%08x010101010101010101010101010101010101010101010101010101010101010155914510010403030101", Uid); + local Byte = Sq_Rsa_Private_Encrypt(str); + local EnStr = MD5.base64_encode(Byte); + //发送登录Token + SocketObject.Write({ + token = EnStr + }); + } else { + SocketObject.Write({ + error = "账号或密码错误!" + }); + } + }.bindenv(this); + + + PackHandleMap["/Register"] <- function(SocketObject, Header, Jso) { + + if (!Jso.rawin("account")) return; + if (!Jso.rawin("passwd")) return; + + local account = Jso.account; + local passwd = Jso.passwd; + + + //正则表达式 只允许字母和数字 + local regex = regexp("^[A-Za-z0-9]{1,19}$"); + if (account.len() == 0 || !regex.match(account)) { + SocketObject.Write({ + error = "账号只能包含字母和数字且长度大于0小于20" + }); + return; + } + if (passwd.len() == 0 || !regex.match(passwd)) { + SocketObject.Write({ + error = "密码只能包含字母和数字且长度大于0小于20" + }); + return; + } + if (GetAccountByName(account) != null) { + SocketObject.Write({ + error = "账号已存在" + }) + return; + } + local passwd_hash = MD5_Hash(passwd); + + + local sql = format("INSERT INTO d_taiwan.accounts(accountname, password)VALUES( '%s' , '%s');", account, passwd_hash); + MysqlObject.Exec_Sql(sql); + local Ret = MysqlObject.Select("SELECT LAST_INSERT_ID()", ["int"]); + if (Ret && Ret.len() > 0) { + local Uid = Ret[0][0].tostring(); + MysqlObject.Exec_Sql(format("INSERT INTO d_taiwan.member_info(m_id, user_id)VALUES('%s','%s');", Uid, Uid)); + MysqlObject.Exec_Sql(format("INSERT INTO d_taiwan.member_white_account(m_id)VALUES('%s')", Uid)); + MysqlObject.Exec_Sql(format("INSERT INTO taiwan_login.member_login(m_id)VALUES('%s')", Uid)); + MysqlObject.Exec_Sql(format("INSERT INTO taiwan_billing.cash_cera(account, cera,mod_tran,mod_date, reg_date)VALUES('%s',0,0,NOW(), NOW())", Uid)); + MysqlObject.Exec_Sql(format("INSERT INTO taiwan_billing.cash_cera_point(account, cera_point,mod_date, reg_date)VALUES('%s',0,NOW(), NOW())", Uid)); + SocketObject.Write({ + success = "账号注册成功,现在您可以登录了!" + }); + return; + } + + }.bindenv(this); + + + } + + + function GetAccountByName(account) { + local sql = format("select UID , accountname, password from d_taiwan.accounts where accountname='%s'", account); + local Ret = MysqlObject.Select(sql, ["int", "string", "string"]); + print(Ret); + if (!Ret || Ret.len() == 0) { + return null; + } + + return { + uid = Ret[0][0], + account = Ret[0][1], + passwd = Ret[0][2] + } + } + + function _HttpServer_Event_DPS_(SocketObject, Header, Msg) { + if (PackHandleMap.rawin(Header.path)) { + local Jso = null; + try { + Jso = Json.Decode(Msg); + } catch (exception) { + + } + if (!Jso) return; + PackHandleMap[Header.path](SocketObject, Header, Jso); + + } else { + SocketObject.Write({ + error = "error url" + }); + } + } + + function MD5_Hash(str) { + local Ctx = Memory.alloc(0x100); + MD5.MD5_Init(Ctx.C_Object); + MD5.MD5_Update(Ctx.C_Object, Memory.allocUtf8String(str).C_Object, str.len()); + local Result = Memory.alloc(16); + MD5.MD5_Final(Result.C_Object, Ctx.C_Object); + return MD5.hex_encode(Result.readUtf8String(16)); + } +} + + +function _DPS_Login_Gateway_Init_(){ + getroottable()._DPS_Login_Gateway_Object_ <- _DPS_Login_Gateway_(); + print("\x1b[96m" + "############DPS启动器网关工作中############" + "\x1b[0m"); +} \ No newline at end of file diff --git a/示例项目/DPS启动器/Proj.ifo b/示例项目/DPS启动器/Proj.ifo new file mode 100644 index 0000000..2ba4d4e --- /dev/null +++ b/示例项目/DPS启动器/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "DPS启动器", + "ProjectDescribe": "DPS作为网关,QT编写的纯净登录器", + "ProjectAuthor": "倾泪寒", + "ProjectVersion": 1.0, + "ProjectConfig": "登录器配置.json", + "ProjectFiles": [ + "DPS启动器.nut" + ], + "ProjectRunFunc": "_DPS_Login_Gateway_Init_" +} \ No newline at end of file diff --git a/示例项目/DPS启动器/登录器配置.json b/示例项目/DPS启动器/登录器配置.json new file mode 100644 index 0000000..eda4d13 --- /dev/null +++ b/示例项目/DPS启动器/登录器配置.json @@ -0,0 +1,51 @@ +{ + "noticeclass": [ + "公告", + "活动", + "玩法" + ], + "notice": [ + { + "type": "img", + "category": 3, + "text": "安图恩攻坚战来袭,荒古武器等你拿!", + "link": "https://dnf.qq.com/cp/a20260108festivalgift/", + "content": "http://192.168.200.25:3000/Lenheart/ImageHosting/raw/branch/main/anton.png" + }, + { + "type": "img", + "category": 2, + "text": "命运序列礼包,2026新春礼包上线!", + "content": "http://192.168.200.25:3000/Lenheart/ImageHosting/raw/branch/main/mingyunxulie.png", + "link": "https://dnf.qq.com/cp/a20260108festivalgift/" + }, + { + "type": "img", + "category": 3, + "text": "终结狄瑞吉的不灭灾厄,终结不灭的宿命把!", + "content": "http://192.168.200.25:3000/Lenheart/ImageHosting/raw/branch/main/diruiji.png", + "link": "https://dnf.qq.com/cp/a20260108diregieactivity/" + }, + { + "type": "link", + "category": 1, + "text": "1月9日 5点停机更新公告", + "link": "https://dnf.qq.com/webplat/info/news_version3/119/495/498/m21449/202601/980616.shtml" + }, + { + "type": "link", + "category": 2, + "text": "DNF吧双福利来袭!感染之地挑战和盖楼互动,7000Q币狂送不停!", + "link": "https://dnf.qq.com/webplat/info/news_version3/119/495/496/m22999/202512/979722.shtml" + }, + { + "type": "link", + "category": 1, + "text": "关于1月8日初始化欢乐代币券的公告", + "link": "https://dnf.qq.com/webplat/info/news_version3/119/495/498/m21449/202512/980292.shtml" + } + ], + "scriptMd5": "", + "pluginsMd5": [], + "updateUrl": "www.baidu.com" +} \ No newline at end of file diff --git a/示例项目/GM便捷操作台/GM便捷操作台.nut b/示例项目/GM便捷操作台/GM便捷操作台.nut new file mode 100644 index 0000000..08e8dbd --- /dev/null +++ b/示例项目/GM便捷操作台/GM便捷操作台.nut @@ -0,0 +1,145 @@ +function _Dps_GmConvenienceConsole_Logic_() { + local Config = GlobalConfig.Get("GM便捷操作台_Lenheart.json"); + + + Gm_InputFunc_Handle[Config["发送道具指令"]] <- function(SUser, CmdString) { + local count = -1; + local pos = 0; + local handler = []; + do { + local start = pos; + pos = CmdString.find(" ", pos + 1); + if (pos != null) { + handler.append(CmdString.slice(start + 1, pos)); + } else + handler.append(CmdString.slice(start + 1)); + count = count + 1 + } while (pos != null) + + //得到空格数量 + if (count == 1) { + local Ret = SUser.GiveItem(handler[1].tointeger(), 1); + if (!Ret) SUser.SendNotiPacketMessage("发送失败背包是不是满了", 8); + } else if (count == 2) { + local Ret = SUser.GiveItem(handler[1].tointeger(), handler[2].tointeger()); + if (!Ret) SUser.SendNotiPacketMessage("发送失败背包是不是满了", 8); + } + } + + Gm_InputFunc_Handle[Config["转职觉醒指令"]] <- function(SUser, CmdString) { + local count = -1; + local pos = 0; + local handler = []; + do { + local start = pos; + pos = CmdString.find(" ", pos + 1); + if (pos != null) { + handler.append(CmdString.slice(start + 1, pos)); + } else + handler.append(CmdString.slice(start + 1)); + count = count + 1 + } while (pos != null) + + //得到空格数量 + if (count == 1) { + SUser.ChangeGrowType(handler[1].tointeger(), 0); + SUser.SendNotiPacket(0, 2, 0); + SUser.InitSkillW(handler[1].tointeger(), 0); + } else if (count == 2) { + SUser.ChangeGrowType(handler[1].tointeger(), handler[2].tointeger()); + SUser.SendNotiPacket(0, 2, 0); + SUser.InitSkillW(handler[1].tointeger(), handler[2].tointeger()); + } + } + + + Gm_InputFunc_Handle[Config["完成任务指令"]] <- function(SUser, CmdString) { + local count = -1; + local pos = 0; + local handler = []; + do { + local start = pos; + pos = CmdString.find(" ", pos + 1); + if (pos != null) { + handler.append(CmdString.slice(start + 1, pos)); + } else + handler.append(CmdString.slice(start + 1)); + count = count + 1 + } while (pos != null) + + //得到空格数量 + if (count == 1) { + SUser.ClearQuest_Gm(handler[1].tointeger()); + } + } + + Gm_InputFunc_Handle[Config["设置等级指令"]] <- function(SUser, CmdString) { + local count = -1; + local pos = 0; + local handler = []; + do { + local start = pos; + pos = CmdString.find(" ", pos + 1); + if (pos != null) { + handler.append(CmdString.slice(start + 1, pos)); + } else + handler.append(CmdString.slice(start + 1)); + count = count + 1 + } while (pos != null) + + //得到空格数量 + if (count == 1) { + SUser.SetCharacLevel(handler[1].tointeger()); + } + } + + //获取位置信息 + Gm_InputFunc_Handle[Config["获取位置指令"]] <- function(SUser, CmdString) { + local Location = SUser.GetLocation(); + SUser.SendNotiPacketMessage("X坐标: " + Location.Pos.X, 8); + SUser.SendNotiPacketMessage("Y坐标: " + Location.Pos.Y, 8); + SUser.SendNotiPacketMessage("城镇编号: " + Location.Town, 8); + SUser.SendNotiPacketMessage("区域编号: " + Location.Area, 8); + }; + + //踢人下线 + Gm_InputFunc_Handle[Config["踢玩家下线指令"]] <- function(SUser, CmdString) { + local count = -1; + local pos = 0; + local handler = []; + do { + local start = pos; + pos = CmdString.find(" ", pos + 1); + if (pos != null) { + handler.append(CmdString.slice(start + 1, pos)); + } else + handler.append(CmdString.slice(start + 1)); + count = count + 1 + } while (pos != null) + + //得到空格数量 + if (count == 1) { + local TUser = World.GetUserByName(handler[1]); + if(TUser)TUser.Kick(); + } + }; +} + + + +function _Dps_GmConvenienceConsole_Main_() { + _Dps_GmConvenienceConsole_Logic_(); +} + +function _Dps_GmConvenienceConsole_Main_Reload_(OldConfig) { + + foreach (Key,Value in OldConfig) { + try { + Gm_InputFunc_Handle.rawdelete(Key); + } catch (exception){ + + } + } + + _Dps_GmConvenienceConsole_Logic_(); +} \ No newline at end of file diff --git a/示例项目/GM便捷操作台/GM便捷操作台_Lenheart.json b/示例项目/GM便捷操作台/GM便捷操作台_Lenheart.json new file mode 100644 index 0000000..e981254 --- /dev/null +++ b/示例项目/GM便捷操作台/GM便捷操作台_Lenheart.json @@ -0,0 +1,16 @@ +{ + "发送道具指令": "GiveItem", + "转职觉醒指令": "GrowType", + "完成任务指令": "MakeQuest", + "设置等级指令": "SetLevel", + "获取位置指令": "GetLocation", + "踢玩家下线指令": "KickPlayer", + + "发送道具指令例子": "当有一个空格时跟道具编号 当有两个空格时 跟道具编号和发送数量 例: '//GiveItem 3037' '//GiveItem 3037 100'", + "转职觉醒指令例子": "当有一个空格时跟转职职业 当有两个空格时 跟转职职业和是否觉醒 例: '//GrowType 1' '//GrowType 1 1'", + "完成任务指令例子": "当有一个空格时跟任务编号 例: '//MakeQuest 1'", + "设置等级指令例子": "当有一个空格时跟设定等级 例: '//SetLevel 100'", + "获取位置指令例子": "无特殊用法 '//GetLocation'", + "踢玩家下线指令例子": "当有一个空格时跟想要踢除的玩家名字 例: '//KickPlayer 倾泪寒'", + "说明" : "所有指令都可更改 相对于的触发方式输入自己设定的指令即可" +} \ No newline at end of file diff --git a/示例项目/GM便捷操作台/Proj.ifo b/示例项目/GM便捷操作台/Proj.ifo new file mode 100644 index 0000000..6be3718 --- /dev/null +++ b/示例项目/GM便捷操作台/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "GM便捷操作台", + "ProjectDescribe": "本项目提供了许多GM的便捷操作 方便平时调试或写代码 \n输入'//' + 设定的指令即可执行逻辑 ", + "ProjectAuthor": "倾泪寒", + "ProjectVersion": 1.0, + "ProjectConfig": "GM便捷操作台_Lenheart.json", + "ProjectFiles": [ + "GM便捷操作台.nut" + ], + "ProjectRunFunc": "_Dps_GmConvenienceConsole_Main_" +} \ No newline at end of file diff --git a/示例项目/NPC商店限购/NPC商店限购.nut b/示例项目/NPC商店限购/NPC商店限购.nut new file mode 100644 index 0000000..1112d9c --- /dev/null +++ b/示例项目/NPC商店限购/NPC商店限购.nut @@ -0,0 +1,290 @@ +g_ShopLimitCache <- {}; + +class _Shop_Limit_Bynangua { + function get_trade_time() { + local currentDate = date() + local year = currentDate.year + local month = currentDate.month + 1 + local day = currentDate.day + local hour = currentDate.hour + local minute = currentDate.min + local second = currentDate.sec + return format("%04d-%02d-%02d %02d:%02d:%02d", year, month, day, hour, minute, second) + } + + // 计算下个月的1号早上6点 + function GetFirstOfNextMonth() { + local currentDate = date() + local year = currentDate.year + local month = currentDate.month + 2 // +2是因为month是从0开始的,且要算下个月 + local day = 1 + local hour = 6 + local minute = 0 + local second = 0 + return format("%04d-%02d-%02d %02d:%02d:%02d", year, month, day, hour, minute, second) + } + + // 计算下周一早上6点 + function GetNextMonday() { + local currentDate = date() + local day = currentDate.wday // 0是周日,1-6是周一到周六 + local diff = (day == 0) ? 1 : (8 - day) + currentDate.day += diff + local year = currentDate.year + local month = currentDate.month + 1 + local day = currentDate.day + local hour = 6 + local minute = 0 + local second = 0 + return format("%04d-%02d-%02d %02d:%02d:%02d", year, month, day, hour, minute, second) + } + + // 计算第二天早上6点 + function GetSixAMNextDay() { + local currentDate = date() + local year = currentDate.year + local month = currentDate.month + 1 + local day = currentDate.day + 1 + local hour = 6 + local minute = 0 + local second = 0 + return format("%04d-%02d-%02d %02d:%02d:%02d", year, month, day, hour, minute, second) + } + + // 获取刷新时间 + function GetRefreshTime(refresh_month, refresh_weeks, refresh_days) { + if (refresh_month == 1) { + return GetFirstOfNextMonth() + } else if (refresh_weeks == 1) { + return GetNextMonday() + } else if (refresh_days == 1) { + return GetSixAMNextDay() + } + return null + } + + function HandlePurchase(SUser, item_id, item_shop_id, item_info, item_cnt) { + local charac_no = SUser.GetCID(); + local cache_key = charac_no + "_" + item_id + "_" + item_shop_id; + + if (g_ShopLimitCache.rawin(cache_key)) { + local cache_data = g_ShopLimitCache[cache_key]; + local buy_count = cache_data.buy_count; + local refresh_time = cache_data.refresh_time; + local current_time = get_trade_time(); + + if (current_time >= refresh_time) { + if (item_cnt > item_info[1]) { + SUser.SendNotiPacketMessage("购买失败,可购买数量剩余 " + item_info[1] + " 个", 8); + return true; + } + + local new_refresh_time = GetRefreshTime(item_info[2], item_info[3], item_info[4]); + g_ShopLimitCache[cache_key] <- { + buy_count = item_cnt, + refresh_time = new_refresh_time + }; + + updateDatabaseAsync(charac_no, item_id, item_shop_id, item_cnt, new_refresh_time); + } else { + local remaining = item_info[1] - buy_count; + if (item_cnt > remaining) { + if (remaining == 0) { + local refresh_msg = ""; + if (item_info[2] == 1) { // 月刷新 + refresh_msg = "每月一日上午六点"; + } else if (item_info[3] == 1) { // 周刷新 + refresh_msg = "每周一日上午六点"; + } else if (item_info[4] == 1) { // 日刷新 + refresh_msg = "每日一日上午六点"; + } + SUser.SendNotiPacketMessage("当前商品已售罄,将在" + refresh_msg + "刷新", 8); + } else { + SUser.SendNotiPacketMessage("购买失败,可购买数量剩余 " + remaining + " 个", 8); + } + return true; + } + + g_ShopLimitCache[cache_key] <- { + buy_count = buy_count + item_cnt, + refresh_time = refresh_time + }; + + updateDatabaseAsync(charac_no, item_id, item_shop_id, buy_count + item_cnt, refresh_time); + } + return false; + } + + local SqlObj = MysqlPool.GetInstance().GetConnect(); + local query = "SELECT id, charac_no, item_id, buy_count, item_shop, refresh_time FROM DP_S.restrict_npc_shop_buy WHERE charac_no=" + charac_no + " AND item_id=" + item_id + " AND item_shop=" + item_shop_id; + local column_type_list = ["int", "int", "int", "int", "int", "string"]; + local result = SqlObj.Select(query, column_type_list); + MysqlPool.GetInstance().PutConnect(SqlObj); + + if (result.len() == 0) { + if (item_cnt > item_info[1]) { + SUser.SendNotiPacketMessage("购买失败,可购买数量剩余 " + item_info[1] + " 个", 8); + return true; + } + + local refresh_time = GetRefreshTime(item_info[2], item_info[3], item_info[4]); + + g_ShopLimitCache[cache_key] <- { + buy_count = item_cnt, + refresh_time = refresh_time + }; + + updateDatabaseAsync(charac_no, item_id, item_shop_id, item_cnt, refresh_time); + } else { + local buy_count = result[0][3]; + local refresh_time = result[0][5]; + local current_time = get_trade_time(); + + g_ShopLimitCache[cache_key] <- { + buy_count = buy_count, + refresh_time = refresh_time + }; + + if (current_time >= refresh_time) { + if (item_cnt > item_info[1]) { + SUser.SendNotiPacketMessage("购买失败,可购买数量剩余 " + item_info[1] + " 个", 8); + return true; + } + + local new_refresh_time = GetRefreshTime(item_info[2], item_info[3], item_info[4]); + g_ShopLimitCache[cache_key] <- { + buy_count = item_cnt, + refresh_time = new_refresh_time + }; + + updateDatabaseAsync(charac_no, item_id, item_shop_id, item_cnt, new_refresh_time); + } else { + local remaining = item_info[1] - buy_count; + if (item_cnt > remaining) { + if (remaining == 0) { + local refresh_msg = ""; + if (item_info[2] == 1) { // 月刷新 + refresh_msg = "每月一日上午六点"; + } else if (item_info[3] == 1) { // 周刷新 + refresh_msg = "每周一日上午六点"; + } else if (item_info[4] == 1) { // 日刷新 + refresh_msg = "每日一日上午六点"; + } + SUser.SendNotiPacketMessage("当前商品已售罄,将在" + refresh_msg + "刷新", 8); + } else { + SUser.SendNotiPacketMessage("购买失败,可购买数量剩余 " + remaining + " 个", 8); + } + return true; + } + + g_ShopLimitCache[cache_key] <- { + buy_count = buy_count + item_cnt, + refresh_time = refresh_time + }; + + updateDatabaseAsync(charac_no, item_id, item_shop_id, buy_count + item_cnt, refresh_time); + } + } + return false; + } +} + +// 更新缓存到数据库 +function updateDatabaseAsync(charac_no, item_id, item_shop_id, buy_count, refresh_time) { + local SqlObj = MysqlPool.GetInstance().GetConnect(); + // 使用 ON DUPLICATE KEY UPDATE 来更新已存在的记录 + local query = "INSERT INTO DP_S.restrict_npc_shop_buy (charac_no, item_id, buy_count, item_shop, refresh_time) " + + "VALUES (" + charac_no + ", " + item_id + ", " + buy_count + ", " + item_shop_id + ", '" + refresh_time + "') " + + "ON DUPLICATE KEY UPDATE buy_count = VALUES(buy_count), refresh_time = VALUES(refresh_time)"; + SqlObj.Select(query, []); + MysqlPool.GetInstance().PutConnect(SqlObj); +} + +// 服务器启动时加载缓存 +function loadShopLimitCache() { + local SqlObj = MysqlPool.GetInstance().GetConnect(); + local query = "SELECT charac_no, item_id, item_shop, buy_count, refresh_time FROM DP_S.restrict_npc_shop_buy"; + local column_type_list = ["int", "int", "int", "int", "string"]; + local result = SqlObj.Select(query, column_type_list); + MysqlPool.GetInstance().PutConnect(SqlObj); + + foreach(row in result) { + local cache_key = row[0] + "_" + row[1] + "_" + row[2]; + g_ShopLimitCache[cache_key] <- { + buy_count = row[3], + refresh_time = row[4] + }; + } +} + +// 服务器关闭时保存缓存 +Cb_Server_Close_Enter_Func.ShopLimitSave <- function(arg) { + foreach(cache_key, data in g_ShopLimitCache) { + local parts = split(cache_key, "_"); + local charac_no = parts[0].tointeger(); + local item_id = parts[1].tointeger(); + local item_shop = parts[2].tointeger(); + updateDatabaseAsync(charac_no, item_id, item_shop, data.buy_count, data.refresh_time); + } + g_ShopLimitCache <- {}; +} + +function _Dps_Shop_Limit_Main_() { + CreateShopLimitTable(); + loadShopLimitCache(); + Cb_BuyItem_check_error_Leave_Func.Shop_Limit_ByNangua <- function(args) { + local Config = GlobalConfig.Get("NPC商店限购配置_Nangua.json"); + local shop_limit_config = Config["商店限购配置"]; + local is_buy = false; + local SUser = User(args[1]); + local msg_base = args[2]; + local Item_id = NativePointer(msg_base).add(13).readU32(); + local item_shop = NativePointer(msg_base).add(21).readU32(); + local item_cnt = NativePointer(msg_base).add(17).readU32(); + local item_shop_id = item_shop.tostring(); + if (item_shop_id in shop_limit_config) { + local items_list = shop_limit_config[item_shop_id]; + foreach(item_info in items_list) { + if (item_info[0] == Item_id) { + is_buy = _Shop_Limit_Bynangua.HandlePurchase(SUser, Item_id, item_shop, item_info, item_cnt); + break; + } + } + } + if (is_buy) { + return 19; + } + } +} + +function CreateShopLimitTable() { + local Config = GlobalConfig.Get("NPC商店限购配置_Nangua.json"); + local PoolObj = MysqlPool.GetInstance(); + local Ip = Config["数据库IP 不是外置数据库不要更改"]; + local Port = Config["数据库端口 不懂不要更改"]; + local DbName = Config["数据库用户名 本地用户名不懂不要更改"]; + local Password = Config["数据库密码 本地密码不懂不要更改"]; + + PoolObj.SetBaseConfiguration(Ip, Port, DbName, Password); + PoolObj.PoolSize = 10; + PoolObj.Init(); + + // 建库建表SQL + local CreateSql1 = "create database if not exists DP_S default charset utf8;"; + local CreateSql2 = "CREATE TABLE IF NOT EXISTS DP_S.restrict_npc_shop_buy (" + + "id int(11) AUTO_INCREMENT, " + + "charac_no int(11) NOT NULL, " + + "item_id int(11) NOT NULL, " + + "buy_count int(11) NOT NULL, " + + "item_shop int(11) NOT NULL, " + + "refresh_time varchar(255) NOT NULL, " + + "PRIMARY KEY (id), " + + "UNIQUE KEY unique_purchase (charac_no, item_id, item_shop)" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8;"; + + // 执行SQL + local SqlObj = MysqlPool.GetInstance().GetConnect(); + SqlObj.Exec_Sql(CreateSql1); + SqlObj.Exec_Sql(CreateSql2); + MysqlPool.GetInstance().PutConnect(SqlObj); +} \ No newline at end of file diff --git a/示例项目/NPC商店限购/NPC商店限购配置_Nangua.json b/示例项目/NPC商店限购/NPC商店限购配置_Nangua.json new file mode 100644 index 0000000..fae9dbc --- /dev/null +++ b/示例项目/NPC商店限购/NPC商店限购配置_Nangua.json @@ -0,0 +1,20 @@ +{ + "提示1": "15代表商店NPC编号[道具ID,限购数量,每月一号刷新,周一刷新,每天上午六点刷新]", + "提示2":"月周日在对应的位置填1代表生效如[2680313, 10, 0, 0, 1]代表道具ID为2680313的道具在15号商店限购10个,每天上午六点刷新", + "提示3":"月周日在对应的位置填1代表生效如[2680313, 10, 0, 1, 0]代表道具ID为2680313的道具在15号商店限购10个,每周一上午六点刷新", + "提示4":"月周日在对应的位置填1代表生效如[2680313, 10, 1, 0, 0]代表道具ID为2680313的道具在15号商店限购10个,每月一号上午六点刷新", + "商店限购配置": { + "15": [ + [2680313, 10, 0, 0, 1], + [326211, 10, 0, 0, 1] + ], + "16": [ + [2680314, 10, 0, 0, 1], + [326211, 10, 0, 0, 1] + ] + }, + "数据库IP 不是外置数据库不要更改": "127.0.0.1", + "数据库端口 不懂不要更改": 3306, + "数据库用户名 本地用户名不懂不要更改": "game", + "数据库密码 本地密码不懂不要更改": "uu5!^%jg" +} \ No newline at end of file diff --git a/示例项目/NPC商店限购/Proj.ifo b/示例项目/NPC商店限购/Proj.ifo new file mode 100644 index 0000000..418fc52 --- /dev/null +++ b/示例项目/NPC商店限购/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "NPC商店限购", + "ProjectDescribe": "指定NPC商店的指定物品的可购买数量,可设为每日、每周一、每月1号的上午六点刷新", + "ProjectAuthor": "南瓜", + "ProjectVersion": 1.0, + "ProjectConfig": "NPC商店限购配置_Nangua.json", + "ProjectFiles": [ + "NPC商店限购.nut" + ], + "ProjectRunFunc": "_Dps_Shop_Limit_Main_" +} \ No newline at end of file diff --git a/示例项目/SP_TP属性石返还/Proj.ifo b/示例项目/SP_TP属性石返还/Proj.ifo new file mode 100644 index 0000000..9f83f25 --- /dev/null +++ b/示例项目/SP_TP属性石返还/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "SP_TP属性石返还", + "ProjectDescribe": "初始化以及转职时返还使用过的sp/tp以及属性石,不是直接返还属性石或者SP/TP书而是直接增加,使用此项目前增加的sp/tp属性石都不会返还,使用后才会增加记录从而进行返还", + "ProjectAuthor": "小南瓜", + "ProjectVersion": 1.2, + "ProjectConfig": "SP_TP属性石返还_Nangua.json", + "ProjectFiles": [ + "SP_TP属性石返还.nut" + ], + "ProjectRunFunc": "_Dps_SPTP_Attributes_Main_" +} \ No newline at end of file diff --git a/示例项目/SP_TP属性石返还/SP_TP属性石返还.nut b/示例项目/SP_TP属性石返还/SP_TP属性石返还.nut new file mode 100644 index 0000000..fe172ef --- /dev/null +++ b/示例项目/SP_TP属性石返还/SP_TP属性石返还.nut @@ -0,0 +1,309 @@ +// 创建数据库和表 +function createAttributesTable() { + local Config = GlobalConfig.Get("SP_TP属性石返还_Nangua.json"); + local PoolObj = MysqlPool.GetInstance(); + local Ip = Config["数据库IP 不是外置数据库不要更改"]; + local Port = Config["数据库端口 不懂不要更改"]; + local DbName = Config["数据库用户名 本地用户名不懂不要更改"]; + local Password = Config["数据库密码 本地密码不懂不要更改"]; + + PoolObj.SetBaseConfiguration(Ip, Port, DbName, Password); + PoolObj.PoolSize = 10; + PoolObj.Init(); + + local CreateSql1 = "create database if not exists DP_S default charset utf8;"; + local CreateSql2 = "CREATE TABLE IF NOT EXISTS DP_S.charac_attributes (" + + "id int(11) AUTO_INCREMENT, " + + "charac_no int(11) NOT NULL, " + + "item_id int(11) DEFAULT NULL, " + + "type int(11) DEFAULT NULL, " + + "guildExpBook int(11) DEFAULT NULL, " + + "total_sp int(11) DEFAULT 0, " + + "total_tp int(11) DEFAULT 0, " + + "PRIMARY KEY (id), " + + "UNIQUE KEY unique_char_item (charac_no, item_id)" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8;"; + + local SqlObj = MysqlPool.GetInstance().GetConnect(); + SqlObj.Exec_Sql(CreateSql1); + SqlObj.Exec_Sql(CreateSql2); + MysqlPool.GetInstance().PutConnect(SqlObj); +} + +// 初始化技能时返还SP/TP +Cb_SkillInit_process_skill_Leave_Func.SkillInitByNangua <- function(args) { + local SUser = User(args[1]); + local Skill = SUser.GetSkillW(); + local charac_no = SUser.GetCID(); + + local SqlObj = MysqlPool.GetInstance().GetConnect(); + local query = "SELECT IFNULL(total_sp, 0) as total_sp, IFNULL(total_tp, 0) as total_tp " + + "FROM DP_S.charac_attributes WHERE charac_no = " + charac_no + " AND item_id IS NULL"; + + local result = SqlObj.Select(query, ["int", "int"]); + MysqlPool.GetInstance().PutConnect(SqlObj); + + local total_sp = 0; + local total_tp = 0; + if (result.len() > 0) { + total_sp = result[0][0]; + total_tp = result[0][1]; + } + + local SkillTreeIndex = Sq_CallFunc(S_Ptr("0x0822f33c"), "int", ["pointer"], SUser.C_Object); + local remain_sp_0 = Sq_CallFunc(S_Ptr("0x08603528"), "int", ["pointer", "int"], Skill, 0); + local remain_sp_1 = Sq_CallFunc(S_Ptr("0x08603528"), "int", ["pointer", "int"], Skill, 1); + local remain_tp_0 = Sq_CallFunc(S_Ptr("0x086035f2"), "int", ["pointer", "int"], Skill, 2); + local remain_tp_1 = Sq_CallFunc(S_Ptr("0x086035f2"), "int", ["pointer", "int"], Skill, 3); + + if (SkillTreeIndex == -1 || SkillTreeIndex == 0) { + Sq_CallFunc(S_Ptr("0x086034f8"), "int", ["pointer", "int", "int"], Skill, remain_sp_0 + total_sp, 0); + Sq_CallFunc(S_Ptr("0x08603590"), "int", ["pointer", "int", "int"], Skill, remain_tp_0 + total_tp, 2); + } + if (SkillTreeIndex == 1) { + Sq_CallFunc(S_Ptr("0x086034f8"), "int", ["pointer", "int", "int"], Skill, remain_sp_1 + total_sp, 1); + Sq_CallFunc(S_Ptr("0x08603590"), "int", ["pointer", "int", "int"], Skill, remain_tp_1 + total_tp, 3); + } +} + +// 转职时返还SP/TP和属性石 +Cb_User_set_grow_Leave_Func.changegrowByNangua <- function(args) { + local SUser = User(args[0]); + local grow = args[2]; + local charac_no = SUser.GetCID(); + local Skill = SUser.GetSkillW(); + + local SqlObj = MysqlPool.GetInstance().GetConnect(); + + // 查询SP/TP + local querySpTp = "SELECT IFNULL(total_sp, 0) as total_sp, IFNULL(total_tp, 0) as total_tp " + + "FROM DP_S.charac_attributes WHERE charac_no = " + charac_no + " AND item_id IS NULL"; + local spTpResult = SqlObj.Select(querySpTp, ["int", "int"]); + + local total_sp = 0; + local total_tp = 0; + if (spTpResult.len() > 0) { + total_sp = spTpResult[0][0]; + total_tp = spTpResult[0][1]; + } + + // 根据转职类型决定是否查询属性石 + local attrResult = []; + if (grow == 0) { + local queryAttr = "SELECT type, guildExpBook FROM DP_S.charac_attributes " + + "WHERE charac_no = " + charac_no + " AND item_id IS NOT NULL"; + attrResult = SqlObj.Select(queryAttr, ["int", "int"]); + } + + MysqlPool.GetInstance().PutConnect(SqlObj); + + // 处理SP/TP + local SkillTreeIndex = Sq_CallFunc(S_Ptr("0x0822f33c"), "int", ["pointer"], SUser.C_Object); + local remain_sp_0 = Sq_CallFunc(S_Ptr("0x08603528"), "int", ["pointer", "int"], Skill, 0); + local remain_sp_1 = Sq_CallFunc(S_Ptr("0x08603528"), "int", ["pointer", "int"], Skill, 1); + local remain_tp_0 = Sq_CallFunc(S_Ptr("0x086035f2"), "int", ["pointer", "int"], Skill, 2); + local remain_tp_1 = Sq_CallFunc(S_Ptr("0x086035f2"), "int", ["pointer", "int"], Skill, 3); + + if (SkillTreeIndex == -1) { + Sq_CallFunc(S_Ptr("0x086034f8"), "int", ["pointer", "int", "int"], Skill, remain_sp_0 + total_sp, 0); + Sq_CallFunc(S_Ptr("0x08603590"), "int", ["pointer", "int", "int"], Skill, remain_tp_0 + total_tp, 2); + Sq_CallFunc(S_Ptr("0x866C46A"), "void", ["pointer"], SUser.C_Object); + } + if (SkillTreeIndex == 0 || SkillTreeIndex == 1) { + Sq_CallFunc(S_Ptr("0x086034f8"), "int", ["pointer", "int", "int"], Skill, remain_sp_0 + total_sp, 0); + Sq_CallFunc(S_Ptr("0x086034f8"), "int", ["pointer", "int", "int"], Skill, remain_sp_1 + total_sp, 1); + Sq_CallFunc(S_Ptr("0x08603590"), "int", ["pointer", "int", "int"], Skill, remain_tp_0 + total_tp, 2); + Sq_CallFunc(S_Ptr("0x08603590"), "int", ["pointer", "int", "int"], Skill, remain_tp_1 + total_tp, 3); + Sq_CallFunc(S_Ptr("0x866C46A"), "void", ["pointer"], SUser.C_Object); + } + + // 处理属性石(仅当 grow == 0 时) + if (grow == 0 && attrResult.len() > 0) { + local userAddInfo = Sq_CallFunc(S_Ptr("0x086960d8"), "pointer", ["pointer"], SUser.C_Object); + + foreach (idx, row in attrResult) { + local type = row[0]; + local guildExpBook = row[1]; + + switch (type) { + case 2: + NativePointer(userAddInfo).writeU32(NativePointer(userAddInfo).readU32() + guildExpBook); + break; + case 3: + NativePointer(userAddInfo).add(4).writeU32(NativePointer(userAddInfo).add(4).readU32() + guildExpBook); + break; + case 4: + NativePointer(userAddInfo).add(8).writeU16(NativePointer(userAddInfo).add(8).readU16() + guildExpBook); + break; + case 5: + NativePointer(userAddInfo).add(10).writeU16(NativePointer(userAddInfo).add(10).readU16() + guildExpBook); + break; + case 6: + NativePointer(userAddInfo).add(12).writeU16(NativePointer(userAddInfo).add(12).readU16() + guildExpBook); + break; + case 7: + NativePointer(userAddInfo).add(14).writeU16(NativePointer(userAddInfo).add(14).readU16() + guildExpBook); + break; + case 8: + NativePointer(userAddInfo).add(66).writeU32(NativePointer(userAddInfo).add(66).readU32() + guildExpBook); + break; + case 9: + for (local j = 0; j < 4; j++) { + local offset = 2 * j * 8; + local ptr = NativePointer(userAddInfo).add(offset); + ptr.writeU16(ptr.readU16() + guildExpBook); + } + break; + } + + api_recover_attributes(SUser, type, guildExpBook, -1); + } + } +} + +// 使用道具时更新数据库 +Cb_User_increase_status_Enter_Func.UseItemByNangua <- function(args) { + local SUser = User(args[0]); + local Slot = args[1]; + local userItem = Memory.alloc(128); + local InvenObj = SUser.GetInven(); + if (!InvenObj) { + return; + } + + Sq_CallFunc(S_Ptr("0x084fb918"), "pointer", ["pointer", "pointer", "int", "int"], userItem.C_Object, InvenObj.C_Object, 1, Slot); + local item_id = NativePointer(userItem.C_Object).add(2).readU32(); + local charac_no = SUser.GetCID(); + + local SqlObj = MysqlPool.GetInstance().GetConnect(); + + // SP道具 + if (item_id == 1031) { + updateSPTP(SqlObj, charac_no, 5, false); + } else if (item_id == 1038) { + updateSPTP(SqlObj, charac_no, 20, false); + } + // TP道具 + else if (item_id == 1204) { + updateSPTP(SqlObj, charac_no, 1, true); + } else if (item_id == 1205) { + updateSPTP(SqlObj, charac_no, 5, true); + } + // 属性石 + else { + local type = -1; + local guildExpBook = 0; + + switch (item_id) { + case 1039: type = 4; guildExpBook = 50; break; + case 1040: type = 6; guildExpBook = 50; break; + case 1041: type = 5; guildExpBook = 50; break; + case 1042: type = 7; guildExpBook = 50; break; + case 1043: type = 2; guildExpBook = 250; break; + case 1044: type = 3; guildExpBook = 250; break; + case 1045: type = 8; guildExpBook = 10; break; + case 1046: type = 9; guildExpBook = 10; break; + } + + if (type != -1) { + local query = "INSERT INTO DP_S.charac_attributes (charac_no, item_id, type, guildExpBook) " + + "VALUES (" + charac_no + ", " + item_id + ", " + type + ", " + guildExpBook + ") " + + "ON DUPLICATE KEY UPDATE guildExpBook = guildExpBook + " + guildExpBook; + SqlObj.Select(query, []); + } + } + + MysqlPool.GetInstance().PutConnect(SqlObj); +} + +Cb_ChangeGrowType_Item_Leave_Func.ApplyAttributesByNangua <- function(args) { + local SUser = User(args[0]); + local slot = args[2]; + local charac_no = SUser.GetCID(); + + local userItem = Memory.alloc(128); + local InvenObj = SUser.GetInven(); + if (!InvenObj) return; + + Sq_CallFunc(S_Ptr("0x084fb918"), "pointer", ["pointer", "pointer", "int", "int"], userItem.C_Object, InvenObj.C_Object, 1, slot); + local item_id = NativePointer(userItem.C_Object).add(2).readU32(); + if (!(item_id == 2660779 || item_id == 2660702)) return; + + local SqlObj = MysqlPool.GetInstance().GetConnect(); + local query = "SELECT type, guildExpBook FROM DP_S.charac_attributes " + + "WHERE charac_no = " + charac_no + " AND item_id IS NOT NULL"; + local attrResult = SqlObj.Select(query, ["int", "int"]); + MysqlPool.GetInstance().PutConnect(SqlObj); + + if (attrResult.len() == 0) return; + + local userAddInfo = Sq_CallFunc(S_Ptr("0x086960d8"), "pointer", ["pointer"], SUser.C_Object); + + foreach (row in attrResult) { + local type = row[0]; + local guildExpBook = row[1]; + + switch (type) { + case 2: NativePointer(userAddInfo).writeU32(NativePointer(userAddInfo).readU32() + guildExpBook); break; + case 3: NativePointer(userAddInfo).add(4).writeU32(NativePointer(userAddInfo).add(4).readU32() + guildExpBook); break; + case 4: NativePointer(userAddInfo).add(8).writeU16(NativePointer(userAddInfo).add(8).readU16() + guildExpBook); break; + case 5: NativePointer(userAddInfo).add(10).writeU16(NativePointer(userAddInfo).add(10).readU16() + guildExpBook); break; + case 6: NativePointer(userAddInfo).add(12).writeU16(NativePointer(userAddInfo).add(12).readU16() + guildExpBook); break; + case 7: NativePointer(userAddInfo).add(14).writeU16(NativePointer(userAddInfo).add(14).readU16() + guildExpBook); break; + case 8: NativePointer(userAddInfo).add(66).writeU32(NativePointer(userAddInfo).add(66).readU32() + guildExpBook); break; + case 9: + for (local j = 0; j < 4; j++) { + local ptr = NativePointer(userAddInfo).add(2 * j * 8); + ptr.writeU16(ptr.readU16() + guildExpBook); + } + break; + } + + api_recover_attributes(SUser, type, guildExpBook, -1); + } +} + +// 更新SP/TP值 +function updateSPTP(SqlObj, charac_no, value, is_tp) { + local query = "SELECT id, total_sp, total_tp FROM DP_S.charac_attributes " + + "WHERE charac_no = " + charac_no + " AND item_id IS NULL"; + local result = SqlObj.Select(query, ["int", "int", "int"]); + + if (result.len() > 0) { + local current_sp = result[0][1]; + local current_tp = result[0][2]; + local new_sp = is_tp ? current_sp : (current_sp + value); + local new_tp = is_tp ? (current_tp + value) : current_tp; + + query = "UPDATE DP_S.charac_attributes " + + "SET total_sp = " + new_sp + ", total_tp = " + new_tp + " " + + "WHERE charac_no = " + charac_no + " AND item_id IS NULL"; + } else { + query = "INSERT INTO DP_S.charac_attributes (charac_no, total_sp, total_tp) " + + "VALUES (" + charac_no + ", " + + (is_tp ? "0" : value.tostring()) + ", " + + (is_tp ? value.tostring() : "0") + ")"; + } + + SqlObj.Select(query, []); +} + +// 发包更新增加玩家属性 +function api_recover_attributes(SUser, type, guildExpBook, slof) { + local Pack = Packet(); + Pack.Put_Header(1, 32); + Pack.Put_Byte(1); + Pack.Put_Short(slof); + Pack.Put_Byte(type); + Pack.Put_Int(guildExpBook); + Pack.Put_Short(0); + Pack.Put_Short(0); + Pack.Finalize(true); + SUser.Send(Pack); + Pack.Delete(); +} + +// 主函数 +function _Dps_SPTP_Attributes_Main_() { + createAttributesTable(); +} \ No newline at end of file diff --git a/示例项目/SP_TP属性石返还/SP_TP属性石返还_Nangua.json b/示例项目/SP_TP属性石返还/SP_TP属性石返还_Nangua.json new file mode 100644 index 0000000..702a348 --- /dev/null +++ b/示例项目/SP_TP属性石返还/SP_TP属性石返还_Nangua.json @@ -0,0 +1,7 @@ +{ + "提示":"挂载后只有新使用的sp,tp,属性石会返还,之前使用过的不会返还", + "数据库IP 不是外置数据库不要更改": "127.0.0.1", + "数据库端口 不懂不要更改": 3306, + "数据库用户名 本地用户名不懂不要更改": "game", + "数据库密码 本地密码不懂不要更改": "uu5!^%jg" +} \ No newline at end of file diff --git a/示例项目/一键分解卷/Proj.ifo b/示例项目/一键分解卷/Proj.ifo new file mode 100644 index 0000000..39d7199 --- /dev/null +++ b/示例项目/一键分解卷/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "一键分解卷", + "ProjectDescribe": "使用道具将需要分解的装备分解,如未开启副职业及开启摆摊分解则调用诺顿分解机分解", + "ProjectAuthor": "南瓜", + "ProjectVersion": 1.3, + "ProjectConfig": "一键分解卷_Lenheart.json", + "ProjectFiles": [ + "一键分解卷.nut" + ], + "ProjectRunFunc": "_Dps_OneClickDisassemblyOfRoll_Main_" +} \ No newline at end of file diff --git a/示例项目/一键分解卷/一键分解卷.nut b/示例项目/一键分解卷/一键分解卷.nut new file mode 100644 index 0000000..0e1990e --- /dev/null +++ b/示例项目/一键分解卷/一键分解卷.nut @@ -0,0 +1,92 @@ +function _Dps_OneClickDisassemblyOfRoll_Logic_() { + local Config = GlobalConfig.Get("一键分解卷_Lenheart.json"); + //分解券 + Cb_Use_Item_Sp_Func[Config["分解卷的道具ID"]] <- function(SUser, ItemId) { + local index = 0; + local InvenObj = SUser.GetInven(); + if (InvenObj) { + for (local i = Config["一键分解的起始位置_就是背包从第几格开始"]; i <= Config["一键分解的结束位置_就是背包到第几格结束"]; i++) { // 遍历9到16号背包格子 + local ItemObj = InvenObj.GetSlot(1, i); + if (!ItemObj.IsEmpty) { + local item_id = ItemObj.GetIndex(); // 获取物品ID + local PvfItemObj = PvfItem.GetPvfItemById(item_id); + local CItem_get_rarity = PvfItemObj.GetRarity(); // 装备品级 + local item_upgrade = ItemObj.GetUpgrade(); + local item_name = PvfItem.GetNameById(item_id); + if (CItem_get_rarity <= Config["分解品级(含)"]) { // 粉装及以下品级装备 + + // 检查副职业是否开启 + local checkTag = Sq_CallFunc(S_Ptr("0x822f8d4"), "int", ["pointer"], SUser.C_Object); + local is = SUser.GetCurCharacExpertJob(); + if (!is) { + // 如果副职业未开启则调用诺顿分解机 + SUser.DisPatcher_DisJointItem_disjoint(i, 28, S_Ptr("0x0")); + } else { + // 如果副职业已开启则调用自身分解机 + SUser.DisPatcher_DisJointItem_disjoint(i, 239, SUser.C_Object); + } + if (item_upgrade > 0) { + item_name = "+" + item_upgrade + item_name; + } + local newItemObj = InvenObj.GetSlot(1, i); + if (newItemObj.IsEmpty) { // 如果分解成功 + index++; + SUser.SendUpdateItemList(1, 0, i); + local AdMsgObj = AdMsg(); + AdMsgObj.PutType(Config["分解成功提示发送位置"]); + if (Config["分解成功提示发送位置"] != 14) { + AdMsgObj.PutString(" "); + } + AdMsgObj.PutString(" 成功分解装备"); + AdMsgObj.PutEquipment("[" + item_name + "]", ItemObj, DisJointItemBynangua.RarityColor(item_id)); + AdMsgObj.Finalize(); + SUser.Send(AdMsgObj.MakePack()); + AdMsgObj.Delete(); + } + } + } + } + + if (index > 0) { + SUser.SendNotiPacketMessage("恭喜: " + index + " 件装备分解成功。", 8); + } else { + SUser.SendNotiPacketMessage("装备分解失败,道具已返还", 8); + } + if (Config["是否返还分解券道具(true代表返还,false代表不返还)"]) { + Timer.setTimeout(function() { + SUser.GiveItem(ItemId, 1); + }, 1); + } + } + }; +} + +class DisJointItemBynangua { + function RarityColor(item_id) { + local PvfItemObj = PvfItem.GetPvfItemById(item_id); + if (PvfItemObj == null) { + return; + } + local CItem_get_rarity = PvfItemObj.GetRarity(); // 装备品级 + return DisJointItemBynangua.rarityColorMap[(CItem_get_rarity).tostring()]; + } + rarityColorMap = { + "0": [255, 255, 255], // 普通 + "1": [104, 213, 237], // 高级 + "2": [179, 107, 255], // 稀有 + "3": [255, 0, 255], // 神器 + "4": [255, 180, 0], // 史诗 + "5": [255, 102, 102], // 勇者 + "6": [255, 20, 147], // 深粉红色 + "7": [255, 215, 0] // 金色 + }; +} + +function _Dps_OneClickDisassemblyOfRoll_Main_() { + _Dps_OneClickDisassemblyOfRoll_Logic_(); +} + +function _Dps_OneClickDisassemblyOfRoll_Main_Reload_(OldConfig) { + Cb_Use_Item_Sp_Func.rawdelete(OldConfig["分解卷的道具ID"]); + _Dps_OneClickDisassemblyOfRoll_Logic_(); +} \ No newline at end of file diff --git a/示例项目/一键分解卷/一键分解卷_Lenheart.json b/示例项目/一键分解卷/一键分解卷_Lenheart.json new file mode 100644 index 0000000..51cd4e9 --- /dev/null +++ b/示例项目/一键分解卷/一键分解卷_Lenheart.json @@ -0,0 +1,9 @@ +{ + "分解卷的道具ID": 7577, + "是否返还分解券道具(true代表返还,false代表不返还)": true, + "一键分解的起始位置_就是背包从第几格开始": 9, + "一键分解的结束位置_就是背包到第几格结束": 16, + "分解品级(含)": 4, + "分解成功提示发送位置": 3, + "提示":"起始位置快捷栏(快捷栏3-8),装备栏(快捷栏9-56,如第一排为9-16,第二排为17-24,第三排为25-32,第四排为33-40,第五排为41-48,第六排为49-56)" +} \ No newline at end of file diff --git a/示例项目/一键存入个人金库/Proj.ifo b/示例项目/一键存入个人金库/Proj.ifo new file mode 100644 index 0000000..6d08451 --- /dev/null +++ b/示例项目/一键存入个人金库/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "一键存入个人金库", + "ProjectDescribe": "使用之前,请务必保证安装了群内的 \"客户端消息框233.dll\" 插件,这个插件放入你客户端的插件加载目录即可,在游戏中输入 //yjcc 即可一键存入个人金库,也可以在游戏中的快捷喊话中添加为快捷键 配置中可更改 yjcc 关键字", + "ProjectAuthor": "倾泪寒 & 南瓜", + "ProjectVersion": 1.3, + "ProjectConfig": "一键存入个人金库_Lenheart.json", + "ProjectFiles": [ + "一键入库.nut" + ], + "ProjectRunFunc": "_Dps_OneClickStorage_Main_" +} \ No newline at end of file diff --git a/示例项目/一键存入个人金库/一键入库.nut b/示例项目/一键存入个人金库/一键入库.nut new file mode 100644 index 0000000..9f38ebc --- /dev/null +++ b/示例项目/一键存入个人金库/一键入库.nut @@ -0,0 +1,116 @@ +/* +文件名:一键入库.nut +路径:MyProject/一键入库.nut +创建日期:2025-03-25 14:42 +文件用途:一键入库 +*/ + +function _Dps_OneClickStorage_Logic_() { + local Config = GlobalConfig.Get("一键存入个人金库_Lenheart.json"); + //在游戏中输入//一键入库即可调用 + Gm_InputFunc_Handle[Config["一键存仓命令"]] <- function(SUser, Cmd) { + // 获取角色背包 + local InvenObj = SUser.GetInven(); + // 角色仓库 + local CargoObj = Sq_CallFunc(S_Ptr("0x08151a94"), "pointer", ["pointer"], SUser.C_Object); + // 添加计数器 + local transferCount = 0; + // 遍历背包消耗品栏及材料栏 + for (local i = 57; i <= 152; ++i) { + // 获取背包物品 + local ItemObj = InvenObj.GetSlot(1, i); + // 获取背包物品ID + local Item_Id = ItemObj.GetIndex(); + local ItemName = PvfItem.GetNameById(Item_Id); + // 如果物品ID为0或3037,跳过(在此可以添加其他需要跳过的物品ID) + if (Item_Id == 0 || Config["指定道具ID不存入"].find(Item_Id) != null) { + continue; + } + + // 角色仓库是否存在背包物品 + local CargoSlot = Sq_CallFunc(S_Ptr("0x850bc14"), "int", ["pointer", "int"], CargoObj, Item_Id); + // 如果角色仓库中没有该物品,跳过 + if (CargoSlot == -1) { + continue; + } + + // 获取仓库物品指针 + local cargoItemPointer = NativePointer(CargoObj).add(4).readPointer(); + // 获取角色仓库物品对象 + local cargoItemObj = NativePointer(cargoItemPointer).add(61 * CargoSlot); + // 获取角色仓库物品ID + local cargoItemId = NativePointer(cargoItemPointer).add(61 * CargoSlot + 2).readU32(); + // 获取角色仓库物品数量 + local cargoItemCount = Sq_CallFunc(S_Ptr("0x80F783A"), "int", ["pointer"], cargoItemObj.C_Object); + + // 获取物品对象 + local PvfItem = PvfItem.GetPvfItemById(cargoItemId); + + // 获取物品可堆叠数量 + local getStackableLimit = Sq_CallFunc(S_Ptr("0x0822C9FC"), "int", ["pointer"], PvfItem.C_Object); + + // 如果仓库已达堆叠上限,跳过此物品 + if (cargoItemCount >= getStackableLimit) { + continue; + } + + // 获取背包物品数量 + local inventoryItemCount = Sq_CallFunc(S_Ptr("0x80F783A"), "int", ["pointer"], ItemObj.C_Object); + // 获取物品是否可堆叠 + local checkStackableLimit = Sq_CallFunc(S_Ptr("0x08501A79"), "int", ["int", "int"], cargoItemId, cargoItemCount + inventoryItemCount); + // 尝试将物品储存至角色仓库中 + local tryAddStackItem = Sq_CallFunc(S_Ptr("0x0850B4B0"), "int", ["pointer", "pointer", "int"], CargoObj, ItemObj.C_Object, CargoSlot); + if (tryAddStackItem >= 0) { + // 正式将物品插入角色仓库中 + Sq_CallFunc(S_Ptr("0x850b672"), "pointer", ["pointer", "pointer", "int"], CargoObj, ItemObj.C_Object, CargoSlot); + // 删除背包中的物品 + ItemObj.Delete(); + transferCount++; + SUser.SendNotiPacketMessage("[ " + ItemName + " ]" + "成功入库 x " + inventoryItemCount, 8); + } + + // 处理可堆叠物品 + if (checkStackableLimit == 0) { + // 获取物品总数 + local totalCount = cargoItemCount + inventoryItemCount; + + // 如果总数不超过上限,全部堆到仓库 + if (totalCount <= getStackableLimit) { + // 将物品堆到仓库 + Sq_CallFunc(S_Ptr("0x80CB884"), "int", ["pointer", "int"], cargoItemObj.C_Object, totalCount); + // 删除背包中的物品 + ItemObj.Delete(); + transferCount++; + SUser.SendNotiPacketMessage("[ " + ItemName + " ]" + "成功入库 x " + inventoryItemCount, 8); + } else { + // 如果总数超过上限 + // 将仓库的物品数量设置为最大数量 + Sq_CallFunc(S_Ptr("0x80CB884"), "int", ["pointer", "int"], cargoItemObj.C_Object, getStackableLimit); + // 将背包数量减去转移至仓库的数量 + local transferAmount = getStackableLimit - cargoItemCount; + Sq_CallFunc(S_Ptr("0x80CB884"), "int", ["pointer", "int"], ItemObj.C_Object, totalCount - getStackableLimit); + transferCount++; + SUser.SendNotiPacketMessage("[ " + ItemName + " ]" + "成功入库 x " + transferAmount, 8); + } + } + + // 通知客户端更新背包 + SUser.SendUpdateItemList(1, 0, i); + } + + if (transferCount == 0) { + SUser.SendNotiBox("没有可转移的物品!", 1); + } + // 通知客户端更新仓库 + SUser.SendItemSpace(2); + } +} +//启动函数 自定义的需要写在ifo中 +function _Dps_OneClickStorage_Main_() { + _Dps_OneClickStorage_Logic_(); +} + +function _Dps_OneClickStorage_Main_Reload(OldConfig) { + Gm_InputFunc_Handle.rawdelete(OldConfig["一键存仓命令"]); + _Dps_OneClickStorage_Logic_(); +} \ No newline at end of file diff --git a/示例项目/一键存入个人金库/一键存入个人金库_Lenheart.json b/示例项目/一键存入个人金库/一键存入个人金库_Lenheart.json new file mode 100644 index 0000000..cc46453 --- /dev/null +++ b/示例项目/一键存入个人金库/一键存入个人金库_Lenheart.json @@ -0,0 +1,4 @@ +{ + "一键存仓命令": "yjcc", + "指定道具ID不存入": [3037, 3038] +} \ No newline at end of file diff --git a/示例项目/上线自动完成任务/Proj.ifo b/示例项目/上线自动完成任务/Proj.ifo new file mode 100644 index 0000000..cfac8d5 --- /dev/null +++ b/示例项目/上线自动完成任务/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "上线自动完成任务", + "ProjectDescribe": "上线自动完成任务", + "ProjectAuthor": "倾泪寒", + "ProjectVersion": 1.0, + "ProjectConfig": "上线自动完成任务_Lenheart.json", + "ProjectFiles": [ + "上线自动完成任务.nut" + ], + "ProjectRunFunc": "_Dps_AutomaticallyCompleteTasksOnline_Main_" +} \ No newline at end of file diff --git a/示例项目/上线自动完成任务/上线自动完成任务.nut b/示例项目/上线自动完成任务/上线自动完成任务.nut new file mode 100644 index 0000000..19ce68e --- /dev/null +++ b/示例项目/上线自动完成任务/上线自动完成任务.nut @@ -0,0 +1,9 @@ +function _Dps_AutomaticallyCompleteTasksOnline_Main_() { + Cb_History_MileageSet_Func["_DPS_上线自动完成任务_"] <- function(SUser, Data) { + local Config = GlobalConfig.Get("上线自动完成任务_Lenheart.json"); + local QuestArr = Config["需要完成的任务编号"]; + foreach(QuestId in QuestArr) { + SUser.ClearQuest_Gm(QuestId); + } + } +} \ No newline at end of file diff --git a/示例项目/上线自动完成任务/上线自动完成任务_Lenheart.json b/示例项目/上线自动完成任务/上线自动完成任务_Lenheart.json new file mode 100644 index 0000000..a2f87c8 --- /dev/null +++ b/示例项目/上线自动完成任务/上线自动完成任务_Lenheart.json @@ -0,0 +1,4 @@ +{ + "需要完成的任务编号" : [674,649,675,650], + "注意事项": "请确保你的任务需求等级是1级,否则可能无法完成。请在PVF中更改任务需要的等级。" +} \ No newline at end of file diff --git a/示例项目/交易邮件播报/Proj.ifo b/示例项目/交易邮件播报/Proj.ifo new file mode 100644 index 0000000..43cb57f --- /dev/null +++ b/示例项目/交易邮件播报/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "交易邮件播报", + "ProjectDescribe": "交易邮件播报", + "ProjectAuthor": "巅峰 & 倾泪寒", + "ProjectVersion": 1.2, + "ProjectConfig": "交易邮件播报.json", + "ProjectFiles": [ + "交易邮件播报.nut" + ], + "ProjectRunFunc": "_Dps_JYYJBB_Main_" +} \ No newline at end of file diff --git a/示例项目/交易邮件播报/交易邮件播报.json b/示例项目/交易邮件播报/交易邮件播报.json new file mode 100644 index 0000000..ddc8bf0 --- /dev/null +++ b/示例项目/交易邮件播报/交易邮件播报.json @@ -0,0 +1,4 @@ +{ + + "提示": "暂时没写配置" +} \ No newline at end of file diff --git a/示例项目/交易邮件播报/交易邮件播报.nut b/示例项目/交易邮件播报/交易邮件播报.nut new file mode 100644 index 0000000..ee1929f --- /dev/null +++ b/示例项目/交易邮件播报/交易邮件播报.nut @@ -0,0 +1,126 @@ +function _Dps_JYYJBB_Main_() { + + Cb_History_ItemUp_Func["交易邮件播报"] <- function(SUser, args) { + local reason = args[18]; + if (reason == "1") { + local pvfitem = PvfItem.GetNameById(args[15].tointeger()); + local LoginMsgObj = AdMsg(); + LoginMsgObj.PutType(14); + LoginMsgObj.PutColorString("玩家[", [255, 255, 0]); + LoginMsgObj.PutColorString(SUser.GetCharacName(), [255, 0, 0]); + LoginMsgObj.PutColorString("]", [255, 255, 0]); + LoginMsgObj.PutColorString("获得", [255, 255, 0]); + LoginMsgObj.PutColorString("[" + args[20] + "]", [255, 255, 0]); + LoginMsgObj.PutColorString("交易的道具", [255, 255, 0]); + LoginMsgObj.PutColorString(pvfitem, [255, 255, 0]); + LoginMsgObj.PutColorString(args[17] + "个", [255, 255, 0]); + LoginMsgObj.Finalize(); + World.SendAll(LoginMsgObj.MakePack()); + LoginMsgObj.Delete(); + } + } + + + + + Cb_History_MoneyUp_Func["交易邮件播报"] <- function(SUser, args) { + local reason = args[16]; + if (reason == "1") { + local LoginMsgObj = AdMsg(); + LoginMsgObj.PutType(14); + LoginMsgObj.PutColorString("玩家[", [255, 255, 0]); + LoginMsgObj.PutColorString(SUser.GetCharacName(), [255, 0, 0]); + LoginMsgObj.PutColorString("]", [255, 255, 0]); + LoginMsgObj.PutColorString("获得", [255, 255, 0]); + LoginMsgObj.PutColorString("[" + getFirstBracketContent(args[17]) + "]", [255, 255, 0]); + LoginMsgObj.PutColorString("交易的", [255, 255, 0]); + LoginMsgObj.PutColorString(args[15], [255, 255, 0]); + LoginMsgObj.PutColorString("金币", [255, 255, 0]); + LoginMsgObj.Finalize(); + World.SendAll(LoginMsgObj.MakePack()); + LoginMsgObj.Delete(); + } + } + + Cb_MailBox11_Send_Leave_Func["交易邮件播报"] <- function(args) { + local jewelSocketID = NativePointer(args[0]).readPointer(); + local SUser = User(jewelSocketID); + local name = SUser.GetCharacName(); + local receive_name = NativePointer(args[1]).add(17).readUtf8String(); + local send_gold_count = NativePointer(args[1]).add(46).readU32(); + local send_item_id = NativePointer(args[1]).add(57).readU32(); + local send_item_count = NativePointer(args[1]).add(61).readU32(); //发送道具数量 + local item_name; + if (send_item_id > 0) { + item_name = PvfItem.GetNameById(send_item_id); + } + // 发送世界公告播报 + if (send_gold_count > 0 && send_item_id > 0) { + local LoginMsgObj = AdMsg(); + LoginMsgObj.PutType(14); + LoginMsgObj.PutColorString("玩家[", [255, 255, 0]); + LoginMsgObj.PutColorString(SUser.GetCharacName(), [0, 255, 0]); + LoginMsgObj.PutColorString("]", [255, 255, 0]); + LoginMsgObj.PutColorString("刚刚通过邮件向", [255, 255, 0]); + LoginMsgObj.PutColorString("[" + receive_name + "]", [0, 255, 0]); + LoginMsgObj.PutColorString("发送了", [255, 255, 0]); + LoginMsgObj.PutColorString("金币*" + send_gold_count, [255, 170, 0]); + LoginMsgObj.PutColorString("和", [255, 255, 0]); + LoginMsgObj.PutColorString(send_item_count + "个", [255, 255, 0]); + LoginMsgObj.PutColorString(item_name, [255, 255, 0]); + LoginMsgObj.Finalize(); + World.SendAll(LoginMsgObj.MakePack()); + LoginMsgObj.Delete(); + } else if (send_gold_count > 0) { + local LoginMsgObj = AdMsg(); + LoginMsgObj.PutType(14); + LoginMsgObj.PutColorString("玩家[", [255, 255, 0]); + LoginMsgObj.PutColorString(SUser.GetCharacName(), [0, 255, 0]); + LoginMsgObj.PutColorString("]", [255, 255, 0]); + LoginMsgObj.PutColorString("刚刚通过邮件向", [255, 255, 0]); + LoginMsgObj.PutColorString("[" + receive_name + "]", [0, 255, 0]); + LoginMsgObj.PutColorString("发送了", [255, 255, 0]); + LoginMsgObj.PutColorString("金币*" + send_gold_count, [255, 170, 0]); + LoginMsgObj.Finalize(); + World.SendAll(LoginMsgObj.MakePack()); + LoginMsgObj.Delete(); + } else if (send_item_id > 0) { + local LoginMsgObj = AdMsg(); + LoginMsgObj.PutType(14); + LoginMsgObj.PutColorString("玩家[", [255, 255, 0]); + LoginMsgObj.PutColorString(SUser.GetCharacName(), [0, 255, 0]); + LoginMsgObj.PutColorString("]", [255, 255, 0]); + LoginMsgObj.PutColorString("刚刚通过邮件向", [255, 255, 0]); + LoginMsgObj.PutColorString("[" + receive_name + "]", [0, 255, 0]); + LoginMsgObj.PutColorString("发送了", [255, 255, 0]); + LoginMsgObj.PutColorString(send_item_count + "个", [255, 255, 0]); + LoginMsgObj.PutColorString(item_name, [255, 255, 0]); + LoginMsgObj.Finalize(); + World.SendAll(LoginMsgObj.MakePack()); + LoginMsgObj.Delete(); + } else { + local LoginMsgObj = AdMsg(); + LoginMsgObj.PutType(14); + LoginMsgObj.PutColorString("玩家[", [255, 255, 0]); + LoginMsgObj.PutColorString(SUser.GetCharacName(), [0, 255, 0]); + LoginMsgObj.PutColorString("]", [255, 255, 0]); + LoginMsgObj.PutColorString("刚刚通过邮件向", [255, 255, 0]); + LoginMsgObj.PutColorString("[" + receive_name + "]", [0, 255, 0]); + LoginMsgObj.PutColorString("发送了一封邮件", [255, 255, 0]); + LoginMsgObj.Finalize(); + World.SendAll(LoginMsgObj.MakePack()); + LoginMsgObj.Delete(); + } + } +} + + + +function getFirstBracketContent(str) { + local startPos = str.find("("); + if (startPos == null) return null; + local endPos = str.find(")", startPos + 1); + if (endPos == null) return null; + // 提取括号内的内容(不包括括号本身) + return str.slice(startPos + 1, endPos); +} \ No newline at end of file diff --git a/示例项目/任务清除卷/Proj.ifo b/示例项目/任务清除卷/Proj.ifo new file mode 100644 index 0000000..74f1ef4 --- /dev/null +++ b/示例项目/任务清除卷/Proj.ifo @@ -0,0 +1,12 @@ +{ + "ProjectName": "任务清除卷", + "ProjectDescribe": "清理各类型任务以及重置相关任务", + "ProjectAuthor": "南瓜", + "ProjectVersion": 1.3, + "ProjectConfig": "任务相关配置_南瓜.json", + "ProjectFiles": [ + "任务清除卷.nut" + ], + "ProjectIcon":"http://103.36.223.176:5244/d/DP_S/logo2.png?sign=aH3AjsyJgmomCqT3To_QfDY6a2RlSI-T3eUmtW0raoA=:0", + "ProjectRunFunc": "_Dps_QuestInfo_nangua_Main_" +} \ No newline at end of file diff --git a/示例项目/任务清除卷/任务清除卷.nut b/示例项目/任务清除卷/任务清除卷.nut new file mode 100644 index 0000000..97eaa1e --- /dev/null +++ b/示例项目/任务清除卷/任务清除卷.nut @@ -0,0 +1,231 @@ +_NG_QUEST_GRADE_COMMON_UNIQUE <- 5 //普通任务 +_NG_QUEST_GRADE_EPIC <- 0 //主线任务 +_NG_QUEST_GRADE_ACHIEVEMENT <- 2 //成就任务 + +// 任务类型与排除列表的映射 +_QUEST_EXCLUDE_MAP <- { + [_NG_QUEST_GRADE_COMMON_UNIQUE] = "普通任务需排除任务ID", + [_NG_QUEST_GRADE_ACHIEVEMENT] = "成就任务需排除任务ID", + [_NG_QUEST_GRADE_EPIC] = "主线任务需排除任务ID" +} + +function clear_all_quest_by_character_level_nangua(SUser, Item_id) { + local Cofig = GlobalConfig.Get("任务相关配置_南瓜.json"); + local poolMapping = { + [Cofig["主线任务完成券道具ID"]] = _NG_QUEST_GRADE_EPIC, + [Cofig["普通任务完成券道具ID"]] = _NG_QUEST_GRADE_COMMON_UNIQUE, + [Cofig["成就任务完成券道具ID"]] = _NG_QUEST_GRADE_ACHIEVEMENT + }; + // 获取对应的type + local quest_type = poolMapping[Item_id]; + // 玩家任务信息 + local user_quest = SUser.GetQuest(); + + // 玩家已完成任务信息 + local WongWork_CQuestClear = NativePointer(user_quest).add(4); + // 玩家当前等级 + local charac_lv = SUser.GetCharacLevel(); + // 本次完成任务数量 + local clear_quest_cnt = 0; + // 获取pvf数据 + local data_manager = Sq_CallFunc(S_Ptr("0x80CC19B"), "pointer"); + + // 使用全局排除列表 + local exclude_quset_id = _QUEST_EXCLUDE_MAP.rawin(quest_type) ? Cofig[_QUEST_EXCLUDE_MAP[quest_type]] : []; + + //完成当前已接任务 + for (local i = 0; i < 20; i++) { + // 任务id + local doing_quest_id = NativePointer(user_quest).add(4 * (i + 7500 + 2)).readInt(); + + if (doing_quest_id > 0) { + // 获取当前任务的数据 + local quest = Sq_CallFunc(S_Ptr("0x835FDC6"), "pointer", ["pointer", "int"], data_manager, doing_quest_id); + if (quest) { + // 任务类型 + local quest_grade = NativePointer(quest).add(8).readInt(); + + // 判断任务类型并且不在排除列表中 + if (quest_grade == quest_type && exclude_quset_id.find(doing_quest_id) == null) { + // 无条件完成任务 + SUser.ClearQuest_Gm(doing_quest_id); + } + } + } + } + + // 遍历所有任务ID + for (local quest_id = 1; quest_id < 30000; quest_id++) { + // 检查任务是否在排除列表中 + if (exclude_quset_id.find(quest_id) != null) { + continue; + } + + // 跳过已完成的任务 + local isCleared = isClearedQuest(WongWork_CQuestClear.C_Object, quest_id); + if (isCleared) { + continue; + } + + // 获取任务数据 + local quest = Sq_CallFunc(S_Ptr("0x835FDC6"), "pointer", ["pointer", "int"], data_manager, quest_id); + if (quest) { + // 任务类型 + local quest_grade = NativePointer(quest).add(8).readInt(); + + if (quest_grade == quest_type) { + // 只判断任务最低等级要求 忽略 职业/前置 等任务要求 可一次性完成当前等级所有任务 + local quest_min_lv = NativePointer(quest).add(0x20).readInt(); + + if (quest_min_lv <= charac_lv) { + Sq_CallFunc(S_Ptr("0x808BA78"), "int", ["pointer", "int"], WongWork_CQuestClear.C_Object, quest_id); + + // 本次自动完成任务计数 + clear_quest_cnt++; + } + } + } + } + + // 通知客户端更新 + if (clear_quest_cnt > 0) { + local Pack = Packet(); + Sq_CallFunc(S_Ptr("0x868B044"), "int", ["pointer"], SUser.C_Object); + Sq_CallFunc(S_Ptr("0x86ABBA8"), "int", ["pointer", "pointer"], user_quest, Pack.C_Object); + SUser.Send(Pack); + Pack.Delete(); + // 公告通知客户端本次自动完成任务数据 + SUser.SendNotiPacketMessage("已自动完成当前等级任务数量: " + clear_quest_cnt, 8); + }else{ + SUser.SendNotiPacketMessage("没有可清理的任务", 8); + SUser.GiveItem(Item_id, 1); + } +} + +//指定每日任务完成券 +function QUEST_ByMRFuncBynangua(SUser, ItemId) { + local Cofig = GlobalConfig.Get("任务相关配置_南瓜.json"); + // 玩家已完成任务信息 + local user_quest = SUser.GetQuest(); + local WongWork_CQuestClear = NativePointer(user_quest).add(4); + + // 是否有任务已被清理 + local anyTaskCleared = false; + + // 遍历并完成每一个任务 + for (local i = 0; i < Cofig["指定完成每日任务ID"].len(); i++) { + local quest_id = Cofig["指定完成每日任务ID"][i]; + local isCleared = isClearedQuest(WongWork_CQuestClear.C_Object, quest_id); + + if (isCleared) { + continue; + } else { + SUser.ClearQuest_Gm(quest_id); + anyTaskCleared = true; + } + } + + if (anyTaskCleared) { + SUser.SendNotiPacketMessage("指定每日任务已完成!", 8); + } else { + SUser.SendNotiPacketMessage("没有可清理的任务", 8); + SUser.GiveItem(ItemId, 1); + } +} + +//指定每日任务重置券 +function QUEST_ByCZMRFuncBynangua(SUser, ItemId) { + local Cofig = GlobalConfig.Get("任务相关配置_南瓜.json"); + // 玩家已完成任务信息 + local user_quest = SUser.GetQuest(); + local WongWork_CQuestClear = NativePointer(user_quest).add(4); + + // 是否有任务被重置 + local anyTaskReset = false; + + // 遍历并重置每一个任务 + for (local i = 0; i < Cofig["指定完成每日任务ID"].len(); i++) { + local quest_id = Cofig["指定完成每日任务ID"][i]; + local isCleared = isClearedQuest(WongWork_CQuestClear.C_Object, quest_id); + + if (!isCleared) { + continue; + } else { + Sq_CallFunc(S_Ptr("0x808BAAC"), "int", ["pointer", "int"], WongWork_CQuestClear.C_Object, quest_id); + anyTaskReset = true; + } + } + + if (anyTaskReset) { + //通知客户端更新任务列表 + Sq_CallFunc(S_Ptr("0x868B044"), "int", ["pointer"], SUser.C_Object); + local Pack = Packet(); + Sq_CallFunc(S_Ptr("0x86ABBA8"), "int", ["pointer", "pointer"], user_quest, Pack.C_Object); + SUser.Send(Pack); + Pack.Delete(); + SUser.SendNotiPacketMessage("指定每日任务已重置!", 8); + } else { + SUser.SendNotiPacketMessage("没有可重置的任务", 8); + SUser.GiveItem(ItemId, 1); + } +} + +//重置所有任务为未完成状态 +function QUEST_ByALLFuncBynangua(SUser, ItemId) { + local GetState = SUser.GetState() + local user_quest = SUser.GetQuest(); + local WongWork_CQuestClear = NativePointer(user_quest).add(4); + //清空已接任务列表 + for (local i = 0; i < 20; i++) { + NativePointer(user_quest).add(4 * (i + 7500 + 2)).writeInt(0); + } + //所有任务设置未完成状态 + for (local i = 0; i < 29999; i++) { + Sq_CallFunc(S_Ptr("0x808BAAC"), "int", ["pointer", "int"], WongWork_CQuestClear.C_Object, i); + } + //通知客户端更新任务列表 + Sq_CallFunc(S_Ptr("0x868B044"), "int", ["pointer"], SUser.C_Object); + local Pack = Packet(); + Sq_CallFunc(S_Ptr("0x86ABBA8"), "int", ["pointer", "pointer"], user_quest, Pack.C_Object); + SUser.Send(Pack); + Pack.Delete(); + SUser.SendNotiPacketMessage("所有任务已重置!", 8); +} + +function isClearedQuest(C_Object, questID) { + return Sq_CallFunc(S_Ptr("0x808BAE0"), "bool", ["pointer", "int"], C_Object, questID); +} + +//加载入口 +function _Dps_QuestInfo_nangua_Main_() { + _Dps_QuestInfo_nangua_Logic_(); +} + +//重载入口 +function _Dps_QuestInfo_nangua_Main_Reload_(OldConfig) { + Cb_Use_Item_Sp_Func.rawdelete(OldConfig["主线任务完成券道具ID"].tointeger()); + Cb_Use_Item_Sp_Func.rawdelete(OldConfig["普通任务完成券道具ID"].tointeger()); + Cb_Use_Item_Sp_Func.rawdelete(OldConfig["成就任务完成券道具ID"].tointeger()); + Cb_Use_Item_Sp_Func.rawdelete(OldConfig["指定每日任务完成券道具ID"].tointeger()); + Cb_Use_Item_Sp_Func.rawdelete(OldConfig["指定每日任务重置券道具ID"].tointeger()); + Cb_Use_Item_Sp_Func.rawdelete(OldConfig["重置所有任务道具ID"].tointeger()); + + //重新注册 + _Dps_QuestInfo_nangua_Logic_(); +} + +function _Dps_QuestInfo_nangua_Logic_() { + local Cofig = GlobalConfig.Get("任务相关配置_南瓜.json"); + // 主线任务完成券 + Cb_Use_Item_Sp_Func[Cofig["主线任务完成券道具ID"]] <- clear_all_quest_by_character_level_nangua; + // 普通任务完成券 + Cb_Use_Item_Sp_Func[Cofig["普通任务完成券道具ID"]] <- clear_all_quest_by_character_level_nangua; + // 成就任务完成券 + Cb_Use_Item_Sp_Func[Cofig["成就任务完成券道具ID"]] <- clear_all_quest_by_character_level_nangua; + // 每日任务完成券 + Cb_Use_Item_Sp_Func[Cofig["指定每日任务完成券道具ID"]] <- QUEST_ByMRFuncBynangua; + // 每日任务重置券 + Cb_Use_Item_Sp_Func[Cofig["指定每日任务重置券道具ID"]] <- QUEST_ByCZMRFuncBynangua; + // 所有任务重置券 + Cb_Use_Item_Sp_Func[Cofig["重置所有任务道具ID"]] <- QUEST_ByALLFuncBynangua; +} \ No newline at end of file diff --git a/示例项目/任务清除卷/任务相关配置_南瓜.json b/示例项目/任务清除卷/任务相关配置_南瓜.json new file mode 100644 index 0000000..b0937b1 --- /dev/null +++ b/示例项目/任务清除卷/任务相关配置_南瓜.json @@ -0,0 +1,12 @@ +{ + "主线任务完成券道具ID":2021458801, + "普通任务完成券道具ID":2021458802, + "成就任务完成券道具ID":2021458804, + "重置所有任务道具ID":2021458813, + "指定每日任务完成券道具ID":2021458803, + "指定每日任务重置券道具ID":20214588091, + "指定完成每日任务ID":[2411, 2412], + "主线任务需排除任务ID":[1111111], + "普通任务需排除任务ID":[4443, 7898, 7889, 7895, 7892, 7873, 7876, 7870, 7879, 4065, 4068, 999, 7827, 7817, 7824, 7820, 7834, 7837, 7831, 7840, 4427, 4428, 4429, 4430, 4431, 4432, 4433, 4434, 4435, 7848, 7842, 7845, 7851, 7866, 7855, 7862, 7859, 7814, 7810, 7807, 7803, 7886, 7882, 2708, 2710, 2712, 2702], + "成就任务需排除任务ID":[1111111] +} \ No newline at end of file diff --git a/示例项目/任务清除卷2/Proj.ifo b/示例项目/任务清除卷2/Proj.ifo new file mode 100644 index 0000000..56e92b0 --- /dev/null +++ b/示例项目/任务清除卷2/Proj.ifo @@ -0,0 +1,12 @@ +{ + "ProjectName": "任务清除卷2", + "ProjectDescribe": "清理各类型任务以及重置相关任务,除了7577类道具 也可以使用礼包类道具触发", + "ProjectAuthor": "倾泪寒", + "ProjectVersion": 1.0, + "ProjectConfig": "任务相关配置_2.json", + "ProjectFiles": [ + "任务清除卷.nut" + ], + "ProjectIcon":"http://103.36.223.176:5244/d/DP_S/logo2.png?sign=aH3AjsyJgmomCqT3To_QfDY6a2RlSI-T3eUmtW0raoA=:0", + "ProjectRunFunc": "_Dps_QuestInfo_nangua_Main_2" +} \ No newline at end of file diff --git a/示例项目/任务清除卷2/任务清除卷.nut b/示例项目/任务清除卷2/任务清除卷.nut new file mode 100644 index 0000000..4009592 --- /dev/null +++ b/示例项目/任务清除卷2/任务清除卷.nut @@ -0,0 +1,260 @@ +_NG_QUEST_GRADE_COMMON_UNIQUE <- 5 //普通任务 +_NG_QUEST_GRADE_EPIC <- 0 //主线任务 +_NG_QUEST_GRADE_ACHIEVEMENT <- 2 //成就任务 + +// 任务类型与排除列表的映射 +_QUEST_EXCLUDE_MAP2 <- { + [_NG_QUEST_GRADE_COMMON_UNIQUE] = "普通任务需排除任务ID", + [_NG_QUEST_GRADE_ACHIEVEMENT] = "成就任务需排除任务ID", + [_NG_QUEST_GRADE_EPIC] = "主线任务需排除任务ID" +} + +function clear_all_quest_by_character_level_nangua2(SUser, Date) { + + local Item_id = Date[15].tointeger(); + + if (Date[18] != "3") { + return; + } + + + local Cofig = GlobalConfig.Get("任务相关配置_2.json"); + local poolMapping = { + [Cofig["主线任务完成券道具ID"]] = _NG_QUEST_GRADE_EPIC, + [Cofig["普通任务完成券道具ID"]] = _NG_QUEST_GRADE_COMMON_UNIQUE, + [Cofig["成就任务完成券道具ID"]] = _NG_QUEST_GRADE_ACHIEVEMENT + }; + // 获取对应的type + local quest_type = poolMapping[Item_id]; + // 玩家任务信息 + local user_quest = SUser.GetQuest(); + + // 玩家已完成任务信息 + local WongWork_CQuestClear = NativePointer(user_quest).add(4); + // 玩家当前等级 + local charac_lv = SUser.GetCharacLevel(); + // 本次完成任务数量 + local clear_quest_cnt = 0; + // 获取pvf数据 + local data_manager = Sq_CallFunc(S_Ptr("0x80CC19B"), "pointer"); + + // 使用全局排除列表 + local exclude_quset_id = _QUEST_EXCLUDE_MAP2.rawin(quest_type) ? Cofig[_QUEST_EXCLUDE_MAP2[quest_type]] : []; + + //完成当前已接任务 + for (local i = 0; i< 20; i++) { + // 任务id + local doing_quest_id = NativePointer(user_quest).add(4 * (i + 7500 + 2)).readInt(); + + if (doing_quest_id > 0) { + // 获取当前任务的数据 + local quest = Sq_CallFunc(S_Ptr("0x835FDC6"), "pointer", ["pointer", "int"], data_manager, doing_quest_id); + if (quest) { + // 任务类型 + local quest_grade = NativePointer(quest).add(8).readInt(); + + // 判断任务类型并且不在排除列表中 + if (quest_grade == quest_type && exclude_quset_id.find(doing_quest_id) == null) { + // 无条件完成任务 + SUser.ClearQuest_Gm(doing_quest_id); + } + } + } + } + + // 遍历所有任务ID + for (local quest_id = 1; quest_id< 30000; quest_id++) { + // 检查任务是否在排除列表中 + if (exclude_quset_id.find(quest_id) != null) { + continue; + } + + // 跳过已完成的任务 + local isCleared = isClearedQuest(WongWork_CQuestClear.C_Object, quest_id); + if (isCleared) { + continue; + } + + // 获取任务数据 + local quest = Sq_CallFunc(S_Ptr("0x835FDC6"), "pointer", ["pointer", "int"], data_manager, quest_id); + if (quest) { + // 任务类型 + local quest_grade = NativePointer(quest).add(8).readInt(); + + if (quest_grade == quest_type) { + // 只判断任务最低等级要求 忽略 职业/前置 等任务要求 可一次性完成当前等级所有任务 + local quest_min_lv = NativePointer(quest).add(0x20).readInt(); + + if (quest_min_lv <= charac_lv) { + Sq_CallFunc(S_Ptr("0x808BA78"), "int", ["pointer", "int"], WongWork_CQuestClear.C_Object, quest_id); + + // 本次自动完成任务计数 + clear_quest_cnt++; + } + } + } + } + + // 通知客户端更新 + if (clear_quest_cnt > 0) { + local Pack = Packet(); + Sq_CallFunc(S_Ptr("0x868B044"), "int", ["pointer"], SUser.C_Object); + Sq_CallFunc(S_Ptr("0x86ABBA8"), "int", ["pointer", "pointer"], user_quest, Pack.C_Object); + SUser.Send(Pack); + Pack.Delete(); + // 公告通知客户端本次自动完成任务数据 + SUser.SendNotiPacketMessage("已自动完成当前等级任务数量: " + clear_quest_cnt, 8); + } else { + SUser.SendNotiPacketMessage("没有可清理的任务", 8); + } +} + +//指定每日任务完成券 +function QUEST_ByMRFuncBynangua2(SUser, Date) { + + local ItemId = Date[15].tointeger(); + + if (Date[18] != "3") { + return; + } + local Cofig = GlobalConfig.Get("任务相关配置_2.json"); + // 玩家已完成任务信息 + local user_quest = SUser.GetQuest(); + local WongWork_CQuestClear = NativePointer(user_quest).add(4); + + // 是否有任务已被清理 + local anyTaskCleared = false; + + // 遍历并完成每一个任务 + for (local i = 0; i< Cofig["指定完成每日任务ID"].len(); i++) { + local quest_id = Cofig["指定完成每日任务ID"][i]; + local isCleared = isClearedQuest(WongWork_CQuestClear.C_Object, quest_id); + + if (isCleared) { + continue; + } else { + SUser.ClearQuest_Gm(quest_id); + anyTaskCleared = true; + } + } + + if (anyTaskCleared) { + SUser.SendNotiPacketMessage("指定每日任务已完成!", 8); + } else { + SUser.SendNotiPacketMessage("没有可清理的任务", 8); + } +} + +//指定每日任务重置券 +function QUEST_ByCZMRFuncBynangua2(SUser, Date) { + local ItemId = Date[15].tointeger(); + + if (Date[18] != "3") { + return; + } + local Cofig = GlobalConfig.Get("任务相关配置_2.json"); + // 玩家已完成任务信息 + local user_quest = SUser.GetQuest(); + local WongWork_CQuestClear = NativePointer(user_quest).add(4); + + // 是否有任务被重置 + local anyTaskReset = false; + + // 遍历并重置每一个任务 + for (local i = 0; i< Cofig["指定完成每日任务ID"].len(); i++) { + local quest_id = Cofig["指定完成每日任务ID"][i]; + local isCleared = isClearedQuest(WongWork_CQuestClear.C_Object, quest_id); + + if (!isCleared) { + continue; + } else { + Sq_CallFunc(S_Ptr("0x808BAAC"), "int", ["pointer", "int"], WongWork_CQuestClear.C_Object, quest_id); + anyTaskReset = true; + } + } + + if (anyTaskReset) { + //通知客户端更新任务列表 + Sq_CallFunc(S_Ptr("0x868B044"), "int", ["pointer"], SUser.C_Object); + local Pack = Packet(); + Sq_CallFunc(S_Ptr("0x86ABBA8"), "int", ["pointer", "pointer"], user_quest, Pack.C_Object); + SUser.Send(Pack); + Pack.Delete(); + SUser.SendNotiPacketMessage("指定每日任务已重置!", 8); + } else { + SUser.SendNotiPacketMessage("没有可重置的任务", 8); + } +} + +//重置所有任务为未完成状态 +function QUEST_ByALLFuncBynangua2(SUser, Date) { + local ItemId = Date[15].tointeger(); + + if (Date[18] != "3") { + return; + } + local GetState = SUser.GetState() + local user_quest = SUser.GetQuest(); + local WongWork_CQuestClear = NativePointer(user_quest).add(4); + //清空已接任务列表 + for (local i = 0; i< 20; i++) { + NativePointer(user_quest).add(4 * (i + 7500 + 2)).writeInt(0); + } + //所有任务设置未完成状态 + for (local i = 0; i< 29999; i++) { + Sq_CallFunc(S_Ptr("0x808BAAC"), "int", ["pointer", "int"], WongWork_CQuestClear.C_Object, i); + } + //通知客户端更新任务列表 + Sq_CallFunc(S_Ptr("0x868B044"), "int", ["pointer"], SUser.C_Object); + local Pack = Packet(); + Sq_CallFunc(S_Ptr("0x86ABBA8"), "int", ["pointer", "pointer"], user_quest, Pack.C_Object); + SUser.Send(Pack); + Pack.Delete(); + SUser.SendNotiPacketMessage("所有任务已重置!", 8); +} + +function isClearedQuest(C_Object, questID) { + return Sq_CallFunc(S_Ptr("0x808BAE0"), "bool", ["pointer", "int"], C_Object, questID); +} + +//加载入口 +function _Dps_QuestInfo_nangua_Main_2() { + _Dps_QuestInfo_nangua_Logic_2(); +} + +//重载入口 +function _Dps_QuestInfo_nangua_Main_Reload_2(OldConfig) { + +} + +function _Dps_QuestInfo_nangua_Logic_2() { + local Cofig = GlobalConfig.Get("任务相关配置_2.json"); + // 主线任务完成券 + Cb_History_ItemDown_Func["任务完成券"] <- Cb_History_ItemDown_FuncMapRW; +} + + +function Cb_History_ItemDown_FuncMapRW(SUser, Date) { + local Cofig = GlobalConfig.Get("任务相关配置_2.json"); + + switch (Date[15].tointeger()) { + case Cofig["主线任务完成券道具ID"]: + clear_all_quest_by_character_level_nangua2(SUser, Date); + break; + case Cofig["普通任务完成券道具ID"]: + clear_all_quest_by_character_level_nangua2(SUser, Date); + break; + case Cofig["成就任务完成券道具ID"]: + clear_all_quest_by_character_level_nangua2(SUser, Date); + break; + case Cofig["指定每日任务完成券道具ID"]: + QUEST_ByMRFuncBynangua2(SUser, Date); + break; + case Cofig["指定每日任务重置券道具ID"]: + QUEST_ByCZMRFuncBynangua2(SUser, Date); + break; + case Cofig["重置所有任务道具ID"]: + QUEST_ByALLFuncBynangua2(SUser, Date); + break; + } +} \ No newline at end of file diff --git a/示例项目/任务清除卷2/任务相关配置_2.json b/示例项目/任务清除卷2/任务相关配置_2.json new file mode 100644 index 0000000..b0937b1 --- /dev/null +++ b/示例项目/任务清除卷2/任务相关配置_2.json @@ -0,0 +1,12 @@ +{ + "主线任务完成券道具ID":2021458801, + "普通任务完成券道具ID":2021458802, + "成就任务完成券道具ID":2021458804, + "重置所有任务道具ID":2021458813, + "指定每日任务完成券道具ID":2021458803, + "指定每日任务重置券道具ID":20214588091, + "指定完成每日任务ID":[2411, 2412], + "主线任务需排除任务ID":[1111111], + "普通任务需排除任务ID":[4443, 7898, 7889, 7895, 7892, 7873, 7876, 7870, 7879, 4065, 4068, 999, 7827, 7817, 7824, 7820, 7834, 7837, 7831, 7840, 4427, 4428, 4429, 4430, 4431, 4432, 4433, 4434, 4435, 7848, 7842, 7845, 7851, 7866, 7855, 7862, 7859, 7814, 7810, 7807, 7803, 7886, 7882, 2708, 2710, 2712, 2702], + "成就任务需排除任务ID":[1111111] +} \ No newline at end of file diff --git a/示例项目/修复卡NPC商店道具/Proj.ifo b/示例项目/修复卡NPC商店道具/Proj.ifo new file mode 100644 index 0000000..0e87ba0 --- /dev/null +++ b/示例项目/修复卡NPC商店道具/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "修复卡NPC商店道具", + "ProjectDescribe": "修复了客户端通过BUG卡NPC商店道具的问题", + "ProjectAuthor": "倾泪寒", + "ProjectVersion": 1.0, + "ProjectConfig": "", + "ProjectFiles": [ + "修复卡NPC商店道具.nut" + ], + "ProjectRunFunc": "_Dps_RepairCardNpcStoreProps_Main_" +} \ No newline at end of file diff --git a/示例项目/修复卡NPC商店道具/修复卡NPC商店道具.nut b/示例项目/修复卡NPC商店道具/修复卡NPC商店道具.nut new file mode 100644 index 0000000..5751ac7 --- /dev/null +++ b/示例项目/修复卡NPC商店道具/修复卡NPC商店道具.nut @@ -0,0 +1,12 @@ + + + +function _Dps_RepairCardNpcStoreProps_Main_() +{ + Cb_BuyItem_Get_Data_Leave_Func["_DPS_RepairCardNpcStoreProps_"] <- function (args) + { + if(NativePointer(args[3]).add(156).readInt() < 0){ + return 10; + } + } +} diff --git a/示例项目/全职业通用转职书/Proj.ifo b/示例项目/全职业通用转职书/Proj.ifo new file mode 100644 index 0000000..97a912f --- /dev/null +++ b/示例项目/全职业通用转职书/Proj.ifo @@ -0,0 +1,12 @@ +{ + "ProjectName": "全职业通用转职书", + "ProjectDescribe": "全职业都可用的转职书。每个转职职业是个数组,可以写多个转职卷编号,仿照7577的道具类型即可,使用\",\"隔开。", + "ProjectAuthor": "倾泪寒", + "ProjectVersion": 1.0, + "ProjectConfig": "", + "ProjectConfig": "全职业通用转职书_Lenheart.json", + "ProjectFiles": [ + "全职业通用转职书.nut" + ], + "ProjectRunFunc": "_Dps_GeneralJobTransferCertificateForAllProfessions_Main_" +} \ No newline at end of file diff --git a/示例项目/全职业通用转职书/全职业通用转职书.nut b/示例项目/全职业通用转职书/全职业通用转职书.nut new file mode 100644 index 0000000..2e06747 --- /dev/null +++ b/示例项目/全职业通用转职书/全职业通用转职书.nut @@ -0,0 +1,50 @@ +/* +文件名:全职业通用转职书.nut +路径:OfficialProject/全职业通用转职书/全职业通用转职书.nut +创建日期:2025-10-24 22:26 +文件用途: +*/ + +//重载入口 +function _Dps_GeneralJobTransferCertificateForAllProfessions_Main_Reload_(OldConfig) { + //先销毁原来注册的 + local JobArr = []; + JobArr.push(GlobalConfig["转职初始职业"]); + JobArr.push(GlobalConfig["转职第一职业"]); + JobArr.push(GlobalConfig["转职第二职业"]); + JobArr.push(GlobalConfig["转职第三职业"]); + JobArr.push(GlobalConfig["转职第四职业"]); + JobArr.push(GlobalConfig["转职第五职业"]); + + foreach(Index, arr in JobArr) { + foreach(ItemId in arr) { + if (Cb_Use_Item_Sp_Func.rawin(ItemId)) Cb_Use_Item_Sp_Func.rawdelete(ItemId); + } + } + + //重新注册 + _Dps_GeneralJobTransferCertificateForAllProfessions_Main_(); +} + +function _Dps_GeneralJobTransferCertificateForAllProfessions_Main_() { + local Config = GlobalConfig.Get("全职业通用转职书_Lenheart.json"); + local JobArr = []; + JobArr.push(Config["转职初始职业"]); + JobArr.push(Config["转职第一职业"]); + JobArr.push(Config["转职第二职业"]); + JobArr.push(Config["转职第三职业"]); + JobArr.push(Config["转职第四职业"]); + JobArr.push(Config["转职第五职业"]); + + + + foreach(Index, arr in JobArr) { + foreach(ItemId in arr) { + Cb_Use_Item_Sp_Func[ItemId] <- function(SUser, ItemId) { + SUser.ChangeGrowType(Index, 0); + SUser.SendNotiPacket(0, 2, 0); + SUser.InitSkillW(0, 0); + } + } + } +} \ No newline at end of file diff --git a/示例项目/全职业通用转职书/全职业通用转职书_Lenheart.json b/示例项目/全职业通用转职书/全职业通用转职书_Lenheart.json new file mode 100644 index 0000000..6573fc7 --- /dev/null +++ b/示例项目/全职业通用转职书/全职业通用转职书_Lenheart.json @@ -0,0 +1,8 @@ +{ + "转职初始职业":[7577], + "转职第一职业":[7577], + "转职第二职业":[7577], + "转职第三职业":[7577], + "转职第四职业":[7577], + "转职第五职业":[7577] +} \ No newline at end of file diff --git a/示例项目/副本使用道具奖励/Proj.ifo b/示例项目/副本使用道具奖励/Proj.ifo new file mode 100644 index 0000000..52fa7b9 --- /dev/null +++ b/示例项目/副本使用道具奖励/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "副本使用道具奖励", + "ProjectDescribe": "特定副本使用特定道具会返还自定义的奖励", + "ProjectAuthor": "至尚 & 倾泪寒", + "ProjectVersion": 1.0, + "ProjectConfig": "副本使用道具奖励.json", + "ProjectFiles": [ + "副本使用道具奖励.nut" + ], + "ProjectRunFunc": "_Dps_ReturnDuplicateItems_Main_" +} \ No newline at end of file diff --git a/示例项目/副本使用道具奖励/副本使用道具奖励.json b/示例项目/副本使用道具奖励/副本使用道具奖励.json new file mode 100644 index 0000000..8299a5f --- /dev/null +++ b/示例项目/副本使用道具奖励/副本使用道具奖励.json @@ -0,0 +1,7 @@ +{ + "副本返还配置列表": [ + [1,1106,3037,10], + [2,1108,3038,10] + ], + "提示": "第一个为副本id 第二个为使用的道具id 第三个为返还的道具id 第四个为返还的数量" +} \ No newline at end of file diff --git a/示例项目/副本使用道具奖励/副本使用道具奖励.nut b/示例项目/副本使用道具奖励/副本使用道具奖励.nut new file mode 100644 index 0000000..e56f98a --- /dev/null +++ b/示例项目/副本使用道具奖励/副本使用道具奖励.nut @@ -0,0 +1,32 @@ + +function _Dps_ReturnDuplicateItems_Main_() { + local Config = GlobalConfig.Get("副本使用道具奖励.json"); + //分解券 + + Cb_History_ItemDown_Func.DWSSSS <- function(user, date) { + + + local itemId = date[15].tointeger(); + + local PartyObj = user.GetParty(); + + if (date[18].tointeger()!= 3) return; + + if (PartyObj) { + local Bfobj = PartyObj.GetBattleField(); + local DgnObj = Bfobj.GetDgn(); + + if (DgnObj) { + local Dungeon_Id = DgnObj.GetId(); + local FBSYDJ = Config["副本返还配置列表"]; + foreach(ints in FBSYDJ) { + + if (ints[0] == Dungeon_Id && itemId == ints[1]) { + + user.GiveItem(ints[2].tointeger(), ints[3].tointeger()); + } + } + } + } + } +} \ No newline at end of file diff --git a/示例项目/副本内禁止丢弃物品/Proj.ifo b/示例项目/副本内禁止丢弃物品/Proj.ifo new file mode 100644 index 0000000..1ded3cc --- /dev/null +++ b/示例项目/副本内禁止丢弃物品/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "副本内禁止丢弃物品", + "ProjectDescribe": "副本内禁止丢弃物品及金币", + "ProjectAuthor": "倾泪寒&南瓜", + "ProjectVersion": 1.0, + "ProjectConfig": "", + "ProjectFiles": [ + "副本内禁止丢弃物品.nut" + ], + "ProjectRunFunc": "_Dps_DgnCannotDropItem_Main_" +} \ No newline at end of file diff --git a/示例项目/副本内禁止丢弃物品/副本内禁止丢弃物品.nut b/示例项目/副本内禁止丢弃物品/副本内禁止丢弃物品.nut new file mode 100644 index 0000000..e3dddb3 --- /dev/null +++ b/示例项目/副本内禁止丢弃物品/副本内禁止丢弃物品.nut @@ -0,0 +1,27 @@ +Cb_DropItem_check_error_Enter_Func.Rindro <- function(args){ + getroottable()._EmptyHook_Flag_ = true; + args[1] = getroottable()._EmptyCharacInfo_Flag_.C_Object; + return args; +} +Cb_DropItem_check_error_Leave_Func.Rindro <- function(args){ + if(getroottable()._EmptyHook_Flag_){ + getroottable()._EmptyHook_Flag_ = false; + return 19; + } +} + +function _Dps_DgnCannotDropItem_Main_() { + _Jump_DropItem_check_error_(); +} + +function _Jump_DropItem_check_error_() { + //如果没有创建过这个模拟内存就创建 + if(!getroottable().rawin("_EmptyCharacInfo_Flag_")){ + getroottable()._EmptyCharacInfo_Flag_ <- Memory.alloc(17); + Memory.reset(getroottable()._EmptyCharacInfo_Flag_,17); + } + //如果没有创建过这个跳转Flag就创建并初始化 + if(!getroottable().rawin("_EmptyHook_Flag_")){ + getroottable()._EmptyHook_Flag_ <- false; + } +} \ No newline at end of file diff --git a/示例项目/副本播报/Proj.ifo b/示例项目/副本播报/Proj.ifo new file mode 100644 index 0000000..10b7e01 --- /dev/null +++ b/示例项目/副本播报/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "副本播报", + "ProjectDescribe": "通关及未通关时播报耗时时长", + "ProjectAuthor": "南瓜", + "ProjectVersion": 1.3, + "ProjectConfig": "副本播报配置_Nangua.json", + "ProjectFiles": [ + "副本播报.nut" + ], + "ProjectRunFunc": "_Dps_send_dungeon_msg_Main_" +} \ No newline at end of file diff --git a/示例项目/副本播报/副本播报.nut b/示例项目/副本播报/副本播报.nut new file mode 100644 index 0000000..6737e20 --- /dev/null +++ b/示例项目/副本播报/副本播报.nut @@ -0,0 +1,227 @@ +dungeon_cleared <- {}; +dungeon_cache <- {}; +function _Dps_send_dungeon_msg_Main_() { + //进入副本加载完毕时 + Cb_Party_OnStartMapFinishLoading_Enter_Func.EnterStartMapByNangua <- function(args) { + local Cofig = GlobalConfig.Get("副本播报配置_Nangua.json"); + local PartyObj = Party(args[0]); + + if(!PartyObj || !Cofig["副本播报开关(true开启,false关闭)"]) + return + for (local i = 0; i < 4; ++i) { + local SUser = PartyObj.GetUser(i); + if (SUser) { + if (Cofig["指定角色CID不播报"].find(SUser.GetCID()) != null && _clear_dgn_Bynangua.CParty_get_member_count(PartyObj.C_Object) < 2) { + return; + } + if(SUser.GetCID() in dungeon_cache){ + dungeon_cache.rawdelete(SUser.GetCID()); + } + dungeon_cleared[SUser.GetCID()] <- false; // 进入副本时初始化标志为未通关 + + local Bfobj = PartyObj.GetBattleField(); + local DgnObj = Bfobj.GetDgn(); + local DgnID = DgnObj.GetId(); + local Dungeon_Name = DgnObj.GetName(); + local dungeon_type = NativePointer(Bfobj.C_Object).add(460).readU8(); // 0 为普通副本,1 为非常困难深渊,2 为困难深渊 + local dungeon_diff = Sq_CallFunc(S_Ptr("0x080F981C"), "int", ["pointer"], Bfobj.C_Object); // 获取副本难度 + local diff_name = Cofig["副本难度命名"][(dungeon_diff).tostring()]; // 获取副本难度名称 + local dgntypeName = _clear_dgn_Bynangua.DungeonType[(dungeon_type).tostring()]; + + local DgnData = { + "entered": true, + "Dungeon_Name": Dungeon_Name, + "dgntypeName": dgntypeName, + "diff_name": diff_name, + "totalTime": 0, + "HellParty_time_recorded": false, + "last_HellParty_time": 0, + "is_HellParty_room": false + }; + //以角色ID为键记录副本信息 + dungeon_cache[SUser.GetCID()] <- DgnData; + } + } + } + + //清理房间完毕时 + Cb_Battle_Field_onClearMap_Leave_Func.onClearMapByNangua <- function(args) { + local Cofig = GlobalConfig.Get("副本播报配置_Nangua.json"); + local retval = args.pop(); + local CBattle_Field = args[0]; + local PartyObj = Party(NativePointer(CBattle_Field).add(-2852).C_Object); + if(!PartyObj) + return + local DgnId = NativePointer(args[0]).add(404).readInt(); + if (!Cofig["副本播报开关(true开启,false关闭)"]) { + return; + } + if (retval == DgnId) { + for (local i = 0; i < 4; ++i) { + local SUser = PartyObj.GetUser(i); + if (SUser) { + if (dungeon_cache.rawin(SUser.GetCID()) && dungeon_cache[SUser.GetCID()].rawin("entered")) { + local time = Sq_CallFunc(S_Ptr("0x085B6768"), "int", ["pointer"], PartyObj.C_Object); + if (!dungeon_cache[SUser.GetCID()]["is_HellParty_room"]) { + dungeon_cache[SUser.GetCID()]["totalTime"] += time; + } else { + dungeon_cache[SUser.GetCID()]["is_HellParty_room"] = false; + } + } + } + } + } + } + + //放弃副本或未通关副本时 + Cb_Party_giveup_game_Enter_Func.giveupByNangua <- function(args) { + local Cofig = GlobalConfig.Get("副本播报配置_Nangua.json"); + local PartyObj = Party(args[0]); + local killcount = Sq_CallFunc(S_Ptr("0x085BF456"), "int", ["pointer"], NativePointer(args[0]).add(812).C_Object); + if (!PartyObj) return; + if (!Cofig["副本播报开关(true开启,false关闭)"]) return; + local SUser = User(args[1]); + local Party_Master = PartyObj.GetMaster(); + local MasterName = Party_Master.GetCharacName(); + local formattedTime = ""; + local DgnId = NativePointer(args[0]).add(814 * 4).readInt(); + local name = SUser.GetCharacName(); + + // 如果副本ID不在允许播报的数组内则跳出 + if (Cofig["不需要播报的副本ID(实例中的ID为怪物攻城的副本ID尽量不要删)"].find(DgnId) != null) { + return; + } + + if (SUser.GetCID() in dungeon_cache) { + local dungeonInfo = dungeon_cache[SUser.GetCID()]; + local dgnTypeName = dungeonInfo["dgntypeName"]; + local dungeonName = dungeonInfo["Dungeon_Name"]; + local diffName = dungeonInfo["diff_name"]; + local totalTime = 0; + if (dungeon_cache.rawin(SUser.GetCID()) && dungeon_cache[SUser.GetCID()].rawin("totalTime")) { + totalTime = dungeon_cache[SUser.GetCID()]["totalTime"]; + formattedTime = _clear_dgn_Bynangua.formatMilliseconds(totalTime); + } + if (dungeon_cleared.rawin(SUser.GetCID()) && dungeon_cleared[SUser.GetCID()] == true) { + dungeon_cache.rawdelete(SUser.GetCID()); + dungeon_cleared[SUser.GetCID()] <- false; + } else { + // 发送未通关信息 + if (totalTime == 0) { + World.SendNotiPacketMessage(format(Cofig["未通过一个小地图播报信息"], name, dgnTypeName, dungeonName, diffName, killcount), Cofig["发送信息位置"]); + } else if (MasterName == SUser.GetCharacName()) { + World.SendNotiPacketMessage(format(Cofig["放弃副本"], name, dgnTypeName, dungeonName, diffName, formattedTime, killcount), Cofig["发送信息位置"]); + } else { + World.SendNotiPacketMessage(format(Cofig["在队伍中提前退出副本"], name, MasterName, dgnTypeName, dungeonName, diffName, formattedTime, killcount), Cofig["发送信息位置"]) + } + dungeon_cache.rawdelete(SUser.GetCID()); + } + } + } + + Cb_Field_KillHellPartyGroupMonsterCnt_Leave_Func.KillHellPartyGroupMonsterCntByNangua <- function(args){ + local Cofig = GlobalConfig.Get("副本播报配置_Nangua.json"); + local CBattle_Field = args[0]; + local PartyObj = Party(NativePointer(CBattle_Field).add(-2852).C_Object); + if(!PartyObj) + return + if (!Cofig["副本播报开关(true开启,false关闭)"]) { + return; + } + local time = Sq_CallFunc(S_Ptr("0x085B6768"), "int", ["pointer"], PartyObj.C_Object); + local CBattle_Field_IsKilledAllHellGruoups = Sq_CallFunc(S_Ptr("0x085BF250"), "int", ["pointer"], CBattle_Field); + for (local i = 0; i < 4; ++i) { + local SUser = PartyObj.GetUser(i); + if (SUser) { + if (dungeon_cache.rawin(SUser.GetCID()) && dungeon_cache[SUser.GetCID()].rawin("entered")) { + dungeon_cache[SUser.GetCID()]["is_HellParty_room"] = true; + if (CBattle_Field_IsKilledAllHellGruoups == 1) { + dungeon_cache[SUser.GetCID()]["HellParty_time_recorded"] <- true; + dungeon_cache[SUser.GetCID()]["last_HellParty_time"] = time; + dungeon_cache[SUser.GetCID()]["totalTime"] += time; + } + } + } + } + } + + //通关副本时 + Cb_CParty_SetBestClearTime_Enter_Func.ClearTimeByNangua <- function (args) { + local Cofig = GlobalConfig.Get("副本播报配置_Nangua.json"); + local PartyObj = Party(args[0]); + local killcount = Sq_CallFunc(S_Ptr("0x085BF456"), "int", ["pointer"], NativePointer(args[0]).add(812).C_Object); + if(!PartyObj || !Cofig["副本播报开关(true开启,false关闭)"]) + return + local dungeon_diff = args[2]; + local clearTime = args[3]; + local Bfobj = PartyObj.GetBattleField(); + local DgnObj = Bfobj.GetDgn(); + local diff_name = Cofig["副本难度命名"][(dungeon_diff).tostring()]; + if (DgnObj) { + local Dungeon_Name = DgnObj.GetName(); + local MemberNames = []; + for (local i = 0; i < 4; ++i) { + local SUser = PartyObj.GetUser(i); + if (SUser) { + if (Cofig["指定角色CID不播报"].find(SUser.GetCID()) != null && _clear_dgn_Bynangua.CParty_get_member_count(PartyObj.C_Object) < 2) { + return; + } + local name = SUser.GetCharacName(); + MemberNames.append(name); + dungeon_cleared[SUser.GetCID()] <- true; + } + } + + local joinedNames = _clear_dgn_Bynangua.join(MemberNames, ", "); + local time = _clear_dgn_Bynangua.formatMilliseconds(clearTime); + World.SendNotiPacketMessage(format(Cofig["通关播报信息"], joinedNames, Dungeon_Name, diff_name, time, killcount), Cofig["发送信息位置"]); + for (local i = 0; i < 4; ++i) { + local TUser = PartyObj.GetUser(i); + if (TUser) { + local CID = TUser.GetCID(); + if (CID) { + dungeon_cache.rawdelete(CID); + } + } + } + } + } +} + +class _clear_dgn_Bynangua { + DungeonType = { + "0": "", + "1": "非常困难级深渊-", + "2": "困难级深渊-", + }; + + function join(array, delimiter) { + local result = ""; + for (local i = 0; i < array.len(); ++i) { + if (i > 0) { + result += delimiter; + } + result += array[i]; + } + return result; + } + function formatMilliseconds(ms) { + local str = ""; + local minutes = ms / 60000; + local seconds = (ms % 60000) / 1000; + local milliseconds = (ms % 1000) / 10; + if (minutes > 0) { + str = minutes + "分" + + (seconds < 10 ? "0" : "") + seconds + "秒" + + (milliseconds < 10 ? "0" : "") + milliseconds; + } else { + str = seconds + "秒" + + (milliseconds < 10 ? "0" : "") + milliseconds; + } + return str; + } + function CParty_get_member_count(C_Object) { + return Sq_CallFunc(S_Ptr("0x0859A16A"), "int", ["pointer"], C_Object); + } +} + diff --git a/示例项目/副本播报/副本播报配置_Nangua.json b/示例项目/副本播报/副本播报配置_Nangua.json new file mode 100644 index 0000000..52def23 --- /dev/null +++ b/示例项目/副本播报/副本播报配置_Nangua.json @@ -0,0 +1,18 @@ +{ + "副本播报开关(true开启,false关闭)":true, + "不需要播报的副本ID(实例中的ID为怪物攻城的副本ID尽量不要删)":[20002, 20001, 20002, 20003, 20004, 20005, 20006, 20007, 20008, 20009, 20010, 20020, 20021, 20022, 20023, 20024], + "指定角色CID不播报":[1111111, 2222222], + "提示": "角色ID可以查看数据库中 taiwan_cain → charac_info → charac_no代表角色ID", + "通关播报信息":"玩家[%s]通关[%s - %s]用时:[%s],击杀怪物数量<%d>", + "未通过一个小地图播报信息":"玩家[%s]在[%s%s-%s]中连一个地图都没通过,击杀怪物数量<%d>", + "放弃副本":"很遗憾,玩家[%s]在[%s%s-%s]中被打的落荒而逃,用时: %s,击杀怪物数量<%d>", + "在队伍中提前退出副本":"玩家[%s]在<%s>的队伍中,提前退出了[%s%s-%s],用时: %s,击杀怪物数量<%d>", + "发送信息位置":14, + "副本难度命名" : { + "0": "普通级", + "1": "冒险级", + "2": "勇士级", + "3": "王者级", + "4": "地狱级" + } +} \ No newline at end of file diff --git a/示例项目/副本难度解锁券/Proj.ifo b/示例项目/副本难度解锁券/Proj.ifo new file mode 100644 index 0000000..db4ee8d --- /dev/null +++ b/示例项目/副本难度解锁券/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "副本难度解锁券", + "ProjectDescribe": "使用指定道具解锁当前账号副本所有难度", + "ProjectAuthor": "南瓜", + "ProjectVersion": 1.0, + "ProjectConfig": "副本难度解锁_Nangua.json", + "ProjectFiles": [ + "副本难度解锁券.nut" + ], + "ProjectRunFunc": "_Dps_unlock_all_dgn_diff_Main_" +} \ No newline at end of file diff --git a/示例项目/副本难度解锁券/副本难度解锁_Nangua.json b/示例项目/副本难度解锁券/副本难度解锁_Nangua.json new file mode 100644 index 0000000..f50a2d8 --- /dev/null +++ b/示例项目/副本难度解锁券/副本难度解锁_Nangua.json @@ -0,0 +1,4 @@ +{ + "解锁成功提示信息": "已将该账号的副本难度开启,将自动返回选择角色界面...", + "副本难度解锁券道具ID": 7577 +} \ No newline at end of file diff --git a/示例项目/副本难度解锁券/副本难度解锁券.nut b/示例项目/副本难度解锁券/副本难度解锁券.nut new file mode 100644 index 0000000..e07194d --- /dev/null +++ b/示例项目/副本难度解锁券/副本难度解锁券.nut @@ -0,0 +1,12 @@ +function _Dps_unlock_all_dgn_diff_Main_() { + local Cofig = GlobalConfig.Get("副本难度解锁_Nangua.json"); + Cb_Use_Item_Sp_Func[Cofig["副本难度解锁券道具ID"]] <- function(SUser, ItemId) { + local a3 = Memory.allocUtf8String("3"); + Sq_CallFunc(S_Ptr("0x0820BA90"), "int", ["pointer", "int", "pointer"], SUser.C_Object, 120, a3.C_Object); + + Timer.SetTimeOut(function(SUser) { + Sq_CallFunc(S_Ptr("0x8686FEE"), "int", ["pointer", "int"], SUser.C_Object, 1); + }, 1, SUser); + SUser.SendNotiPacketMessage(Cofig["解锁成功提示信息"], 8); + } +} diff --git a/示例项目/副本需要持有道具进入/Proj.ifo b/示例项目/副本需要持有道具进入/Proj.ifo new file mode 100644 index 0000000..a46ca83 --- /dev/null +++ b/示例项目/副本需要持有道具进入/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "副本需要持有道具进入", + "ProjectDescribe": "副本需要持有道具进入", + "ProjectAuthor": "凌众", + "ProjectVersion": 1.0, + "ProjectConfig": "副本需要持有道具进入_Lenheart.json", + "ProjectFiles": [ + "副本需要持有道具进入.nut" + ], + "ProjectRunFunc": "_Dps_MapNeedItem_Main_" +} \ No newline at end of file diff --git a/示例项目/副本需要持有道具进入/副本需要持有道具进入.nut b/示例项目/副本需要持有道具进入/副本需要持有道具进入.nut new file mode 100644 index 0000000..5f32d5d --- /dev/null +++ b/示例项目/副本需要持有道具进入/副本需要持有道具进入.nut @@ -0,0 +1,38 @@ + + + + + +function _Dps_MapNeedItem_Main_() { + +// 禁止进入副本 +Cb_SelectDungeon_Check_Error_Leave_Func.MapNeedItem <- function (args) { + + + local Config = GlobalConfig.Get("副本需要持有道具进入_Lenheart.json"); + + local body = Config["副本需要持有道具才允许进入"]; + + local SUser = User(args[1]); + + local mapid = NativePointer(args[2]).add(13).readShort(); + if(body.rawin(mapid.tostring())){ + local ItemId = body[mapid.tostring()]; + local PartyObj = SUser.GetParty(); + if (!PartyObj) { + return; + } + for (local i = 0; i < 4; ++i) { + local PSUser = PartyObj.GetUser(i); + if (PSUser) { + local InvenObj = SUser.GetInven(); + local SlotIdx = InvenObj.GetSlotById(ItemId); + if(SlotIdx == -1){ + SUser.SendNotiBox(Config["公告"]+PvfItem.GetNameById(ItemId),1); + return 1; // 禁止进入副本 + } + } + } + } +} +} \ No newline at end of file diff --git a/示例项目/副本需要持有道具进入/副本需要持有道具进入_Lenheart.json b/示例项目/副本需要持有道具进入/副本需要持有道具进入_Lenheart.json new file mode 100644 index 0000000..cf2b3d8 --- /dev/null +++ b/示例项目/副本需要持有道具进入/副本需要持有道具进入_Lenheart.json @@ -0,0 +1,7 @@ +{ + "公告": "缺少进入凭证:", + "副本需要持有道具才允许进入":{ + "65":3037, + "64":3038 + } +} \ No newline at end of file diff --git a/示例项目/史诗免确认/Proj.ifo b/示例项目/史诗免确认/Proj.ifo new file mode 100644 index 0000000..66d1ea4 --- /dev/null +++ b/示例项目/史诗免确认/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "史诗免确认", + "ProjectDescribe": "史诗免确认", + "ProjectAuthor": "倾泪寒", + "ProjectVersion": 1.1, + "ProjectConfig": "", + "ProjectFiles": [ + "史诗免确认.nut" + ], + "ProjectRunFunc": "_Dps_EpicNoConfirmationRequired_Main_" +} \ No newline at end of file diff --git a/示例项目/史诗免确认/史诗免确认.nut b/示例项目/史诗免确认/史诗免确认.nut new file mode 100644 index 0000000..e0a88b6 --- /dev/null +++ b/示例项目/史诗免确认/史诗免确认.nut @@ -0,0 +1,7 @@ +function _Dps_EpicNoConfirmationRequired_Main_() { + NativePointer("0x085A56CE").add(2).writeU8(9); + Cb_CItem_IsRoutingItem_Leave_Func["DPSOFFICIAL"] <- function (args) + { + return 0 + } +} \ No newline at end of file diff --git a/示例项目/史诗掉落奖励/Proj.ifo b/示例项目/史诗掉落奖励/Proj.ifo new file mode 100644 index 0000000..affefe4 --- /dev/null +++ b/示例项目/史诗掉落奖励/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "史诗掉落奖励", + "ProjectDescribe": "获取指定数量获得奖励以及指定道具获得奖励", + "ProjectAuthor": "南瓜", + "ProjectVersion": 1.1, + "ProjectConfig": "史诗掉落奖励配置_南瓜.json", + "ProjectFiles": [ + "史诗掉落奖励.nut" + ], + "ProjectRunFunc": "_Dps_SSDL_nangua_Main_" +} \ No newline at end of file diff --git a/示例项目/史诗掉落奖励/史诗掉落奖励.nut b/示例项目/史诗掉落奖励/史诗掉落奖励.nut new file mode 100644 index 0000000..cba4e2d --- /dev/null +++ b/示例项目/史诗掉落奖励/史诗掉落奖励.nut @@ -0,0 +1,333 @@ +_SpecialItemBynangua <- { + "Rarity4": {}, + "Rarity3": {}, + "Rarity2": {} +} + +function _Dps_SSDL_nangua_Main_() { + //进入副本 + Cb_History_DungeonEnter_Func.RindroSSEnterByNangua <- function(SUser, Data) { + _SpecialItemBynangua.Rarity4.rawset(SUser.GetCID(), []); + _SpecialItemBynangua.Rarity3.rawset(SUser.GetCID(), []); + _SpecialItemBynangua.Rarity2.rawset(SUser.GetCID(), []); + } + + //离开副本 + Cb_History_DungeonLeave_Func.RindroSSLeaveByNangua <- function(SUser, Data) { + local Cofig = GlobalConfig.Get("史诗掉落奖励配置_南瓜.json"); + local PartyObj = SUser.GetParty(); + if (!PartyObj) return; + local Bfobj = PartyObj.GetBattleField(); + local DgnObj = Bfobj.GetDgn(); + if (DgnObj) { + local Dungeon_Name = DgnObj.GetName(); + + // 处理史诗装备 + if (_SpecialItemBynangua.Rarity4.rawin(SUser.GetCID())) { + local ItemObjS = _SpecialItemBynangua.Rarity4[SUser.GetCID()]; + if (ItemObjS != null && ItemObjS.len() > 0) { + local config = Cofig["奖励控制"]["多件SS对应奖励(史诗)"]; + local key = ItemObjS.len().tostring(); + if (config.rawin(key)) { + Nangua_SSMSG.SendTitle(SUser, Dungeon_Name, "中获得史诗装备"); + foreach(ItemObj in ItemObjS) { + local item_id = ItemObj.GetIndex(); + Nangua_SSMSG.ItemMsg(item_id, ItemObj); + } + Nangua_SSMSG.SendCntMsg(ItemObjS.len()); + Nangua_SSMSG.SendMiddle(); + ProcessRewards(SUser, config[key]); + Nangua_SSMSG.EndMsg(); + } + } + _SpecialItemBynangua.Rarity4.rawset(SUser.GetCID(), []); + } + + // 处理神器装备 + if (_SpecialItemBynangua.Rarity3.rawin(SUser.GetCID())) { + local ItemObjS = _SpecialItemBynangua.Rarity3[SUser.GetCID()]; + if (ItemObjS != null && ItemObjS.len() > 0) { + local config = Cofig["奖励控制"]["多件神器对应奖励"]; + local key = ItemObjS.len().tostring(); + if (config.rawin(key)) { + Nangua_SSMSG.SendTitle(SUser, Dungeon_Name, "中获得神器装备"); + foreach(ItemObj in ItemObjS) { + local item_id = ItemObj.GetIndex(); + Nangua_SSMSG.ItemMsg(item_id, ItemObj); + } + Nangua_SSMSG.SendCntMsg(ItemObjS.len()); + Nangua_SSMSG.SendMiddle(); + ProcessRewards(SUser, config[key]); + Nangua_SSMSG.EndMsg(); + } + } + _SpecialItemBynangua.Rarity3.rawset(SUser.GetCID(), []); + } + + // 处理稀有装备 + if (_SpecialItemBynangua.Rarity2.rawin(SUser.GetCID())) { + local ItemObjS = _SpecialItemBynangua.Rarity2[SUser.GetCID()]; + if (ItemObjS != null && ItemObjS.len() > 0) { + local config = Cofig["奖励控制"]["多件稀有对应奖励"]; + local key = ItemObjS.len().tostring(); + if (config.rawin(key)) { + Nangua_SSMSG.SendTitle(SUser, Dungeon_Name, "中获得稀有装备"); + foreach(ItemObj in ItemObjS) { + local item_id = ItemObj.GetIndex(); + Nangua_SSMSG.ItemMsg(item_id, ItemObj); + } + Nangua_SSMSG.SendCntMsg(ItemObjS.len()); + Nangua_SSMSG.SendMiddle(); + ProcessRewards(SUser, config[key]); + Nangua_SSMSG.EndMsg(); + } + } + _SpecialItemBynangua.Rarity2.rawset(SUser.GetCID(), []); + } + } + } + + Cb_UserHistoryLog_ItemAdd_Enter_Func.ItemAddByNangua <- function(args) { + local Cofig = GlobalConfig.Get("史诗掉落奖励配置_南瓜.json"); + local SUser = User(NativePointer(args[0]).readPointer()); + local InvenObj = SUser.GetInven(); + local ItemObj = Item(args[4]); + local type = args[5]; + local PartyObj = SUser.GetParty(); + if (!PartyObj) return; + local Bfobj = PartyObj.GetBattleField(); + local DgnObj = Bfobj.GetDgn(); + if (!DgnObj) return; + local Dungeon_Name = DgnObj.GetName(); + if (!ItemObj) return; + local item_id = ItemObj.GetIndex(); + local ItemType = Sq_CallFunc(S_Ptr("0x085018D2"), "int", ["pointer", "int"], InvenObj.C_Object, item_id); + local pvfitem = PvfItem.GetPvfItemById(item_id); + + // 史诗装备记录 + if (pvfitem.GetRarity() == 4 && type == 4 && ItemType == 1 && Cofig["奖励控制"]["多件史诗奖励控制开关"]) { + if (!_SpecialItemBynangua.Rarity4.rawin(SUser.GetCID())) { + _SpecialItemBynangua.Rarity4.rawset(SUser.GetCID(), []); + } + _SpecialItemBynangua.Rarity4[SUser.GetCID()].append(ItemObj); + } + // 神器装备记录 + else if (pvfitem.GetRarity() == 3 && type == 4 && ItemType == 1 && Cofig["奖励控制"]["多件神器奖励控制开关"]) { + if (!_SpecialItemBynangua.Rarity3.rawin(SUser.GetCID())) { + _SpecialItemBynangua.Rarity3.rawset(SUser.GetCID(), []); + } + _SpecialItemBynangua.Rarity3[SUser.GetCID()].append(ItemObj); + } + // 稀有装备记录 + else if (pvfitem.GetRarity() == 2 && type == 4 && ItemType == 1 && Cofig["奖励控制"]["多件稀有奖励控制开关"]) { + if (!_SpecialItemBynangua.Rarity2.rawin(SUser.GetCID())) { + _SpecialItemBynangua.Rarity2.rawset(SUser.GetCID(), []); + } + _SpecialItemBynangua.Rarity2[SUser.GetCID()].append(ItemObj); + } + + // 指定道具奖励逻辑保持不变 + if (!Cofig["指定道具奖励控制"]["指定道具奖励控制开关"]) return; + local Itemid = item_id.tostring(); + if (Cofig["指定道具奖励控制"]["指定道具对应奖励"].rawin(Itemid) && type == 4) { + Nangua_SSMSG.SendTitle(SUser, Dungeon_Name, "中获得特定道具"); + Nangua_SSMSG.ItemMsg(item_id, ItemObj); + Nangua_SSMSG.SendMiddle(); + ProcessRewards(SUser, Cofig["指定道具奖励控制"]["指定道具对应奖励"][Itemid]); + Nangua_SSMSG.EndMsg(); + } + } + // 处理奖励逻辑 + function ProcessRewards(SUser, rewards) { + local Cofig = GlobalConfig.Get("史诗掉落奖励配置_南瓜.json"); + local config = Cofig["信息播报"]; + local totalRewards = []; + local rewardMessages = []; + // 遍历奖励列表 + foreach(reward in rewards) { + local itemId = reward[0]; + local itemCnt = reward[1]; + if (itemId == 0) { + // 点券奖励 + local ceraMsgObj = AdMsg(); + ceraMsgObj.PutType(config["发送位置"]); + if (config["发送位置"] != 14) { + ceraMsgObj.PutString(" "); + } + ceraMsgObj.PutColorString("点券", config["内容颜色"]); + ceraMsgObj.PutColorString("[" + itemCnt + "]", [255, 20, 0]); + ceraMsgObj.Finalize(); + World.SendAll(ceraMsgObj.MakePack()); + ceraMsgObj.Delete(); + SUser.RechargeCera(itemCnt); + } else { + totalRewards.append([itemId, itemCnt]); + local itemName = PvfItem.GetNameById(itemId); + local color = Nangua_SSMSG.RarityColor(itemId); + rewardMessages.append([itemName, itemCnt, color]); + } + } + Nangua_SSMSG.api_CUser_Add_Item_list(SUser, totalRewards); + // 发送奖励物品信息通知 + if (rewardMessages.len() > 0) { + local rewardMsgObj = AdMsg(); + rewardMsgObj.PutType(config["发送位置"]); + if (config["发送位置"] != 14) { + rewardMsgObj.PutString(" "); + } + foreach(message in rewardMessages) { + rewardMsgObj.PutColorString("[" + message[0] + "]", message[2]); + rewardMsgObj.PutColorString("<" + message[1] + ">", [255, 20, 0]); + rewardMsgObj.PutColorString("个", config["内容颜色"]); + } + rewardMsgObj.Finalize(); + World.SendAll(rewardMsgObj.MakePack()); + rewardMsgObj.Delete(); + } + } +} + +class Nangua_SSMSG { + function SendTitle(SUser, Dungeon_Name, msg) { + local Cofig = GlobalConfig.Get("史诗掉落奖励配置_南瓜.json"); + local config = Cofig["信息播报"]; + local title = AdMsg(); + title.PutType(config["发送位置"]); + if (config["发送位置"] != 14) { + title.PutString(" "); + } + title.PutColorString(config["标题"][0], config["标题"][1]); + title.PutColorString("恭喜玩家", config["内容颜色"]); + title.PutColorString("[" + SUser.GetCharacName() + "]", [255, 20, 0]); + title.PutColorString("在", config["内容颜色"]); + title.PutColorString("[" + Dungeon_Name + "]", [255, 20, 0]); + title.PutColorString("" + msg + "", config["内容颜色"]); + title.Finalize(); + World.SendAll(title.MakePack()); + title.Delete(); + } + + function SendMiddle() { + local Cofig = GlobalConfig.Get("史诗掉落奖励配置_南瓜.json"); + local config = Cofig["信息播报"]; + local MiddleMsgObj = AdMsg(); + MiddleMsgObj.PutType(config["发送位置"]); + if (config["发送位置"] != 14) { + MiddleMsgObj.PutString(" "); + } + MiddleMsgObj.PutColorString("特此奖励:", config["内容颜色"]); + MiddleMsgObj.Finalize(); + World.SendAll(MiddleMsgObj.MakePack()); + MiddleMsgObj.Delete(); + } + + function SendCntMsg(cnt) { + local Cofig = GlobalConfig.Get("史诗掉落奖励配置_南瓜.json"); + local config = Cofig["信息播报"]; + local CntMsgObj = AdMsg(); + CntMsgObj.PutType(config["发送位置"]); + if (config["发送位置"] != 14) { + CntMsgObj.PutString(" "); + } + CntMsgObj.PutColorString("合计", config["内容颜色"]); + CntMsgObj.PutColorString("[" + cnt + "]", [255, 20, 0]); + CntMsgObj.PutColorString("件装备", config["内容颜色"]); + CntMsgObj.Finalize(); + World.SendAll(CntMsgObj.MakePack()); + CntMsgObj.Delete(); + } + + function ItemMsg(item_id, ItemObj) { + local Cofig = GlobalConfig.Get("史诗掉落奖励配置_南瓜.json"); + local config = Cofig["信息播报"]; + local MsgObj = AdMsg(); + MsgObj.PutType(config["发送位置"]); + if (config["发送位置"] != 14) { + MsgObj.PutString(" "); + } + MsgObj.PutEquipment("[" + PvfItem.GetNameById(item_id) + "]", ItemObj, Nangua_SSMSG.RarityColor(item_id)); + MsgObj.Finalize(); + World.SendAll(MsgObj.MakePack()); + MsgObj.Delete(); + } + + function EndMsg() { + local Cofig = GlobalConfig.Get("史诗掉落奖励配置_南瓜.json"); + local config = Cofig["信息播报"]; + local endMsgObj = AdMsg(); + endMsgObj.PutType(config["发送位置"]); + if (config["发送位置"] != 14) { + endMsgObj.PutString(" "); + } + endMsgObj.PutColorString(config["结尾"][0], config["结尾"][1]); + endMsgObj.Finalize(); + World.SendAll(endMsgObj.MakePack()); + endMsgObj.Delete(); + } + function api_CUser_Add_Item_list(SUser, item_list) { + for (local i = 0; i < item_list.len(); i++) { + local item_id = item_list[i][0]; // 道具代码 + local quantity = item_list[i][1]; // 道具数量 + local cnt = Nangua_SSMSG.checkInventorySlot(SUser, item_id); + if (cnt == 1) { + SUser.GiveItem(item_id, quantity); //使用窗口发送奖励 + } else { + local RewardItems = []; + RewardItems.append([item_id, quantity]); + local title = "GM" + local Text = "由于背包空间不足,已通过邮件发送,请查收!" + //角色类 发送邮件函数 (标题, 正文, 金币, 道具列表[[3037,100],[3038,100]]) + SUser.ReqDBSendMultiMail(title, Text, 0, RewardItems) + } + } + Nangua_SSMSG.SendItemWindowNotification(SUser, item_list); + } + function SendItemWindowNotification(SUser, item_list) { + local Pack = Packet(); + Pack.Put_Header(1, 163); //协议 + Pack.Put_Byte(1); //默认1 + Pack.Put_Short(0); //槽位id 填入0即可 + Pack.Put_Int(0); //未知 0以上即可 + Pack.Put_Short(item_list.len()); //道具组数 + //写入道具代码和道具数量 + for (local i = 0; i < item_list.len(); i++) { + Pack.Put_Int(item_list[i][0]); //道具代码 + Pack.Put_Int(item_list[i][1]); //道具数量 装备/时装时 任意均可 + } + Pack.Finalize(true); //确定发包内容 + SUser.Send(Pack); //发包 + Pack.Delete(); //清空buff区 + } + /** + * 根据道具类型背包空格数量 + * @param {pointer} SUser - 用户 + * @param {int} item_id - 需要查找的道具ID + * @returns {int} - 空格数量 + */ + function checkInventorySlot(SUser, itemid) { + local InvenObj = SUser.GetInven(); + local type = Sq_CallFunc(S_Ptr("0x085018D2"), "int", ["pointer", "int"], InvenObj.C_Object, itemid); + local cnt = Sq_CallFunc(S_Ptr("0x08504F64"), "int", ["pointer", "int", "int"], InvenObj.C_Object, type, 1); + + return cnt; + } + function RarityColor(item_id) { + local PvfItemObj = PvfItem.GetPvfItemById(item_id); + if (PvfItemObj == null) { + return; + } + local CItem_get_rarity = PvfItemObj.GetRarity(); // 装备品级 + return Nangua_SSMSG.rarityColorMap[(CItem_get_rarity).tostring()]; + } + //品级对应的RGB + rarityColorMap = { + "0": [255, 255, 255], // 普通 + "1": [104, 213, 237], // 高级 + "2": [179, 107, 255], // 稀有 + "3": [255, 0, 255], // 神器 + "4": [255, 180, 0], // 史诗 + "5": [255, 102, 102], // 勇者 + "6": [255, 20, 147], // 深粉红色 + "7": [255, 215, 0] // 金色 + }; +} \ No newline at end of file diff --git a/示例项目/史诗掉落奖励/史诗掉落奖励配置_南瓜.json b/示例项目/史诗掉落奖励/史诗掉落奖励配置_南瓜.json new file mode 100644 index 0000000..4908752 --- /dev/null +++ b/示例项目/史诗掉落奖励/史诗掉落奖励配置_南瓜.json @@ -0,0 +1,94 @@ +{ + "奖励控制": { + "开关说明(true为开启,false为关闭)这是提示请无视,后面的0也不用管":0, + "多件史诗奖励控制开关": true, + "多件神器奖励控制开关": false, + "多件稀有奖励控制开关": false, + "稀有装备开关如果开启会导致可以在副本内丢弃拾取领取奖励,需搭配禁止丢弃物品使用(这是提示请无视,后面的0也不用管)":0, + "tips>>>如果奖励填0代表点券(这是提示请无视,后面的0也不用管)":0, + "多件SS对应奖励(史诗)": { + "2": [ + [3038, 2], + [3037, 10] + ], + "3": [ + [0, 10000], + [123014, 10] + ], + "4": [ + [0, 50000], + [3037, 2000], + [3038, 2000] + ], + "5": [ + [0, 50000], + [3037, 2000], + [3038, 2000] + ] + }, + "多件神器对应奖励": { + "2": [ + [3038, 1], + [3037, 5] + ], + "3": [ + [0, 5000], + [123014, 5] + ], + "4": [ + [0, 20000], + [3037, 1000], + [3038, 1000] + ], + "5": [ + [0, 20000], + [3037, 1000], + [3038, 1000] + ] + }, + "多件稀有对应奖励": { + "2": [ + [3038, 1], + [3037, 2] + ], + "3": [ + [0, 2000], + [123014, 2] + ], + "4": [ + [0, 10000], + [3037, 500], + [3038, 500] + ], + "5": [ + [0, 10000], + [3037, 500], + [3038, 500] + ] + } + }, + "指定道具奖励控制": { + "指定道具奖励控制开关": true, + "指定道具对应奖励": { + "3037": [ + [3038, 2], + [3037, 10] + ], + "3038": [ + [0, 10000], + [123014, 10] + ], + "3039": [ + [0, 50000], + [3037, 2000], + [3038, 2000] + ] + } + }, + "信息播报":{ + "发送位置":14, + "标题":["-----------☆天降鸿运☆--------------", [255, 130, 0]], + "内容颜色":[255, 120, 0], + "结尾":["-----------☆恭喜这逼☆--------------", [255, 130, 0]] + } +} \ No newline at end of file diff --git a/示例项目/史诗药剂/Proj.ifo b/示例项目/史诗药剂/Proj.ifo new file mode 100644 index 0000000..0bcfd8a --- /dev/null +++ b/示例项目/史诗药剂/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "史诗药剂", + "ProjectDescribe": "在单人深渊中使用指定ID的道具使玩家获得爆率加成。(组队无效),史诗药剂道具的冷却时间和持续时间需一致!!", + "ProjectAuthor": "倾泪寒 & 南瓜", + "ProjectVersion": 1.7, + "ProjectConfig": "史诗药剂配置文件.json", + "ProjectFiles": [ + "史诗药剂.nut" + ], + "ProjectRunFunc": "_Dps_EpicPotion_Main_" +} \ No newline at end of file diff --git a/示例项目/史诗药剂/史诗药剂.nut b/示例项目/史诗药剂/史诗药剂.nut new file mode 100644 index 0000000..3f5b412 --- /dev/null +++ b/示例项目/史诗药剂/史诗药剂.nut @@ -0,0 +1,47 @@ +/* +文件名:史诗药剂.nut +路径:MyProject/史诗药剂.nut +创建日期:2025-03-28 10:21 +文件用途:史诗药剂 +*/ + +//启动函数 自定义的需要写在ifo中 +function _Dps_EpicPotion_Main_() { + //注册获取道具稀有度进入回调 + Cb_GetItemRarity_Enter_Func["史诗药剂_逻辑"] <- function(args) { + // print("获取道具稀有度进入回调"); + local Cofig = GlobalConfig.Get("史诗药剂配置文件.json"); + local Addr = NativePointer(args[0]); + local VectorSize = (Addr.add(4).readU32() - Addr.readU32()) / 4; + // 遍历队伍成员,找到使用了史诗药剂的玩家 + local userWithPotion = null; + for (local i = 0; i< VectorSize; i++) { + local elementAddr = NativePointer(Addr.readPointer()).add(i * 4); + local user = elementAddr.readPointer(); + if (user && Sq_CallFunc(S_Ptr("0x865E994"), "int", ["pointer", "int"], user, Cofig["史诗药剂ID"])) { + userWithPotion = User(user); + break; + } + } + if (userWithPotion && Haker.NextReturnAddress == "0x853583a") { + local partyobj = userWithPotion.GetParty(); + // 检查是否单人 + if (Sq_CallFunc(S_Ptr("0x0859A16A"), "int", ["pointer", ], partyobj.C_Object) == 1) { + local MaxRoll = NativePointer(args[1]).add(16).readU32(); + local odds = Cofig["史诗药剂默认加成几率"]; // 默认药剂的增加几率 + // 检查是否VIP玩家 + local charac_no = userWithPotion.GetCID(); + local cfg = Cofig["指定角色ID额外叠加加成几率"]; + local key = charac_no.tostring(); + if (cfg != null && cfg.rawin(key)) { + odds = cfg[key]; + //print("VIP玩家:" + key + " 加成几率:" + odds); + } + // 计算新的roll值 + args[2] = MathClass.getMin(args[2] + args[2] * odds, MaxRoll); + // print("新的roll值:" + args[2] + " 加成几率:" + odds + " 最大roll值:" + MaxRoll + " 角色ID:" + charac_no); + } + } + return args; + } +} \ No newline at end of file diff --git a/示例项目/史诗药剂/史诗药剂配置文件.json b/示例项目/史诗药剂/史诗药剂配置文件.json new file mode 100644 index 0000000..8d9057c --- /dev/null +++ b/示例项目/史诗药剂/史诗药剂配置文件.json @@ -0,0 +1,14 @@ +{ + "史诗药剂ID": 2600010, + "史诗药剂默认加成几率": 0.2, + "指定角色ID额外叠加加成几率": { + "1": 1.0, + "2": 1.0 + }, + "提示1": "史诗药剂道具的冷却时间和持续时间需一致否则无效!!!", + "提示2": "比如默认几率是0.2,指定角色ID是1,那么最终几率是0.2+1=1.2", + "提示3": "群文件中有史诗药剂的stk文件,可以自行修改!!!", + "提示4": "角色ID可以查看数据库中 taiwan_cain → charac_info → charac_no代表角色ID", + "提示5": "0.1几率是加成10%以此类推,受幸运值影响,具体加成请自行测算!", + "提示6": "2600010是和平呐喊的道具ID,用此道具有图标提示以及时间提示,但是具体字符串相关自行解决" +} \ No newline at end of file diff --git a/示例项目/在线泡点/Proj.ifo b/示例项目/在线泡点/Proj.ifo new file mode 100644 index 0000000..f99650b --- /dev/null +++ b/示例项目/在线泡点/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "在线泡点", + "ProjectDescribe": "根据设定时间间隔给与点券或代币活跃奖励", + "ProjectAuthor": "南瓜", + "ProjectVersion": 1.0, + "ProjectConfig": "在线泡点配置_Nangua.json", + "ProjectFiles": [ + "在线泡点.nut" + ], + "ProjectRunFunc": "_Dps_Online_rewardsBynangua_Main_" +} \ No newline at end of file diff --git a/示例项目/在线泡点/在线泡点.nut b/示例项目/在线泡点/在线泡点.nut new file mode 100644 index 0000000..ad3655e --- /dev/null +++ b/示例项目/在线泡点/在线泡点.nut @@ -0,0 +1,97 @@ +_total_rewardBynangua <- {}; // 存储每个玩家的累计奖励 + +function _Online_rewardsBynangua() { + local Config = GlobalConfig.Get("在线泡点配置_Nangua.json"); + local OnlinePlayerList = World.GetOnlinePlayer(); + + // 发放奖励 + foreach(SUser in OnlinePlayerList) { + local IP = _Online_rewardsBynangua_api.CUser_get_public_ip_address(SUser); + if (!Config["是否发放奖励给假人(true发放/false不发放)"] && IP == Config["离线假人IP"]) { + continue; + } + + // 获取奖励类型和数量 + local isCera = Config["在线泡点配置"]["奖励类型(true点券/false代币)"]; + local rewardAmount = isCera ? Config["在线泡点配置"]["点券奖励"] : Config["在线泡点配置"]["代币奖励"]; + + if (rewardAmount > 0) { + // 初始化累计奖励 + if (!(SUser.GetCID() in _total_rewardBynangua)) { + _total_rewardBynangua[SUser.GetCID()] <- 0; + } + _total_rewardBynangua[SUser.GetCID()] += rewardAmount; + + // 发放奖励 + if (isCera) { + SUser.RechargeCera(rewardAmount); + } else { + SUser.RechargeCeraPoint(rewardAmount); + } + + // 发送消息 + local AdMsgObj = AdMsg(); + AdMsgObj.PutType(Config["在线泡点配置"]["信息发送位置"]); + if (Config["在线泡点配置"]["信息发送位置"] != 14) { + AdMsgObj.PutString(" "); + } + AdMsgObj.PutImoticon(Config["在线泡点配置"]["图标"]); + AdMsgObj.PutColorString(Config["在线泡点配置"]["标题"], Config["在线泡点配置"]["文字颜色rgb"]); + AdMsgObj.PutColorString("[" + rewardAmount + "]", Config["在线泡点配置"]["数量颜色rgb"]); + AdMsgObj.PutColorString(Config["在线泡点配置"]["奖励内容(代币/点券)"], Config["在线泡点配置"]["文字颜色rgb"]); + AdMsgObj.PutColorString(Config["在线泡点配置"]["奖励"], Config["在线泡点配置"]["文字颜色rgb"]); + AdMsgObj.PutColorString("[" + _total_rewardBynangua[SUser.GetCID()] + "]", Config["在线泡点配置"]["数量颜色rgb"]); + AdMsgObj.PutColorString(Config["在线泡点配置"]["奖励内容(代币/点券)"], Config["在线泡点配置"]["文字颜色rgb"]); + AdMsgObj.Finalize(); + SUser.Send(AdMsgObj.MakePack()); + AdMsgObj.Delete(); + } + } +} + +// 在线泡点活动入口点 +function _Dps_Online_rewardsBynangua_Main_() { + _Dps_Online_rewardsBynangua_Logic_(); +} + +// 在线泡点活动重载入口点 +function _Dps_Online_rewardsBynangua_Main_Reload_(OldConfig) { + // 先移除旧的定时任务 + Timer.RemoveCronTask("OnlineRewardTask"); + + // 重新注册 + _Dps_Online_rewardsBynangua_Logic_(); + // 清理缓存 + _total_rewardBynangua <- {}; +} + +// 在线泡点活动逻辑入口点 +function _Dps_Online_rewardsBynangua_Logic_() { + local Config = GlobalConfig.Get("在线泡点配置_Nangua.json"); + local interval = Config["在线泡点配置"]["时间间隔(分钟)"]; + local cronExpression = format("0 */%d * * * *", interval); + // 注册定时任务 + Timer.SetCronTask(_Online_rewardsBynangua, { + Cron = cronExpression, + Name = "OnlineRewardTask" + }); +} + +class _Online_rewardsBynangua_api { + function CUser_get_public_ip_address(SUser){ + local s_addr = Sq_CallFunc(S_Ptr("0x084EC90A"), "int", ["pointer"], SUser.C_Object); + if(s_addr){ + local inet_ntoa = Sq_CallFunc(S_Ptr("0x0807DDC0"), "pointer", ["int"], s_addr); + return NativePointer(inet_ntoa).readUtf8String(); + } + return null; + } +} + +// 玩家下线时,清空累计奖励 +Cb_CUser_LogoutToPCRoom_Enter_Func.Online_rewardsBynangua <- function(args) { + local SUser = User(args[0]); + if (SUser.GetCID() in _total_rewardBynangua) { + _total_rewardBynangua.rawdelete(SUser.GetCID()); + } +} diff --git a/示例项目/在线泡点/在线泡点配置_Nangua.json b/示例项目/在线泡点/在线泡点配置_Nangua.json new file mode 100644 index 0000000..46c1113 --- /dev/null +++ b/示例项目/在线泡点/在线泡点配置_Nangua.json @@ -0,0 +1,17 @@ +{ + "在线泡点配置": { + "时间间隔(分钟)":1, + "点券奖励":10, + "代币奖励":10, + "奖励类型(true点券/false代币)":true, + "信息发送位置":0, + "图标":1, + "文字颜色rgb":[120, 255, 0], + "数量颜色rgb":[255, 215, 0], + "标题":"活跃奖励获得", + "奖励内容(代币/点券)":"点券", + "奖励":" 累计获得" + }, + "是否发放奖励给假人(true发放/false不发放)":false, + "离线假人IP":"10.0.0.1" +} \ No newline at end of file diff --git a/示例项目/多彩蜜蜡改跨界石/Proj.ifo b/示例项目/多彩蜜蜡改跨界石/Proj.ifo new file mode 100644 index 0000000..b890375 --- /dev/null +++ b/示例项目/多彩蜜蜡改跨界石/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "多彩蜜蜡改跨界石", + "ProjectDescribe": "将物品跨界存放至账号仓库,需dll或启动器支持,否则无法放入物品", + "ProjectAuthor": "南瓜", + "ProjectVersion": 1.0, + "ProjectConfig": "多彩蜜蜡改跨界石_Nangua.json", + "ProjectFiles": [ + "多彩蜜蜡改跨界石.nut" + ], + "ProjectRunFunc": "_Dps_Cross_Stone_Main_" +} \ No newline at end of file diff --git a/示例项目/多彩蜜蜡改跨界石/多彩蜜蜡改跨界石.nut b/示例项目/多彩蜜蜡改跨界石/多彩蜜蜡改跨界石.nut new file mode 100644 index 0000000..cd53e34 --- /dev/null +++ b/示例项目/多彩蜜蜡改跨界石/多彩蜜蜡改跨界石.nut @@ -0,0 +1,108 @@ +function _Dps_Cross_Stone_Main_() { + Cb_ModItemattr_Enter_Func.Cross_Stone <- function(args) { + local SUser = User(args[1]); + local pack = NativePointer(args[2]); + local a = NativePointer(pack.add(20).readPointer()); + local itemSlot = a.add(13).add(6).readShort(); + local equSlot = a.add(13).add(0).readShort(); + local InvenObj = SUser.GetInven(); + if (InvenObj) { + local ItemObj = InvenObj.GetSlot(1, itemSlot); + if (ItemObj.GetIndex() == 2675422) { + Sq_WriteByteArr(S_Ptr("0x820110E"), array(5, 0x90)); + Sq_WriteByteArr(S_Ptr("0x8201647"), array(5, 0x90)); + } + } + } + + Cb_ModItemattr_Leave_Func.Cross_Stone <- function(args) { + local Config = GlobalConfig.Get("多彩蜜蜡改跨界石_Nangua.json"); + local SUser = User(args[1]); + local pack = NativePointer(args[2]); + local a = NativePointer(pack.add(20).readPointer()); + local itemSlot = a.add(13).add(6).readShort(); + local equSlot = a.add(13).add(0).readShort(); + local InvenObj = SUser.GetInven(); + local result = 0; + if (InvenObj) { + local ItemObj = InvenObj.GetSlot(1, itemSlot); + local equObj = InvenObj.GetSlot(1, equSlot); + if (ItemObj.GetIndex() == 2675422) { + local PvfItemObj = PvfItem.GetPvfItemById(equObj.GetIndex()); + local qixi1 = NativePointer(equObj.C_Object).add(31).readU8(); + local qixi2 = NativePointer(equObj.C_Object).add(32).readU8(); + local ItemType = NativePointer(PvfItemObj.C_Object).add(141 * 4).readU32(); + //获取账号金库对象 + local CargoObj = SUser.GetAccountCargo(); + //获取账号金库中的一个空格子 + local EmptySlot = CargoObj.GetEmptySlot(); + local CheckItemLock = Sq_CallFunc(S_Ptr("0x8646942"), "int", ["pointer", "int", "int"], SUser.C_Object, 1, equSlot); + if (CheckItemLock) { + _Cross_StoneBynangua.CUser_SendCmdErrorPacket(SUser, 84, 13) + } else if(qixi1 > 0 || qixi2 > 0) { + _Cross_StoneBynangua.sendNotification(SUser, true, itemSlot, Config["跨界失败提示4"]); + _Cross_StoneBynangua.CUser_SendCmdErrorPacket(SUser, 84, 13) + } else if (Config["不可跨界装备"].find(equObj.GetIndex()) != null) { + _Cross_StoneBynangua.sendNotification(SUser, true, itemSlot, format(Config["跨界失败提示2"], PvfItem.GetNameById(equObj.GetIndex()))); + _Cross_StoneBynangua.CUser_SendCmdErrorPacket(SUser, 84, 13) + } else if (Config["不可跨界的装备类型"].find(ItemType) != null) { + _Cross_StoneBynangua.sendNotification(SUser, true, itemSlot, Config["跨界失败提示3"]); + _Cross_StoneBynangua.CUser_SendCmdErrorPacket(SUser, 84, 13) + } else if (EmptySlot == -1) { + _Cross_StoneBynangua.sendNotification(SUser, true, itemSlot, Config["跨界失败提示1"]); + _Cross_StoneBynangua.CUser_SendCmdErrorPacket(SUser, 84, 13) + } else { + local Flag = CargoObj.InsertItem(equObj, EmptySlot); + if (Flag == -1) { + _Cross_StoneBynangua.sendNotification(SUser, true, itemSlot, Config["跨界失败提示5"]); + _Cross_StoneBynangua.CUser_SendCmdErrorPacket(SUser, 84, 13) + } else { + local num = NativePointer(ItemObj.C_Object).add(7).readU32(); + _Cross_StoneBynangua.SendConsume(SUser, itemSlot, num); + //销毁背包中的道具 + equObj.Delete(); + //刷新玩家背包列表 + SUser.SendUpdateItemList(1, 0, equSlot); + //刷新账号金库列表 + Timer.SetTimeOut(function() { + CargoObj.SendItemList(); + }, 1); + _Cross_StoneBynangua.sendNotification(SUser, false, itemSlot, format(Config["跨界成功提示"], PvfItem.GetNameById(equObj.GetIndex()))); + } + } + Sq_WriteByteArr(S_Ptr("0x820110E"), [0x3D, 0x4E, 0xD1, 0x28, 0x00]); + Sq_WriteByteArr(S_Ptr("0x8201647"), [0xE8, 0xEC, 0x02, 0x00, 0x00]); + } + } + return result; + } +} + +class _Cross_StoneBynangua { + function sendNotification(SUser, bool, slot, message) { + local Config = GlobalConfig.Get("多彩蜜蜡改跨界石_Nangua.json"); + if(bool){ + SUser.GiveItem(2675422, 1); + SUser.SendUpdateItemList(1, 0, slot); + } + if (Config["233弹窗提示开启(true开启/false关闭)"]) { + SUser.SendNotiBox(message, 1); + } else { + SUser.SendNotiPacketMessage(message, 8); + } + } + function CUser_SendCmdErrorPacket(SUser, a, b) { + return Sq_CallFunc(S_Ptr("0x0867BF42"), "int", ["pointer", "int", "int"], SUser.C_Object, a, b); + } + function SendConsume(SUser,ItemSlot,num){ + local Pack = Packet(); + Pack.Put_Header(1, 84); + Pack.Put_Byte(1); + Pack.Put_Short(ItemSlot); + Pack.Put_Int(num); + Pack.Put_Short(2); + Pack.Finalize(true); + SUser.Send(Pack); + Pack.Delete(); + } +} \ No newline at end of file diff --git a/示例项目/多彩蜜蜡改跨界石/多彩蜜蜡改跨界石_Nangua.json b/示例项目/多彩蜜蜡改跨界石/多彩蜜蜡改跨界石_Nangua.json new file mode 100644 index 0000000..0fb63a8 --- /dev/null +++ b/示例项目/多彩蜜蜡改跨界石/多彩蜜蜡改跨界石_Nangua.json @@ -0,0 +1,15 @@ +{ + "请看这里": "此功能需要登录器支持或者dll支持,否则无法使用(多彩蜜蜡)", + "跨界成功提示": "已成功将 [%s] 跨界存放至账号仓库", + "跨界失败提示1": "账号仓库已满 无法进行跨界", + "跨界失败提示2": "跨界失败, [%s] 不可以跨界", + "跨界失败提示3": "跨界失败, 该装备类型无法跨界", + "跨界失败提示4": "跨界失败, 携带异界气息的装备无法进行跨界", + "跨界失败提示5": "跨界失败", + "不可跨界装备": [270981, 33333], + "不可跨界的装备类型":[10], + "装备类型(10武器 11称号 12上衣 13头肩 14下装 15鞋子 16腰带 17项链 18手镯 19戒指 20左槽 21右槽)":"", + "跨界石固定道具ID为2675422更改无效": "", + "233弹窗提示开启(true开启/false关闭)":true, + "提示":"开启弹窗提示需在群文件下载 <客户端消息框233.dll> 插件,否则会导致游戏崩溃" +} \ No newline at end of file diff --git a/示例项目/宠物附魔/Proj.ifo b/示例项目/宠物附魔/Proj.ifo new file mode 100644 index 0000000..25f51c9 --- /dev/null +++ b/示例项目/宠物附魔/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "宠物附魔", + "ProjectDescribe": "可以实现宠物附魔,需要登录器或dll支持!", + "ProjectAuthor": "七游云赞助", + "ProjectVersion": 1.0, + "ProjectConfig": "", + "ProjectFiles": [ + "宠物附魔.nut" + ], + "ProjectRunFunc": "_Dps_PetEnchantment_Main_" +} \ No newline at end of file diff --git a/示例项目/宠物附魔/宠物附魔.nut b/示例项目/宠物附魔/宠物附魔.nut new file mode 100644 index 0000000..9f80e76 --- /dev/null +++ b/示例项目/宠物附魔/宠物附魔.nut @@ -0,0 +1,84 @@ +function _Dps_UpdateCreatureEnchantMysql_(card_id, charac_no, uuid) { + local sql = "select card from `taiwan_cain_2nd`.`creature_items_enchant` where ui_id = " + uuid + ";"; + local column_type_list = ["int"]; + local SqlObj = MysqlPool.GetInstance().GetConnect(); + local result = SqlObj.Select(sql, column_type_list); + local UpdateSql = "update `taiwan_cain_2nd`.`creature_items_enchant` set `card` = " + card_id + " where `ui_id` = " + uuid + ";"; + if (result.len() <= 0 || result[0].len() <= 0) { + UpdateSql = "INSERT INTO `taiwan_cain_2nd`.`creature_items_enchant`(`ui_id`, `charac_no`, `card`) VALUES (" + uuid + "," + charac_no + "," + card_id + ");"; + } + SqlObj.Exec_Sql(UpdateSql); + MysqlPool.GetInstance().PutConnect(SqlObj); +} + +function _Dps_GetCreatureEnchantMysql_(charac_no) { + local sql = "select ui_id,card from `taiwan_cain_2nd`.`creature_items_enchant` where charac_no = " + charac_no + ";"; + local column_type_list = ["int", "int"]; + local SqlObj = MysqlPool.GetInstance().GetConnect(); + local result = SqlObj.Select(sql, column_type_list); + MysqlPool.GetInstance().PutConnect(SqlObj); + return result; +} + +function _Dps_PetEnchantment_Main_() { + //初始建表指令 + local CreateSql = "CREATE TABLE `taiwan_cain_2nd`.`creature_items_enchant` (`ui_id` int(11) NOT NULL DEFAULT '0',`charac_no` int(11) DEFAULT NULL,`card` int(11) NOT NULL DEFAULT '0',PRIMARY KEY (`ui_id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;"; + local SqlObj = MysqlPool.GetInstance().GetConnect(); + SqlObj.Exec_Sql(CreateSql); + MysqlPool.GetInstance().PutConnect(SqlObj); + //忽略宠物附魔错误 + Sq_WriteByteArr(S_Ptr("0x850dede"), array(2, 0x90)); + + Cb_ExpertOnEnchantByBead_Leave_Func["宠物附魔"] <- function(args) { + if (args[5] > 56) { + local SUser = User(args[1]); + local InvenObj = SUser.GetInven(); + local Bead_id = InvenObj.GetSlot(1, args[3]).GetIndex() + local PvfItemObj = PvfItem.GetPvfItemById(InvenObj.GetSlot(1, args[3]).GetIndex()) + local Card_Id = Sq_CallFunc(S_Ptr("0x0849F530"), "int", ["pointer"], PvfItemObj.C_Object); + local a7 = args[5] - 57; + local ItemObj = InvenObj.GetSlot(3, a7) + local Item_Id = ItemObj.GetIndex(); + NativePointer(ItemObj.C_Object).add(13).writeU32(Card_Id); + if (a7< 140) { + local uuid = NativePointer(ItemObj.C_Object).add(7).readInt(); + _Dps_UpdateCreatureEnchantMysql_(Card_Id, SUser.GetCID(), uuid); + } + local Pack = Packet(); + Pack.Put_Header(1, 275); + Pack.Put_Byte(1); + Pack.Finalize(true); + SUser.Send(Pack); + Pack.Delete(); + SUser.SendUpdateItemList(1, 7, a7); + InvenObj.DeleteItemCount(Bead_id, 1); + SUser.SendUpdateItemList(1, 0, args[3]); + return 0; + } + } + + Cb_reach_game_world_Func["宠物附魔"] <- function(SUser) { + local Data = _Dps_GetCreatureEnchantMysql_(SUser.GetCID()); + local DMap = {}; + foreach(data_obj in Data) { + local uuid = data_obj[0]; + local card_id = data_obj[1]; + DMap.rawset(uuid, card_id); + } + local InvenObj = SUser.GetInven(); + for (local i = 0; i< 141; i++) { + local equ; + if (i == 140) { + equ = InvenObj.GetSlot(0, 22); //获取当前格子的宠物 + } else { + equ = InvenObj.GetSlot(3, i); + } + local equ_uuid = NativePointer(equ.C_Object).add(7).readInt(); + if (DMap.rawin(equ_uuid)) { + NativePointer(equ.C_Object).add(13).writeU32(DMap[equ_uuid]); + if (i == 140) SUser.SendUpdateItemList(1, 0, 22); + else SUser.SendUpdateItemList(1, 7, i); + } + } + } +} diff --git a/示例项目/异界重置/Proj.ifo b/示例项目/异界重置/Proj.ifo new file mode 100644 index 0000000..eed75d8 --- /dev/null +++ b/示例项目/异界重置/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "异界重置", + "ProjectDescribe": "异界重置", + "ProjectAuthor": "南瓜", + "ProjectVersion": 1.1, + "ProjectConfig": "异界重置_Lenheart.json", + "ProjectFiles": [ + "异界重置.nut" + ], + "ProjectRunFunc": "_Dps_MapReset_Main_" +} \ No newline at end of file diff --git a/示例项目/异界重置/异界重置.nut b/示例项目/异界重置/异界重置.nut new file mode 100644 index 0000000..7b9d419 --- /dev/null +++ b/示例项目/异界重置/异界重置.nut @@ -0,0 +1,39 @@ +function ResetDimensionInout(SUser, index) { + local data_manager = Sq_CallFunc(S_Ptr("0x80CC19B"), "pointer"); + local dimensionInout = Sq_CallFunc(S_Ptr("0x0822b612"), "int", ["pointer", "int"], data_manager, index); + Sq_CallFunc(S_Ptr("0x0822f184"), "int", ["pointer", "int", "int"], SUser.C_Object, index, dimensionInout); +} +function ResetE2DgnFuncBynangua(SUser, ItemId) { + for (local i = 0; i <= 2; i++) { + ResetDimensionInout(SUser, i) + } + SUser.SendNotiPacketMessage("已重置E2异界地下城次数", 8); +} +function ResetE3DgnFuncBynangua(SUser, ItemId) { + for (local i = 3; i <= 5; i++) { + ResetDimensionInout(SUser, i) + } + SUser.SendNotiPacketMessage("已重置E3异界地下城次数", 8); +} +//加载入口 +function _Dps_MapReset_Main_() { + _Dps_MapReset_Logic_(); +} + +//重载入口 +function _Dps_MapReset_Main_Reload_(OldConfig) { + local Cofig = GlobalConfig.Get("异界重置_Lenheart.json"); + Cb_Use_Item_Sp_Func.rawdelete(OldConfig["重置E2异界地下城次数"].tointeger()); + Cb_Use_Item_Sp_Func.rawdelete(OldConfig["重置E3异界地下城次数"].tointeger()); + + //重新注册 + _Dps_MapReset_Logic_(); +} + +function _Dps_MapReset_Logic_() { + local Cofig = GlobalConfig.Get("异界重置_Lenheart.json"); + // E2异界重置券 + Cb_Use_Item_Sp_Func[Cofig["重置E2异界地下城次数"]] <- ResetE2DgnFuncBynangua; + // E3异界重置券 + Cb_Use_Item_Sp_Func[Cofig["重置E3异界地下城次数"]] <- ResetE3DgnFuncBynangua; +} diff --git a/示例项目/异界重置/异界重置_Lenheart.json b/示例项目/异界重置/异界重置_Lenheart.json new file mode 100644 index 0000000..d55ecc0 --- /dev/null +++ b/示例项目/异界重置/异界重置_Lenheart.json @@ -0,0 +1,4 @@ +{ + "重置E2异界地下城次数": 2021458805, + "重置E3异界地下城次数": 2021458806 +} \ No newline at end of file diff --git a/示例项目/强化增幅药剂/Proj.ifo b/示例项目/强化增幅药剂/Proj.ifo new file mode 100644 index 0000000..d26273b --- /dev/null +++ b/示例项目/强化增幅药剂/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "强化增幅药剂", + "ProjectDescribe": "使用指定药剂增加强化/增幅", + "ProjectAuthor": "Pluto", + "ProjectVersion": 1.1, + "ProjectConfig": "强化增幅药剂_Pluto.json", + "ProjectFiles": [ + "强化增幅药剂.nut" + ], + "ProjectRunFunc": "_Dps_EnhancementPotion_Main_" +} \ No newline at end of file diff --git a/示例项目/强化增幅药剂/强化增幅药剂.nut b/示例项目/强化增幅药剂/强化增幅药剂.nut new file mode 100644 index 0000000..18896c5 --- /dev/null +++ b/示例项目/强化增幅药剂/强化增幅药剂.nut @@ -0,0 +1,63 @@ +/* +文件名:强化药剂.nut +路径:强化药剂/强化药剂.nut +创建日期:2025-03-10 +文件用途:使用强化药剂增加强化成功几率 +*/ + +function _Dps_EnhancementPotion_Main_() { + // 注册强化进入回调 + Cb_WongWork_CItemUpgrade_Enter_Func["强化药剂_逻辑"] <- function(args) { + local Config = GlobalConfig.Get("强化增幅药剂_Pluto.json"); + local SUser = User(args[1]); + local invenItem = args[2]; + local upgradeInfo = args[3]; + + // 检查开关 + if (!Config["开关(true/false)"]) { + return args; + } + + // 检查用户是否拥有强化药剂 + local hasPotion = Sq_CallFunc( + S_Ptr("0x865E994"), + "int", + ["pointer", "int"], + SUser.C_Object, + Config["强化药剂ID"] + ); + + if (!hasPotion) { + return args; + } + + // 读取基础概率 + local probBase = NativePointer(args[0]).add(0x4EC).readU32(); + + // 获取当前强化等级和目标等级 + local currentLevel = Sq_CallFunc(S_Ptr("0x080F506C"), "int", ["pointer"], invenItem); + local targetLevel = currentLevel + 1; + + // 读取原始失败率 + local originalFailRate = NativePointer(upgradeInfo).add(32).readU32(); + + // 获取概率加成值 + local charac_no = SUser.GetCID().tostring(); + local boost = Config["默认增加概率"]; + + // 检查是否是指定角色,使用指定角色的概率 + if (Config["指定角色概率"].rawin(charac_no)) { + boost = Config["指定角色概率"][charac_no]; + } + + // 计算新的失败率 + if (boost > 0) { + local boostAmount = (probBase * boost / 100).tointeger(); + local newFailRate = originalFailRate - boostAmount; + if (newFailRate < 0) newFailRate = 0; + NativePointer(upgradeInfo).add(32).writeU32(newFailRate); + } + + return args; + } +} diff --git a/示例项目/强化增幅药剂/强化增幅药剂_Pluto.json b/示例项目/强化增幅药剂/强化增幅药剂_Pluto.json new file mode 100644 index 0000000..2dbeb13 --- /dev/null +++ b/示例项目/强化增幅药剂/强化增幅药剂_Pluto.json @@ -0,0 +1,11 @@ +{ + "开关(true/false)": true, + "强化药剂ID": 2600025, + "默认增加概率": 10, + "指定角色概率": { + "1": 100, + "2": 50 + }, + "概率说明":"概率值10代表增加10%的概率", + "指定角色说明":"角色ID可以查看数据库中 taiwan_cain → charac_info → charac_no代表角色ID" +} diff --git a/示例项目/强化增幅锻造药剂/Proj.ifo b/示例项目/强化增幅锻造药剂/Proj.ifo new file mode 100644 index 0000000..51cb131 --- /dev/null +++ b/示例项目/强化增幅锻造药剂/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "强化增幅锻造药剂", + "ProjectDescribe": "使用药剂或持有指定物品时增加强化/增幅/锻造概率", + "ProjectAuthor": "南瓜", + "ProjectVersion": 1.0, + "ProjectConfig": "强化增幅锻造药剂配置_nangua.json", + "ProjectFiles": [ + "强化增幅锻造药剂.nut" + ], + "ProjectRunFunc": "_Dps_Increase_probability_Main_" +} \ No newline at end of file diff --git a/示例项目/强化增幅锻造药剂/强化增幅锻造药剂.nut b/示例项目/强化增幅锻造药剂/强化增幅锻造药剂.nut new file mode 100644 index 0000000..8653551 --- /dev/null +++ b/示例项目/强化增幅锻造药剂/强化增幅锻造药剂.nut @@ -0,0 +1,64 @@ +// 强化时使用角色 +upgrade_user <- null; +// 强化时使用材料 +current_material <- null; +// 锻造时使用角色 +Separate_user <- null; +// 强化装备时 +function _Dps_Increase_probability_Main_() { + Cb_WongWork_CItemUpgrade_Enter_Func._Upgrade_Item <- function(args) { + upgrade_user = User(args[1]); + local upgrade_info_t = args[3]; + local Upgrade_rand = NativePointer(upgrade_info_t).add(32).readU32(); + // 如果材料是3242则代表增幅,如果是3171则代表强化 + current_material = NativePointer(upgrade_info_t).add(44).readU32(); + } + + // 锻造装备时 + Cb_WongWork_CItemUpgrade_Separate_Enter_Func.Separate_Upgrade <- function(args) { + Separate_user = User(args[1]); + local upgrade_info_t = args[3]; + local Upgrade_rand = NativePointer(upgrade_info_t).add(4).readU32(); + local Separate_rand = 10 * Upgrade_rand; + } + // 随机值 + Cb_CMTRand_randInt_Leave_Func._Upgrade_Item <- function(args) { + local address = Haker.NextReturnAddress; + local Config = GlobalConfig.Get("强化增幅锻造药剂配置_nangua.json"); + if(address == "0x8547768") { + local rand = args.pop(); + local charac_no = upgrade_user.GetCID().tostring(); + if (current_material == Config["强化药剂"]["强化材料(如你的强化材料是3171,则这里写3171)"] && Sq_CallFunc(S_Ptr("0x865E994"), "int", ["pointer", "int"], upgrade_user.C_Object, Config["强化药剂"]["药剂ID"]) && Config["强化药剂"]["开关(true开启/false关闭)"]) { + local prob = Config["强化药剂"]["增加概率值"]; + if (Config["强化药剂"]["指定角色概率值"].rawin(charac_no)) { + prob = Config["强化药剂"]["指定角色概率值"][charac_no]; + } + local increase = rand * prob; + local result = rand + increase; + if (result > 100000) result = 100000; + return result; + }else if (current_material == Config["增幅药剂"]["增幅材料(如你的增幅材料是3242,则这里写3242)"] && Sq_CallFunc(S_Ptr("0x865E994"), "int", ["pointer", "int"], upgrade_user.C_Object, Config["增幅药剂"]["药剂ID"]) && Config["增幅药剂"]["开关(true开启/false关闭)"]) { + local prob = Config["增幅药剂"]["增加概率值"]; + if (Config["增幅药剂"]["指定角色概率值"].rawin(charac_no)) { + prob = Config["增幅药剂"]["指定角色概率值"][charac_no]; + } + local increase = rand * prob; + local result = rand + increase; + if (result > 100000) result = 100000; + return result; + } + }else if(address == "0x811e506" && Config["锻造药剂"]["开关(true开启/false关闭)"]) { + local rand = args.pop(); + local charac_no = Separate_user.GetCID().tostring(); + if (Sq_CallFunc(S_Ptr("0x865E994"), "int", ["pointer", "int"], Separate_user.C_Object, Config["锻造药剂"]["药剂ID"])) { + local prob = Config["锻造药剂"]["增加概率值"]; + if (Config["锻造药剂"]["指定角色概率值"].rawin(charac_no)) { + prob = Config["锻造药剂"]["指定角色概率值"][charac_no]; + } + local result = rand / (1 + prob); + if (result < 1) result = 1; + return result; + } + } + } +} diff --git a/示例项目/强化增幅锻造药剂/强化增幅锻造药剂配置_nangua.json b/示例项目/强化增幅锻造药剂/强化增幅锻造药剂配置_nangua.json new file mode 100644 index 0000000..55f16dd --- /dev/null +++ b/示例项目/强化增幅锻造药剂/强化增幅锻造药剂配置_nangua.json @@ -0,0 +1,35 @@ +{ + "强化药剂": { + "开关(true开启/false关闭)":true, + "药剂ID":2600025, + "强化材料(如你的强化材料是3171,则这里写3171)":3171, + "增加概率值":0.1, + "指定角色概率值":{ + "1":0.1, + "2":0.1 + } + }, + "增幅药剂": { + "开关(true开启/false关闭)":true, + "药剂ID":2600025, + "增幅材料(如你的增幅材料是3242,则这里写3242)":3242, + "增加概率值":0.1, + "指定角色概率值":{ + "1":0.1, + "2":0.1 + } + }, + "锻造药剂": { + "开关(true开启/false关闭)":true, + "药剂ID":2600025, + "增加概率值":0.1, + "指定角色概率值":{ + "1":0.1, + "2":0.1 + } + }, + "概率说明1":"1概率值0.1代表10%的概率,10%并不是说提高10%的概率,因为强化时有一个随机数0-10万", + "概率说明2":"所以强化时如果随机值是1万,提高10%的概率就是10000*0.1=1000,最终随机数是11000(10000+1000)", + "概率说明3":"在强化时如成功率是20%,那么他的随机值就是20000,如果概率值大于20000就会强化成功,如果概率值小于20000就会失败", + "指定角色说明":"角色ID可以查看数据库中 taiwan_cain → charac_info → charac_no代表角色ID" +} \ No newline at end of file diff --git a/示例项目/强化相关概率提升/Proj.ifo b/示例项目/强化相关概率提升/Proj.ifo new file mode 100644 index 0000000..5bb6317 --- /dev/null +++ b/示例项目/强化相关概率提升/Proj.ifo @@ -0,0 +1,9 @@ +{ + "ProjectName": "强化相关概率提升", + "ProjectDescribe": "强化、增幅、锻造可以根据角色ID或者穿戴的物品ID提升概率可以叠加概率,指定角色优先级最高", + "ProjectAuthor": "小南瓜", + "ProjectVersion": 1.0, + "ProjectConfig": "强化相关概率配置.json", + "ProjectFiles": ["强化增幅锻造.nut"], + "ProjectRunFunc": "_Dps_Upgrade_Boost_Main_" +} \ No newline at end of file diff --git a/示例项目/强化相关概率提升/强化增幅锻造.nut b/示例项目/强化相关概率提升/强化增幅锻造.nut new file mode 100644 index 0000000..dec45bd --- /dev/null +++ b/示例项目/强化相关概率提升/强化增幅锻造.nut @@ -0,0 +1,134 @@ +function _Dps_Upgrade_Boost_Main_() { + _Dps_Upgrade_Boost_Logic_(); +} + +function _Dps_Upgrade_Boost_Main_Reload_(OldConfig) { + _Dps_Upgrade_Boost_Logic_(); +} + +function _Dps_Upgrade_Boost_Logic_() { + + Cb_WongWork_CItemUpgrade_Enter_Func._Upgrade_Boost <- function(args) { + local SUser = User(args[1]); + local characNo = SUser.GetCID(); + local invenItem = args[2]; + local upgradeInfo = args[3]; + local probBase = NativePointer(args[0]).add(0x4EC).readU32(); + + local currentLevel = Sq_CallFunc(S_Ptr("0x080F506C"), "int", ["pointer"], invenItem); + local targetLevel = currentLevel + 1; + + local originalFailRate = NativePointer(upgradeInfo).add(32).readU32(); + local isAmplify = _UpgradeBoostHelper.hasAmplifyAbility(invenItem); + local upgradeType = isAmplify ? "增幅" : "强化"; + + local boost = _UpgradeBoostHelper.getFinalBoost(SUser, targetLevel, upgradeType); + + if (boost > 0) { + local boostAmount = (probBase * boost / 100).tointeger(); + local newFailRate = originalFailRate - boostAmount; + if (newFailRate < 0) newFailRate = 0; + NativePointer(upgradeInfo).add(32).writeU32(newFailRate); + } + } + + Cb_WongWork_CItemUpgrade_Separate_Enter_Func._Separate_Boost <- function(args) { + local thisPtr = args[0]; + local SUser = User(args[1]); + local invenItem = args[2]; + local upgradeInfo = args[3]; + + local originalFailRate = NativePointer(upgradeInfo).add(4).readU32(); + local probBase = NativePointer(thisPtr).add(400).readU32(); + local currentLevel = NativePointer(invenItem).add(51).readU8(); + local targetLevel = currentLevel + 1; + + local boost = _UpgradeBoostHelper.getFinalBoost(SUser, targetLevel, "锻造"); + + if (boost > 0) { + local boostAmount = ((probBase / 10) * boost / 100).tointeger(); + local newFailRate = originalFailRate + boostAmount; + if (newFailRate > 10000) newFailRate = 10000; + NativePointer(upgradeInfo).add(4).writeU32(newFailRate); + } + } +} + +class _UpgradeBoostHelper { + + function hasAmplifyAbility(invenItem) { + return NativePointer(invenItem).add(17).readU8() != 0; + } + + function isInRange(level, rangeStr) { + local parts = split(rangeStr, "-"); + + if (parts.len() == 1) { + return level == parts[0].tointeger(); + } else if (parts.len() == 2) { + local min = parts[0].tointeger(); + local max = parts[1].tointeger(); + return level >= min && level <= max; + } + + return false; + } + + function getBoostFromConfig(config, level, upgradeType) { + if (!config.rawin(upgradeType)) return 0; + + local typeConfig = config[upgradeType]; + + if (typeof typeConfig == "array") { + foreach (rule in typeConfig) { + if (rule.rawin("目标等级") && rule.rawin("加成")) { + if (_UpgradeBoostHelper.isInRange(level, rule["目标等级"])) { + return rule["加成"]; + } + } + } + return 0; + } + + if (typeof typeConfig == "table") { + local levelKey = level.tostring(); + if (typeConfig.rawin(levelKey)) { + return typeConfig[levelKey]; + } + } + + return 0; + } + + function getFinalBoost(user, level, upgradeType) { + local Config = GlobalConfig.Get("强化相关概率配置.json"); + local characNo = user.GetCID(); + + // 1. 检查VIP + foreach (vip in Config["VIP配置"]) { + if (vip["角色ID"] == characNo) { + return _UpgradeBoostHelper.getBoostFromConfig(vip, level, upgradeType); + } + } + + // 2. 检查穿戴物品(叠加) + local InvenObj = user.GetInven(); + if (!InvenObj) return 0; + + local totalBoost = 0; + + foreach (itemCfg in Config["物品配置"]) { + local itemObj = InvenObj.GetSlot(Inven.INVENTORY_TYPE_BODY, itemCfg["槽位"]); + if (itemObj.IsEmpty) continue; + + local itemId = itemObj.GetIndex(); + + if (itemId == itemCfg["物品ID"]) { + local itemBoost = _UpgradeBoostHelper.getBoostFromConfig(itemCfg, level, upgradeType); + totalBoost += itemBoost; + } + } + + return totalBoost; + } +} diff --git a/示例项目/强化相关概率提升/强化相关概率配置.json b/示例项目/强化相关概率提升/强化相关概率配置.json new file mode 100644 index 0000000..6c317bd --- /dev/null +++ b/示例项目/强化相关概率提升/强化相关概率配置.json @@ -0,0 +1,71 @@ +{ + "VIP配置": [ + { + "角色ID": 1111111, + "备注": "GM测试号", + "强化": [ + {"目标等级": "1-4", "加成": 20}, + {"目标等级": "5-7", "加成": 15}, + {"目标等级": "8-10", "加成": 10}, + {"目标等级": "11-15", "加成": 5} + ], + "增幅": [ + {"目标等级": "1-4", "加成": 20}, + {"目标等级": "5-7", "加成": 15}, + {"目标等级": "8-10", "加成": 10}, + {"目标等级": "11-15", "加成": 5} + ], + "锻造": [ + {"目标等级": "1-3", "加成": 10}, + {"目标等级": "4-7", "加成": 5} + ] + } + ], + "物品配置": [ + { + "槽位": 11, + "物品ID": 600330013, + "备注": "称号", + "强化": [ + {"目标等级": "1-4", "加成": 20}, + {"目标等级": "5-10", "加成": 15}, + {"目标等级": "11-15", "加成": 10} + ], + "增幅": [ + {"目标等级": "1-4", "加成": 20}, + {"目标等级": "5-10", "加成": 15}, + {"目标等级": "11-15", "加成": 10} + ], + "锻造": [ + {"目标等级": "1-3", "加成": 10}, + {"目标等级": "4-7", "加成": 5} + ] + }, + { + "槽位": 22, + "物品ID": 63118, + "备注": "宠物", + "强化": [ + {"目标等级": "1-10", "加成": 15}, + {"目标等级": "11-15", "加成": 10} + ], + "增幅": [ + {"目标等级": "1-10", "加成": 15}, + {"目标等级": "11-15", "加成": 10} + ], + "锻造": [ + {"目标等级": "1-7", "加成": 8} + ] + } + ], + "配置说明": { + "目标等级说明": "目标等级5表示强化到+5(从+4强到+5)", + "范围说明": "5-10表示强化到+5~+10都享受此加成", + "单个数字": "也可以写单个数字如 10 表示只有+10享受加成", + "加成计算": "原概率+加成值,如原本10%加成20则变30%", + "优先级": "VIP配置优先,物品配置叠加", + "槽位": "身上穿的装备槽位(0-25)//0时装帽子, 1时装头发, 2时装脸部, 3时装上衣, 4时装下装, 5时装鞋子, 6时装护肩, 7时装腰带, 8时装皮肤, 9光环, 10武器, 11称号, 12上衣, 13头肩, 14下装, 15鞋子, 16腰带, 17项链, 18手镯, 19戒指, 20左槽, 21右槽, 22宠物, 23宠物装备红, 24宠物装备蓝, 25宠物装备绿", + "角色ID": "角色ID可以自行查看数据库中 库名taiwan_cain → 表名charac_info → 字段charac_no代表角色ID, 可以理解为角色注册顺序, 第一个注册的角色ID就是1" + } +} + diff --git a/示例项目/心悦播报/Proj.ifo b/示例项目/心悦播报/Proj.ifo new file mode 100644 index 0000000..635cc91 --- /dev/null +++ b/示例项目/心悦播报/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "心悦播报", + "ProjectDescribe": "登录时根据完成的心悦任务对应等级播报不同的提示", + "ProjectAuthor": "南瓜", + "ProjectVersion": 1.0, + "ProjectConfig": "心悦播报配置_Nangua.json", + "ProjectFiles": [ + "心悦播报.nut" + ], + "ProjectRunFunc": "_Dps_LoginByXY_Main_" +} \ No newline at end of file diff --git a/示例项目/心悦播报/心悦播报.nut b/示例项目/心悦播报/心悦播报.nut new file mode 100644 index 0000000..fb33de1 --- /dev/null +++ b/示例项目/心悦播报/心悦播报.nut @@ -0,0 +1,45 @@ +function _Dps_LoginByXY_Main_() { + Cb_reach_game_world_Func.LoginByXy <- function(SUser) { + local Config = GlobalConfig.Get("心悦播报配置_Nangua.json"); + local quest = SUser.GetQuest(); // 获取任务对象 + local WongWork_CQuestClear = NativePointer(quest).add(4); + local highestRank = 0; + local highestRankConfig = null; + foreach(taskConfig in Config["心悦播报配置"]["任务等级配置"]) { + local isCleared = _LoginBynangua.isClearedQuest(WongWork_CQuestClear.C_Object, taskConfig["任务ID"]); + if (isCleared && taskConfig["心悦等级"] > highestRank) { + highestRank = taskConfig["心悦等级"]; + highestRankConfig = taskConfig; + } + } + + local config = Config["心悦播报配置"]["心悦会员登录播报"]; + if (highestRank > 0 && config["开关(true为开启,false为关闭)"]) { + Timer.SetTimeOut(function(SUser, config, highestRankConfig) { + _LoginBynangua.VIP_LoginBynangua(SUser, config, highestRankConfig); + }, 1, SUser, config, highestRankConfig); + } + } +} + +//心悦会员登录播报 +class _LoginBynangua { + function VIP_LoginBynangua(SUser, config, rankConfig) { + local LoginMsgObj = AdMsg(); + LoginMsgObj.PutType(config["信息播报发送位置"]); + if (config["信息播报发送位置"] != 14) { + LoginMsgObj.PutString(" "); + } + LoginMsgObj.PutImoticon(rankConfig["图标"]); + LoginMsgObj.PutColorString(config["标题"], rankConfig["文本颜色"]); + LoginMsgObj.PutColorString("<" + rankConfig["心悦等级"] + ">", rankConfig["心悦等级颜色"]); + LoginMsgObj.PutColorString("[" + SUser.GetCharacName() + "]", rankConfig["角色名字颜色"]); + LoginMsgObj.PutColorString(config["结尾信息内容"], rankConfig["文本颜色"]); + LoginMsgObj.Finalize(); + World.SendAll(LoginMsgObj.MakePack()); + LoginMsgObj.Delete(); + } + function isClearedQuest(C_Object, questID) { + return Sq_CallFunc(S_Ptr("0x808BAE0"), "bool", ["pointer", "int"], C_Object, questID); + } +} \ No newline at end of file diff --git a/示例项目/心悦播报/心悦播报配置_Nangua.json b/示例项目/心悦播报/心悦播报配置_Nangua.json new file mode 100644 index 0000000..1366d91 --- /dev/null +++ b/示例项目/心悦播报/心悦播报配置_Nangua.json @@ -0,0 +1,36 @@ +{ + "心悦播报配置":{ + "心悦会员登录播报":{ + "开关(true为开启,false为关闭)":true, + "信息播报发送位置":14, + "标题":" 尊贵心悦", + "结尾信息内容":"已登录游戏" + }, + "任务等级配置": [ + { + "任务ID": 102, + "心悦等级": 1, + "心悦等级颜色":[0, 255, 128], + "图标":48, + "角色名字颜色":[255, 20, 0], + "文本颜色":[255, 120, 0] + }, + { + "任务ID": 103, + "心悦等级": 2, + "心悦等级颜色":[255, 0, 128], + "图标":49, + "角色名字颜色":[255, 20, 0], + "文本颜色":[255, 120, 0] + }, + { + "任务ID": 104, + "心悦等级": 3, + "心悦等级颜色":[255, 255, 0], + "图标":50, + "角色名字颜色":[255, 20, 0], + "文本颜色":[255, 120, 0] + } + ] + } +} \ No newline at end of file diff --git a/示例项目/怪物攻城/Proj.ifo b/示例项目/怪物攻城/Proj.ifo new file mode 100644 index 0000000..068d2d2 --- /dev/null +++ b/示例项目/怪物攻城/Proj.ifo @@ -0,0 +1,12 @@ +{ + "ProjectName": "怪物攻城", + "ProjectDescribe": "根据json的时间开启怪物攻城,可实时修改,没有阶段性没有惩罚,单纯杀怪给奖励", + "ProjectAuthor": "南瓜", + "ProjectVersion": 1.2, + "ProjectIcon": "http://su782j8qj.hd-bkt.clouddn.com/image/%E6%80%AA%E7%89%A9%E6%94%BB%E5%9F%8E%E5%9B%BE%E6%A0%87.jpg", + "ProjectConfig": "怪物攻城_南瓜.json", + "ProjectFiles": [ + "怪物攻城.nut" + ], + "ProjectRunFunc": "_Dps_VillageAttack_nangua_Main_" +} \ No newline at end of file diff --git a/示例项目/怪物攻城/怪物攻城.nut b/示例项目/怪物攻城/怪物攻城.nut new file mode 100644 index 0000000..29f4096 --- /dev/null +++ b/示例项目/怪物攻城/怪物攻城.nut @@ -0,0 +1,397 @@ +//活动开始阶段 +local VILLAGEATTACK_STATE_START = 0; +//活动结束阶段 +local VILLAGEATTACK_STATE_END = 3; + +//怪物攻城活动数据 +local villageAttackEventInfo = { + "state": VILLAGEATTACK_STATE_END, //活动当前状态 + "score": 0, //当前阶段频道内总PT + "start_time": 0, //活动开始时间 + "defend_success": 0, //怪物攻城活动防守成功 + "user_pt_info": {}, //角色个人pt数据 +} + +//重置活动数据 +function Reset_Villageattack_info() { + villageAttackEventInfo.state = VILLAGEATTACK_STATE_START; + villageAttackEventInfo.score = 0; + villageAttackEventInfo.user_pt_info = {}; + villageAttackEventInfo.start_time = time(); + villageAttackEventInfo.defend_success = 0; + print(">>>>>>>>>>>>>>活动数据已初始化成功<<<<<<<<<<<<<<") +} + +//怪物攻城活动计时器(每5秒触发一次) +function Event_Villageattack_Timer() { + if (villageAttackEventInfo.state == VILLAGEATTACK_STATE_END) + return; + //活动结束检测 + local remain_time = event_villageattack_get_remain_time(); + if (remain_time <= 0) { + //活动结束 + on_end_event_villageattack(); + return; + } +} + +//计算活动剩余时间 +function event_villageattack_get_remain_time() { + local Config = GlobalConfig.Get("怪物攻城_南瓜.json"); + local cur_time = time(); + local event_end_time = villageAttackEventInfo.start_time + Config["怪物攻城配置"]["怪物攻城总时长(秒)"]; + local remain_time = event_end_time - cur_time; + return remain_time; +} + +//更新怪物攻城当前进度(广播给频道内在线玩家) +function Gameworld_Update_Villageattack_Score() { + local Config = GlobalConfig.Get("怪物攻城_南瓜.json"); + //计算活动剩余时间 + local remain_time = event_villageattack_get_remain_time(); + if ((remain_time <= 0) || (villageAttackEventInfo.state == VILLAGEATTACK_STATE_END)) + return; + + local Pack = Packet(); + Pack.Put_Header(0, 247); //协议: ENUM_NOTIPACKET_UPDATE_VILLAGE_ATTACKED + Pack.Put_Int(remain_time); //活动剩余时间 + Pack.Put_Int(villageAttackEventInfo.score); //当前频道PT点数 + Pack.Put_Int(Config["怪物攻城配置"]["怪物攻城胜利所需pt点"]); //成功防守所需点数 + + Pack.Finalize(true); + World.SendAll(Pack); + Pack.Delete(); +} + +//开启怪物攻城活动 +function Start_Villageattack() { + local Config = GlobalConfig.Get("怪物攻城_南瓜.json"); + local a3 = Memory.alloc(100); + //活动剩余时间 + NativePointer(a3).add(10).writeInt(Config["怪物攻城配置"]["怪物攻城总时长(秒)"]); + //当前频道PT点数 + NativePointer(a3).add(14).writeInt(villageAttackEventInfo.score); + //成功防守所需点数 + NativePointer(a3).add(18).writeInt(Config["怪物攻城配置"]["怪物攻城胜利所需pt点"]); + //开启怪物攻城 + Sq_CallFunc(S_Ptr("0x84DF47A"), "pointer", ["pointer", "pointer", "pointer"], S_Ptr("0x0"), S_Ptr("0x0"), a3.C_Object); + //设置副本难度 + NativePointer(S_Ptr("0x085B9605")).writeInt(Config["怪物攻城配置"]["活动默认难度(0-4: 普通-英雄)"]); + //patch相关函数, 修复活动流程 + Hook_VillageAttack(); + print(">>>>>>>>>>>>>>怪物攻城已启动<<<<<<<<<<<<<<<<<<<<"); +} + +//结束怪物攻城活动(立即销毁攻城怪物, 不开启逆袭之谷, 不发送活动奖励) +function end_villageattack() { + local GlobalData_s_villageMonsterMgr = NativePointer("0x941F77C").readPointer(); + Sq_CallFunc(S_Ptr("0x086B43D4"), "pointer", ["pointer", "int"], GlobalData_s_villageMonsterMgr, 2); + print(">>>>>>>>>>>>>>怪物攻城已结束<<<<<<<<<<<<<<"); +} + +// 开始怪物攻城活动 +function On_Start_Event_VillageattackBynangua() { + // 重置活动数据 + Reset_Villageattack_info(); + // 注册5秒检查定时器 + Timer.SetCronTask(Event_Villageattack_Timer, { + Cron = "*/5 * * * * *", // 每5秒执行一次 + Name = "VillageAttackCheckTask" + }); + // 通知全服玩家活动开始 并刷新城镇怪物 + Start_Villageattack(); + Gameworld_Update_Villageattack_Score(); + + // 发送活动开始公告 + local Config = GlobalConfig.Get("怪物攻城_南瓜.json"); + World.SendNotiPacketMessage(Config["怪物攻城公告"]["开始"], Config["怪物攻城公告"]["发送位置"]); +} + +Cb_reach_game_world_Func.villageattackByNangua <- function(SUser) { + //怪物攻城活动更新进度 + if (villageAttackEventInfo.state != VILLAGEATTACK_STATE_END) { + //通知客户端打开活动UI + Notify_Villageattack_Score(SUser); + } +} + +//通知玩家怪物攻城进度 +function Notify_Villageattack_Score(SUser) { + local Config = GlobalConfig.Get("怪物攻城_南瓜.json"); + // 玩家当前PT点 + local charac_no = SUser.GetCID().tostring(); + local villageattack_pt = 0; + + if (charac_no in villageAttackEventInfo.user_pt_info) + villageattack_pt = villageAttackEventInfo.user_pt_info[charac_no][1]; + + // 计算活动剩余时间 + local remain_time = event_villageattack_get_remain_time(); + + if ((remain_time <= 0) || (villageAttackEventInfo.state == VILLAGEATTACK_STATE_END)) + return; + // 发包通知角色打开怪物攻城UI并更新当前进度 + local Pack = Packet(); + Pack.Put_Header(0, 248); + Pack.Put_Int(remain_time); // 活动剩余时间 + Pack.Put_Int(villageAttackEventInfo.score); // 当前频道PT点数 + Pack.Put_Int(Config["怪物攻城配置"]["怪物攻城胜利所需pt点"]); // 成功防守所需点数 + Pack.Put_Int(villageattack_pt); // 个人PT点数 + Pack.Finalize(true); + SUser.Send(Pack); + Pack.Delete(); +} + +//怪物攻城活动相关patch +function Hook_VillageAttack() { + Cb_CVillageMonster_SendVillageMonsterFightResult_Leave_Func.Bynangua <- function(args) { + local Config = GlobalConfig.Get("怪物攻城_南瓜.json"); + local village_monster = args[0]; + local SUser = User(args[1]); + local result = args[2]; + if (result) { + local bonus_pt = 1; + local PartyObj = SUser.GetParty(); + if (!PartyObj) return; + for (local i = 0; i < 4; ++i) { + local TUser = PartyObj.GetUser(i); + if (TUser) { + local charac_no = TUser.GetCID().tostring(); + if (!(charac_no in villageAttackEventInfo.user_pt_info)){ + villageAttackEventInfo.user_pt_info[charac_no] <- [TUser.GetUID(), 0]; + } + villageAttackEventInfo.user_pt_info[charac_no][1] += bonus_pt; + VillageAttackedRewardSendReward(TUser); + } + } + villageAttackEventInfo.score += bonus_pt; + Gameworld_Update_Villageattack_Score(); + if (villageAttackEventInfo.score >= Config["怪物攻城配置"]["怪物攻城胜利所需pt点"]) { + //怪物攻城活动防守成功, 立即结束活动 + villageAttackEventInfo.defend_success = 1; + Timer.SetTimeOut(on_end_event_villageattack, 15); + return; + } + //通知队伍中的所有玩家更新PT点数 + for (local i = 0; i < 4; ++i) { + local TUser = PartyObj.GetUser(i); + if (TUser) { + Notify_Villageattack_Score(TUser); + } + } + } + } +} + +//结束怪物攻城活动 +function on_end_event_villageattack() { + local Config = GlobalConfig.Get("怪物攻城_南瓜.json"); + if (villageAttackEventInfo.state == VILLAGEATTACK_STATE_END) + return; + // 设置活动状态 + villageAttackEventInfo.state = VILLAGEATTACK_STATE_END; + // 移除5秒检查定时器 + Timer.RemoveCronTask("VillageAttackCheckTask"); + // 立即结束怪物攻城活动 + end_villageattack(); + local OnlinePlayerList = World.GetOnlinePlayer(); + // 防守成功 + if (villageAttackEventInfo.defend_success == 1) { + // 频道内在线玩家发奖 + foreach(SUser in OnlinePlayerList) { + local RewardItems = []; + + // 从配置文件中获取固定奖励 + foreach(reward in Config["怪物攻城配置"]["怪物攻城防守成功固定奖励配置"]) { + RewardItems.append([reward[0], reward[1]]); + } + + // 发送邮件 + local title = "<怪物攻城活动>" + local Text = "恭喜勇士成功守护我们的家园!" + SUser.ReqDBSendMultiMail(title, Text, 0, RewardItems) + } + + // 榜一大哥 + local rank_first_charac_no = 0; + local rank_first_account_id = 0; + local max_pt = 0; + + // 论功行赏 + foreach(charac_no, data in villageAttackEventInfo.user_pt_info) { + // 发点券 + local account_id = villageAttackEventInfo.user_pt_info[charac_no][0]; + local pt = villageAttackEventInfo.user_pt_info[charac_no][1]; + local reward_cera = pt * Config["怪物攻城配置"]["怪物攻城其余玩家点券奖励"]; + // 点券奖励 = 个人PT * 10 + + local user_pr = World.GetUserByUid(account_id); + if(Config["怪物攻城配置"]["怪物攻城其余玩家点券奖励"] > 0){ + user_pr.RechargeCera(reward_cera); + user_pr.SendNotiPacketMessage("<怪物攻城活动> 防守成功, 获得pt点 * " + Config["怪物攻城配置"]["怪物攻城其余玩家点券奖励"] + " 的点券奖励", 14); + } + // 找出榜一大哥 + if (pt > max_pt) { + rank_first_charac_no = charac_no; + rank_first_account_id = account_id; + max_pt = pt; + } + } + + // 频道内公告活动已结束 + World.SendNotiPacketMessage(Config["怪物攻城公告"]["防守成功"], Config["怪物攻城公告"]["发送位置"]); + if (rank_first_charac_no) { + // 个人积分排行榜第一名 额外获得10倍点券奖励 + local user_pr = World.GetUserByUid(rank_first_account_id); + if(Config["怪物攻城配置"]["怪物攻城榜一大哥额外点券奖励"] > 0){ + user_pr.RechargeCera(max_pt * Config["怪物攻城配置"]["怪物攻城榜一大哥额外点券奖励"]); + user_pr.SendNotiPacketMessage("<怪物攻城活动> 排行榜第一名, 获得pt点 * " + Config["怪物攻城配置"]["怪物攻城榜一大哥额外点券奖励"] + " 的点券奖励", 14); + } + if(Config["怪物攻城配置"]["怪物攻城榜一大哥额外奖励道具(true开启/false关闭)"]){ + local reward_items = {}; + foreach(reward in Config["怪物攻城配置"]["怪物攻城榜一大哥额外奖励道具"]){ + reward_items[reward[0]] <- reward[1]; + } + Timer.SetTimeOut(function(){ + user_pr.SendMail(reward_items, { + Title = "<怪物攻城活动>", + Text = "恭喜获得排行榜第一名!" + }); + user_pr.SendNotiPacketMessage("<怪物攻城活动> 恭喜获得排行榜第一名,请前往邮箱查看奖励", 14); + }, 3); + } + // 频道内广播本轮活动排行榜第一名玩家名字 + local rank_first_charac_name = _VillageAttackBynangua.Get_charac_name_ByCID(rank_first_charac_no.tointeger()); + World.SendNotiPacketMessage("<怪物攻城活动> 恭喜勇士 【" + rank_first_charac_name + "】 成为个人积分排行榜第一名(" + max_pt + "pt)!", 14); + } + } else { + // 防守失败 + World.SendNotiPacketMessage(Config["怪物攻城公告"]["防守失败"], Config["怪物攻城公告"]["发送位置"]); + } + + // 释放空间 + villageAttackEventInfo.user_pt_info = {}; +} + +function VillageAttackedRewardSendReward(SUser) { + local villageAttack_ConfigBynangua = GlobalConfig.Get("怪物攻城_南瓜.json"); + local charac_no = SUser.GetCID().tostring(); + if ((charac_no in villageAttackEventInfo.user_pt_info)) { + local Count = villageAttackEventInfo.user_pt_info[charac_no][1]; + local Config = villageAttack_ConfigBynangua["怪物攻城配置"]["怪物攻城奖励配置"]["击杀次数"]; + + if (Config.rawin(Count.tostring())) { + local rewardConfig = Config[Count.tostring()]; + if (rewardConfig) { + local title = "<怪物攻城活动>" + local Text = "恭喜勇士成功守护我们的家园!" + local RewardItems = []; + RewardItems.append([rewardConfig[0], rewardConfig[1]]); + SUser.ReqDBSendMultiMail(title, Text, 0, RewardItems); + } + } else { + local title = "<怪物攻城活动>" + local Text = "恭喜勇士成功守护我们的家园!" + // 使用默认奖励配置 + local defaultRewards = villageAttack_ConfigBynangua["怪物攻城配置"]["不在配置表里的击杀次数默认奖励道具"]; + local RewardItems = []; + RewardItems.append([defaultRewards[0], defaultRewards[1]]); + SUser.ReqDBSendMultiMail(title, Text, 0, RewardItems); + } + } +} +class _VillageAttackBynangua{ + function Get_charac_name_ByCID(charac_no) { + local Config = GlobalConfig.Get("怪物攻城_南瓜.json"); + local PoolObj = MysqlPool.GetInstance(); + local Ip = Config["数据库IP 不是外置数据库不要更改"]; + local Port = Config["数据库端口 不懂不要更改"]; + local DbName = Config["数据库用户名 本地用户名不懂不要更改"]; + local Password = Config["数据库密码 本地密码不懂不要更改"]; + //设置数据库连接信息 + PoolObj.SetBaseConfiguration(Ip, Port, DbName, Password); + //连接池大小 + PoolObj.PoolSize = 10; + //初始化 + PoolObj.Init(); + local sql = "select charac_name from charac_info where charac_no=" + charac_no + ";"; + local column_type_list = ["string"]; + local SqlObj = MysqlPool.GetInstance().GetConnect(); + local result = SqlObj.Select(sql, column_type_list); + MysqlPool.GetInstance().PutConnect(SqlObj); + if (result.len() > 0) { + return result[0][0]; + } + return null; + } +} + +// 怪物攻城活动入口点 +function _Dps_VillageAttack_nangua_Main_() { + _Dps_VillageAttack_nangua_Logic_(); +} + +// 怪物攻城活动重载入口点 +function _Dps_VillageAttack_nangua_Main_Reload_(OldConfig) { + // 先移除旧的定时任务 + Timer.RemoveCronTask("VillageAttackTask"); + + // 重新注册 + _Dps_VillageAttack_nangua_Logic_(); +} + +// 怪物攻城活动逻辑入口点 +function _Dps_VillageAttack_nangua_Logic_() { + // 从配置中读取开启时间 + local Config = GlobalConfig.Get("怪物攻城_南瓜.json"); + local hour = Config["怪物攻城配置"]["开启时间[XX点(24小时制), XX分(0-59)]"][0]; + local minute = Config["怪物攻城配置"]["开启时间[XX点(24小时制), XX分(0-59)]"][1]; + + // cron 表达式 (秒 分 时 日 月 周) + local cronExpression = format("0 %d %d * * *", minute, hour); + + // 注册定时任务 + Timer.SetCronTask(On_Start_Event_VillageattackBynangua, { + Cron = cronExpression, + Name = "VillageAttackTask" + }); +} +//发送Item邮件 Pubilc +function User::SendItemMailtest(UID, ItemList, title, content) { + local SUser = World.GetUserByUid(UID); + local CID = SUser.GetCID(); + local sql = "select letter_id from taiwan_cain_2nd.postal order by letter_id DESC"; + local column_type_list = ["int"]; + + local SqlObj = MysqlPool.GetInstance().GetConnect(); + local result = SqlObj.Select(sql, column_type_list); + local sl = 1; + if (result.len() > 0) { + sl = result[0][0] + 1; + } + + local time = date(); + local timeStr = time["year"] + "-" + (time["month"] + 1) + "-" + time["day"] + " " + time["hour"] + ":" + time["min"] + ":" + time["sec"]; + + foreach(value in ItemList) { + local sql1 = "insert into taiwan_cain_2nd.postal (occ_time,send_charac_name,receive_charac_no,amplify_option,amplify_value,seperate_upgrade,seal_flag,item_id,add_info,upgrade,gold,letter_id,avata_flag,creature_flag) values ('" + timeStr + "','" + title + "'," + CID + ",0,0,0,0," + value.item + "," + value.num + ",0,0," + sl + ",'0','0')"; + SqlObj.Select(sql1, []); + } + + local sql2 = "insert into taiwan_cain_2nd.letter (letter_id,charac_no,send_charac_name,letter_text,reg_date,stat) values ('" + sl + "'," + CID + ",'" + title + "','" + content + "','" + timeStr + "','1')"; + SqlObj.Select(sql2, []); + + MysqlPool.GetInstance().PutConnect(SqlObj); + + if (SUser) { + local Pack = Packet(); + Pack.Put_Header(0, 9); + local MailBox = Sq_CallFunc(S_Ptr("0x0823020C"), "int", ["pointer"], SUser.C_Object); + Sq_CallFunc(S_Ptr("0x0823455A"), "int", ["int"], MailBox); + local Not_Count = Sq_CallFunc(S_Ptr("0x084ED330"), "int", ["int"], MailBox); + Pack.Put_Short(Not_Count); + Pack.Finalize(true); + SUser.Send(Pack); + Pack.Delete(); + } +} diff --git a/示例项目/怪物攻城/怪物攻城_南瓜.json b/示例项目/怪物攻城/怪物攻城_南瓜.json new file mode 100644 index 0000000..d611dd3 --- /dev/null +++ b/示例项目/怪物攻城/怪物攻城_南瓜.json @@ -0,0 +1,63 @@ +{ + "怪物攻城配置":{ + "如果攻城开启后没有怪物自行排查PVF是否有攻城副本NPK等":"", + "开关(true开启/false关闭)":true, + "开启时间[XX点(24小时制), XX分(0-59)]":[20, 30], + "怪物攻城胜利所需pt点":300, + "怪物攻城总时长(秒)":1800, + "活动默认难度(0-4: 普通-英雄)":3, + "怪物攻城奖励配置":{ + "击杀次数":{ + "1":[3037, 5], + "2":[3037, 5], + "3":[3037, 20], + "4":[1085, 5], + "5":[8, 5], + "6":[1085, 5], + "7":[8, 5], + "8":[15, 1], + "9":[15, 2], + "10":[15, 2], + "11":[15, 2], + "12":[15, 2], + "13":[15, 2], + "14":[15, 2], + "15":[15, 2], + "16":[15, 2], + "17":[15, 2], + "18":[15, 2], + "19":[15, 2], + "20":[15, 2], + "21":[15, 2], + "22":[15, 2], + "23":[15, 2], + "24":[15, 2], + "25":[15, 2], + "26":[15, 2], + "27":[15, 2], + "28":[15, 2], + "29":[15, 2], + "30":[15, 2] + } + }, + "不在配置表里的击杀次数默认奖励道具":[3037, 10], + "怪物攻城防守成功固定奖励配置":[[3037, 10], [3038, 10]], + "怪物攻城榜一大哥额外点券奖励":10, + "怪物攻城其余玩家点券奖励":10, + "怪物攻城榜一大哥额外奖励道具(true开启/false关闭)":false, + "怪物攻城榜一大哥额外奖励道具":[[3037,20], [3038,20]], + "提示1":"点券奖励不想发放,把点券后面的数值改为0就不会发放", + "提示2":"点券奖励的发放是由PT点乘以设置的点券,比如设置的是10,那么就是个人PT点*10" + }, + "怪物攻城公告":{ + "开始":"怪物攻城活动已开始,请各位玩家前往挑战守护我们的家园!", + "防守成功":"怪物攻城活动防守成功,感谢各位玩家的努力,奖励已发放!", + "防守失败":"怪物攻城活动防守失败,请各位玩家继续努力!", + "发送位置":14, + ">>>发送位置(0系统, 1私聊, 2组队, 3普通, 6公会, 8师徒, 14喇叭, 37系统)":"" + }, + "数据库IP 不是外置数据库不要更改": "127.0.0.1", + "数据库端口 不懂不要更改": 3306, + "数据库用户名 本地用户名不懂不要更改": "game", + "数据库密码 本地密码不懂不要更改": "uu5!^%jg" +} \ No newline at end of file diff --git a/示例项目/战力榜/Proj.ifo b/示例项目/战力榜/Proj.ifo new file mode 100644 index 0000000..4effa8f --- /dev/null +++ b/示例项目/战力榜/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "战力榜", + "ProjectDescribe": "首先根据使用的登录器在配置文件中选取对应的登录器。PVF相关不提供自行研究,暂不支持RS启动器", + "ProjectAuthor": "南瓜", + "ProjectVersion": 1.4, + "ProjectConfig": "战力榜配置_南瓜.json", + "ProjectFiles": [ + "战力榜.nut" + ], + "ProjectRunFunc": "_Dps_Rank_nangua_Main_" +} \ No newline at end of file diff --git a/示例项目/战力榜/战力榜.nut b/示例项目/战力榜/战力榜.nut new file mode 100644 index 0000000..84582ab --- /dev/null +++ b/示例项目/战力榜/战力榜.nut @@ -0,0 +1,326 @@ +// 排行榜前三名数组 默认数据 +local Ranklist = { + "1": { + "rank": 100, + "characname": "帝尊天穹", + "job": 0, + "lev": 60, + "Grow": 4, + "Guilkey": 0, + "GuildName": "诸神之荣丶", + "str": "", + "equip": [0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0] + }, + "2": { + "rank": 90, + "characname": "圣皇极境", + "job": 1, + "lev": 60, + "Grow": 4, + "Guilkey": 0, + "GuildName": "诸神之荣丶", + "str": "", + "equip": [0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0] + }, + "3": { + "rank": 80, + "characname": "神王之巅", + "job": 2, + "lev": 60, + "Grow": 4, + "Guilkey": 0, + "GuildName": "诸神之荣丶", + "str": "", + "equip": [0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0] + } +}; + +/** + * 获得rank分(排名分数) + * 根据配置选择不同的数据库获取战力值 + * @param {string} charac_no + * @returns 返回对应的战力值 + */ +function GetRankNumberBynangua(charac_no) { + local Config = GlobalConfig.Get("战力榜配置_南瓜.json"); + local currentDB = Config["战力榜配置"]["当前选择"]; + local dbConfig = Config["战力榜配置"]["数据库选择"][currentDB]; + local SqlObj = MysqlPool.GetInstance().GetConnect(); + // 构建查询语句 - 直接使用角色ID查询 + local insertQuery = "SELECT " + dbConfig["字段名"] + " FROM " + dbConfig["数据库名"] + "." + dbConfig["表名"] + " WHERE " + dbConfig["条件字段"] + "=" + charac_no; + local column_type_list = ["string"]; + local result = SqlObj.Select(insertQuery, column_type_list); + MysqlPool.GetInstance().PutConnect(SqlObj); + if (result.len() > 0) { + return result[0][0].tointeger(); + } + return 0; +} + +//获取个人信息 +function GetMyEquInfo(SUser) { + local MyRanklist = { + "rank": 0, + "characname": "", + "job": 0, + "lev": 0, + "Grow": 0, + "Guilkey": 0, + "GuildName": "", + "str": "", + "equip": [] + }; + local Config = GlobalConfig.Get("战力榜配置_南瓜.json"); + // 检查CID是否在排除列表中 + if ("角色CID" in Config["战力榜配置"]["排除名单"]) { + foreach(excludedCID in Config["战力榜配置"]["排除名单"]["角色CID"]) { + if (SUser.GetCID() == excludedCID) { + return MyRanklist; + } + } + } + MyRanklist.rank = GetRankNumberBynangua(SUser.GetCID()); + MyRanklist.characname = SUser.GetCharacName(); + MyRanklist.job = SUser.GetCharacJob(); + MyRanklist.lev = SUser.GetCharacLevel(); + MyRanklist.Grow = Sq_CallFunc(S_Ptr("0x815741C"), "int", ["pointer"], SUser.C_Object); + MyRanklist.Guilkey = Sq_CallFunc(S_Ptr("0x822F46C"), "int", ["pointer"], SUser.C_Object); + MyRanklist.GuildName = SUser.GetGuildName(); + if (MyRanklist.GuildName.len() == 0) { + MyRanklist.GuildName = "天地会"; //当公会不存在时,设置默认公会名字 + } + local InvenObj = SUser.GetInven(); + if (!InvenObj) { + return; + } + if (InvenObj) { + for (local i = 0; i <= 10; i++) { + if (i != 9) { + local clearAvatar = Sq_CallFunc(S_Ptr("0x0850D374"), "int", ["pointer", "int"], InvenObj.C_Object, i); + local ItemObj = InvenObj.GetSlot(0, i); + local item_id = clearAvatar > 0 ? clearAvatar : ItemObj.GetIndex(); + MyRanklist.equip.push(item_id); + } else { + MyRanklist.equip.push(-1); + } + } + } + return MyRanklist; +} + +// 玩家下线时,保存自身信息并且和排行榜进行排名 +function SetRankingBynangua(SUser) { + local IP = Rank_nangua.api_CUser_get_public_ip_address(SUser); + if (IP == "10.0.0.1") { + return; + } + local MyRanklist = GetMyEquInfo(SUser); + local existingIndex = -1; + foreach(index, rankInfo in Ranklist) { + if (rankInfo.characname == MyRanklist.characname) { + existingIndex = index; + break; + } + } + + if (MyRanklist.rank > 0) { + if (existingIndex != -1) { + Ranklist[existingIndex] = MyRanklist; + } else { + if (!Ranklist.rawin("4")) { + Ranklist["4"] <- {}; + } + Ranklist["4"] = MyRanklist; + } + + local rankArray = []; + foreach(key, value in Ranklist) { + rankArray.append(value); + } + + rankArray.sort(function(a, b) { + return b.rank - a.rank; + }); + + // 获取前三名玩家的信息 + local topThree = rankArray.slice(0, 3); + if (Ranklist.rawin("4")) { + Ranklist.rawdelete("4"); + } + Ranklist = { + "1": topThree[0], + "2": topThree[1], + "3": topThree[2] + }; + } +} + +//清除排名的自定义留言信息 +function DeleteSpecificMessage() { + local G_CGameManager = Sq_CallFunc(S_Ptr("0x080cc18e"), "pointer", []); + local Message = Sq_CallFunc(S_Ptr("0x08298EEC"), "pointer", ["pointer"], G_CGameManager); + Sq_CallFunc(S_Ptr("0x08600D0C"), "char", ["pointer", "int"], Message, 1); + Sq_CallFunc(S_Ptr("0x08600D0C"), "char", ["pointer", "int"], Message, 2); + Sq_CallFunc(S_Ptr("0x08600D0C"), "char", ["pointer", "int"], Message, 3); +} + +//发送留言信息 +function UpdateServerMessageByBroadcast(SUser, Ranking, msg) { + DeleteSpecificMessage() + local Pack = Packet(); + Pack.Put_Header(0, 192); + Pack.Put_Byte(1); + Pack.Put_Byte(1); + Pack.Put_Byte(Ranking); + Rank_nangua.api_InterfacePacketBuf_put_string(Pack, msg); + Pack.Finalize(true); + SUser.Send(Pack); + Pack.Delete(); +} + +function SendRanklistPacket(SUser, Ranklist, bool) { + local Pack = Packet(); + Pack.Put_Header(0, 182); + + local rankArray = [Ranklist["1"], Ranklist["2"], Ranklist["3"]]; + + Pack.Put_Byte(rankArray.len()); + foreach(index, value in rankArray) { + Rank_nangua.api_InterfacePacketBuf_put_string(Pack, value.characname); + Pack.Put_Byte(value.lev); + Pack.Put_Byte(value.job); + Pack.Put_Byte(value.Grow); + Rank_nangua.api_InterfacePacketBuf_put_string(Pack, value.GuildName + ""); + Pack.Put_Int(value.Guilkey); + + for (local i = 0; i < value.equip.len(); i++) { + Pack.Put_Int((i != 9) ? value.equip[i] : -1); + } + } + Pack.Finalize(true); + + if (bool) { + World.SendAll(Pack); + } else { + SUser.Send(Pack); + } + + Pack.Delete(); +} + +//因客户端原因,只有名称有变动或排名有变动时才会刷新外观,所以发送一次加空格的名字,然后删除空格再发送一次 +function SendRankLists(SUser, bool) { + local ip = Rank_nangua.api_CUser_get_public_ip_address(SUser); + if (ip == "10.0.0.1") { + return; + } + local Config = GlobalConfig.Get("战力榜配置_南瓜.json"); + foreach(key, value in Ranklist) { + value["characname"] = value["characname"] + " "; + } + SendRanklistPacket(SUser, Ranklist, bool); + + foreach(key, value in Ranklist) { + value["characname"] = value["characname"].slice(0, value["characname"].find(" ")) + value["characname"].slice(value["characname"].find(" ") + 1); + } + //发送排名留言信息 + UpdateServerMessageByBroadcast(SUser, 1, Config["战力榜配置"]["留言信息"]["第一名"]); + UpdateServerMessageByBroadcast(SUser, 2, Config["战力榜配置"]["留言信息"]["第二名"]); + UpdateServerMessageByBroadcast(SUser, 3, Config["战力榜配置"]["留言信息"]["第三名"]); + SendRanklistPacket(SUser, Ranklist, bool); +} + +class Rank_nangua { + function api_InterfacePacketBuf_put_string(Pack, s) { + local p = Memory.allocUtf8String(s); + local len = s.len(); + Pack.Put_Int(len); + Pack.Put_BinaryEx(p.C_Object, len); + } + function api_CUser_get_public_ip_address(SUser){ + local s_addr = Sq_CallFunc(S_Ptr("0x084EC90A"), "int", ["pointer"], SUser.C_Object); + if(s_addr){ + local inet_ntoa = Sq_CallFunc(S_Ptr("0x0807DDC0"), "pointer", ["int"], s_addr); + return NativePointer(inet_ntoa).readUtf8String(); + } + return null; + } +} + +// 创建数据库和表 +function createRankEventTable() { + local CreateSql1 = "create database if not exists DP_S default charset utf8;"; + local CreateSql2 = "CREATE TABLE IF NOT EXISTS DP_S.event (" + + "event_id varchar(30) NOT NULL, " + + "event_info mediumtext, " + + "PRIMARY KEY (event_id)" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8;"; + local SqlObj = MysqlPool.GetInstance().GetConnect(); + SqlObj.Exec_Sql(CreateSql1); + SqlObj.Exec_Sql(CreateSql2); + MysqlPool.GetInstance().PutConnect(SqlObj); +} + +// 保存战力榜数据到数据库 +function saveRanklistToDatabase() { + local SqlObj = MysqlPool.GetInstance().GetConnect(); + local ranklistJson = Json.Encode(Ranklist); + local query = "REPLACE INTO DP_S.event (event_id, event_info) VALUES ('ranklist', '" + ranklistJson + "')"; + SqlObj.Select(query, []); + MysqlPool.GetInstance().PutConnect(SqlObj); +} + +// 从数据库加载战力榜数据 +function loadRanklistFromDatabase() { + local SqlObj = MysqlPool.GetInstance().GetConnect(); + local query = "SELECT event_info FROM DP_S.event WHERE event_id = 'ranklist'"; + local result = SqlObj.Select(query, ["string"]); + if (result.len() > 0) { + local savedRanklist = Json.Decode(result[0][0]); + if (savedRanklist != null) { + Ranklist = savedRanklist; + } + } + MysqlPool.GetInstance().PutConnect(SqlObj); +} + +// 服务器被Kill时(不含炸频道等)保存战力榜数据 +Cb_Server_ClossByKill_Leave_Func.ServerClossByKill <- function(args) { + local result = args.pop(); + if (result <= 0) { + saveRanklistToDatabase(); + } +} +// 服务器异常崩溃时保存战力榜数据 +Cb_Server_Close_Enter_Func.Server_Close <- function(args) { + saveRanklistToDatabase(); +} + +function _Dps_Rank_nangua_Main_() { + local Config = GlobalConfig.Get("战力榜配置_南瓜.json"); + local PoolObj = MysqlPool.GetInstance(); + local Ip = Config["数据库IP 不是外置数据库不要更改"]; + local Port = Config["数据库端口 不懂不要更改"]; + local DbName = Config["数据库用户名 本地用户名不懂不要更改"]; + local Password = Config["数据库密码 本地密码不懂不要更改"]; + PoolObj.SetBaseConfiguration(Ip, Port, DbName, Password); + PoolObj.PoolSize = 10; + PoolObj.Init(); + // 创建表 + createRankEventTable(); + // 加载战力榜数据 + loadRanklistFromDatabase(); + // 玩家下线时 + Cb_CUser_LogoutToPCRoom_Enter_Func.ReturnSelectCharac <- function(args) { + local SUser = User(args[0]); + SetRankingBynangua(SUser); + SendRankLists(SUser, false); + }; + + //玩家登录时 + Cb_reach_game_world_Func.RankByNangua <- function(SUser) { + Timer.SetTimeOut(function(SUser) { + SendRankLists(SUser, true); + }, 3, SUser); + }; +} \ No newline at end of file diff --git a/示例项目/战力榜/战力榜配置_南瓜.json b/示例项目/战力榜/战力榜配置_南瓜.json new file mode 100644 index 0000000..316a61e --- /dev/null +++ b/示例项目/战力榜/战力榜配置_南瓜.json @@ -0,0 +1,80 @@ +{ + "战力榜配置": { + "当前选择": "长虹", + "数据库选择": { + "入梦": { + "数据库名": "mengyiqu", + "表名": "m_ranks", + "字段名": "m_rank", + "条件字段": "m_cid" + }, + "苍穹": { + "数据库名": "d_starsky", + "表名": "zhanli", + "字段名": "ZLZ", + "条件字段": "CID" + }, + "万国": { + "数据库名": "d_login", + "表名": "zhanli", + "字段名": "ZLZ", + "条件字段": "CID" + }, + "天子": { + "数据库名": "taiwan_cain", + "表名": "charac_info", + "字段名": "zhandouli", + "条件字段": "charac_no" + }, + "晴空": { + "数据库名": "qk", + "表名": "fightingcapacity", + "字段名": "price", + "条件字段": "CID" + }, + "黑爵": { + "数据库名": "huazhi_v5", + "表名": "zhanli", + "字段名": "ZLZ", + "条件字段": "CID" + }, + "暴雨": { + "数据库名": "d_baoyu", + "表名": "zhanli", + "字段名": "ZLZ", + "条件字段": "CID" + }, + "花枝": { + "数据库名": "huazhi", + "表名": "zhanli", + "字段名": "ZLZ", + "条件字段": "CID" + }, + "神话": { + "数据库名": "asion_login", + "表名": "myzhanli", + "字段名": "zlz", + "条件字段": "zcid" + }, + "长虹": { + "数据库名": "d_changhong", + "表名": "zhanli", + "字段名": "ZLZ", + "条件字段": "CID" + } + }, + "留言信息": { + "第一名": "普天之下,唯我独尊!", + "第二名": "四方之内,唯我独霸!", + "第三名": "世间万物,唯我独步!" + }, + "排除名单":{ + "角色CID":[111111, 222222] + }, + "提示":"角色CID可以查看数据库中 taiwan_cain → charac_info → charac_no代表角色ID" + }, + "数据库IP 不是外置数据库不要更改": "127.0.0.1", + "数据库端口 不懂不要更改": 3306, + "数据库用户名 本地用户名不懂不要更改": "game", + "数据库密码 本地密码不懂不要更改": "uu5!^%jg" +} \ No newline at end of file diff --git a/示例项目/技能拓展14键/Proj.ifo b/示例项目/技能拓展14键/Proj.ifo new file mode 100644 index 0000000..306b7a2 --- /dev/null +++ b/示例项目/技能拓展14键/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "技能拓展14键", + "ProjectDescribe": "14键位技能的服务端修复程序,需要客户端已经加载了14键技能的插件。", + "ProjectAuthor": "倾泪寒", + "ProjectVersion": 1.0, + "ProjectConfig": "", + "ProjectFiles": [ + "技能拓展14键.nut" + ], + "ProjectRunFunc": "_Dps_SkillExpansion_14Keys_Main_" +} \ No newline at end of file diff --git a/示例项目/技能拓展14键/技能拓展14键.nut b/示例项目/技能拓展14键/技能拓展14键.nut new file mode 100644 index 0000000..87b42b2 --- /dev/null +++ b/示例项目/技能拓展14键/技能拓展14键.nut @@ -0,0 +1,7 @@ + + + +function _Dps_SkillExpansion_14Keys_Main_() +{ + GameManager.Fix14Skill(); +} diff --git a/示例项目/抗魔值进入副本/Proj.ifo b/示例项目/抗魔值进入副本/Proj.ifo new file mode 100644 index 0000000..42d365c --- /dev/null +++ b/示例项目/抗魔值进入副本/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "抗魔值进入副本", + "ProjectDescribe": "指定的副本按难度及抗魔值才能进入副本", + "ProjectAuthor": "南瓜 & 倾泪寒", + "ProjectVersion": 1.0, + "ProjectConfig": "抗魔值进入副本配置_Nangua.json", + "ProjectFiles": [ + "抗魔值进入副本.nut" + ], + "ProjectRunFunc": "_Dps_anti_evilDgnBynangua_Main_" +} \ No newline at end of file diff --git a/示例项目/抗魔值进入副本/抗魔值进入副本.nut b/示例项目/抗魔值进入副本/抗魔值进入副本.nut new file mode 100644 index 0000000..201c4eb --- /dev/null +++ b/示例项目/抗魔值进入副本/抗魔值进入副本.nut @@ -0,0 +1,123 @@ +class _evilDgnBynangua { + function get_required_gradeBynangua(dgn_id, dgn_diff) { + local Config = GlobalConfig.Get("抗魔值进入副本配置_Nangua.json"); + if (Config["抗魔值进入副本"]["副本配置"].rawin(dgn_id.tostring())) { + local requirements = Config["抗魔值进入副本"]["副本配置"][dgn_id.tostring()]; + for (local i = 0; i < requirements.len(); i += 2) { + if (requirements[i] == dgn_diff) { + return requirements[i + 1]; + } + } + } + return null; + } + function api_CDungeon_getDungeonName(dungeon_id) { + local G_CDataManager = Sq_CallFunc(S_Ptr("0x80CC19B"), "pointer", []); + local cdungeon = Sq_CallFunc(S_Ptr("0x835F9F8"), "pointer", ["pointer", "int"], G_CDataManager, dungeon_id); + if (!cdungeon) { + return; + } + if (cdungeon) { + local dgnobj = Sq_CallFunc(S_Ptr("0x81455A6"), "pointer", ["pointer"], cdungeon); + local name = NativePointer(dgnobj).readUtf8String(); + return name; + } + return null; + } + function join(array, delimiter) { + local result = ""; + for (local i = 0; i < array.len(); ++i) { + if (i > 0) { + result += delimiter; + } + result += array[i]; + } + return result; + } +} + +getroottable()._Nangua_EquipmentData_List_ <- {}; +getroottable()._Nangua_EquipmentData_Anti_ <- {}; +function _Nangua_GetEquipmentData_Anti_(Equipment_id) { + try { + //如果数据存在 返回数据 + if (getroottable()._Nangua_EquipmentData_Anti_.rawin(Equipment_id))return getroottable()._Nangua_EquipmentData_Anti_[Equipment_id]; + //通过装备ID获取装备路径 + local Path = getroottable()._Nangua_EquipmentData_List_[Equipment_id]; + Path = "equipment/" + Path; + //读取装备数据 + local Data = ScriptData.GetFileData(Path, function(DataTable, Data) { + while (!Data.Eof()) { + local Key = Data.Get(); + if (Key == "[anti evil]") { + getroottable()._Nangua_EquipmentData_Anti_[Equipment_id] <- Data.Get(); + return getroottable()._Nangua_EquipmentData_Anti_[Equipment_id]; + } + } + }); + } catch (exception){ + + } + return 0; +} + +function _Dps_anti_evilDgnBynangua_Main_() { + local Config = GlobalConfig.Get("抗魔值进入副本配置_Nangua.json"); + Script(Config["PVF储存路径_没有更改的话不要改动此配置"]); + //if (Config["PVF储存路径_没有更改的话不要改动此配置"].len() > 0) Script(Config["PVF储存路径_没有更改的话不要改动此配置"]); + //else Script(); + + //读取装备列表 + getroottable()._Nangua_EquipmentData_List_ = ScriptData.GetFileData("equipment/equipment.lst", function(DataTable, Data) { + while (!Data.Eof()) { + local Key = Data.Get(); + //注册装备列表 路径写入 数据未读取 + DataTable.rawset(Key, Data.Get()); + } + }); + + Cb_SelectDungeon_Check_Error_Leave_Func.Check_anti_evilByNangua <- function(args) { + local Config = GlobalConfig.Get("抗魔值进入副本配置_Nangua.json"); + local SUser = User(args[1]); + local msg_base = args[2]; + local PartyObj = SUser.GetParty(); + local dgn_id = NativePointer(msg_base).add(13).readU16(); // 获取副本ID + local dgn_diff = NativePointer(msg_base).add(15).readU8(); // 获取副本难度 + local required_grade = _evilDgnBynangua.get_required_gradeBynangua(dgn_id, dgn_diff); + local diff_name = Config["副本难度命名"][(dgn_diff).tostring()]; + local Dungeon_Name = _evilDgnBynangua.api_CDungeon_getDungeonName(dgn_id); + + if (PartyObj && Config["抗魔值进入副本"]["抗魔值进入副本开关(true为开启,false为关闭)"]) { + local not_enough_users = []; + for (local i = 0; i < 4; ++i) { + local Tuser = PartyObj.GetUser(i); + if (Tuser) { + local total_grade = 0; + local InvenObj = Tuser.GetInven(); + if (InvenObj) { + for (local j = 0; j <= 25; j++) { + local ItemObj = InvenObj.GetSlot(0, j); + if (!ItemObj.IsEmpty) { + local item_id = ItemObj.GetIndex(); + print(item_id); + local Anti = _Nangua_GetEquipmentData_Anti_(item_id); + total_grade += Anti; + } + } + } + // 比对抗魔值是否足够 + if (total_grade < required_grade) { + not_enough_users.push(Tuser.GetCharacName()); + } + } + } + + // 处理未达到抗魔值要求的用户 + if (not_enough_users.len() > 0) { + local joinedNames = _evilDgnBynangua.join(not_enough_users, ", "); + SUser.SendNotiBox("队伍中玩家[" + joinedNames + "] 抗魔值低于 " + required_grade + " ,无法进入 [" + Dungeon_Name + " - " + diff_name + "]", 2) + return 1; // 禁止进入副本 + } + } + } +} diff --git a/示例项目/抗魔值进入副本/抗魔值进入副本配置_Nangua.json b/示例项目/抗魔值进入副本/抗魔值进入副本配置_Nangua.json new file mode 100644 index 0000000..d0ff8fa --- /dev/null +++ b/示例项目/抗魔值进入副本/抗魔值进入副本配置_Nangua.json @@ -0,0 +1,23 @@ +{ + "抗魔值进入副本":{ + "提示":"抗魔值进入副本配置(副本ID[普通级,所需抗魔值,冒险级,所需抗魔值,勇士级,所需抗魔值,王者级,所需抗魔值,地狱级,所需抗魔值])", + "抗魔值进入副本开关(true为开启,false为关闭)":false, + "副本配置":{ + "1":[0, 100, 1, 200, 2, 300, 3, 400, 4, 500, 5, 600], + "2":[0, 100, 1, 200, 2, 300, 3, 400, 4, 500, 5, 600], + "3":[0, 100, 1, 200, 2, 300, 3, 400, 4, 500, 5, 600], + "4":[0, 100, 1, 200, 2, 300, 3, 400, 4, 500, 5, 600], + "5":[0, 100, 1, 200, 2, 300, 3, 400, 4, 500, 5, 600], + "6":[0, 100, 1, 200, 2, 300, 3, 400, 4, 500, 5, 600], + "7":[0, 100, 1, 200, 2, 300, 3, 400, 4, 500, 5, 600] + } + }, + "副本难度命名" : { + "0": "普通级", + "1": "冒险级", + "2": "勇士级", + "3": "王者级", + "4": "地狱级" + }, + "PVF储存路径_没有更改的话不要改动此配置":"/home/neople/game/Script.pvf" +} \ No newline at end of file diff --git a/示例项目/拓展瞬间移动药剂/Proj.ifo b/示例项目/拓展瞬间移动药剂/Proj.ifo new file mode 100644 index 0000000..0808363 --- /dev/null +++ b/示例项目/拓展瞬间移动药剂/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "拓展瞬间移动药剂", + "ProjectDescribe": "开启后可参考原PVF2600014瞬间移动药剂,复制出不同的移动药剂ID,可无限拓展", + "ProjectAuthor": "倾泪寒", + "ProjectVersion": 1.0, + "ProjectConfig": "", + "ProjectFiles": [ + "拓展瞬间移动药剂.nut" + ], + "ProjectRunFunc": "_Dps_expand_teleport_item_" +} diff --git a/示例项目/拓展瞬间移动药剂/拓展瞬间移动药剂.nut b/示例项目/拓展瞬间移动药剂/拓展瞬间移动药剂.nut new file mode 100644 index 0000000..673c524 --- /dev/null +++ b/示例项目/拓展瞬间移动药剂/拓展瞬间移动药剂.nut @@ -0,0 +1,4 @@ +function _Dps_expand_teleport_item_() { + NativePointer("0x81D063A").writeS8(0xEB); + NativePointer("0x81D0651").writeS8(0xEB); +} \ No newline at end of file diff --git a/示例项目/指定用户强化必定成功/Proj.ifo b/示例项目/指定用户强化必定成功/Proj.ifo new file mode 100644 index 0000000..b9a8ce7 --- /dev/null +++ b/示例项目/指定用户强化必定成功/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "指定用户强化必定成功", + "ProjectDescribe": "指定UID用户强化必定成功", + "ProjectAuthor": "Pluto", + "ProjectVersion": 1.2, + "ProjectConfig": "指定用户强化必定成功_Pluto.json", + "ProjectFiles": [ + "指定用户强化必定成功.nut" + ], + "ProjectRunFunc": "_Dps_VipUpgrade_Main_" +} \ No newline at end of file diff --git a/示例项目/指定用户强化必定成功/指定用户强化必定成功.nut b/示例项目/指定用户强化必定成功/指定用户强化必定成功.nut new file mode 100644 index 0000000..d07f172 --- /dev/null +++ b/示例项目/指定用户强化必定成功/指定用户强化必定成功.nut @@ -0,0 +1,52 @@ +// ------------------------------------------------------------ +// 指定用户强化必定成功 by Pluto +// ------------------------------------------------------------ + +// 核心逻辑函数 +function _Dps_VipUpgrade_Main_() { + + // 原生 C 函数指针:让装备强化等级 +1 + local Inven_Item_IncUpgrade_Ptr = S_Ptr("0x0854B4BE"); + + // 封装调用函数 + function IncUpgrade(item) { + return Sq_CallFunc(Inven_Item_IncUpgrade_Ptr, "int", ["pointer"], item); + } + + // VIP 强化必成功回调 + Cb_WongWork_CItemUpgrade_Leave_Func.VipUpgrade <- function(args) { + + local Config = GlobalConfig.Get("指定用户强化必定成功_Pluto.json"); + local vip_user_uid = Config["指定用户的UID"]; + local vip_user_cid = Config["指定用户的CID"]; + + print("=== 强化回调触发 ==="); + + // 原函数返回值(不破坏 args) + local OldRet = args[args.len() - 1]; + + // args[1] 是玩家对象,args[2] 是装备对象 + local SUser = User(args[1]); + local item = args[2]; + local uid = SUser.GetUID(); + local cid = SUser.GetCID(); + + // 仅对 VIP UID 生效 + if (vip_user_uid.find(uid) != null || vip_user_cid.find(cid) != null) { + if (OldRet == 0) { + print("VIP 玩家原强化失败,强制成功: " + SUser.GetCharacName()); + + local newLvl = IncUpgrade(item); + print("强化后等级: " + newLvl); + + return 1; // 强制返回成功 + } else { + print("VIP 玩家原强化成功,不修改: " + SUser.GetCharacName()); + } + } + + // 默认返回原结果 + //return OldRet; + } +} + diff --git a/示例项目/指定用户强化必定成功/指定用户强化必定成功_Pluto.json b/示例项目/指定用户强化必定成功/指定用户强化必定成功_Pluto.json new file mode 100644 index 0000000..ffe454a --- /dev/null +++ b/示例项目/指定用户强化必定成功/指定用户强化必定成功_Pluto.json @@ -0,0 +1,4 @@ +{ + "指定用户的UID":[18000001,18000003], + "指定用户的CID":[1,2] +} \ No newline at end of file diff --git a/示例项目/整点在线奖励/Proj.ifo b/示例项目/整点在线奖励/Proj.ifo new file mode 100644 index 0000000..001a0e8 --- /dev/null +++ b/示例项目/整点在线奖励/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "整点在线奖励", + "ProjectDescribe": "可以设置某一个时间点 全服发放奖励", + "ProjectAuthor": "南瓜", + "ProjectVersion": 1.3, + "ProjectConfig": "整点在线奖励_Lenheart.json", + "ProjectFiles": [ + "整点在线奖励.nut" + ], + "ProjectRunFunc": "_Dps_TimeReward_Main_" +} \ No newline at end of file diff --git a/示例项目/整点在线奖励/整点在线奖励.nut b/示例项目/整点在线奖励/整点在线奖励.nut new file mode 100644 index 0000000..b901b2d --- /dev/null +++ b/示例项目/整点在线奖励/整点在线奖励.nut @@ -0,0 +1,103 @@ +// 在线奖励活动入口点 +function _Dps_TimeReward_Main_() { + _Dps_TimeReward_Logic_(); +} + +// 格式化时间字符串 +function normalize_time_format(time_str) { + local colonPos = time_str.find(":"); + if (colonPos == null) return null; + + local hour = time_str.slice(0, colonPos).tointeger(); + local minute = time_str.slice(colonPos + 1).tointeger(); + + return format("%02d:%02d", hour, minute); +} + +// 在线奖励活动重载入口点 +function _Dps_TimeReward_Main_Reload_(OldConfig) { + // 移除旧配置中的所有定时任务 + if (OldConfig && "奖励时间和内容" in OldConfig) { + foreach(time, reward in OldConfig["奖励时间和内容"]) { + local task_id = "TimeRewardTask_" + normalize_time_format(time); + Timer.RemoveCronTask(task_id); + } + } + + // 重新注册 + _Dps_TimeReward_Logic_(); +} + +// 在线奖励活动逻辑入口点 +function _Dps_TimeReward_Logic_() { + local Config = GlobalConfig.Get("整点在线奖励_Lenheart.json"); + + if(!Config["是否启用在线奖励(true启用/false不启用)"]) { + return; + } + + // 遍历配置的所有奖励时间 + foreach(time, reward in Config["奖励时间和内容"]) { + local hour = time.slice(0, time.find(":")).tointeger(); + local minute = time.slice(time.find(":") + 1).tointeger(); + + // 为每个时间点创建独立的回调函数 + local callback = function(originalTime = time) { + SendTimeReward(originalTime); + } + + // 为每个时间点注册一个定时任务 + local cronExpression = format("0 %d %d * * *", minute, hour); + Timer.SetCronTask(callback, { + Cron = cronExpression, + Name = "TimeRewardTask_" + normalize_time_format(time) + }); + } +} + +// 发放在线奖励 +function SendTimeReward(rewardTime) { + local Config = GlobalConfig.Get("整点在线奖励_Lenheart.json"); + local OnlinePlayerList = World.GetOnlinePlayer(); + + // 直接使用原始时间格式查找奖励 + local reward = Config["奖励时间和内容"][rewardTime]; + if (!reward) return; + + // 发放奖励 + foreach(SUser in OnlinePlayerList) { + // 检查是否为假人 + local IP = _Dps_TimeReward_api.api_CUser_get_public_ip_address(SUser); + if (!Config["是否发放奖励给假人(true发放/false不发放)"] && IP == Config["离线假人IP"]) { + continue; + } + + // 准备邮件奖励物品列表 + local RewardItems = []; + foreach(itemR in reward) { + RewardItems.append([itemR[0], itemR[1]]); + } + + // 发送邮件(显示时使用规范化的时间格式) + local title = Config["邮件标题"]; + local Text = format(Config["邮件内容"], normalize_time_format(rewardTime)); + SUser.ReqDBSendMultiMail(title, Text, 0, RewardItems); + } + + // 发送全服公告(显示时使用规范化的时间格式) + World.SendNotiPacketMessage(format(Config["公告"], normalize_time_format(rewardTime)), 14); +} + +// API类 +class _Dps_TimeReward_api { + // 获取玩家IP地址 + function api_CUser_get_public_ip_address(SUser){ + local s_addr = Sq_CallFunc(S_Ptr("0x084EC90A"), "int", ["pointer"], SUser.C_Object); + if(s_addr){ + local inet_ntoa = Sq_CallFunc(S_Ptr("0x0807DDC0"), "pointer", ["int"], s_addr); + return NativePointer(inet_ntoa).readUtf8String(); + } + return null; + } +} + diff --git a/示例项目/整点在线奖励/整点在线奖励_Lenheart.json b/示例项目/整点在线奖励/整点在线奖励_Lenheart.json new file mode 100644 index 0000000..8fccbe6 --- /dev/null +++ b/示例项目/整点在线奖励/整点在线奖励_Lenheart.json @@ -0,0 +1,19 @@ +{ + "是否启用在线奖励(true启用/false不启用)":true, + "是否发放奖励给假人(true发放/false不发放)":false, + "离线假人IP":"10.0.0.1", + "奖励时间和内容":{ + "12:00":[ + [3037,10], + [3038,11] + ], + "19:30":[ + [3037,10], + [3038,10] + ] + }, + "邮件标题":"每日在线福利", + "邮件内容":"亲爱的玩家,这是[%s]的在线奖励,请查收!", + "公告":"今日 [%s] 的在线奖励已发放,请通过邮件查收", + "时间格式(24小时制)":"上午8点整则为08:00,晚上19点30分则为19:30,也可以是8:0来表示,也可08:0表示都是允许的" +} \ No newline at end of file diff --git a/示例项目/时装与宠物清除卷/Proj.ifo b/示例项目/时装与宠物清除卷/Proj.ifo new file mode 100644 index 0000000..4118ae0 --- /dev/null +++ b/示例项目/时装与宠物清除卷/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "时装与宠物清除卷", + "ProjectDescribe": "通过指定ID的道具,将装备背包第前两行的时装或宠物清除。", + "ProjectAuthor": "倾泪寒", + "ProjectVersion": 1.3, + "ProjectConfig": "时装与宠物清除卷_Lenheart.json", + "ProjectFiles": [ + "时装与宠物清除卷.nut" + ], + "ProjectRunFunc": "_Dps_FashionAndPetClearanceRoll_Main_" +} \ No newline at end of file diff --git a/示例项目/时装与宠物清除卷/时装与宠物清除卷.nut b/示例项目/时装与宠物清除卷/时装与宠物清除卷.nut new file mode 100644 index 0000000..ce7f187 --- /dev/null +++ b/示例项目/时装与宠物清除卷/时装与宠物清除卷.nut @@ -0,0 +1,75 @@ +/* +文件名:时装与宠物清除卷.nut +路径:OfficialProject/时装与宠物清除卷/时装与宠物清除卷.nut +创建日期:2025-04-01 21:42 +文件用途: +*/ + +function _Dps_FashionAndPetClearanceRoll_Logic_() { + local Config = GlobalConfig.Get("时装与宠物清除卷_Lenheart.json"); + //宠物删除 + Cb_Use_Item_Sp_Func[Config["宠物清除卷ID"]] <- function(SUser, ItemId) { + if (Config["宠物清除券是否返还"]) SUser.GiveItem(ItemId, 1); + local Cid = SUser.GetCID(); + local InvenObj = SUser.GetInven(); + if (InvenObj) { + for (local i = 0; i <= 13; i++) { + local ItemObj = InvenObj.GetSlot(3, i); + local Flag = false; + if (ItemObj) { + deleteCreature(InvenObj, ItemObj); + ItemObj.Delete(); + } + } + local Sql = "delete from taiwan_cain_2nd.creature_items where charac_no=" + Cid + " and slot < 13 ;"; + local SqlObj = MysqlPool.GetInstance().GetConnect(); + SqlObj.Exec_Sql(Sql); + //把连接还池子 + MysqlPool.GetInstance().PutConnect(SqlObj); + SUser.SendItemSpace(7); + SUser.SendNotiPacketMessage(Config["宠物清除完成提示"], 8); + } + } + + //时装删除 + Cb_Use_Item_Sp_Func[Config["时装清除卷ID"]] <- function(SUser, ItemId) { + if (Config["时装清除券是否返还"]) SUser.GiveItem(ItemId, 1); + local Cid = SUser.GetCID(); + local InvenObj = SUser.GetInven(); + if (InvenObj) { + for (local i = 0; i <= 13; i++) { + local ItemObj = InvenObj.GetSlot(2, i); + if (ItemObj) { + ItemObj.Delete(); + } + } + local Sql = "delete from taiwan_cain_2nd.user_items where charac_no=" + Cid + " and slot >= 10 and slot <= 23"; + local SqlObj = MysqlPool.GetInstance().GetConnect(); + SqlObj.Exec_Sql(Sql); + //把连接还池子 + MysqlPool.GetInstance().PutConnect(SqlObj); + SUser.SendItemSpace(1); + SUser.SendNotiPacketMessage(Config["时装清除完成提示"], 8); + } + } +} + +function deleteCreature(InvenObj, ItemObj) { + local creatureMgrPtr = Sq_CallFunc(S_Ptr("0x080dd568"), "pointer", ["pointer"], InvenObj.C_Object); + if (!creatureMgrPtr)return; + local creature_UID = NativePointer(ItemObj.C_Object).add(7).readU32(); + if(creature_UID == 0) return; + Sq_CallFunc(S_Ptr("0x0833A854"), "int", ["pointer", "int"], creatureMgrPtr, creature_UID); +} + +function _Dps_FashionAndPetClearanceRoll_Main_() { + _Dps_FashionAndPetClearanceRoll_Logic_(); +} + +function _Dps_FashionAndPetClearanceRoll_Main_Reload_(OldConfig) { + Cb_Use_Item_Sp_Func.rawdelete(OldConfig["宠物清除卷ID"]); + Cb_Use_Item_Sp_Func.rawdelete(OldConfig["时装清除卷ID"]); + + _Dps_FashionAndPetClearanceRoll_Logic_(); +} +//Timer.SetTimeOut(_Dps_FashionAndPetClearanceRoll_Main_,1) \ No newline at end of file diff --git a/示例项目/时装与宠物清除卷/时装与宠物清除卷_Lenheart.json b/示例项目/时装与宠物清除卷/时装与宠物清除卷_Lenheart.json new file mode 100644 index 0000000..5a98e6e --- /dev/null +++ b/示例项目/时装与宠物清除卷/时装与宠物清除卷_Lenheart.json @@ -0,0 +1,8 @@ +{ + "时装清除卷ID": 2021458808, + "宠物清除卷ID": 2021458807, + "时装清除完成提示": "清除前两行时装成功", + "宠物清除完成提示": "清除前两行宠物成功", + "时装清除券是否返还": true, + "宠物清除券是否返还": true +} \ No newline at end of file diff --git a/示例项目/时装潜能/Proj.ifo b/示例项目/时装潜能/Proj.ifo new file mode 100644 index 0000000..ad9ab88 --- /dev/null +++ b/示例项目/时装潜能/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "时装潜能", + "ProjectDescribe": "默认开启时装潜能以及随机分配潜能属性", + "ProjectAuthor": "南瓜", + "ProjectVersion": 1.1, + "ProjectConfig": "", + "ProjectFiles": [ + "时装潜能.nut" + ], + "ProjectRunFunc": "_Dps_Avatar_hidden_option_Main_" +} \ No newline at end of file diff --git a/示例项目/时装潜能/时装潜能.nut b/示例项目/时装潜能/时装潜能.nut new file mode 100644 index 0000000..f26e49f --- /dev/null +++ b/示例项目/时装潜能/时装潜能.nut @@ -0,0 +1,9 @@ +function _Dps_Avatar_hidden_option_Main_() { + NativePointer(S_Ptr("0x08509D49")).writeByteArray([0xEB]); + Cb_Inventory_AddAvatarItem_Enter_Func.hidden_option <- function(args) { + NativePointer(S_Ptr("0x08509D34")).writeUShort(MathClass.Rand(1, 64)); + } + Cb_Item_IsHiddenOption_Leave_Func.hidden_option <- function(args) { + return 1; + } +} \ No newline at end of file diff --git a/示例项目/是否允许创建缔造者/Proj.ifo b/示例项目/是否允许创建缔造者/Proj.ifo new file mode 100644 index 0000000..6667346 --- /dev/null +++ b/示例项目/是否允许创建缔造者/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "是否允许创建缔造者", + "ProjectDescribe": "是否允许创建缔造者.", + "ProjectAuthor": "倾泪寒", + "ProjectVersion": 1.0, + "ProjectConfig": "是否允许创建缔造者_Lenheart.json", + "ProjectFiles": [ + "是否允许创建缔造者.nut" + ], + "ProjectRunFunc": "_Dps_EnableTheCreationOfCreators_Main_" +} \ No newline at end of file diff --git a/示例项目/是否允许创建缔造者/是否允许创建缔造者.nut b/示例项目/是否允许创建缔造者/是否允许创建缔造者.nut new file mode 100644 index 0000000..e668dab --- /dev/null +++ b/示例项目/是否允许创建缔造者/是否允许创建缔造者.nut @@ -0,0 +1,16 @@ +function _Dps_EnableTheCreationOfCreators_Logic_() +{ + local Config = GlobalConfig.Get("是否允许创建缔造者_Lenheart.json"); + Sq_WriteByteArr(S_Ptr("0x81C029F"), Config["是否允许创建"] ? [0xF] : [0xA]); +} + + +function _Dps_EnableTheCreationOfCreators_Main_() +{ + GameManager.OpenCreateJob_CreatorMage(); +} + +function _Dps_EnableTheCreationOfCreators_Main_Reload_(OldConfig) +{ + GameManager.OpenCreateJob_CreatorMage(); +} \ No newline at end of file diff --git a/示例项目/是否允许创建缔造者/是否允许创建缔造者_Lenheart.json b/示例项目/是否允许创建缔造者/是否允许创建缔造者_Lenheart.json new file mode 100644 index 0000000..7999137 --- /dev/null +++ b/示例项目/是否允许创建缔造者/是否允许创建缔造者_Lenheart.json @@ -0,0 +1,3 @@ +{ + "是否允许创建": true +} \ No newline at end of file diff --git a/示例项目/更加优雅的跨界石/Proj.ifo b/示例项目/更加优雅的跨界石/Proj.ifo new file mode 100644 index 0000000..a787d14 --- /dev/null +++ b/示例项目/更加优雅的跨界石/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "更加优雅的跨界石", + "ProjectDescribe": "通过指定ID的宝珠道具,跨界你的装备(和附魔一样)。", + "ProjectAuthor": "Pluto", + "ProjectVersion": 1.0, + "ProjectConfig": "更加优雅的跨界石_Pluto.json", + "ProjectFiles": [ + "更加优雅的跨界石.nut" + ], + "ProjectRunFunc": "_Dps_Crossover_Main_" +} \ No newline at end of file diff --git a/示例项目/更加优雅的跨界石/更加优雅的跨界石.nut b/示例项目/更加优雅的跨界石/更加优雅的跨界石.nut new file mode 100644 index 0000000..cd6f482 --- /dev/null +++ b/示例项目/更加优雅的跨界石/更加优雅的跨界石.nut @@ -0,0 +1,76 @@ +/* +文件名:更加优雅的跨界石.nut +路径:MyProject/更加优雅的跨界石/更加优雅的跨界石.nut +创建日期:2026-03-23 +文件用途:使用附魔宝珠指针进行跨界 +作者:Pluto +*/ + +function _Dps_Crossover_Main_() { + + Cb_ExpertOnEnchantByBead_Enter_Func.Crossover <- function (args) { + local cfg = GlobalConfig.Get("更加优雅的跨界石_Pluto.json"); + local SUser = User(args[1]); + local beadSlot = args[3]; + local equipSlot = args[5]; + local inven = SUser.GetInven(); + local bead = inven.GetSlot(1,beadSlot); + local bead_id = bead.GetIndex(); + local bead_add_info = bead.GetAdd_Info(); + + if(bead_id == cfg["功能配置"]["跨界石ID"]) + { + Sq_WriteByteArr(S_Ptr("0x0849ED23"), Haker.AsmGenerateMcd( + "add esp, 0x6C", + "pop ebx", + "pop esi", + "pop edi", + "pop ebp", + "xor eax, eax", //返回值 0 + "ret" + )); + local equ = inven.GetSlot(1,equipSlot); + local equ_id = equ.GetIndex(); + //print("[Enchant Hook] 玩家正在使用道具 ID: " + bead_id + " 装备 ID: " + equ_id + " 宝珠数量: " + bead_add_info); + //禁止跨界物品的id + local forbiddenItemIds = [27601, 27602]; + //获取账号金库对象 + local CargoObj = SUser.GetAccountCargo(); + //获取账号金库中的一个空格子 + local EmptySlot = CargoObj.GetEmptySlot(); + //禁止跨界 + if (cfg["功能配置"]["禁止跨界装备ID"].find(equ_id) != null) { + SUser.SendNotiBox("该装备无法跨界!", 1); + return; + } + //账号金库已满 + if(EmptySlot == -1) { + SUser.SendNotiBox("账号金库已满!", 1); + return; + } + //跨界 + local Flag = CargoObj.InsertItem(equ, EmptySlot); + if(Flag == -1) { + SUser.SendNotiBox("跨界失败!", 1); + return; + }else{ + //销毁装备栏中的道具 + equ.Delete(); + //宝珠数量减一 + if(bead_add_info == 1) { + bead.Delete(); + }else{ + bead.SetAdd_Info(bead_add_info - 1); + bead.Flush(); + } + //刷新玩家装备列表 + SUser.SendUpdateItemList(1, 0, equipSlot); + //刷新玩家道具列表 + SUser.SendUpdateItemList(1, 0, beadSlot); + //刷新账号金库列表 + CargoObj.SendItemList(); + SUser.SendNotiBox("跨界成功!", 1); + } + } + } +} \ No newline at end of file diff --git a/示例项目/更加优雅的跨界石/更加优雅的跨界石_Pluto.json b/示例项目/更加优雅的跨界石/更加优雅的跨界石_Pluto.json new file mode 100644 index 0000000..921244c --- /dev/null +++ b/示例项目/更加优雅的跨界石/更加优雅的跨界石_Pluto.json @@ -0,0 +1,9 @@ +{ + "功能配置": { + "跨界石ID": 12600294, + "禁止跨界装备ID": [27601, 27602] + }, + "提示1":"跨界石类型为宝珠", + "提示2":"请自行修改跨界石宝珠图标、描述", + "提示3":"注意宝珠可以附魔的装备类型可以直接影响可跨界的装备类型" +} \ No newline at end of file diff --git a/示例项目/服务器防入侵/Proj.ifo b/示例项目/服务器防入侵/Proj.ifo new file mode 100644 index 0000000..7c6751e --- /dev/null +++ b/示例项目/服务器防入侵/Proj.ifo @@ -0,0 +1,12 @@ +{ + "ProjectName": "服务器防入侵", + "ProjectDescribe": "本项目会持续更新,因特殊原因逻辑不会公开", + "ProjectAuthor": "倾泪寒", + "ProjectVersion": 1.0, + "ProjectIcon": "http://su782j8qj.hd-bkt.clouddn.com/image/%E9%BB%91%E5%AE%A2.png", + "ProjectConfig": "", + "ProjectFiles": [ + "服务器防入侵.sut" + ], + "ProjectRunFunc": "_Dps_ServerIntrusionPrevention_Main_" +} \ No newline at end of file diff --git a/示例项目/服务器防入侵/服务器防入侵.sut b/示例项目/服务器防入侵/服务器防入侵.sut new file mode 100644 index 0000000000000000000000000000000000000000..ab682463e7f16c6f1504f1b4e0a0035612a9cc3d GIT binary patch literal 232 zcmVyFykl=#D?<@!=@OI>~^ zYZVa&1s(tn3q&z592q`679Jfb7bgZ069)%G4jnWa0~`zw5e)$aGbaEC9Wz5^Zxv!i iK~hv|03HD)2LuQP02V+k1rjI#1q~P_3IIS%T6hT) 10) { + Sq_CallFunc(S_Ptr("0x8556B68"), "int", ["pointer", "pointer", "int", "int", "int", "pointer", "int", "int", "int", "int"], + args[0], args[1], 10, args[3], args[4], args[5], args[6], args[7], args[8], args[9]); + local left = num - 10; + local pack = Memory.alloc(0x1000); + for(local i = 0; i < left; i++){ + Memory.copy(pack.add(61 * i), NativePointer(args[1]).add(61 * (i + 10)), 61); + } + Sq_CallFunc(S_Ptr("0x8556B68"), "int", ["pointer", "pointer", "int", "int", "int", "pointer", "int", "int", "int", "int"], + args[0], pack.C_Object, left, args[3], args[4], args[5], args[6], args[7], args[8], args[9]); + } + } + } + + // 设置最大等级 + if(config["杂项功能开关"]["设置最高等级"][0]){ + local level = config["杂项功能开关"]["设置最高等级"][1]; + GameManager.SetGameMaxLevel(level); + } + + // 装备解锁时间 + if(config["杂项功能开关"]["装备解锁(单位:秒)"][0]){ + local time = config["杂项功能开关"]["装备解锁(单位:秒)"][1]; + GameManager.SetItemLockTime(time); + } + + // 创建缔造者 + if(config["杂项功能开关"]["创建缔造者"]){ + GameManager.OpenCreateJob_CreatorMage(); + } + + // 自动解除魔法封印 + if(config["杂项功能开关"]["自动解除魔法封印"]){ + GameManager.OpenRandomAutomaticUnblocking(); + } + + // 开启装备与时装镶嵌 + if(config["杂项功能开关"]["开启装备与时装镶嵌"]){ + GameManager.FixEquipUseJewel(); + } + + // 修复下线卡城镇 + if(config["杂项功能开关"]["修复下线卡城镇"]){ + GameManager.FixSaveTown(); + } + + // 修复绝望之塔金币异常 + if(config["杂项功能开关"]["修复绝望之塔金币异常"]){ + GameManager.FixDespairGold(); + } + + // 绝望之塔通关后可以用门票继续进入 + if(config["杂项功能开关"]["绝望之塔通关后可以用门票继续进入"]){ + Sq_WriteByteArr(S_Ptr("0x0864416F"), [0xEB, 0x47]); + } + + // 修复绝望之塔每10层进不去副本 + Cb_TowerOfDespairMgr_SendAPCInfo_Enter_Func._TowerOfDespair <- function(args) { + local config = GlobalConfig.Get("杂项功能开关_Nangua.json"); + if(config["杂项功能开关"]["修复绝望之塔每10层进不去副本"]){ + local this_ptr = args[0]; + local tod_layer = args[1]; + local user = args[2]; + local apc_mgr = NativePointer(this_ptr).add(213 * 4).readPointer(); + local layer = NativePointer(tod_layer).readU16(); + local apc_info = Memory.alloc(0x100000); + local new_tod_layer = Memory.alloc(2); + new_tod_layer.writeU16(layer); + Sq_CallFunc(S_Ptr("0x085FED2E"), "void", ["pointer", "pointer", "pointer"], apc_mgr, new_tod_layer.C_Object, apc_info.C_Object); + } + } + + // 设置每日可交易金币上限值 + if(config["杂项功能开关"]["设置每日可交易金币上限值"][0]){ + local max = config["杂项功能开关"]["设置每日可交易金币上限值"][1]; + GameManager.FixGlodTradeDaily(max); + } + + // 强化13以上免刷新 + if(config["杂项功能开关"]["强化13以上免刷新"]){ + GameManager.Fix_13Upgrade(); + } + + // 开启14格技能栏 + if(config["杂项功能开关"]["开启14格技能栏"]){ + GameManager.Fix14Skill(); + } + + // 修复拍卖行消耗品上架 + if(config["杂项功能开关"]["修复拍卖行消耗品上架"]){ + GameManager.Fix_Auction_Regist_Item(); + } + + // 副本内可丢弃物品品级 + if(config["杂项功能开关"]["副本内可丢弃物品品级(开启后可丢弃粉装,3为装备品级)"][0]){ + local level = config["杂项功能开关"]["副本内可丢弃物品品级(开启后可丢弃粉装,3为装备品级)"][1]; + GameManager.FixDungeonDropGrade(level); + } + + // 发送邮件时验证关闭 + if(config["杂项功能开关"]["发送邮件时验证关闭"]){ + GameManager.FixEmailRemovalVerification(); + } + + // 关闭商店回购 + Cb_Item_IsBanRedeemItem_Leave_Func.BanRedeemItemByNangua <- function(args) { + local config = GlobalConfig.Get("杂项功能开关_Nangua.json"); + if (config["杂项功能开关"]["关闭商店回购"]) { + return 1; + } + } + + // 无视创建角色时间直接删除角色 + Cb_User_CheckDeleteCharacTime_Leave_Func.DeleteCharacByNangua <- function(args) { + local config = GlobalConfig.Get("杂项功能开关_Nangua.json"); + if (config["杂项功能开关"]["无视创建角色时间直接删除角色"]) { + return 1; + } + } + + // 忽略在副本门口禁止摆摊 + Cb_CPrivateStore_IsAreaNearEntranceDungeon_Leave_Func.PrivateStoreByNangua <- function(args) { + local config = GlobalConfig.Get("杂项功能开关_Nangua.json"); + if (config["杂项功能开关"]["忽略在副本门口禁止摆摊"]) { + return 1; + } + } + + // 脱离公会后可立马加入新公会 + Cb_MonitorNoticeGuildSecede_dispatch_Enter_Func.DeleteSecedeByNangua <- function(args) { + local config = GlobalConfig.Get("杂项功能开关_Nangua.json"); + if (config["杂项功能开关"]["脱离公会后可立马加入新公会"]) { + local SUser = User(args[1]); + local charac_no = SUser.GetCID(); + local updateQuery = "UPDATE d_guild.guild_member SET secede_time = '0000-00-00 00:00:00' where charac_no=" + charac_no + ";"; + local column_type_list = ["int"]; + local SqlObj = MysqlPool.GetInstance().GetConnect(); + SqlObj.Select(updateQuery, column_type_list); + MysqlPool.GetInstance().PutConnect(SqlObj); + } + } + + //公会普通信息互通 + Cb_MonitorNoticeGuildChatMsg_Leave_Func.GUildMessagetoCommonByNangua <- function(args) { + local config = GlobalConfig.Get("杂项功能开关_Nangua.json"); + if (config["杂项功能开关"]["公会聊天互通"]) { + local SUser = User(args[1]); + local msgData = args[2]; + + local senderName = NativePointer(msgData).add(18).readUtf8String(); + if (SUser.GetCharacName() == senderName) { + local Guild_name = SUser.GetGuildName(); + _Dps_GuildmsgBynangua_Main_.SendMessagetoCommon(msgData, Guild_name); + } + } + } + //公会超链接信息互通 + Cb_MonitorNoticeGuildChatMsgHyperLink_Leave_Func.GUildMessagetoHyperLinkByNangua <- function(args) { + local config = GlobalConfig.Get("杂项功能开关_Nangua.json"); + local SUser = User(args[1]); + local msgData = args[2]; + + local senderName = NativePointer(msgData).add(18).readUtf8String(); + if (config["杂项功能开关"]["公会聊天互通"] && SUser.GetCharacName() == senderName) { + local guild_name = SUser.GetGuildName(); + local Pack = _Dps_GuildmsgBynangua_Main_.SendMessagetoHyperLink(msgData, guild_name); + local users = World.GetOnlinePlayer(); + users.apply(function(Value) { + local onguildName = Value.GetGuildName() + local guildkey = Sq_CallFunc(S_Ptr("0x822F46C"), "int", ["pointer"], Value.C_Object); + if (onguildName != guild_name && guildkey > 0) { + Value.Send(Pack); + } + }); + Pack.Delete(); + } + } + + class _Dps_GuildmsgBynangua_Main_ { + function SendMessagetoCommon(msg, guild_name) { + local Pack = Packet(); + Pack.Put_Header(0, 65); + Pack.Put_Byte(6); + Pack.Put_Byte(0); + local name = NativePointer(msg).add(18).readUtf8String(); + _Dps_GuildmsgBynangua_Main_.api_InterfacePacketBuf_put_string(Pack, name); + Pack.Put_Byte(0); + local msgLen = NativePointer(msg).add(0x30).readU8(); + local content = NativePointer(msg).add(0x31); + local extraMsg = " [" + guild_name + "]跨公会信息"; + local extraContent = Memory.allocUtf8String(extraMsg); + local extraLen = extraMsg.len(); + local newMsgLen = msgLen + extraLen; + Pack.Put_Int(newMsgLen) + Pack.Put_BinaryEx(content.C_Object, msgLen); + Pack.Put_BinaryEx(extraContent.C_Object, extraLen); + Pack.Put_Byte(0); + Pack.Finalize(true); + local users = World.GetOnlinePlayer(); + users.apply(function(Value) { + local onguildName = Value.GetGuildName() + local guildkey = Sq_CallFunc(S_Ptr("0x822F46C"), "int", ["pointer"], Value.C_Object); + if (onguildName != guild_name && guildkey > 0) { + Value.Send(Pack); + } + }); + Pack.Delete(); + } + + function SendMessagetoHyperLink(msg, guild_name) { + local Pack = Packet(); + Pack.Put_Header(0, 371); + Pack.Put_Byte(6); + Pack.Put_Byte(0); + // 角色名 + local name = NativePointer(msg).add(18).readUtf8String(); + _Dps_GuildmsgBynangua_Main_.api_InterfacePacketBuf_put_string(Pack, name); + Pack.Put_Byte(0); + local msgLen = NativePointer(msg).add(361).readU8(); + local content = NativePointer(msg).add(362); + local extraMsg = " [" + guild_name + "]跨公会信息"; + local extraContent = Memory.allocUtf8String(extraMsg); + local extraLen = extraMsg.len(); + local newMsgLen = msgLen + extraLen; + Pack.Put_Int(newMsgLen) + Pack.Put_BinaryEx(content.C_Object, msgLen); + Pack.Put_BinaryEx(extraContent.C_Object, extraLen); + + local itemCount = NativePointer(msg).add(48).readU8(); + Pack.Put_Byte(itemCount); + for (local i = 0; i < itemCount; i++) { + Pack.Put_BinaryEx(NativePointer(msg).add(104 * i + 49).C_Object, 104); + } + Pack.Finalize(true); + return Pack; + } + function api_InterfacePacketBuf_put_string(Pack, s) { + local p = Memory.allocUtf8String(s); + local len = s.len(); + Pack.Put_Int(len); + Pack.Put_BinaryEx(p.C_Object, len); + } + } +} \ No newline at end of file diff --git a/示例项目/杂项功能/杂项功能开关_Nangua.json b/示例项目/杂项功能/杂项功能开关_Nangua.json new file mode 100644 index 0000000..4402e20 --- /dev/null +++ b/示例项目/杂项功能/杂项功能开关_Nangua.json @@ -0,0 +1,30 @@ +{ + "杂项功能开关":{ + "提示":"开关(true为开启,false为关闭)", + "提示2":"摆摊开启两排格子、14格技能栏、镶嵌这几个功能续启动器支持或dll支持", + "关闭商店回购":true, + "无视创建角色时间直接删除角色":true, + "忽略在副本门口禁止摆摊":true, + "脱离公会后可立马加入新公会":true, + "公会聊天互通":true, + "↑↑↑上述功能可随时更改无须五国↑↑↑":"", + + "↓↓↓下方功能更改须五国生效↓↓↓":"", + "摆摊开启两排格子":true, + "设置最高等级":[false, 60], + "装备解锁(单位:秒)":[false, 1], + "创建缔造者":false, + "自动解除魔法封印":false, + "开启装备与时装镶嵌":false, + "修复下线卡城镇":true, + "修复绝望之塔金币异常":true, + "绝望之塔通关后可以用门票继续进入":true, + "修复绝望之塔每10层进不去副本":true, + "设置每日可交易金币上限值":[false, 50000000], + "强化13以上免刷新":true, + "开启14格技能栏":false, + "修复拍卖行消耗品上架":true, + "副本内可丢弃物品品级(开启后可丢弃粉装,3为装备品级)":[false, 3], + "发送邮件时验证关闭":true + } +} \ No newline at end of file diff --git a/示例项目/武器锻造券/Proj.ifo b/示例项目/武器锻造券/Proj.ifo new file mode 100644 index 0000000..a238f73 --- /dev/null +++ b/示例项目/武器锻造券/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "武器锻造券", + "ProjectDescribe": "提升武器的锻造等级", + "ProjectAuthor": "南瓜", + "ProjectVersion": 1.0, + "ProjectConfig": "武器锻造券配置_Nangua.json", + "ProjectFiles": [ + "武器锻造券.nut" + ], + "ProjectRunFunc": "_Dps_UpdateWeaponSeparate_Main_" +} \ No newline at end of file diff --git a/示例项目/武器锻造券/武器锻造券.nut b/示例项目/武器锻造券/武器锻造券.nut new file mode 100644 index 0000000..6489f09 --- /dev/null +++ b/示例项目/武器锻造券/武器锻造券.nut @@ -0,0 +1,99 @@ +function WeaponForgingBynangua(SUser, ItemId) { + local Config = GlobalConfig.Get("武器锻造券配置_Nangua.json"); + local ItemIdStr = ItemId.tostring(); + + if (Config["武器锻造券配置"].rawin(ItemIdStr)) { + local forgingConfig = Config["武器锻造券配置"][ItemIdStr]; + local Level = forgingConfig["锻造等级"]; + local SuccessRate = forgingConfig["成功率"]; + + // 获取玩家背包 + local InvenObj = SUser.GetInven(); + if (!InvenObj) return; + + // 获取玩家背包类型1的第9个格子 + local ItemObj = InvenObj.GetSlot(1, 9); + + // 判断装备是否存在 + if (ItemObj.IsEmpty) { + _UpdateWeaponNotify.NotifyUserAndReturn(SUser, ItemId, Config["提示信息"]["装备不存在"]); + return; + } + + // 获取装备类型 + local ItemInfo = PvfItem.GetPvfItemById(ItemObj.GetIndex()); + local ItemType = NativePointer(ItemInfo.C_Object).add(141 * 4).readU32(); + if (ItemType != 10) { + _UpdateWeaponNotify.NotifyUserAndReturn(SUser, ItemId, Config["提示信息"]["非武器装备"]); + return; + } + + // 获取原装备的锻造等级 + local OldLevel = ItemObj.GetForging(); + if (OldLevel >= Level) { + _UpdateWeaponNotify.NotifyUserAndReturn(SUser, ItemId, Config["提示信息"]["锻造等级已达到"]); + return; + } + + // 随机数生成 + local RandNum = MathClass.Rand(0, 101); + if (RandNum <= SuccessRate) { + // 设置装备的锻造等级 + ItemObj.SetForging(Level); + ItemObj.Flush(); + SUser.SendUpdateItemList(1, 0, 9); + + // 根据配置选择发送消息的方式 + if (Config["启用233发包(true/false)"]) { + SUser.SendNotiBox(format(Config["提示信息"]["锻造成功"], Level), 1); + } else { + SUser.SendNotiPacketMessage(format(Config["提示信息"]["锻造成功"], Level), 8); + } + } else { + // 根据配置选择发送消息的方式 + if (Config["启用233发包(true/false)"]) { + SUser.SendNotiBox(Config["提示信息"]["锻造失败"], 1); + } else { + SUser.SendNotiPacketMessage(Config["提示信息"]["锻造失败"], 8); + } + } + } +} +class _UpdateWeaponNotify { + function NotifyUserAndReturn(SUser, ItemId, message) { + local Config = GlobalConfig.Get("武器锻造券配置_Nangua.json"); + + // 根据配置选择发送消息的方式 + if (Config["启用233发包(true/false)"]) { + SUser.SendNotiBox(message, 1); + } else { + SUser.SendNotiPacketMessage(message, 8); + } + + SUser.GiveItem(ItemId, 1); + } +} + +//加载入口 +function _Dps_UpdateWeaponSeparate_Main_() { + _Dps_WeaponForging_Logic_(); +} + +//重载入口 +function _Dps_WeaponForging_Main_Reload_(OldConfig) { + local Config = GlobalConfig.Get("武器锻造券配置_Nangua.json"); + // 删除旧的注册 + foreach(itemId, _ in OldConfig["武器锻造券配置"]) { + Cb_Use_Item_Sp_Func.rawdelete(itemId.tointeger()); + } + //重新注册 + _Dps_WeaponForging_Logic_(); +} + +function _Dps_WeaponForging_Logic_() { + local Config = GlobalConfig.Get("武器锻造券配置_Nangua.json"); + // 注册所有武器锻造券 + foreach(itemId, _ in Config["武器锻造券配置"]) { + Cb_Use_Item_Sp_Func[itemId.tointeger()] <- WeaponForgingBynangua; + } +} \ No newline at end of file diff --git a/示例项目/武器锻造券/武器锻造券配置_Nangua.json b/示例项目/武器锻造券/武器锻造券配置_Nangua.json new file mode 100644 index 0000000..17edee7 --- /dev/null +++ b/示例项目/武器锻造券/武器锻造券配置_Nangua.json @@ -0,0 +1,41 @@ +{ + "武器锻造券配置": { + "123007": { + "锻造等级": 4, + "成功率": 100 + }, + "123008": { + "锻造等级": 5, + "成功率": 80 + }, + "123009": { + "锻造等级": 6, + "成功率": 60 + }, + "123010": { + "锻造等级": 7, + "成功率": 40 + }, + "123011": { + "锻造等级": 8, + "成功率": 20 + }, + "123012": { + "锻造等级": 9, + "成功率": 10 + }, + "123013": { + "锻造等级": 10, + "成功率": 5 + } + }, + "提示信息": { + "装备不存在": "请将需要锻造的武器放在背包第一排第一格", + "非武器装备": "只能对武器进行锻造", + "锻造等级已达到": "当前武器锻造等级已经达到或超过目标等级", + "锻造成功": "恭喜!武器锻造成功,当前锻造等级:%d", + "锻造失败": "很遗憾,锻造失败,请再次尝试" + }, + "提示": "如果使用233发包需要客户端插件,在群文件搜索<客户端插件消息框233>下载使用,否则会导致游戏崩溃", + "启用233发包(true/false)": true +} \ No newline at end of file diff --git a/示例项目/深渊模式称号/Proj.ifo b/示例项目/深渊模式称号/Proj.ifo new file mode 100644 index 0000000..4659d32 --- /dev/null +++ b/示例项目/深渊模式称号/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "深渊模式称号", + "ProjectDescribe": "指定称号ID并且佩戴可不用每次都点开启挑战深渊派对按钮,进入默认深渊模式(需要完成深渊任务,拥有深渊票),另外还有一个小功能,深渊次次非常困难。", + "ProjectAuthor": "Pluto", + "ProjectVersion": 1.0, + "ProjectConfig": "深渊模式功能_Pluto.json", + "ProjectFiles": [ + "深渊模式称号.nut" + ], + "ProjectRunFunc": "_Dps_hellParty_Main_" +} \ No newline at end of file diff --git a/示例项目/深渊模式称号/深渊模式功能_Pluto.json b/示例项目/深渊模式称号/深渊模式功能_Pluto.json new file mode 100644 index 0000000..69c5b38 --- /dev/null +++ b/示例项目/深渊模式称号/深渊模式功能_Pluto.json @@ -0,0 +1,5 @@ +{ + "称号ID":26697, + "非常困难":0, + "次次都是非常困难开关":"开=1,关=0" +} \ No newline at end of file diff --git a/示例项目/深渊模式称号/深渊模式称号.nut b/示例项目/深渊模式称号/深渊模式称号.nut new file mode 100644 index 0000000..3db745f --- /dev/null +++ b/示例项目/深渊模式称号/深渊模式称号.nut @@ -0,0 +1,47 @@ +//尊贵的客人:26700 +//称号位:11 +function _Dps_hellParty_Main_() { + //深渊模式开关 + local hellPartyTag = false; + //主逻辑 + Cb_CParty_DungeonStart_Enter_Func.hellparty <- function(args) { + if (hellPartyTag == false) { + return; + } + args[3] = 1; + return args; + } + //获取玩家称号 + Cb_StartGame_check_error_Leave_Func.hellparty <- function(args) { + local Config = GlobalConfig.Get("深渊模式功能_Pluto.json"); + local VipID = Config["称号ID"]; + local SUser = User(args[1]); + local cid = SUser.GetCID(); + print("cid = " + cid + "称号ID = " + VipID); + local InvenObj = SUser.GetInven(); + if (InvenObj) { + local ItemObj = InvenObj.GetSlot(0, 11); + local item_id = ItemObj.GetIndex(); // 获取物品ID + //print(item_id); + if(item_id == VipID) { + hellPartyTag = true; + }else{ + hellPartyTag = false; + } + print("hellPartyTag = " + hellPartyTag); + } + } + + //================================================== + // 获取地狱派对难度功能 + Cb_GetHellPartyDifficulty_Leave_Func.abc <- function (args) { + local Config = GlobalConfig.Get("深渊模式功能_Pluto.json"); + local GetHellPartyDifficulty = Config["非常困难"]; + print("难度开关 " + GetHellPartyDifficulty); + if (GetHellPartyDifficulty == 1) { + local retval = args.pop(); + print("获取地狱派对难度 " + retval); + return 1; + } + } +} \ No newline at end of file diff --git a/示例项目/深渊活动/Proj.ifo b/示例项目/深渊活动/Proj.ifo new file mode 100644 index 0000000..f551671 --- /dev/null +++ b/示例项目/深渊活动/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "深渊活动", + "ProjectDescribe": "定时开启深渊活动,获得指定物品时获得奖励", + "ProjectAuthor": "南瓜", + "ProjectVersion": 1.0, + "ProjectConfig": "深渊活动配置_nangua.json", + "ProjectFiles": [ + "深渊活动.nut" + ], + "ProjectRunFunc": "_Dps_Start_Hellparty_Event_Main_" +} \ No newline at end of file diff --git a/示例项目/深渊活动/深渊活动.nut b/示例项目/深渊活动/深渊活动.nut new file mode 100644 index 0000000..c21d7e4 --- /dev/null +++ b/示例项目/深渊活动/深渊活动.nut @@ -0,0 +1,954 @@ +// 深渊活动状态枚举 +local HELLPARTY_STATE_END = 0; // 活动结束状态 +local HELLPARTY_STATE_RUNNING = 1; // 活动进行中状态 + +// 深渊活动数据 +local hellpartyEventInfo = { + "state": HELLPARTY_STATE_END, // 当前活动状态 + "start_time": 0, // 活动开始时间 + "reward_counts": {}, // 每个指定道具的剩余奖励次数 + "last_reminder_minute": -1, // 上次提醒的分钟数,初始化为-1 + "last_reminder_second": -1, // 上次提醒的秒数,初始化为-1 + "current_time_slot": null, // 当前活动时间段配置 + "drop_prevention_enabled": false // 是否启用了禁止丢弃物品 +} + +// 初始化禁止丢弃物品功能 +function Init_Hellparty_Drop_Prevention() { + // 如果没有创建过这个模拟内存就创建 + if(!getroottable().rawin("_HellpartyEmptyCharacInfo_")) { + getroottable()._HellpartyEmptyCharacInfo_ <- Memory.alloc(17); + Memory.reset(getroottable()._HellpartyEmptyCharacInfo_, 17); + } + // 如果没有创建过这个跳转Flag就创建并初始化 + if(!getroottable().rawin("_HellpartyDropPreventFlag_")) { + getroottable()._HellpartyDropPreventFlag_ <- false; + } +} + +// 深渊活动入口点 +function _Dps_Start_Hellparty_Event_Main_() { + _Dps_Start_Hellparty_Event_Logic_(); +} + +// 深渊活动重载入口点 +function _Dps_Start_Hellparty_Event_Main_Reload_(OldConfig) { + + // 移除检查任务 + Timer.RemoveCronTask("HellpartyCheckTask"); + + // 移除旧的定时任务 + if (OldConfig && "深渊活动配置" in OldConfig && "活动时间段" in OldConfig["深渊活动配置"]) { + foreach(timeSlot in OldConfig["深渊活动配置"]["活动时间段"]) { + local start_time = timeSlot["开启时间"]; + local task_id = format("HellpartyEventTask_%d_%d", start_time[0], start_time[1]); + Timer.RemoveCronTask(task_id); + } + } + + // 设置活动状态为结束(如果当前正在进行) + if (hellpartyEventInfo.state == HELLPARTY_STATE_RUNNING) { + hellpartyEventInfo.state = HELLPARTY_STATE_END; + World.SendNotiPacketMessage("【深渊探险活动】因配置更新已结束", 14); + } + + // 广播活动时间更改通知 + broadcast_hellparty_time_changes(); + + // 重新注册 + _Dps_Start_Hellparty_Event_Logic_(); +} + +// 深渊活动逻辑入口点 +function _Dps_Start_Hellparty_Event_Logic_() { + local Config = GlobalConfig.Get("深渊活动配置_nangua.json"); + + // 检查活动总开关 + if (!Config["深渊活动配置"]["活动总开关(true开启/false关闭)"]) { + return; + } + + // 注册禁止丢弃物品的回调函数 + Cb_DropItem_check_error_Enter_Func.HellpartyDropPrevention <- function(args) { + local Config = GlobalConfig.Get("深渊活动配置_nangua.json"); + // 只在活动进行中、启用了禁止丢弃功能且功能开关打开时才生效 + if (hellpartyEventInfo.state == HELLPARTY_STATE_RUNNING && + hellpartyEventInfo.drop_prevention_enabled && + Config["深渊活动配置"]["活动期间开启物品禁止丢弃功能(true开启/false关闭)"]) { + // 确保必要的变量存在 + if(!getroottable().rawin("_HellpartyEmptyCharacInfo_")) { + getroottable()._HellpartyEmptyCharacInfo_ <- Memory.alloc(17); + Memory.reset(getroottable()._HellpartyEmptyCharacInfo_, 17); + } + if(!getroottable().rawin("_HellpartyDropPreventFlag_")) { + getroottable()._HellpartyDropPreventFlag_ <- false; + } + + getroottable()._HellpartyDropPreventFlag_ = true; + args[1] = getroottable()._HellpartyEmptyCharacInfo_.C_Object; + } + return args; + } + + Cb_DropItem_check_error_Leave_Func.HellpartyDropPrevention <- function(args) { + local Config = GlobalConfig.Get("深渊活动配置_nangua.json"); + // 只在活动进行中、启用了禁止丢弃功能且功能开关打开时才生效 + if (hellpartyEventInfo.state == HELLPARTY_STATE_RUNNING && + hellpartyEventInfo.drop_prevention_enabled && + Config["深渊活动配置"]["活动期间开启物品禁止丢弃功能(true开启/false关闭)"]) { + if(!getroottable().rawin("_HellpartyDropPreventFlag_")) { + getroottable()._HellpartyDropPreventFlag_ <- false; + return; + } + + if(getroottable()._HellpartyDropPreventFlag_) { + getroottable()._HellpartyDropPreventFlag_ = false; + return 19; + } + } + } + + local open_days = Config["深渊活动配置"]["每周几开放活动"]; + local current_date = date(); + local current_weekday = current_date.wday; + if (current_weekday == 0) current_weekday = 7; // 转换为1-7格式 + + // 检查今天是否开放 + local is_open_today = false; + foreach(day in open_days) { + if (day == current_weekday) { + is_open_today = true; + break; + } + } + + if (!is_open_today) { + return; + } + + // 为每个时间段注册定时任务 + foreach(timeSlot in Config["深渊活动配置"]["活动时间段"]) { + local start_time = timeSlot["开启时间"]; + local start_hour = start_time[0]; + local start_minute = start_time[1]; + + // 使用格式化的唯一任务ID + local task_id = format("HellpartyEventTask_%d_%d", start_hour, start_minute); + + // 创建配置的本地副本 + local timeSlotCopy = { + "开启时间": [start_time[0], start_time[1]], + "持续时间(分钟)": timeSlot["持续时间(分钟)"], + "指定道具奖励配置": { + "指定道具ID": [] + } + }; + + // 复制指定道具奖励配置 + foreach(item in timeSlot["指定道具奖励配置"]["指定道具ID"]) { + local itemCopy = { + "指定道具": item["指定道具"], + "奖励次数": item["奖励次数"], + "奖励列表": [] + }; + foreach(reward in item["奖励列表"]) { + itemCopy["奖励列表"].append([reward[0], reward[1]]); + } + timeSlotCopy["指定道具奖励配置"]["指定道具ID"].append(itemCopy); + } + + // cron 表达式 (秒 分 时 日 月 周) + local cronExpression = format("0 %d %d * * *", start_minute, start_hour); + + // 注册定时任务,传入时间段配置的副本 + Timer.SetCronTask(function() { + On_Start_Hellparty_Event(timeSlotCopy); + }, { + Cron = cronExpression, + Name = task_id + }); + + } + + // 注册回调函数 + Cb_UserHistoryLog_ItemAdd_Enter_Func.Hellparty_EventByNangua <- function(args) { + // 检查活动是否在进行中 + if (hellpartyEventInfo.state == HELLPARTY_STATE_END) return; + + // 检查活动是否已超时 + if (is_hellparty_event_timeout()) return; + + local Config = GlobalConfig.Get("深渊活动配置_nangua.json"); + local SUser = User(NativePointer(args[0]).readPointer()); + + // 使用当前活动时间段配置 + if (!hellpartyEventInfo.current_time_slot) return; + local specifiedItems = hellpartyEventInfo.current_time_slot["指定道具奖励配置"]["指定道具ID"]; + + local InvenObj = SUser.GetInven(); + local ItemObj = Item(args[4]); + // 拾取原因 4代表在副本拾取 + local type = args[5]; + local PartyObj = SUser.GetParty(); + if (!PartyObj) return; + local Bfobj = PartyObj.GetBattleField(); + local DgnObj = Bfobj.GetDgn(); + if (!DgnObj) return; + local Dungeon_Name = DgnObj.GetName(); + if (!ItemObj) return; + local item_id = ItemObj.GetIndex(); + + // 检查是否在副本中拾取了物品 + if (type == 4) { + // 检查是否是指定道具 + foreach(itemConfig in specifiedItems) { + if (itemConfig["指定道具"] == item_id) { + // 检查该道具的奖励次数是否已用完 + local item_id_str = item_id.tostring(); + if (!(item_id_str in hellpartyEventInfo.reward_counts) || hellpartyEventInfo.reward_counts[item_id_str] <= 0) { + // 奖励次数已用完 + local MsgObj = AdMsg(); + MsgObj.PutType(Config["深渊活动配置"]["信息发送位置"]); + if(Config["深渊活动配置"]["信息发送位置"] != 14) { + MsgObj.PutString(" "); + } + MsgObj.PutColorString("很遗憾", [255, 130, 0]); + MsgObj.PutEquipment(item_id); + MsgObj.PutColorString("的奖励次数已达上限", [255, 130, 0]); + MsgObj.Finalize(); + SUser.Send(MsgObj.MakePack()); + MsgObj.Delete(); + return; + } + + // 减少奖励次数 + hellpartyEventInfo.reward_counts[item_id_str]--; + local remaining_count = hellpartyEventInfo.reward_counts[item_id_str]; + + // 发送奖励通知 + local MsgObj = AdMsg(); + MsgObj.PutType(Config["深渊活动配置"]["信息发送位置"]); + if(Config["深渊活动配置"]["信息发送位置"] != 14) { + MsgObj.PutString(" "); + } + MsgObj.PutColorString("◆━━━━━━ 深渊探险活动 ━━━━━━◆", [255, 255, 0]); + MsgObj.Finalize(); + World.SendAll(MsgObj.MakePack()); + MsgObj.Delete(); + + local MsgObj2 = AdMsg(); + MsgObj2.PutType(Config["深渊活动配置"]["信息发送位置"]); + if(Config["深渊活动配置"]["信息发送位置"] != 14) { + MsgObj2.PutString(" "); + } + MsgObj2.PutColorString("恭喜 ", [255, 255, 0]); + MsgObj2.PutColorString("[" + SUser.GetCharacName() + "]", [255, 20, 0]); + MsgObj2.PutColorString(" 获得指定物品:", [255, 255, 0]); + MsgObj2.Finalize(); + World.SendAll(MsgObj2.MakePack()); + MsgObj2.Delete(); + + local MsgObj3 = AdMsg(); + MsgObj3.PutType(Config["深渊活动配置"]["信息发送位置"]); + if(Config["深渊活动配置"]["信息发送位置"] != 14) { + MsgObj3.PutString(" "); + } + MsgObj3.PutColorString("★ ", [255, 20, 0]); + MsgObj3.PutEquipment(item_id); + MsgObj3.Finalize(); + World.SendAll(MsgObj3.MakePack()); + MsgObj3.Delete(); + + // 发送奖励列表头部 + local rewardHeader = AdMsg(); + rewardHeader.PutType(Config["深渊活动配置"]["信息发送位置"]); + if(Config["深渊活动配置"]["信息发送位置"] != 14) { + rewardHeader.PutString(" "); + } + rewardHeader.PutColorString("├ 获得奖励:", [255, 255, 0]); + rewardHeader.Finalize(); + World.SendAll(rewardHeader.MakePack()); + rewardHeader.Delete(); + + // 收集所有奖励 + local allRewards = []; + foreach(reward in itemConfig["奖励列表"]) { + local rewardId = reward[0]; + local rewardCount = reward[1]; + + // 收集非点券和代币的道具奖励 + if (rewardId != 0 && rewardId != 1) { + allRewards.append([rewardId, rewardCount]); + } + + // 发放点券和代币奖励 + if (rewardId == 0) { + SUser.RechargeCera(rewardCount); + } else if (rewardId == 1) { + SUser.RechargeCeraPoint(rewardCount); + } + + // 显示奖励信息 + local rewardMsg = AdMsg(); + rewardMsg.PutType(Config["深渊活动配置"]["信息发送位置"]); + if(Config["深渊活动配置"]["信息发送位置"] != 14) { + rewardMsg.PutString(" "); + } + rewardMsg.PutColorString("│", [255, 255, 0]); + rewardMsg.PutColorString(" ★ ", [0, 191, 255]); + + if (rewardId == 0) { + rewardMsg.PutColorString("[点券]", [0, 191, 255]); + } else if (rewardId == 1) { + rewardMsg.PutColorString("[代币]", [0, 191, 255]); + } else { + rewardMsg.PutEquipment(rewardId); + } + + rewardMsg.PutColorString(" x ", [255, 255, 255]); + rewardMsg.PutColorString("<" + rewardCount + ">", [255, 20, 0]); + rewardMsg.Finalize(); + World.SendAll(rewardMsg.MakePack()); + rewardMsg.Delete(); + } + + // 一次性发放所有道具奖励 + if (allRewards.len() > 0) { + Nangua_Hellparty.api_CUser_Add_Item_list(SUser, allRewards); + } + + // 显示剩余次数 + local remainMsg = AdMsg(); + remainMsg.PutType(Config["深渊活动配置"]["信息发送位置"]); + if(Config["深渊活动配置"]["信息发送位置"] != 14) { + remainMsg.PutString(" "); + } + remainMsg.PutColorString("└ 剩余次数:", [255, 255, 0]); + remainMsg.PutColorString("<" + remaining_count + ">", [255, 20, 0]); + remainMsg.PutColorString("次", [255, 255, 0]); + remainMsg.Finalize(); + World.SendAll(remainMsg.MakePack()); + remainMsg.Delete(); + + // 发送底部分隔线 + local footerMsg = AdMsg(); + footerMsg.PutType(Config["深渊活动配置"]["信息发送位置"]); + if(Config["深渊活动配置"]["信息发送位置"] != 14) { + footerMsg.PutString(" "); + } + footerMsg.PutColorString("◆━━━━━━━━━━━━━━━━━━━◆", [255, 255, 0]); + footerMsg.Finalize(); + World.SendAll(footerMsg.MakePack()); + footerMsg.Delete(); + + break; + } + } + } + } + + // 玩家登录时播报此次深渊活动进度及指定道具信息 + Cb_reach_game_world_Func.hellpartyByNangua <- function(SUser) { + local Config = GlobalConfig.Get("深渊活动配置_nangua.json"); + //深渊活动更新进度 + if (hellpartyEventInfo.state != HELLPARTY_STATE_END) { + //通知客户端当前活动状态 + local remain_time = event_hellparty_get_remain_time(); + local minutes_left = (remain_time / 60).tointeger(); + local seconds_left = remain_time % 60; + + // 发送活动剩余时间信息 + local timeMsg = AdMsg(); + timeMsg.PutType(Config["深渊活动配置"]["信息发送位置"]); + if(Config["深渊活动配置"]["信息发送位置"] != 14) { + timeMsg.PutString(" "); + } + timeMsg.PutColorString("◆━━━━ 深渊探险活动进行中 ━━━━◆", [255, 255, 0]); + timeMsg.Finalize(); + SUser.Send(timeMsg.MakePack()); + timeMsg.Delete(); + + local timeMsg2 = AdMsg(); + timeMsg2.PutType(Config["深渊活动配置"]["信息发送位置"]); + if(Config["深渊活动配置"]["信息发送位置"] != 14) { + timeMsg2.PutString(" "); + } + timeMsg2.PutColorString(" 剩余时间: ", [255, 255, 255]); + if (minutes_left > 0) { + timeMsg2.PutColorString("<" + minutes_left + ">", [255, 20, 0]); + timeMsg2.PutColorString("分", [255, 255, 255]); + } + if (seconds_left > 0 || minutes_left == 0) { + timeMsg2.PutColorString("<" + seconds_left + ">", [255, 20, 0]); + timeMsg2.PutColorString("秒", [255, 255, 255]); + } + timeMsg2.Finalize(); + SUser.Send(timeMsg2.MakePack()); + timeMsg2.Delete(); + + // 如果启用了禁止丢弃功能,发送提示 + if (Config["深渊活动配置"]["活动期间开启物品禁止丢弃功能(true开启/false关闭)"]) { + local dropMsg1 = AdMsg(); + dropMsg1.PutType(Config["深渊活动配置"]["信息发送位置"]); + if(Config["深渊活动配置"]["信息发送位置"] != 14) { + dropMsg1.PutString(" "); + } + dropMsg1.PutColorString(" 特别提示:活动期间", [255, 255, 0]); + dropMsg1.PutColorString("无法丢弃物品", [255, 20, 0]); + dropMsg1.Finalize(); + SUser.Send(dropMsg1.MakePack()); + dropMsg1.Delete(); + } + + // 向玩家单独发送指定道具信息 + if (!hellpartyEventInfo.current_time_slot) return; + local specifiedItems = hellpartyEventInfo.current_time_slot["指定道具奖励配置"]["指定道具ID"]; + + // 发送活动道具信息头部 + local headerMsg = AdMsg(); + headerMsg.PutType(Config["深渊活动配置"]["信息发送位置"]); + if(Config["深渊活动配置"]["信息发送位置"] != 14) { + headerMsg.PutString(" "); + } + headerMsg.PutColorString("◆━━━━━ 本次活动指定道具 ━━━━━◆", [255, 255, 0]); + headerMsg.Finalize(); + SUser.Send(headerMsg.MakePack()); + headerMsg.Delete(); + + // 发送每个指定道具的信息 + foreach(itemConfig in specifiedItems) { + local itemId = itemConfig["指定道具"]; + local itemIdStr = itemId.tostring(); + local remainingCount = itemIdStr in hellpartyEventInfo.reward_counts ? hellpartyEventInfo.reward_counts[itemIdStr] : 0; + + // 显示道具名称 + local itemMsgLine1 = AdMsg(); + itemMsgLine1.PutType(Config["深渊活动配置"]["信息发送位置"]); + if(Config["深渊活动配置"]["信息发送位置"] != 14) { + itemMsgLine1.PutString(" "); + } + itemMsgLine1.PutColorString("★ ", [255, 20, 0]); + itemMsgLine1.PutEquipment(itemId); + itemMsgLine1.Finalize(); + SUser.Send(itemMsgLine1.MakePack()); + itemMsgLine1.Delete(); + + // 显示可获奖励提示 + local rewardHeader = AdMsg(); + rewardHeader.PutType(Config["深渊活动配置"]["信息发送位置"]); + rewardHeader.PutColorString("├ 可获奖励:", [255, 255, 0]); + rewardHeader.Finalize(); + SUser.Send(rewardHeader.MakePack()); + rewardHeader.Delete(); + + // 显示每个奖励 + foreach(reward in itemConfig["奖励列表"]) { + local rewardId = reward[0]; + local rewardCount = reward[1]; + + local itemMsgLine2 = AdMsg(); + itemMsgLine2.PutType(Config["深渊活动配置"]["信息发送位置"]); + if(Config["深渊活动配置"]["信息发送位置"] != 14) { + itemMsgLine2.PutString(" "); + } + itemMsgLine2.PutColorString("│", [255, 255, 0]); + itemMsgLine2.PutColorString(" ★ ", [0, 191, 255]); + + if (rewardId == 0) { + itemMsgLine2.PutColorString("[点券]", [0, 191, 255]); + } else if (rewardId == 1) { + itemMsgLine2.PutColorString("[代币]", [0, 191, 255]); + } else { + itemMsgLine2.PutEquipment(rewardId); + } + + itemMsgLine2.PutColorString(" x ", [255, 255, 255]); + itemMsgLine2.PutColorString("<" + rewardCount + ">", [255, 20, 0]); + itemMsgLine2.Finalize(); + SUser.Send(itemMsgLine2.MakePack()); + itemMsgLine2.Delete(); + } + + // 显示剩余次数 + local remainMsg = AdMsg(); + remainMsg.PutType(Config["深渊活动配置"]["信息发送位置"]); + if(Config["深渊活动配置"]["信息发送位置"] != 14) { + remainMsg.PutString(" "); + } + remainMsg.PutColorString("└ 剩余次数:", [255, 255, 0]); + remainMsg.PutColorString("<" + remainingCount + ">", [255, 20, 0]); + remainMsg.PutColorString("次", [255, 255, 0]); + remainMsg.Finalize(); + SUser.Send(remainMsg.MakePack()); + remainMsg.Delete(); + } + + // 发送底部分隔线 + local footerMsg = AdMsg(); + footerMsg.PutType(Config["深渊活动配置"]["信息发送位置"]); + if(Config["深渊活动配置"]["信息发送位置"] != 14) { + footerMsg.PutString(" "); + } + footerMsg.PutColorString("◆━━━━━━━━━━━━━━━━━━━◆", [255, 255, 0]); + footerMsg.Finalize(); + SUser.Send(footerMsg.MakePack()); + footerMsg.Delete(); + } + } +} + +// 开始深渊活动 +function On_Start_Hellparty_Event(timeSlot) { + // 先确保移除任何已存在的检查任务,避免重复 + Timer.RemoveCronTask("HellpartyCheckTask"); + + // 初始化活动状态 + Reset_Hellparty_Info(); + hellpartyEventInfo.current_time_slot = timeSlot; + + local Config = GlobalConfig.Get("深渊活动配置_nangua.json"); + // 启用禁止丢弃物品(根据配置决定) + if (Config["深渊活动配置"]["活动期间开启物品禁止丢弃功能(true开启/false关闭)"]) { + if (!hellpartyEventInfo.drop_prevention_enabled) { + Init_Hellparty_Drop_Prevention(); + hellpartyEventInfo.drop_prevention_enabled = true; + } + } + + // 注册定时检查任务(每5秒检查一次) + Timer.SetCronTask(Event_Hellparty_Timer, { + Cron = "*/5 * * * * *", // 每5秒执行一次 + Name = "HellpartyCheckTask" + }); + + // 发送活动开始公告 + local start_time = timeSlot["开启时间"]; + local event_start = AdMsg(); + event_start.PutType(Config["深渊活动配置"]["信息发送位置"]); + if(Config["深渊活动配置"]["信息发送位置"] != 14) { + event_start.PutString(" "); + } + event_start.PutColorString("◆━━━━━ 深渊探险活动开启 ━━━━━◆", [255, 255, 0]); + event_start.Finalize(); + World.SendAll(event_start.MakePack()); + event_start.Delete(); + + local event_start2 = AdMsg(); + event_start2.PutType(Config["深渊活动配置"]["信息发送位置"]); + if(Config["深渊活动配置"]["信息发送位置"] != 14) { + event_start2.PutString(" "); + } + event_start2.PutColorString(" 活动持续时间:", [255, 255, 0]); + event_start2.PutColorString("<" + timeSlot["持续时间(分钟)"] + ">", [255, 20, 0]); + event_start2.PutColorString("分钟", [255, 255, 0]); + event_start2.PutColorString(" 祝各位勇士好运!", [255, 255, 0]); + event_start2.Finalize(); + World.SendAll(event_start2.MakePack()); + event_start2.Delete(); + + // 如果启用了禁止丢弃功能,发送提示 + if (Config["深渊活动配置"]["活动期间开启物品禁止丢弃功能(true开启/false关闭)"]) { + local dropMsg2 = AdMsg(); + dropMsg2.PutType(Config["深渊活动配置"]["信息发送位置"]); + if(Config["深渊活动配置"]["信息发送位置"] != 14) { + dropMsg2.PutString(" "); + } + dropMsg2.PutColorString(" 特别提示:活动期间", [255, 255, 0]); + dropMsg2.PutColorString("无法丢弃物品", [255, 20, 0]); + dropMsg2.Finalize(); + World.SendAll(dropMsg2.MakePack()); + dropMsg2.Delete(); + } + + // 播报本次指定道具信息 + SendSpecifiedItemsInfo(); +} + +// 播报本次深渊活动指定道具信息 +function SendSpecifiedItemsInfo() { + if (!hellpartyEventInfo.current_time_slot) return; + + local specifiedItems = hellpartyEventInfo.current_time_slot["指定道具奖励配置"]["指定道具ID"]; + local Config = GlobalConfig.Get("深渊活动配置_nangua.json"); + + // 发送活动道具信息头部 + local headerMsg = AdMsg(); + headerMsg.PutType(Config["深渊活动配置"]["信息发送位置"]); + if(Config["深渊活动配置"]["信息发送位置"] != 14) { + headerMsg.PutString(" "); + } + headerMsg.PutColorString("◆━━━━━ 本次活动指定道具 ━━━━━◆", [255, 255, 0]); + headerMsg.Finalize(); + World.SendAll(headerMsg.MakePack()); + headerMsg.Delete(); + + // 发送每个指定道具的信息 + foreach(itemConfig in specifiedItems) { + local itemId = itemConfig["指定道具"]; + local rewardTimes = itemConfig["奖励次数"]; + + // 初始化该道具的奖励次数 + hellpartyEventInfo.reward_counts[itemId.tostring()] <- rewardTimes; + + // 显示道具名称 + local itemMsgLine1 = AdMsg(); + itemMsgLine1.PutType(Config["深渊活动配置"]["信息发送位置"]); + if(Config["深渊活动配置"]["信息发送位置"] != 14) { + itemMsgLine1.PutString(" "); + } + itemMsgLine1.PutColorString("★ ", [255, 20, 0]); + itemMsgLine1.PutEquipment(itemId); + itemMsgLine1.Finalize(); + World.SendAll(itemMsgLine1.MakePack()); + itemMsgLine1.Delete(); + + // 显示可获奖励提示 + local rewardHeader = AdMsg(); + rewardHeader.PutType(Config["深渊活动配置"]["信息发送位置"]); + if(Config["深渊活动配置"]["信息发送位置"] != 14) { + rewardHeader.PutString(" "); + } + rewardHeader.PutColorString("├ 可获奖励:", [255, 255, 0]); + rewardHeader.Finalize(); + World.SendAll(rewardHeader.MakePack()); + rewardHeader.Delete(); + + // 显示每个奖励 + foreach(reward in itemConfig["奖励列表"]) { + local rewardId = reward[0]; + local rewardCount = reward[1]; + + local itemMsgLine2 = AdMsg(); + itemMsgLine2.PutType(Config["深渊活动配置"]["信息发送位置"]); + if(Config["深渊活动配置"]["信息发送位置"] != 14) { + itemMsgLine2.PutString(" "); + } + itemMsgLine2.PutColorString("│", [255, 255, 0]); + itemMsgLine2.PutColorString(" ★ ", [0, 191, 255]); + + if (rewardId == 0) { + // 点券奖励 + itemMsgLine2.PutColorString("[点券]", [0, 191, 255]); + } else if (rewardId == 1) { + // 代币奖励 + itemMsgLine2.PutColorString("[代币]", [0, 191, 255]); + } else { + // 普通道具奖励 + itemMsgLine2.PutEquipment(rewardId); + } + + itemMsgLine2.PutColorString(" x ", [255, 255, 255]); + itemMsgLine2.PutColorString("<" + rewardCount + ">", [255, 20, 0]); + itemMsgLine2.Finalize(); + World.SendAll(itemMsgLine2.MakePack()); + itemMsgLine2.Delete(); + } + + // 显示剩余次数 + local remainMsg = AdMsg(); + remainMsg.PutType(Config["深渊活动配置"]["信息发送位置"]); + if(Config["深渊活动配置"]["信息发送位置"] != 14) { + remainMsg.PutString(" "); + } + remainMsg.PutColorString("└ 剩余次数:", [255, 255, 0]); + remainMsg.PutColorString("<" + rewardTimes + ">", [255, 20, 0]); + remainMsg.PutColorString("次", [255, 255, 0]); + remainMsg.Finalize(); + World.SendAll(remainMsg.MakePack()); + remainMsg.Delete(); + } + + // 发送底部分隔线 + local footerMsg = AdMsg(); + footerMsg.PutType(Config["深渊活动配置"]["信息发送位置"]); + if(Config["深渊活动配置"]["信息发送位置"] != 14) { + footerMsg.PutString(" "); + } + footerMsg.PutColorString("◆━━━━━━━━━━━━━━━━━━━◆", [255, 255, 0]); + footerMsg.Finalize(); + World.SendAll(footerMsg.MakePack()); + footerMsg.Delete(); +} + +// 重置深渊活动信息 +function Reset_Hellparty_Info() { + hellpartyEventInfo.state = HELLPARTY_STATE_RUNNING; + hellpartyEventInfo.start_time = time(); + hellpartyEventInfo.reward_counts = {}; // 清空奖励次数记录 + hellpartyEventInfo.last_reminder_minute = -1; // 重置上次提醒的分钟数 + hellpartyEventInfo.last_reminder_second = -1; // 重置上次提醒的秒数 + print(">>>>>>>>>>>>>>深渊活动数据已初始化<<<<<<<<<<<<<<"); +} + +// 深渊活动定时器检查 +function Event_Hellparty_Timer() { + if (hellpartyEventInfo.state == HELLPARTY_STATE_END) + return; + + // 检查活动是否结束 + if (is_hellparty_event_timeout()) { + // 活动结束 + on_end_event_hellparty(); + return; + } + + // 发送活动剩余时间提醒 + local remain_time = event_hellparty_get_remain_time(); + local minutes_left = (remain_time / 60).tointeger(); + local seconds_left = remain_time % 60; + + // 使用秒数范围来判断提醒时机,确保更精确 + if (remain_time <= 65 && remain_time > 60 && hellpartyEventInfo.last_reminder_minute != 1) { + // 即将进入最后一分钟 + World.SendNotiPacketMessage("【深渊探险活动】还剩最后<1>分钟结束!", 14); + hellpartyEventInfo.last_reminder_minute = 1; + } else if (remain_time <= 305 && remain_time > 300 && hellpartyEventInfo.last_reminder_minute != 5) { + // 即将进入最后五分钟 + World.SendNotiPacketMessage("【深渊探险活动】还剩<5>分钟结束!", 14); + hellpartyEventInfo.last_reminder_minute = 5; + } else if (remain_time <= 245 && remain_time > 240 && hellpartyEventInfo.last_reminder_minute != 4) { + // 即将进入最后四分钟 + World.SendNotiPacketMessage("【深渊探险活动】还剩<4>分钟结束!", 14); + hellpartyEventInfo.last_reminder_minute = 4; + } else if (remain_time <= 185 && remain_time > 180 && hellpartyEventInfo.last_reminder_minute != 3) { + // 即将进入最后三分钟 + World.SendNotiPacketMessage("【深渊探险活动】还剩<3>分钟结束!", 14); + hellpartyEventInfo.last_reminder_minute = 3; + } else if (remain_time <= 125 && remain_time > 120 && hellpartyEventInfo.last_reminder_minute != 2) { + // 即将进入最后两分钟 + World.SendNotiPacketMessage("【深渊探险活动】还剩<2>分钟结束!", 14); + hellpartyEventInfo.last_reminder_minute = 2; + } + + // 最后30秒倒计时,每10秒提醒一次 + if (minutes_left == 0 && seconds_left <= 30 && seconds_left > 0) { + if (seconds_left % 10 == 0 && hellpartyEventInfo.last_reminder_second != seconds_left) { + World.SendNotiPacketMessage("【深渊探险活动】还剩<" + seconds_left + ">秒结束!", 14); + hellpartyEventInfo.last_reminder_second = seconds_left; + } + } +} + +// 检查活动是否已超时 +function is_hellparty_event_timeout() { + if (!hellpartyEventInfo.current_time_slot) return true; + return event_hellparty_get_remain_time() <= 0; +} + +// 计算活动剩余时间(秒) +function event_hellparty_get_remain_time() { + if (!hellpartyEventInfo.current_time_slot) return 0; + local cur_time = time(); + local duration_seconds = hellpartyEventInfo.current_time_slot["持续时间(分钟)"] * 60; + local event_end_time = hellpartyEventInfo.start_time + duration_seconds; + local remain_time = event_end_time - cur_time; + return remain_time; +} + +// 结束深渊活动 +function on_end_event_hellparty() { + if (hellpartyEventInfo.state == HELLPARTY_STATE_END) + return; + + // 设置活动状态为结束 + hellpartyEventInfo.state = HELLPARTY_STATE_END; + + // 关闭禁止丢弃物品 + if (hellpartyEventInfo.drop_prevention_enabled) { + // 重置丢弃物品的Hook标志 + if (getroottable().rawin("_HellpartyDropPreventFlag_")) { + getroottable()._HellpartyDropPreventFlag_ = false; + } + hellpartyEventInfo.drop_prevention_enabled = false; + } + + // 移除定时检查任务 + Timer.RemoveCronTask("HellpartyCheckTask"); + + // 发送活动结束公告 + World.SendNotiPacketMessage("【深渊探险活动】已结束, 感谢各位勇士的参与!", 14); + + // 获取下一个活动时间 + local Config = GlobalConfig.Get("深渊活动配置_nangua.json"); + + local current_time = date(); + local current_hour = current_time.hour; + local current_minute = current_time.min; + + // 获取当前是周几 + local current_date = date(); + local current_weekday = current_date.wday; + if (current_weekday == 0) current_weekday = 7; // 转换为1-7格式 + + // 获取开放日期配置 + local open_days = Config["深渊活动配置"]["每周几开放活动"]; + local time_slots = Config["深渊活动配置"]["活动时间段"]; + + // 将时间段按时间排序 + local sorted_time_slots = []; + foreach(slot in time_slots) { + sorted_time_slots.append(slot); + } + // 按时间排序(冒泡排序) + for(local i = 0; i < sorted_time_slots.len() - 1; i++) { + for(local j = 0; j < sorted_time_slots.len() - 1 - i; j++) { + local time1 = sorted_time_slots[j]["开启时间"]; + local time2 = sorted_time_slots[j + 1]["开启时间"]; + if(time1[0] > time2[0] || (time1[0] == time2[0] && time1[1] > time2[1])) { + local temp = sorted_time_slots[j]; + sorted_time_slots[j] = sorted_time_slots[j + 1]; + sorted_time_slots[j + 1] = temp; + } + } + } + + // 查找下一个活动时间 + local next_time_slot = null; + local next_day = null; + + // 先尝试找今天剩余的时间段 + foreach(timeSlot in sorted_time_slots) { + local start_time = timeSlot["开启时间"]; + local start_hour = start_time[0]; + local start_minute = start_time[1]; + + // 如果找到未来的时间点,就使用它 + if ((start_hour > current_hour) || (start_hour == current_hour && start_minute > current_minute)) { + next_time_slot = timeSlot; + next_day = current_weekday; + break; + } + } + + // 如果没找到今天剩余的时间段,查找下一个开放日 + if (!next_time_slot) { + // 查找下一个开放日 + for (local i = 1; i <= 7; i++) { + local check_day = (current_weekday + i) % 7; + if (check_day == 0) check_day = 7; + + foreach(open_day in open_days) { + if (open_day == check_day) { + next_day = check_day; + next_time_slot = sorted_time_slots[0]; // 使用排序后的第一个时间段 + break; + } + } + if (next_time_slot) break; + } + } + + // 如果找到下一个活动时间,发送通知 + if (next_time_slot) { + local start_time = next_time_slot["开启时间"]; + local day_names = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"]; + local day_text = ""; + + if (next_day == current_weekday) { + day_text = "今天"; + } else if (next_day > current_weekday) { + day_text = "本周" + day_names[next_day - 1]; + } else { + day_text = "下" + day_names[next_day - 1]; + } + + World.SendNotiPacketMessage(format("【深渊探险活动】下次活动时间为%s [%d:%02d],敬请期待!", day_text, start_time[0], start_time[1]), 14); + } + + print(">>>>>>>>>>>>>>深渊活动已结束<<<<<<<<<<<<<<"); +} + +// 广播活动时间更改通知 +function broadcast_hellparty_time_changes() { + local Config = GlobalConfig.Get("深渊活动配置_nangua.json"); + local time_slots = Config["深渊活动配置"]["活动时间段"]; + + // 将时间段按时间排序 + local sorted_time_slots = []; + foreach(slot in time_slots) { + sorted_time_slots.append(slot); + } + // 按时间排序 + for(local i = 0; i < sorted_time_slots.len() - 1; i++) { + for(local j = 0; j < sorted_time_slots.len() - 1 - i; j++) { + local time1 = sorted_time_slots[j]["开启时间"]; + local time2 = sorted_time_slots[j + 1]["开启时间"]; + if(time1[0] > time2[0] || (time1[0] == time2[0] && time1[1] > time2[1])) { + local temp = sorted_time_slots[j]; + sorted_time_slots[j] = sorted_time_slots[j + 1]; + sorted_time_slots[j + 1] = temp; + } + } + } + + // 获取开放日期 + local open_days = Config["深渊活动配置"]["每周几开放活动"]; + local day_names = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"]; + local open_days_text = ""; + + foreach(idx, day in open_days) { + open_days_text += day_names[day - 1]; + if (idx < open_days.len() - 1) { + open_days_text += "、"; + } + } + + // 发送开放日期通知 + World.SendNotiPacketMessage("【深渊探险活动】开放日期为: " + open_days_text, 14); + + // 发送时间段通知 + local time_text = ""; + foreach(idx, slot in sorted_time_slots) { + local start_time = slot["开启时间"]; + time_text += format("%02d:%02d", start_time[0], start_time[1]); + if (idx < sorted_time_slots.len() - 1) { + time_text += "、"; + } + } + + World.SendNotiPacketMessage("【深渊探险活动】开放时间为: " + time_text + ",敬请期待!", 14); +} + +class Nangua_Hellparty { + function checkInventorySlot(SUser, itemid) { + local InvenObj = SUser.GetInven(); + local type = Sq_CallFunc(S_Ptr("0x085018D2"), "int", ["pointer", "int"], InvenObj.C_Object, itemid); + local cnt = Sq_CallFunc(S_Ptr("0x08504F64"), "int", ["pointer", "int", "int"], InvenObj.C_Object, type, 1); + + return cnt; + } + function api_CUser_Add_Item_list(SUser, item_list) { + for (local i = 0; i < item_list.len(); i++) { + local item_id = item_list[i][0]; // 道具代码 + local quantity = item_list[i][1]; // 道具数量 + local cnt = Nangua_Hellparty.checkInventorySlot(SUser, item_id); + if (cnt == 1) { + SUser.GiveItem(item_id, quantity); //使用窗口发送奖励 + } else { + local RewardItems = []; + RewardItems.append([item_id, quantity]); + local title = "GM" + local Text = "由于背包空间不足,已通过邮件发送,请查收!" + //角色类 发送邮件函数 (标题, 正文, 金币, 道具列表[[3037,100],[3038,100]]) + SUser.ReqDBSendMultiMail(title, Text, 0, RewardItems) + } + } + Nangua_Hellparty.SendItemWindowNotification(SUser, item_list); + } + function SendItemWindowNotification(SUser, item_list) { + local Pack = Packet(); + Pack.Put_Header(1, 163); //协议 + Pack.Put_Byte(1); //默认1 + Pack.Put_Short(0); //槽位id 填入0即可 + Pack.Put_Int(0); //未知 0以上即可 + Pack.Put_Short(item_list.len()); //道具组数 + //写入道具代码和道具数量 + for (local i = 0; i < item_list.len(); i++) { + Pack.Put_Int(item_list[i][0]); //道具代码 + Pack.Put_Int(item_list[i][1]); //道具数量 装备/时装时 任意均可 + } + Pack.Finalize(true); //确定发包内容 + SUser.Send(Pack); //发包 + Pack.Delete(); //清空buff区 + } +} \ No newline at end of file diff --git a/示例项目/深渊活动/深渊活动配置_nangua.json b/示例项目/深渊活动/深渊活动配置_nangua.json new file mode 100644 index 0000000..b7d0c94 --- /dev/null +++ b/示例项目/深渊活动/深渊活动配置_nangua.json @@ -0,0 +1,59 @@ +{ + "深渊活动配置":{ + "活动总开关(true开启/false关闭)":true, + "活动期间开启物品禁止丢弃功能(true开启/false关闭)":true, + "每周几开放活动":[1, 2], + "信息发送位置":14, + "1.说明_每周开放日期": "1代表周一,2代表周二,3代表周三,4代表周四,5代表周五,6代表周六,7代表周日", + "2.说明_活动时间段": "12,0代表12点0分,19,30代表19点30分", + "3.说明_奖励列表": "0代表点券,1代表代币,否则为道具ID", + "4.说明_例子":"如每周开放日期为1和2,活动时间段为12,0和19,30,代表每周一和周二12点0分以及19点30分开启活动", + "活动时间段":[ + { + "开启时间":[12, 0], + "持续时间(分钟)":30, + "指定道具奖励配置": { + "指定道具ID": [ + { + "指定道具": 27098, + "奖励次数": 3, + "奖励列表": [ + [3037, 1], + [3171, 2] + ] + }, + { + "指定道具": 27099, + "奖励次数": 5, + "奖励列表": [ + [0, 1] + ] + } + ] + } + }, + { + "开启时间":[19, 30], + "持续时间(分钟)":30, + "指定道具奖励配置": { + "指定道具ID": [ + { + "指定道具": 27099, + "奖励次数": 2, + "奖励列表": [ + [3038, 1] + ] + }, + { + "指定道具": 27098, + "奖励次数": 2, + "奖励列表": [ + [1, 1] + ] + } + ] + } + } + ] + } +} \ No newline at end of file diff --git a/示例项目/深渊爆率实时控制/Proj.ifo b/示例项目/深渊爆率实时控制/Proj.ifo new file mode 100644 index 0000000..fdaa19c --- /dev/null +++ b/示例项目/深渊爆率实时控制/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "深渊爆率实时控制", + "ProjectDescribe": "同PVF里etc/itemdropinfo_monster_hell.etc的爆率一致用法,建议使用工具调整后对应填入数值", + "ProjectAuthor": "南瓜", + "ProjectVersion": 1.1, + "ProjectConfig": "深渊爆率实时控制_Nangua.json", + "ProjectFiles": [ + "深渊爆率实时控制.nut" + ], + "ProjectRunFunc": "_Dps_HellPartyDrop_Main_" +} \ No newline at end of file diff --git a/示例项目/深渊爆率实时控制/深渊爆率实时控制.nut b/示例项目/深渊爆率实时控制/深渊爆率实时控制.nut new file mode 100644 index 0000000..9612507 --- /dev/null +++ b/示例项目/深渊爆率实时控制/深渊爆率实时控制.nut @@ -0,0 +1,25 @@ +function _Dps_HellPartyDrop_Main_() { + Cb_WongWork_CMonsterDrop_Hell_Enter_Func.CMonsterDrop_Hell <- function(args) { + local config = GlobalConfig.Get("深渊爆率实时控制_Nangua.json"); + local CMonsterDrop_Hell_ptr = args[0]; + local rand = NativePointer(CMonsterDrop_Hell_ptr).add(72); + local fir = config["非常困难(大牛)"]; + local sec = config["困难(小牛)"]; + + local Buf = blob(0); + for (local i = 0; i < 5; i++) { + Buf.writen(fir[i], 'i'); + } + for (local i = 0; i < 5; i++) { + Buf.writen(sec[i], 'i'); + } + + local arr = []; + local len = Buf.len(); + for (local i = 0; i < len; i++) { + Buf.seek(i, 'b'); + arr.append(Buf.readn('b')); + } + rand.writeByteArray(arr); + } +} \ No newline at end of file diff --git a/示例项目/深渊爆率实时控制/深渊爆率实时控制_Nangua.json b/示例项目/深渊爆率实时控制/深渊爆率实时控制_Nangua.json new file mode 100644 index 0000000..04de499 --- /dev/null +++ b/示例项目/深渊爆率实时控制/深渊爆率实时控制_Nangua.json @@ -0,0 +1,6 @@ +{ + "非常困难(大牛)":[550000, 950000, 983000, 985000, 1000000, 1000001], + "困难(小牛)":[600001, 960001, 989001, 990001, 1000001, 1000002], + "提示": "参数最后的一位是区分大牛和小牛,是固定值不要修改", + "提示2": "最好使用工具调整后再抄PVF里的数值etc/itemdropinfo_monster_hell.etc" +} \ No newline at end of file diff --git a/示例项目/清理点券付费错误/Proj.ifo b/示例项目/清理点券付费错误/Proj.ifo new file mode 100644 index 0000000..823ef66 --- /dev/null +++ b/示例项目/清理点券付费错误/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "清理点券付费错误", + "ProjectDescribe": "定时清理付费错误。", + "ProjectAuthor": "zz", + "ProjectVersion": 1.2, + "ProjectConfig": "清理付费错误.json", + "ProjectFiles": [ + "清理点券付费错误.nut" + ], + "ProjectRunFunc": "_Dps_CleanUpBillingErrors_Main_" +} \ No newline at end of file diff --git a/示例项目/清理点券付费错误/清理付费错误.json b/示例项目/清理点券付费错误/清理付费错误.json new file mode 100644 index 0000000..cfd377d --- /dev/null +++ b/示例项目/清理点券付费错误/清理付费错误.json @@ -0,0 +1,11 @@ +{ + ">>>>>>>>每天00:00定时清理,这行是备注<<<<<<<<":6666666666, + "清理付费错误配置":{ + "开启小时(24小时制)":0, + "开启分钟":0 + }, + "数据库IP 不是外置数据库不要更改": "127.0.0.1", + "数据库端口 不懂不要更改": 3306, + "数据库用户名 本地用户名不懂不要更改": "game", + "数据库密码 本地密码不懂不要更改": "uu5!^%jg" +} \ No newline at end of file diff --git a/示例项目/清理点券付费错误/清理点券付费错误.nut b/示例项目/清理点券付费错误/清理点券付费错误.nut new file mode 100644 index 0000000..a09aa43 --- /dev/null +++ b/示例项目/清理点券付费错误/清理点券付费错误.nut @@ -0,0 +1,43 @@ +//清理付费错误 +function CleanUpBillingErrors() { + local Config = GlobalConfig.Get("清理付费错误.json"); + local PoolObj = MysqlPool.GetInstance(); + local Ip = Config["数据库IP 不是外置数据库不要更改"]; + local Port = Config["数据库端口 不懂不要更改"]; + local DbName = Config["数据库用户名 本地用户名不懂不要更改"]; + local Password = Config["数据库密码 本地密码不懂不要更改"]; + //设置数据库连接信息 + PoolObj.SetBaseConfiguration(Ip, Port, DbName, Password); + //连接池大小 + PoolObj.PoolSize = 10; + local CheckSql = "UPDATE taiwan_billing.cash_cera SET cera_cold = 0"; + //从池子拿连接 + local SqlObj = MysqlPool.GetInstance().GetConnect(); + local Ret = SqlObj.Select(CheckSql, ["int"]); + //把连接还池子 + MysqlPool.GetInstance().PutConnect(SqlObj); + World.SendNotiPacketMessage("玩家付费错误已解决!", 16); +}; + +// 入口点 +function _Dps_CleanUpBillingErrors_Main_() { + _Dps_CleanUpBillingErrors_Logic_(); +} + + +// 逻辑入口点 +function _Dps_CleanUpBillingErrors_Logic_() { + // 从配置中读取开启时间 + local Config = GlobalConfig.Get("清理付费错误.json"); + local hour = Config["清理付费错误配置"]["开启小时(24小时制)"]; + local minute = Config["清理付费错误配置"]["开启分钟"]; + + // cron 表达式 (秒 分 时 日 月 周) + local cronExpression = format("0 %d %d * * *", minute, hour); + + // 注册定时任务 + Timer.SetCronTask(CleanUpBillingErrors, { + Cron = cronExpression, + Name = "CleanUpBillingErrors" + }); +} \ No newline at end of file diff --git a/示例项目/点券充值卡/Proj.ifo b/示例项目/点券充值卡/Proj.ifo new file mode 100644 index 0000000..b59e7ae --- /dev/null +++ b/示例项目/点券充值卡/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "点券充值卡", + "ProjectDescribe": "根据道具ID充值对应的点券数量", + "ProjectAuthor": "南瓜", + "ProjectVersion": 1.0, + "ProjectConfig": "点券充值卡配置_Nangua.json", + "ProjectFiles": [ + "点券充值卡.nut" + ], + "ProjectRunFunc": "_Dps_handleCeraCard_Main_" +} \ No newline at end of file diff --git a/示例项目/点券充值卡/点券充值卡.nut b/示例项目/点券充值卡/点券充值卡.nut new file mode 100644 index 0000000..ffd18a0 --- /dev/null +++ b/示例项目/点券充值卡/点券充值卡.nut @@ -0,0 +1,38 @@ +function handleCeraCardBynangua(SUser, ItemId) { + local Config = GlobalConfig.Get("点券充值卡配置_Nangua.json"); + local ItemIdStr = ItemId.tostring(); + + if (Config["充值卡配置"].rawin(ItemIdStr)) { + local CeraAmount = Config["充值卡配置"][ItemIdStr]; + SUser.RechargeCera(CeraAmount); + if (Config["启用233发包(true/false)"]) { + SUser.SendNotiBox(format(Config["充值成功提示"], CeraAmount), 1); + }else{ + SUser.SendNotiPacketMessage(format(Config["充值成功提示"], CeraAmount), 8); + } + } +} + +//加载入口 +function _Dps_handleCeraCard_Main_() { + _Dps_handleCeraCard_Logic_(); +} + +//重载入口 +function _Dps_handleCeraCard_Main_Reload_(OldConfig) { + local Config = GlobalConfig.Get("点券充值卡配置_Nangua.json"); + // 删除旧的注册 + foreach(itemId, _ in OldConfig["充值卡配置"]) { + Cb_Use_Item_Sp_Func.rawdelete(itemId.tointeger()); + } + //重新注册 + _Dps_handleCeraCard_Logic_(); +} + +function _Dps_handleCeraCard_Logic_() { + local Config = GlobalConfig.Get("点券充值卡配置_Nangua.json"); + // 注册所有充值卡 + foreach(itemId, _ in Config["充值卡配置"]) { + Cb_Use_Item_Sp_Func[itemId.tointeger()] <- handleCeraCardBynangua; + } +} \ No newline at end of file diff --git a/示例项目/点券充值卡/点券充值卡配置_Nangua.json b/示例项目/点券充值卡/点券充值卡配置_Nangua.json new file mode 100644 index 0000000..46dc515 --- /dev/null +++ b/示例项目/点券充值卡/点券充值卡配置_Nangua.json @@ -0,0 +1,12 @@ +{ + "充值卡配置": { + "2024120601": 1000, + "2024120602": 5000, + "2024120603": 10000, + "2024120604": 30000, + "2024120605": 50000 + }, + "提示": "如果使用233发包需要客户端插件,在群文件搜索<客户端插件消息框233>下载使用,否则会导致游戏崩溃", + "启用233发包(true/false)": false, + "充值成功提示": "成功充值 %d 点券" +} \ No newline at end of file diff --git a/示例项目/物品回收UI/Proj.ifo b/示例项目/物品回收UI/Proj.ifo new file mode 100644 index 0000000..8785395 --- /dev/null +++ b/示例项目/物品回收UI/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "物品回收UI", + "ProjectDescribe": "需启动器支持或者dll支持,否则物品无法放入回收箱,根据放入的物品获取对应的奖励,", + "ProjectAuthor": "南瓜", + "ProjectVersion": 1.0, + "ProjectConfig": "物品回收UI配置_Nangua.json", + "ProjectFiles": [ + "物品回收UI.nut" + ], + "ProjectRunFunc": "_Dps_RecycleItemUI_Main_" +} diff --git a/示例项目/物品回收UI/物品回收UI.nut b/示例项目/物品回收UI/物品回收UI.nut new file mode 100644 index 0000000..d826fcb --- /dev/null +++ b/示例项目/物品回收UI/物品回收UI.nut @@ -0,0 +1,330 @@ +function _Dps_RecycleItemUI_Main_() { + // 跳过指定道具ID原逻辑1 + Cb_UseLimitCube_Check_Error_Leave_Func.crossoverByNangua <- function(args) { + local Config = GlobalConfig.Get("物品回收UI配置_Nangua.json"); + local ItemObj = Item(args[5]); + local ItemId = ItemObj.GetIndex(); + if (ItemId == Config["回收箱道具ID"]) { + return 17; + } + } + + // 回收逻辑 + Cb_UseLimitCube_Process_Leave_Func.crossoverByNangua <- function(args) { + local Config = GlobalConfig.Get("物品回收UI配置_Nangua.json"); + local SUser = User(args[1]); + local msg_base = args[2]; + if (msg_base) { + local slot = NativePointer(msg_base).add(13).readShort(); + local boxslot = NativePointer(msg_base).add(19).readShort(); + local InvenObj = SUser.GetInven(); + local boxobj = InvenObj.GetSlot(Inven.INVENTORY_TYPE_ITEM, boxslot); + local box_index = boxobj.GetIndex(); + local ItemObj = InvenObj.GetSlot(Inven.INVENTORY_TYPE_ITEM, slot); + local Item_Id = ItemObj.GetIndex(); + if (!ItemObj) return; + if (box_index == Config["回收箱道具ID"]) { + local Recycleitem_name = PvfItem.GetNameById(Item_Id); + local CheckItemLock = Sq_CallFunc(S_Ptr("0x8646942"), "int", ["pointer", "int", "int"], SUser.C_Object, 1, slot); + if (CheckItemLock) { + return; + } + // 检查装备是否在排除列表中 + if ("指定装备ID不参与回收" in Config["回收配置"] && Config["回收配置"]["指定装备ID不参与回收"].len() > 0) { + local isExcluded = false; + foreach (excludeId in Config["回收配置"]["指定装备ID不参与回收"]) { + if (excludeId == Item_Id) { + isExcluded = true; + break; + } + } + if (isExcluded) { + _RecycleItemUI_nangua.RecycleError(SUser, format(Config["回收失败信息"], Recycleitem_name)); + return; + } + } + + local PvfItemObj = PvfItem.GetPvfItemById(Item_Id); + local Item_type = PvfItemObj.IsStackable(); + local Item_Count = ItemObj.GetAdd_Info(); + local rarity = PvfItemObj.GetRarity(); + local level = PvfItemObj.GetUsableLevel(); + local allRewards = []; + local foundValidItem = false; + + // 检查指定装备回收配置 + foreach(item in Config["回收配置"]["指定装备回收"]) { + if(item[0] == Item_Id) { + local Rewarditem = item[1]; + local minCount = item[2]; + local maxCount = item[3]; + local Rewarditem_name = PvfItem.GetNameById(Rewarditem); + local RewardItemObj = PvfItem.GetPvfItemById(Rewarditem); + local item_upgrade = ItemObj.GetUpgrade(); + if (item_upgrade > 0) { + Recycleitem_name = "+" + item_upgrade + Recycleitem_name; + } + local count = 0; + if (Item_type == 1 && Item_Count > 1) { + for (local i = 0; i < Item_Count; i++) { + count += MathClass.Rand(minCount, maxCount + 1); + } + Recycleitem_name = Recycleitem_name + "x" + Item_Count; + } else { + count = MathClass.Rand(minCount, maxCount + 1); + } + + if (Rewarditem == 0) { + SUser.RechargeCera(count); + _RecycleItemUI_nangua.sendRewardMessageForCera(SUser, Recycleitem_name, ItemObj, count, Item_Id); + } else { + local equ_type = NativePointer(RewardItemObj.C_Object).add(141 * 4).readU32(); + if (equ_type > 0) { + count = 1; + } + allRewards.append([Rewarditem, count]); + _RecycleItemUI_nangua.sendRewardMessageForItem(SUser, Recycleitem_name, ItemObj, Rewarditem_name, count, equ_type, Item_Id, Rewarditem); + } + + _RecycleItemUI_nangua.DeleteAndUpdate(SUser, ItemObj, slot); + + foundValidItem = true; + break; + } + } + + // 如果没有找到指定装备配置,尝试品级回收 + if (!foundValidItem && Config["品级回收配置"]["开启品级以及装备等级的回收(true开启/false关闭)"]) { + foreach(cfg in Config["品级回收配置"]["配置列表"]) { + if (cfg["装备品级"] == rarity && cfg["装备等级"] == level) { + local reward = cfg["奖励"][0]; + local count = MathClass.Rand(reward[1], reward[2] + 1); + if (reward[0] == 0) { + SUser.RechargeCera(count); + _RecycleItemUI_nangua.sendRewardMessageForCera(SUser, Recycleitem_name, ItemObj, count, Item_Id); + } else { + local Rewarditem_name = PvfItem.GetNameById(reward[0]); + local RewardItemObj = PvfItem.GetPvfItemById(reward[0]); + local equ_type = NativePointer(RewardItemObj.C_Object).add(141 * 4).readU32(); + if (equ_type > 0) { + count = 1; + } + allRewards.append([reward[0], count]); + _RecycleItemUI_nangua.sendRewardMessageForItem(SUser, Recycleitem_name, ItemObj, Rewarditem_name, count, equ_type, Item_Id, reward[0]); + } + _RecycleItemUI_nangua.DeleteAndUpdate(SUser, ItemObj, slot); + foundValidItem = true; + break; + } + } + } + + // 处理回收结果 + if (foundValidItem) { + if (allRewards.len() > 0) { + _RecycleItemUI_nangua.api_CUser_Add_Item_list(SUser, allRewards); + _RecycleItemUI_nangua.SendItemWindowNotification(SUser, allRewards); + if(Config["回收成功是否扣除回收券道具(true扣除/false不扣除)"]){ + InvenObj.DeleteItemCount(box_index, 1); + } + } + } else { + // 如果没有匹配到任何回收配置 + _RecycleItemUI_nangua.RecycleError(SUser, format(Config["回收失败信息"], Recycleitem_name)); + } + return; + } + } + } +} + +class _RecycleItemUI_nangua { + // 删除道具并更新客户端 + function DeleteAndUpdate(SUser, ItemObj, slot) { + ItemObj.Delete(); + SUser.SendUpdateItemList(1, 0, slot); + } + + // 回收失败 + function RecycleError(SUser, msg) { + local Config = GlobalConfig.Get("物品回收UI配置_Nangua.json"); + if(Config["信息提示窗口提示(true开启/false关闭)"]) { + SUser.SendNotiBox(msg, 1); + } else { + SUser.SendNotiPacketMessage(msg, Config["信息播报发送位置"]); + } + } + + // 发送点券奖励消息 + function sendRewardMessageForCera(SUser, Recycleitem_name, ItemObj, count, Item_Id) { + local Config = GlobalConfig.Get("物品回收UI配置_Nangua.json"); + local AdMsgObj = AdMsg(); + AdMsgObj.PutType(Config["信息播报发送位置"]); + if (Config["信息播报发送位置"] != 14){ + AdMsgObj.PutString(" "); + } + AdMsgObj.PutImoticon(Config["表情ID"]); + AdMsgObj.PutString(Config["成功回收"]["标题"]); + AdMsgObj.PutEquipment("[" + Recycleitem_name + "]", ItemObj, _RecycleItemUI_nangua.RarityColor(Item_Id)); + AdMsgObj.PutString(Config["成功回收"]["奖励提示"]); + AdMsgObj.PutColorString("[" + count + "]", [255, 20, 0]); + AdMsgObj.PutString("点券"); + AdMsgObj.Finalize(); + SUser.Send(AdMsgObj.MakePack()); + AdMsgObj.Delete(); + } + + // 发送道具奖励消息 + function sendRewardMessageForItem(SUser, Recycleitem_name, ItemObj, Rewarditem_name, count, equ_type, Item_Id, Rewarditem) { + local Config = GlobalConfig.Get("物品回收UI配置_Nangua.json"); + local AdMsgObj = AdMsg(); + AdMsgObj.PutType(Config["信息播报发送位置"]); + if (Config["信息播报发送位置"] != 14){ + AdMsgObj.PutString(" "); + } + AdMsgObj.PutImoticon(Config["表情ID"]); + AdMsgObj.PutString(Config["成功回收"]["标题"]); + AdMsgObj.PutEquipment("[" + Recycleitem_name + "]", ItemObj, _RecycleItemUI_nangua.RarityColor(Item_Id)); + AdMsgObj.PutString(Config["成功回收"]["奖励提示"]); + if (equ_type > 0) { + AdMsgObj.PutEquipment(Rewarditem); + } else { + AdMsgObj.PutColorString("[" + count + "]", [255, 20, 0]); + AdMsgObj.PutString(Config["成功回收"]["单位"]); + AdMsgObj.PutEquipment(Rewarditem); + } + AdMsgObj.Finalize(); + SUser.Send(AdMsgObj.MakePack()); + AdMsgObj.Delete(); + } + function RarityColor(item_id) { + local PvfItemObj = PvfItem.GetPvfItemById(item_id); + if (PvfItemObj == null) { + return; + } + local CItem_get_rarity = PvfItemObj.GetRarity(); // 装备品级 + return _RecycleItemUI_nangua.rarityColorMap[(CItem_get_rarity).tostring()]; + } + //品级对应的RGB + rarityColorMap = { + "0": [255, 255, 255], // 普通 + "1": [104, 213, 237], // 高级 + "2": [179, 107, 255], // 稀有 + "3": [255, 0, 255], // 神器 + "4": [255, 180, 0], // 史诗 + "5": [255, 102, 102], // 勇者 + "6": [255, 20, 147], // 深粉红色 + "7": [255, 215, 0] // 金色 + }; + function api_CUser_Add_Item_list(SUser, item_list) { + for (local i = 0; i < item_list.len(); i++) { + local item_id = item_list[i][0]; // 道具代码 + local quantity = item_list[i][1]; // 道具数量 + local InvenObj = SUser.GetInven(); + // 获取道具对象 + local PvfItemObj = PvfItem.GetPvfItemById(item_id); + // 获取道具类型 + local equ_type = NativePointer(PvfItemObj.C_Object).add(141 * 4).readU32(); + // 获取最大堆叠数量 + local maxStack = Sq_CallFunc(S_Ptr("0x0822C9FC"), "int", ["pointer"], PvfItemObj.C_Object); + + // 如果是装备,直接检查空格并处理 + if (equ_type > 0) { + local cnt = _RecycleItemUI_nangua.checkInventorySlot(SUser, item_id); + if (cnt == 1) { + SUser.GiveItem(item_id, quantity); + } else { + local RewardItems = []; + RewardItems.append([item_id, quantity]); + local title = "GM"; + local Text = "由于背包空间不足,已通过邮件发送,请查收!"; + SUser.ReqDBSendMultiMail(title, Text, 0, RewardItems); + } + continue; + } + + // 获取道具在背包中的槽位 + local slot = InvenObj.GetSlotById(item_id); + if (slot != -1) { + // 获取槽位中的道具对象 + local ItemObj = InvenObj.GetSlot(1, slot); + // 获取当前堆叠数量 + local currentCount = Sq_CallFunc(S_Ptr("0x80F783A"), "int", ["pointer"], ItemObj.C_Object); + + // 如果当前堆叠未满,计算可以添加的数量 + if (currentCount < maxStack) { + local canAdd = maxStack - currentCount; + if (quantity <= canAdd) { + // 如果奖励数量小于等于可添加数量,直接添加 + Sq_CallFunc(S_Ptr("0x80CB884"), "int", ["pointer", "int"], ItemObj.C_Object, currentCount + quantity); + // 刷新背包显示 + SUser.SendUpdateItemList(1, 0, slot); + } else { + // 如果奖励数量大于可添加数量 + // 先将当前堆叠设置为最大 + Sq_CallFunc(S_Ptr("0x80CB884"), "int", ["pointer", "int"], ItemObj.C_Object, maxStack); + SUser.SendUpdateItemList(1, 0, slot); + // 将剩余数量通过邮件发送 + local remaining = quantity - canAdd; + local RewardItems = []; + RewardItems.append([item_id, remaining]); + local title = "GM"; + local Text = "由于背包堆叠已满,部分道具已通过邮件发送,请查收!"; + SUser.ReqDBSendMultiMail(title, Text, 0, RewardItems); + } + continue; + } else { + local RewardItems = []; + RewardItems.append([item_id, quantity]); + local title = "GM"; + local Text = "由于背包堆叠已满,已通过邮件发送,请查收!"; + SUser.ReqDBSendMultiMail(title, Text, 0, RewardItems); + continue; + } + } + + // 如果道具不在背包中,检查空格 + local cnt = _RecycleItemUI_nangua.checkInventorySlot(SUser, item_id); + if (cnt == 1) { + // 如果道具有空格,直接添加到背包 + SUser.GiveItem(item_id, quantity); + } else { + // 如果背包空间不足,通过邮件发送 + local RewardItems = []; + RewardItems.append([item_id, quantity]); + local title = "GM"; + local Text = "由于背包空间不足,已通过邮件发送,请查收!"; + SUser.ReqDBSendMultiMail(title, Text, 0, RewardItems); + } + } + } + function SendItemWindowNotification(SUser, item_list) { + local Pack = Packet(); + Pack.Put_Header(1, 163); //协议 + Pack.Put_Byte(1); //默认1 + Pack.Put_Short(0); //槽位id 填入0即可 + Pack.Put_Int(0); //未知 0以上即可 + Pack.Put_Short(item_list.len()); //道具组数 + //写入道具代码和道具数量 + for (local i = 0; i < item_list.len(); i++) { + Pack.Put_Int(item_list[i][0]); //道具代码 + Pack.Put_Int(item_list[i][1]); //道具数量 装备/时装时 任意均可 + } + Pack.Finalize(true); //确定发包内容 + SUser.Send(Pack); //发包 + Pack.Delete(); //清空buff区 + } + /** + * 根据道具类型背包空格数量 + * @param {pointer} SUser - 用户 + * @param {int} item_id - 需要查找的道具ID + * @returns {int} - 空格数量1 + */ + function checkInventorySlot(SUser, itemid) { + local InvenObj = SUser.GetInven(); + local type = Sq_CallFunc(S_Ptr("0x085018D2"), "int", ["pointer", "int"], InvenObj.C_Object, itemid); + local cnt = Sq_CallFunc(S_Ptr("0x08504F64"), "int", ["pointer", "int", "int"], InvenObj.C_Object, type, 1); + + return cnt; + } +} \ No newline at end of file diff --git a/示例项目/物品回收UI/物品回收UI配置_Nangua.json b/示例项目/物品回收UI/物品回收UI配置_Nangua.json new file mode 100644 index 0000000..7380f96 --- /dev/null +++ b/示例项目/物品回收UI/物品回收UI配置_Nangua.json @@ -0,0 +1,84 @@ +{ + "回收箱道具ID":7942, + "回收箱固定三个道具ID<7953,7942,690000274>更改其他道具ID无效":"", + "且此功能需要登录器支持或者dll支持目前已知入梦启动器以及长虹启动器支持":"", + "如果非要使用,那么到PVF里把需要回收的物品全部写入PVF该道具里,否则物品无法拖入回收箱":"", + "回收配置": { + "指定装备回收": [ + [27098, 3340, 1, 10], + [27102, 0, 1, 10], + [2711001, 3038, 1, 10], + [3340, 3038, 1, 2] + ], + "指定装备ID不参与回收": [27100, 27101], + "提示1":"[回收装备的ID,奖励道具的ID(0代表点券),数量最小值,数量最大值]", + "提示2":"如果指定装备回收和品级回收都命中的情况下,优先以指定装备回收为准", + "提示3":"排除装备ID中的装备不会参与回收,即使它们符合其他回收条件" + }, + "品级回收配置": { + "开启品级以及装备等级的回收(true开启/false关闭)": true, + "奖励说明": "奖励[道具ID(0代表点券),数量最小值,数量最大值]", + "品级说明":"0代表白装,1蓝装,2紫装,3粉装,4史诗,5勇者", + "配置列表": [{ + "装备品级": 2, + "装备等级": 55, + "奖励": [ + [3037, 10, 20] + ] + }, + { + "装备品级": 3, + "装备等级": 55, + "奖励": [ + [7421, 10, 20] + ] + }, + { + "装备品级": 4, + "装备等级": 50, + "奖励": [ + [3037, 10, 20] + ] + }, + { + "装备品级": 4, + "装备等级": 55, + "奖励": [ + [3037, 10, 20] + ] + }, + { + "装备品级": 4, + "装备等级": 60, + "奖励": [ + [3037, 10, 20] + ] + }, + { + "装备品级": 4, + "装备等级": 65, + "奖励": [ + [3037, 10, 20] + ] + }, + { + "装备品级": 4, + "装备等级": 70, + "奖励": [ + [3037, 10, 20] + ] + } + ] + }, + "回收失败信息": " 装备回收失败\n[%s]无法回收", + "信息播报发送位置": 37, + "信息提示窗口提示(true开启/false关闭)": false, + "提示":"开启弹窗提示需在群文件下载 <客户端消息框233.dll> 插件,否则会导致游戏崩溃", + "回收成功是否扣除回收券道具(true扣除/false不扣除)":true, + "表情ID": 59, + "成功回收": { + "标题": " 成功回收", + "奖励提示":"奖励", + "单位": "个" + } +} \ No newline at end of file diff --git a/示例项目/独立掉落/Proj.ifo b/示例项目/独立掉落/Proj.ifo new file mode 100644 index 0000000..c2eda21 --- /dev/null +++ b/示例项目/独立掉落/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "独立掉落", + "ProjectDescribe": "将掉落模式更改为独立掉落,(队伍中各玩家掉落互不干扰!)", + "ProjectAuthor": "挺迷的 & 倾泪寒", + "ProjectVersion": 1.1, + "ProjectConfig": "", + "ProjectFiles": [ + "独立掉落.nut" + ], + "ProjectRunFunc": "_Dps_IndependentDrop_Main_" +} \ No newline at end of file diff --git a/示例项目/独立掉落/独立掉落.nut b/示例项目/独立掉落/独立掉落.nut new file mode 100644 index 0000000..a6b61ea --- /dev/null +++ b/示例项目/独立掉落/独立掉落.nut @@ -0,0 +1,170 @@ +function api_copy_mapinfo_items_between_lists(list_dest, list_src) { + local it = Memory.alloc(4); + local end = Memory.alloc(4); + local next = Memory.alloc(4); + Sq_CallFunc(S_Ptr("0x08152B88"), "int", ["pointer", "pointer"], it.C_Object, list_src.C_Object); + Sq_CallFunc(S_Ptr("0x08152BBE"), "int", ["pointer", "pointer"], end.C_Object, list_src.C_Object); + + while (Sq_CallFunc(S_Ptr("0x08311E70"), "bool", ["pointer", "pointer"], it.C_Object, end.C_Object)) { + local map_item = Sq_CallFunc(S_Ptr("0x08311EBC"), "pointer", ["pointer"], it.C_Object); + Sq_CallFunc(S_Ptr("0x08152B52"), "pointer", ["pointer", "pointer"], list_dest.C_Object, map_item); + Sq_CallFunc(S_Ptr("0x08311E84"), "pointer", ["pointer", "pointer"], next.C_Object, it.C_Object); + } +} + + +function validate_pickup_permission(user, drop_id) { + if (!user || !drop_id) { + return 1; + } + + local SUser = User(user); + local SParty = SUser.GetParty(); + local SBattleField = SParty.GetBattleField(); + local fit = Memory.alloc(4); + + local currentMapInfo = Sq_CallFunc(S_Ptr("0x0822CBCC"), "pointer", ["pointer"], SBattleField.C_Object); + local drop_id_ptr = Memory.alloc(4); + drop_id_ptr.writeInt(drop_id); + + Sq_CallFunc(S_Ptr("0x08152532"), "pointer", ["pointer", "pointer", "pointer"], fit.C_Object, NativePointer(currentMapInfo).add(36).C_Object, drop_id_ptr.C_Object); + + local find_map_item = NativePointer(Sq_CallFunc(S_Ptr("0x081529B4"), "pointer", ["pointer"], fit.C_Object)).add(4); + if (!find_map_item.C_Object) { + return 1; + } + + local find_map_item_index = find_map_item.readU8(); + if (!find_map_item_index) + return 0; + + local find_map_item_partyIndex = find_map_item.add(8).readU32(); + + if (find_map_item_partyIndex< 1 || find_map_item_partyIndex > 4) + return 0; + + local cur_partyIndex = -1; + SParty.ForeachMember(function(PSUser, Index) { + if (PSUser.GetCID() == SUser.GetCID()) { + cur_partyIndex = Index + 1; + } + }); + + if (cur_partyIndex == find_map_item_partyIndex) + return 0; + return 1; +} + + +function _Dps_IndependentDrop_Main_() { + Cb_Battle_Field_MakeDropItems_Enter_Func._DPS_IndepentDrop <- function(args) { + local SParty = Party(NativePointer(args[0]).readPointer()); + local map_monster_param = NativePointer(Haker.CpuContext.ebp).add(0x10).readPointer(); + local monster_uid = NativePointer(NativePointer(Haker.CpuContext.ebp).add(0x18).readPointer()).readU16(); + + SParty.ForeachMember(function(SUser, Index) { + local user_item_list = Memory.alloc(8); + Sq_CallFunc(S_Ptr("0x08152A2C"), "pointer", ["pointer"], user_item_list.C_Object); + + Sq_CallFunc(S_Ptr("0x0830ADF6"), "void", ["pointer", "pointer", "pointer", "pointer", "int", "int", "int", "int", "int", "pointer", "float", "int"], args[0], user_item_list.C_Object, args[2], args[3], Index + 1, 1, args[6], args[7], args[8], args[9], args[10], args[11]); + + api_copy_mapinfo_items_between_lists(NativePointer(args[1]), user_item_list); + + local Pack = Packet(); + Sq_CallFunc(S_Ptr("0x0830B8CC"), "pointer", ["pointer", "pointer", "pointer", "int", "int", "pointer", "pointer"], args[0], Pack.C_Object, user_item_list.C_Object, monster_uid, Index + 1, args[3], map_monster_param); + SUser.Send(Pack); + Pack.Delete(); + + Sq_CallFunc(S_Ptr("0x08151F6E"), "pointer", ["pointer"], user_item_list.C_Object); + }); + + //跳过原逻辑 + Sq_WriteByteArr(S_Ptr("0x830ADFC"), Haker.AsmGenerateMcd( + "pop ebx", + "pop esi", + "pop edi", + "pop ebp", + "ret")); + return null; + }; + Cb_Battle_Field_MakeDropItems_Leave_Func._DPS_IndepentDrop <- function(args) { + //还原 + Sq_WriteByteArr(S_Ptr("0x830ADFC"), [0x81, 0xec, 0x7c, 0x01, 0x00, 0x00, 0x8B, 0x55, 0x18]); + return null; + } + + + //跳过原本的怪物死亡掉落包构造 + CBattle_Field_MakeNotiPacketDieMonster_DPS_IndepentDrop_JmpFlag <- false; + Cb_CBattle_Field_MakeNotiPacketDieMonster_Enter_Func._DPS_IndepentDrop <- function(args) { + if (Haker.NextReturnAddress == "0x830ce08") { + local v109 = NativePointer(NativePointer(Haker.CpuContext.ebp).sub(0x90).readPointer()); + local v106 = NativePointer(Haker.CpuContext.ebp).sub(0x96) + if (v109.add(44).readU32() == 100 && v106.readU8() != 1 && NativePointer(args[0]).add(109 * 4).readU32() != 1) { + //跳过原逻辑 + Sq_WriteByteArr(S_Ptr("0x830B8D3"), Haker.AsmGenerateMcd( + "add esp , 64", + "pop ebx", + "pop ebp", + "ret")); + CBattle_Field_MakeNotiPacketDieMonster_DPS_IndepentDrop_JmpFlag = true; + } + } + return null; + }; + Cb_CBattle_Field_MakeNotiPacketDieMonster_Leave_Func._DPS_IndepentDrop <- function(args) { + if (CBattle_Field_MakeNotiPacketDieMonster_DPS_IndepentDrop_JmpFlag) { + CBattle_Field_MakeNotiPacketDieMonster_DPS_IndepentDrop_JmpFlag = false; + Sq_WriteByteArr(S_Ptr("0x830B8D3"), [0x8B, 0x45, 0x18, 0x66, 0x89, 0x45, 0xB4, 0x8B, 0x45, 0x0C]); + return 1; + } + return null; + }; + + //跳过原本的怪物死亡掉落包队伍发包 + CParty_send_to_party_DPS_IndepentDrop_JmpFlag <- false; + Cb_CParty_send_to_party_Enter_Func._DPS_IndepentDrop <- function(args) { + if (Haker.NextReturnAddress == "0x85a39db" && Sq_CallFunc(S_Ptr("0x0848F438"), "int", ["pointer"], args[1]) == 0) { + //跳过原逻辑 + Sq_WriteByteArr(S_Ptr("0x859D154"), Haker.AsmGenerateMcd( + "add esp , 28", + "pop ebp", + "ret")); + CParty_send_to_party_DPS_IndepentDrop_JmpFlag = true; + } + return null; + }; + Cb_CParty_send_to_party_Leave_Func._DPS_IndepentDrop <- function(args) { + if (CParty_send_to_party_DPS_IndepentDrop_JmpFlag) { + Sq_WriteByteArr(S_Ptr("0x859D154"), [0xC7, 0x45, 0xF4, 0x00, 0x00, 0x00, 0x00]); + return 0; + } + return null; + }; + + //DisPatcher_GetItem::check_error 校验用户是否非法捡取物品 + Cb_GetItem_Check_Error_Leave_Func._DPS_IndepentDrop <- function(args) { + local user = NativePointer(Haker.CpuContext.ebp).add(0xc).readPointer(); + local msg_base = NativePointer(Haker.CpuContext.ebp).add(0x10).readPointer(); + local drop_id = NativePointer(msg_base).add(13).readU16(); + local res = validate_pickup_permission(user, drop_id); + return res; + }; + + //跳过物品掷点 + Cb_CItem_check_item_routing_Leave_Func._DPS_IndepentDrop <- function(args) { + return false; + }; + + //取消队友分金币 + Cb_CParty_get_live_count_enter_map_Leave_Func._DPS_IndepentDrop <- function(args) { + return 1; + }; + + //SS免确认 + Cb_CItem_IsRoutingItem_Leave_Func._DPS_IndepentDrop <- function(args) { + return 0; + } + Sq_WriteByteArr(S_Ptr("0x085A56D0"), [9]); + Sq_WriteByteArr(S_Ptr("0x085A41D1"), [0xB8, 0x00, 0x00, 0x00, 0x00, 0x90]); +} \ No newline at end of file diff --git a/示例项目/红字自动取消和道具自动打上全身红字/Proj.ifo b/示例项目/红字自动取消和道具自动打上全身红字/Proj.ifo new file mode 100644 index 0000000..282b783 --- /dev/null +++ b/示例项目/红字自动取消和道具自动打上全身红字/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "红字自动取消和道具自动打上全身红字", + "ProjectDescribe": "红字自动取消和道具自动打上全身红字", + "ProjectAuthor": "倾泪寒", + "ProjectVersion": 1.2, + "ProjectConfig": "红字自动取消和道具自动打上全身红字.json", + "ProjectFiles": [ + "红字自动取消和道具自动打上全身红字.nut" + ], + "ProjectRunFunc": "_Dps_HZZDQXHDJZDDSQSHZ_Main_" +} \ No newline at end of file diff --git a/示例项目/红字自动取消和道具自动打上全身红字/红字自动取消和道具自动打上全身红字.json b/示例项目/红字自动取消和道具自动打上全身红字/红字自动取消和道具自动打上全身红字.json new file mode 100644 index 0000000..617b738 --- /dev/null +++ b/示例项目/红字自动取消和道具自动打上全身红字/红字自动取消和道具自动打上全身红字.json @@ -0,0 +1,9 @@ +{ + "全身红字道具": [ + 75771, + 7588, + 7589, + 7590 + ], + "提示": "道具请用7577复制出来的道具 第一个体力 第二个精神 第三个力量 第四个 智力" +} \ No newline at end of file diff --git a/示例项目/红字自动取消和道具自动打上全身红字/红字自动取消和道具自动打上全身红字.nut b/示例项目/红字自动取消和道具自动打上全身红字/红字自动取消和道具自动打上全身红字.nut new file mode 100644 index 0000000..04f9b78 --- /dev/null +++ b/示例项目/红字自动取消和道具自动打上全身红字/红字自动取消和道具自动打上全身红字.nut @@ -0,0 +1,85 @@ +function _Dps_HZZDQXHDJZDDSQSHZ_Main_() { + local Config = GlobalConfig.Get("红字自动取消和道具自动打上全身红字.json"); + Cb_Use_Item_Sp_Func[Config["全身红字道具"][0]] <- function(SUser, ItemId) { + qsdshz(SUser, 1281); + } + + Cb_Use_Item_Sp_Func[Config["全身红字道具"][1]] <- function(SUser, ItemId) { + qsdshz(SUser, 1282); + } + + Cb_Use_Item_Sp_Func[Config["全身红字道具"][2]] <- function(SUser, ItemId) { + qsdshz(SUser, 1283); + } + + Cb_Use_Item_Sp_Func[Config["全身红字道具"][3]] <- function(SUser, ItemId) { + qsdshz(SUser, 1284); + } + + + + + //自动取消红字 + Cb_User_Insert_Item_Leave_Func.hzzdqc <- function(args) { + local SUser = User(NativePointer(args[0]).readPointer()); + local InvenObj = SUser.GetInven(); + local idx = args.pop(); + if (idx > 0) { + local inven_item = InvenObj.GetSlot(1, idx); + local item_id = inven_item.GetIndex(); + local pvfitem = PvfItem.GetPvfItemById(item_id); + if (Sq_CallFunc(S_Ptr("0x08150812"), "int", ["pointer"], inven_item.C_Object)) { + if(inven_item.GetAmplification() >=1281 &&inven_item.GetAmplification()<= 1284){ + + inven_item.SetAmplification(0); + inven_item.Flush(); + SUser.SendUpdateItemList(1, 0, idx); + } + } + + } + + } +} + + + + +function qsdshz(SUser, type) { + + local InvenObj = SUser.GetInven(); + //遍历身上的每一件装备 + /* + for (local u = 10; u <= 21; u++) { + if (u != 11) { + + local EquObj = InvenObj.GetSlot(Inven.INVENTORY_TYPE_BODY, u); + if (EquObj && !EquObj.IsEmpty) { + local ItemId1 = EquObj.GetIndex(); + + EquObj.SetAmplification(type); + EquObj.Flush(); + SUser.SendUpdateItemList(1, 3, u); + } + } + } + */ + + for (local u = 9; u <= 56; u++) { + + + local EquObj = InvenObj.GetSlot(Inven.INVENTORY_TYPE_ITEM, u); + if (EquObj && !EquObj.IsEmpty) { + local ItemId1 = EquObj.GetIndex(); + + EquObj.SetAmplification(type); + EquObj.Flush(); + SUser.SendUpdateItemList(1, 0, u); + } + + } + + +} + + diff --git a/示例项目/自动刷新GM工具的邮件/Proj.ifo b/示例项目/自动刷新GM工具的邮件/Proj.ifo new file mode 100644 index 0000000..3fe0e0c --- /dev/null +++ b/示例项目/自动刷新GM工具的邮件/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "自动刷新GM工具的邮件", + "ProjectDescribe": "自动刷新GM工具的邮件", + "ProjectAuthor": "倾泪寒 & Pluto.", + "ProjectVersion": 1.2, + "ProjectConfig": "", + "ProjectFiles": [ + "自动刷新GM工具的邮件.nut" + ], + "ProjectRunFunc": "_Dps_AutomaticallyRefreshGmToolEmails_Main_" +} \ No newline at end of file diff --git a/示例项目/自动刷新GM工具的邮件/自动刷新GM工具的邮件.nut b/示例项目/自动刷新GM工具的邮件/自动刷新GM工具的邮件.nut new file mode 100644 index 0000000..6136a1b --- /dev/null +++ b/示例项目/自动刷新GM工具的邮件/自动刷新GM工具的邮件.nut @@ -0,0 +1,88 @@ +/* +文件名:自动刷新GM工具的邮件.nut +路径:OfficialProject/自动刷新GM工具的邮件/自动刷新GM工具的邮件.nut +创建日期:2025-12-16 20:49 +文件用途: +*/ + + +//数据库连接 +_DetectingDatabaseEmailMysql_ <- null; +//上次查询的最新ID +_DetectingDatabaseEmailLastId_ <- -1; + +function _DetectingDatabaseEmailRefresh_() { + //第一次初始化数据库连接 + if (_DetectingDatabaseEmailMysql_ == null) { + _DetectingDatabaseEmailMysql_ = Mysql(Str_Ptr("127.0.0.1"), 3306, Str_Ptr("taiwan_cain"), Str_Ptr("game"), Str_Ptr("uu5!^%jg")); + if (_DetectingDatabaseEmailMysql_ == null) { + print("数据库连接失败!"); + return; + } + _DetectingDatabaseEmailMysql_.Exec_Sql(format("SET NAMES %s", "latin1")); + } + + //查询最新主键 + local Ret = _DetectingDatabaseEmailMysql_.Select("SELECT letter_id FROM taiwan_cain_2nd.letter ORDER BY letter_id DESC LIMIT 1", ["int"]); + if (!Ret || Ret.len() == 0) { + // print("查询最新letter_id失败或无数据"); + return; + } + + local LatestId = Ret[0][0]; + //第一次初始化,记录最新ID,不处理数据 + if (_DetectingDatabaseEmailLastId_ == -1) { + _DetectingDatabaseEmailLastId_ = LatestId; + print("首次初始化,最新letter_id:" + LatestId); + return; + } + + //有新的邮件数据(最新ID大于上次记录的ID) + if (LatestId > _DetectingDatabaseEmailLastId_) { + //查询新增数据的条数和charac_no字段 + local sql = format("SELECT charac_no FROM taiwan_cain_2nd.letter WHERE letter_id > %d AND letter_id <= %d", _DetectingDatabaseEmailLastId_, LatestId); + local NewDataRet = _DetectingDatabaseEmailMysql_.Select(sql, ["int"]); + + if (NewDataRet && NewDataRet.len() > 0) { + local NewCount = NewDataRet.len(); + print("新增邮件数据条数:" + NewCount); + + local CharacMap = {}; + //遍历获取所有新增的charac_no + for (local i = 0; i< NewDataRet.len(); i++) { + local charac_no = NewDataRet[i][0]; + CharacMap.rawset(charac_no, true); + } + + foreach(cid,_ in CharacMap) { + local SUser = World.GetUserByCid(cid); + if (SUser) { + local MailBox = Sq_CallFunc(S_Ptr("0x0823020C"), "pointer", ["pointer"], SUser.C_Object); + local NotLoaded_Count = Sq_CallFunc(S_Ptr("0x084ED330"), "int", ["pointer"], MailBox); + local Not_Count = Sq_CallFunc(S_Ptr("0x0823455A"), "int", ["pointer"], MailBox); + + local Pack = Packet(); + Pack.Put_Header(0, 99); + Pack.Put_Short(Not_Count); + Pack.Finalize(true); + SUser.Send(Pack); + Pack.Delete(); + } + } + } else { + print("查询新增数据失败"); + } + + //更新上次记录的最新ID为当前最新ID + _DetectingDatabaseEmailLastId_ = LatestId; + } else { + //无新增数据 + //print("无新增邮件数据"); + } +} + +function _Dps_AutomaticallyRefreshGmToolEmails_Main_() { + Timer.SetCronTask(function() { + _DetectingDatabaseEmailRefresh_(); + }, "*/1 * * * * ?"); +} \ No newline at end of file diff --git a/示例项目/装备回收/Proj.ifo b/示例项目/装备回收/Proj.ifo new file mode 100644 index 0000000..9e35402 --- /dev/null +++ b/示例项目/装备回收/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "装备回收", + "ProjectDescribe": "装备回收,根据指定回收的ID奖励随机数量的奖励,以及根据品级以及装备等级进行回收给与奖励", + "ProjectAuthor": "南瓜", + "ProjectVersion": 1.4, + "ProjectConfig": "装备回收配置_Nangua.json", + "ProjectFiles": [ + "装备回收.nut" + ], + "ProjectRunFunc": "_Dps_RecycleItem_Main_" +} \ No newline at end of file diff --git a/示例项目/装备回收/装备回收.nut b/示例项目/装备回收/装备回收.nut new file mode 100644 index 0000000..8c120b7 --- /dev/null +++ b/示例项目/装备回收/装备回收.nut @@ -0,0 +1,340 @@ +// 回收装备的主函数 +function RecycleItemFuncBynangua(SUser, ItemId) { + local Config = GlobalConfig.Get("装备回收配置_Nangua.json"); + + // 获取用户的背包对象,如果不存在则直接返回 + local InvenObj = SUser.GetInven(); + if (!InvenObj) { + return; + } + + local foundValidSlot = false; + local index = 0; + local allRewards = []; + + // 遍历装备栏指定范围的格子 + for (local i = Config["回收配置"]["回收位置"][0]; i <= Config["回收配置"]["回收位置"][1]; i++) { + local ItemObj = InvenObj.GetSlot(1, i); + + // 如果该槽为空,跳过 + if (ItemObj.IsEmpty) { + continue; + } + + // 检查装备是否上锁,若上锁则跳过该装备 + local CheckItemLock = Sq_CallFunc(S_Ptr("0x8646942"), "int", ["pointer", "int", "int"], SUser.C_Object, 1, i); + if (CheckItemLock) { + continue; + } + + local Item_Id = ItemObj.GetIndex(); + + // 检查装备是否在排除列表中,如果在则跳过 + if ("指定装备ID不参与回收" in Config["回收配置"] && Config["回收配置"]["指定装备ID不参与回收"].len() > 0) { + local isExcluded = false; + foreach (excludeId in Config["回收配置"]["指定装备ID不参与回收"]) { + if (excludeId == Item_Id) { + isExcluded = true; + break; + } + } + if (isExcluded) { + continue; + } + } + + local PvfItemObj = PvfItem.GetPvfItemById(Item_Id); + local rarity = PvfItemObj.GetRarity(); + local level = PvfItemObj.GetUsableLevel(); + local Recycleitem_name = PvfItem.GetNameById(Item_Id); + + // 检查指定装备回收配置 + local found = false; + foreach(item in Config["回收配置"]["指定装备回收"]) { + if(item[0] == Item_Id) { + local Rewarditem = item[1]; + local minCount = item[2]; + local maxCount = item[3]; + local Rewarditem_name = PvfItem.GetNameById(Rewarditem); + local RewardItemObj = PvfItem.GetPvfItemById(Rewarditem); + local item_upgrade = ItemObj.GetUpgrade(); + local totalRewards = []; + if (item_upgrade > 0) { + Recycleitem_name = "+" + item_upgrade + Recycleitem_name; + } + + local count = MathClass.Rand(minCount, maxCount + 1); + if (Rewarditem == 0) { + SUser.RechargeCera(count); + _RecycleItem_nangua.sendRewardMessageForCera(SUser, Recycleitem_name, ItemObj, count, Item_Id); + } else { + local equ_type = NativePointer(RewardItemObj.C_Object).add(141 * 4).readU32(); + if (equ_type > 0) { + count = 1; + } + totalRewards.append([Rewarditem, count]); + allRewards.append([Rewarditem, count]); // 添加到总奖励列表 + _RecycleItem_nangua.api_CUser_Add_Item_list(SUser, totalRewards); + _RecycleItem_nangua.sendRewardMessageForItem(SUser, Recycleitem_name, ItemObj, Rewarditem_name, count, equ_type, Item_Id, Rewarditem); + } + foundValidSlot = true; + ItemObj.Delete(); + SUser.SendUpdateItemList(1, 0, i); + index++; + found = true; + break; + } + } + + if(found) continue; + + // 检查品级回收配置 + if (Config["品级回收配置"]["开启品级以及装备等级的回收(true开启/false关闭)"]) { + foreach(cfg in Config["品级回收配置"]["配置列表"]) { + if (cfg["装备品级"] == rarity && cfg["装备等级"] == level) { + foundValidSlot = true; + local reward = cfg["奖励"][0]; + local count = MathClass.Rand(reward[1], reward[2] + 1); + local totalReward = []; + if (reward[0] == 0) { + SUser.RechargeCera(count); + _RecycleItem_nangua.sendRewardMessageForCera(SUser, Recycleitem_name, ItemObj, count, Item_Id); + } else { + local Rewarditem_name = PvfItem.GetNameById(reward[0]); + // 获取装备类型大于0为装备 + local RewardItemObj = PvfItem.GetPvfItemById(reward[0]); + local equ_type = NativePointer(RewardItemObj.C_Object).add(141 * 4).readU32(); + if (equ_type > 0) { + count = 1; + } + totalReward.append([reward[0], count]); + allRewards.append([reward[0], count]); + _RecycleItem_nangua.api_CUser_Add_Item_list(SUser, totalReward); + _RecycleItem_nangua.sendRewardMessageForItem(SUser, Recycleitem_name, ItemObj, Rewarditem_name, count, equ_type, Item_Id, reward[0]); + } + ItemObj.Delete(); + SUser.SendUpdateItemList(1, 0, i); + index++; + } + } + } + } + + // 发送结果消息 + if (foundValidSlot) { + if (index > 0) { + SUser.SendNotiPacketMessage("恭喜: " + index + " 件装备回收成功。", Config["信息播报发送位置"]); + if (allRewards.len() > 0) { + _RecycleItem_nangua.SendItemWindowNotification(SUser, allRewards); + } + } + if(Config["回收成功是否返还回收券道具(true返还/false不返还)"]){ + SUser.GiveItem(ItemId, 1); + } + } else { + _RecycleItem_nangua.RecycleError(SUser, Config["回收失败信息"], ItemId); + } +} +class _RecycleItem_nangua { + // 发送通知和返还道具 + function RecycleError(SUser, msg, ItemId) { + local Config = GlobalConfig.Get("装备回收配置_Nangua.json"); + if(Config["信息提示窗口提示(true开启/false关闭)"]) { + SUser.SendNotiBox(msg, 1); + } else { + SUser.SendNotiPacketMessage(msg, Config["信息播报发送位置"]); + } + SUser.GiveItem(ItemId, 1); + } + + // 发送点券奖励消息 + function sendRewardMessageForCera(SUser, Recycleitem_name, ItemObj, count, Item_Id) { + local Config = GlobalConfig.Get("装备回收配置_Nangua.json"); + local AdMsgObj = AdMsg(); + AdMsgObj.PutType(Config["信息播报发送位置"]); + AdMsgObj.PutString(" "); + AdMsgObj.PutImoticon(Config["表情ID"]); + AdMsgObj.PutString(Config["成功回收"]["标题"]); + AdMsgObj.PutEquipment("[" + Recycleitem_name + "]", ItemObj, _RecycleItem_nangua.RarityColor(Item_Id)); + AdMsgObj.PutString(Config["成功回收"]["奖励提示"]); + AdMsgObj.PutColorString("[" + count + "]", [255, 20, 0]); + AdMsgObj.PutString("点券"); + AdMsgObj.Finalize(); + SUser.Send(AdMsgObj.MakePack()); + AdMsgObj.Delete(); + } + + // 发送道具奖励消息 + function sendRewardMessageForItem(SUser, Recycleitem_name, ItemObj, Rewarditem_name, count, equ_type, Item_Id, Rewarditem) { + local Config = GlobalConfig.Get("装备回收配置_Nangua.json"); + local AdMsgObj = AdMsg(); + AdMsgObj.PutType(Config["信息播报发送位置"]); + AdMsgObj.PutString(" "); + AdMsgObj.PutImoticon(Config["表情ID"]); + AdMsgObj.PutString(Config["成功回收"]["标题"]); + AdMsgObj.PutEquipment("[" + Recycleitem_name + "]", ItemObj, _RecycleItem_nangua.RarityColor(Item_Id)); + AdMsgObj.PutString(Config["成功回收"]["奖励提示"]); + if (equ_type > 0) { + AdMsgObj.PutColorString("[" + Rewarditem_name + "]", _RecycleItem_nangua.RarityColor(Rewarditem)); + } else { + AdMsgObj.PutColorString("[" + count + "]", [255, 20, 0]); + AdMsgObj.PutString(Config["成功回收"]["单位"]); + AdMsgObj.PutColorString("[" + Rewarditem_name + "]", _RecycleItem_nangua.RarityColor(Rewarditem)); + } + AdMsgObj.Finalize(); + SUser.Send(AdMsgObj.MakePack()); + AdMsgObj.Delete(); + } + function RarityColor(item_id) { + local PvfItemObj = PvfItem.GetPvfItemById(item_id); + if (PvfItemObj == null) { + return; + } + local CItem_get_rarity = PvfItemObj.GetRarity(); // 装备品级 + return _RecycleItem_nangua.rarityColorMap[(CItem_get_rarity).tostring()]; + } + //品级对应的RGB + rarityColorMap = { + "0": [255, 255, 255], // 普通 + "1": [104, 213, 237], // 高级 + "2": [179, 107, 255], // 稀有 + "3": [255, 0, 255], // 神器 + "4": [255, 180, 0], // 史诗 + "5": [255, 102, 102], // 勇者 + "6": [255, 20, 147], // 深粉红色 + "7": [255, 215, 0] // 金色 + }; + function api_CUser_Add_Item_list(SUser, item_list) { + for (local i = 0; i < item_list.len(); i++) { + local item_id = item_list[i][0]; // 道具代码 + local quantity = item_list[i][1]; // 道具数量 + local InvenObj = SUser.GetInven(); + + // 获取道具对象 + local PvfItemObj = PvfItem.GetPvfItemById(item_id); + // 获取道具类型 + local equ_type = NativePointer(PvfItemObj.C_Object).add(141 * 4).readU32(); + // 获取最大堆叠数量 + local maxStack = Sq_CallFunc(S_Ptr("0x0822C9FC"), "int", ["pointer"], PvfItemObj.C_Object); + + // 如果是装备,直接检查空格并处理 + if (equ_type > 0) { + local cnt = _RecycleItem_nangua.checkInventorySlot(SUser, item_id); + if (cnt == 1) { + SUser.GiveItem(item_id, quantity); + } else { + local RewardItems = []; + RewardItems.append([item_id, quantity]); + local title = "GM"; + local Text = "由于背包空间不足,已通过邮件发送,请查收!"; + SUser.ReqDBSendMultiMail(title, Text, 0, RewardItems); + } + continue; + } + + // 获取道具在背包中的槽位 + local slot = InvenObj.GetSlotById(item_id); + if (slot != -1) { + // 获取槽位中的道具对象 + local ItemObj = InvenObj.GetSlot(1, slot); + // 获取当前堆叠数量 + local currentCount = Sq_CallFunc(S_Ptr("0x80F783A"), "int", ["pointer"], ItemObj.C_Object); + + // 如果当前堆叠未满,计算可以添加的数量 + if (currentCount < maxStack) { + local canAdd = maxStack - currentCount; + if (quantity <= canAdd) { + // 如果奖励数量小于等于可添加数量,直接添加 + Sq_CallFunc(S_Ptr("0x80CB884"), "int", ["pointer", "int"], ItemObj.C_Object, currentCount + quantity); + // 刷新背包显示 + SUser.SendUpdateItemList(1, 0, slot); + } else { + // 如果奖励数量大于可添加数量 + // 先将当前堆叠设置为最大 + Sq_CallFunc(S_Ptr("0x80CB884"), "int", ["pointer", "int"], ItemObj.C_Object, maxStack); + SUser.SendUpdateItemList(1, 0, slot); + // 将剩余数量通过邮件发送 + local remaining = quantity - canAdd; + local RewardItems = []; + RewardItems.append([item_id, remaining]); + local title = "GM"; + local Text = "由于背包堆叠已满,部分道具已通过邮件发送,请查收!"; + SUser.ReqDBSendMultiMail(title, Text, 0, RewardItems); + } + continue; + } else { + local RewardItems = []; + RewardItems.append([item_id, quantity]); + local title = "GM"; + local Text = "由于背包堆叠已满,已通过邮件发送,请查收!"; + SUser.ReqDBSendMultiMail(title, Text, 0, RewardItems); + continue; + } + } + + // 如果道具不在背包中,检查空格 + local cnt = _RecycleItem_nangua.checkInventorySlot(SUser, item_id); + if (cnt == 1) { + // 如果道具有空格,直接添加到背包 + SUser.GiveItem(item_id, quantity); + } else { + // 如果背包空间不足,通过邮件发送 + local RewardItems = []; + RewardItems.append([item_id, quantity]); + local title = "GM"; + local Text = "由于背包空间不足,已通过邮件发送,请查收!"; + SUser.ReqDBSendMultiMail(title, Text, 0, RewardItems); + } + } + } + function SendItemWindowNotification(SUser, item_list) { + local Pack = Packet(); + Pack.Put_Header(1, 163); //协议 + Pack.Put_Byte(1); //默认1 + Pack.Put_Short(0); //槽位id 填入0即可 + Pack.Put_Int(0); //未知 0以上即可 + Pack.Put_Short(item_list.len()); //道具组数 + //写入道具代码和道具数量 + for (local i = 0; i < item_list.len(); i++) { + Pack.Put_Int(item_list[i][0]); //道具代码 + Pack.Put_Int(item_list[i][1]); //道具数量 装备/时装时 任意均可 + } + Pack.Finalize(true); //确定发包内容 + SUser.Send(Pack); //发包 + Pack.Delete(); //清空buff区 + } + /** + * 根据道具类型背包空格数量 + * @param {pointer} SUser - 用户 + * @param {int} item_id - 需要查找的道具ID + * @returns {int} - 空格数量 + */ + function checkInventorySlot(SUser, itemid) { + local InvenObj = SUser.GetInven(); + local type = Sq_CallFunc(S_Ptr("0x085018D2"), "int", ["pointer", "int"], InvenObj.C_Object, itemid); + local cnt = Sq_CallFunc(S_Ptr("0x08504F64"), "int", ["pointer", "int", "int"], InvenObj.C_Object, type, 1); + + return cnt; + } +} + +//加载入口 +function _Dps_RecycleItem_Main_() { + _Dps_RecycleItem_Logic_(); +} + +//重载入口 +function _Dps_RecycleItem_Main_Reload_(OldConfig) { + local Cofig = GlobalConfig.Get("装备回收配置_Nangua.json"); + Cb_Use_Item_Sp_Func.rawdelete(OldConfig["回收配置"]["回收箱道具ID"].tointeger()); + + //重新注册 + _Dps_RecycleItem_Logic_(); +} + +function _Dps_RecycleItem_Logic_() { + local Cofig = GlobalConfig.Get("装备回收配置_Nangua.json"); + // 装备回收 + Cb_Use_Item_Sp_Func[Cofig["回收配置"]["回收箱道具ID"]] <- RecycleItemFuncBynangua; +} + diff --git a/示例项目/装备回收/装备回收配置_Nangua.json b/示例项目/装备回收/装备回收配置_Nangua.json new file mode 100644 index 0000000..e484901 --- /dev/null +++ b/示例项目/装备回收/装备回收配置_Nangua.json @@ -0,0 +1,81 @@ +{ + "回收配置": { + "回收箱道具ID": 2021458811, + "回收位置": [9, 16], + "指定装备回收": [ + [27098, 3340, 1, 10], + [27102, 0, 1, 10], + [2711001, 3038, 1, 10] + ], + "指定装备ID不参与回收": [27099, 27100, 27101], + "提示1":"[回收装备的ID,奖励道具的ID(0代表点券),数量最小值,数量最大值]", + "提示2":"回收位置快捷栏(快捷栏3-8),装备栏(快捷栏9-56,如第一排为9-16,第二排为17-24,第三排为25-32,第四排为33-40,第五排为41-48,第六排为49-56)", + "提示3":"如果指定装备回收和品级回收都命中的情况下,优先以指定装备回收为准", + "提示4":"排除装备ID中的装备不会参与回收,即使它们符合其他回收条件" + }, + "品级回收配置": { + "开启品级以及装备等级的回收(true开启/false关闭)": false, + "提示": "奖励[道具ID(0代表点券),数量最小值,数量最大值]", + "配置列表": [{ + "装备品级": 2, + "装备等级": 55, + "奖励": [ + [3037, 10, 20] + ] + }, + { + "装备品级": 3, + "装备等级": 10, + "奖励": [ + [7421, 10, 20] + ] + }, + { + "装备品级": 4, + "装备等级": 50, + "奖励": [ + [3037, 10, 20] + ] + }, + { + "装备品级": 4, + "装备等级": 55, + "奖励": [ + [3037, 10, 20] + ] + }, + { + "装备品级": 4, + "装备等级": 60, + "奖励": [ + [3037, 10, 20] + ] + }, + { + "装备品级": 4, + "装备等级": 65, + "奖励": [ + [3037, 10, 20] + ] + }, + { + "装备品级": 4, + "装备等级": 70, + "奖励": [ + [3037, 10, 20] + ] + } + ] + }, + "回收失败信息": " 装备回收失败\n 没有合适的装备或已上锁", + "信息播报发送位置": 37, + "信息提示窗口提示(true开启/false关闭)": false, + "提示":"开启弹窗提示需在群文件下载 <客户端消息框233.dll> 插件,否则会导致游戏崩溃", + "回收成功是否返还回收券道具(true返还/false不返还)":false, + "表情ID": 59, + "成功回收": { + "标题": " 成功回收", + "奖励提示":"奖励", + "单位": "个" + } +} \ No newline at end of file diff --git a/示例项目/装备继承券/Proj.ifo b/示例项目/装备继承券/Proj.ifo new file mode 100644 index 0000000..74245e1 --- /dev/null +++ b/示例项目/装备继承券/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "装备继承券", + "ProjectDescribe": "可以将装备的强化附魔增幅等属性更改到另外一个装备", + "ProjectAuthor": "南瓜", + "ProjectVersion": 1.3, + "ProjectConfig": "装备继承券配置_Nangua.json", + "ProjectFiles": [ + "装备继承券.nut" + ], + "ProjectRunFunc": "_Dps_InheritItem_Main_" +} diff --git a/示例项目/装备继承券/装备继承券.nut b/示例项目/装备继承券/装备继承券.nut new file mode 100644 index 0000000..ec6883a --- /dev/null +++ b/示例项目/装备继承券/装备继承券.nut @@ -0,0 +1,166 @@ + +function InheritFuncBynangua(SUser, ItemId) { + local Config = GlobalConfig.Get("装备继承券配置_Nangua.json"); + //获取玩家背包 + local InvenObj = SUser.GetInven(); + if(!InvenObj) { + return; + } + //获取装备栏第一栏装备 + local firstItemObj = InvenObj.GetSlot(Inven.INVENTORY_TYPE_ITEM, Config["继承配置"]["装备1摆放位置"]); + //获取装备栏第二栏装备 + local secondItemObj = InvenObj.GetSlot(Inven.INVENTORY_TYPE_ITEM, Config["继承配置"]["装备2摆放位置"]); + //装备不存在 + if (firstItemObj.IsEmpty || secondItemObj.IsEmpty) { + _InheritBynangua.NotifyUserAndReturn(SUser, ItemId, Config["继承配置"]["继承失败提示1"]); + return; + } + //获取ID + local firstItemIndex = firstItemObj.GetIndex(); + local secondItemIndex = secondItemObj.GetIndex(); + //获取类型 + local firstItemInfo = PvfItem.GetPvfItemById(firstItemIndex); + local firstItemType = NativePointer(firstItemInfo.C_Object).add(141 * 4).readU32(); + local secondItemInfo = PvfItem.GetPvfItemById(secondItemIndex); + local secondItemType = NativePointer(secondItemInfo.C_Object).add(141 * 4).readU32(); + //判断是否在禁止继承装备中 + foreach(exceptItemIndex in Config["继承配置"]["禁止继承装备ID"]) { + if(firstItemIndex == exceptItemIndex || secondItemIndex == exceptItemIndex) { + //获取装备名字 + local ItemName = PvfItem.GetNameById(exceptItemIndex); + _InheritBynangua.NotifyUserAndReturn(SUser, ItemId, format(Config["继承配置"]["继承失败提示5"], ItemName)); + return; + } + } + // 禁止继承的装备类型(10武器,11称号,12上衣,13头肩,14下装,15鞋子,16腰带,17项链,18手镯,19戒指,20左槽,21右槽) + if (Config["继承配置"]["禁止继承装备类型"].find(firstItemType || secondItemType) != null) { + _InheritBynangua.NotifyUserAndReturn(SUser, ItemId, Config["继承配置"]["继承失败提示2"]); + return; + } + // 1、2栏装备类型不同无法继承 + if (firstItemType != secondItemType) { + _InheritBynangua.NotifyUserAndReturn(SUser, ItemId, Config["继承配置"]["继承失败提示2"]); + return; + } + //装备等级大于等于50级才可以继承,1、2栏装备等级差距超过5级无法继承 + if (firstItemInfo.GetUsableLevel() < Config["继承配置"]["装备等级大于等于多少允许继承"] || secondItemInfo.GetUsableLevel() < Config["继承配置"]["装备等级大于等于多少允许继承"] || _InheritBynangua.CheckUsableLevelBynangua(firstItemInfo.GetUsableLevel() - secondItemInfo.GetUsableLevel()) > Config["继承配置"]["装备等级跨度超过多少不允许继承"]) { + _InheritBynangua.NotifyUserAndReturn(SUser, ItemId, Config["继承配置"]["继承失败提示3"]); + return; + } + //装备品级低于3无法继承 + if (firstItemInfo.GetRarity() < Config["继承配置"]["装备品级低于多少不允许继承"] || secondItemInfo.GetRarity() < Config["继承配置"]["装备品级低于多少不允许继承"]) { + _InheritBynangua.NotifyUserAndReturn(SUser, ItemId, Config["继承配置"]["继承失败提示4"]); + return; + } + if (!Config["继承配置"]["是否允许不同品级的装备继承(true可以,false不可以)"]) { + if (firstItemInfo.GetRarity() != secondItemInfo.GetRarity()) { + _InheritBynangua.NotifyUserAndReturn(SUser, ItemId, Config["继承配置"]["继承失败提示4"]); + return; + } + } + //满足以上条件后开始执行继承逻辑 + //将第一个装备的属性继承到第二个装备 + Sq_CallFunc(S_Ptr("0x8671EB2"), "int", ["pointer", "pointer", "pointer"], SUser.C_Object, secondItemObj.C_Object, firstItemObj.C_Object); + // 将第一个装备的镶嵌数据库ID设置为0 + NativePointer(firstItemObj.C_Object).add(25).writeU32(0); + //获取异界气息属性 + local firstBreath = NativePointer(firstItemObj.C_Object).add(31).readU8(); + local secondBreath = NativePointer(firstItemObj.C_Object).add(32).readU8(); + if(firstBreath > 0 && Config["继承配置"]["是否允许继承异界气息(true可以,false不可以)"]) { + //写入异界气息属性 + NativePointer(secondItemObj.C_Object).add(31).writeU8(firstBreath); + NativePointer(secondItemObj.C_Object).add(32).writeU8(secondBreath); + //清除第一件装备异界气息属性 + NativePointer(firstItemObj.C_Object).add(31).writeU8(0); + NativePointer(firstItemObj.C_Object).add(32).writeU8(0); + } + //移除被继承装备属性 + local removeAttributes = [6, 17, 51, 13]; + foreach (attributes in removeAttributes) { + NativePointer(firstItemObj.C_Object).add(attributes).writeU8(0); + } + //刷新背包信息 + SUser.SendUpdateItemList(1, 0, Config["继承配置"]["装备1摆放位置"]); + SUser.SendUpdateItemList(1, 0, Config["继承配置"]["装备2摆放位置"]); + firstItemObj = InvenObj.GetSlot(Inven.INVENTORY_TYPE_ITEM, Config["继承配置"]["装备1摆放位置"]); + secondItemObj = InvenObj.GetSlot(Inven.INVENTORY_TYPE_ITEM, Config["继承配置"]["装备2摆放位置"]); + //获取装备名字 + local firstItemName = PvfItem.GetNameById(firstItemObj.GetIndex()); + local secondItemName = PvfItem.GetNameById(secondItemObj.GetIndex()); + //获取强化信息 + local upgrade = secondItemObj.GetUpgrade();//强化 + //字符串合成 + if (upgrade > 0) { + secondItemName = "+" + upgrade + " " + secondItemName; + } + //组合消息 + local AdMsgObj = AdMsg() + AdMsgObj.PutType(Config["继承成功提示"]["信息发送位置"]); + if (Config["继承成功提示"]["信息发送位置"] != 14){ + AdMsgObj.PutString(" "); + } + AdMsgObj.PutImoticon(Config["继承成功提示"]["表情"]); + AdMsgObj.PutString(Config["继承成功提示"]["信息1"]); + AdMsgObj.PutEquipment("[" + firstItemName + "]", firstItemObj, _InheritBynangua.RarityColor(firstItemIndex)); + AdMsgObj.PutString(Config["继承成功提示"]["信息2"]); + AdMsgObj.PutEquipment("[" + secondItemName + "]", secondItemObj, _InheritBynangua.RarityColor(secondItemIndex)); + AdMsgObj.Finalize(); + SUser.Send(AdMsgObj.MakePack()); + AdMsgObj.Delete(); +} + +class _InheritBynangua { + function CheckUsableLevelBynangua(value) { + return value >= 0 ? value : -value; + } + function NotifyUserAndReturn(SUser, ItemId, message) { + local Config = GlobalConfig.Get("装备继承券配置_Nangua.json"); + //发送通知 + if(Config["弹窗提示(true为开启,false为关闭)"]) { + SUser.SendNotiBox(message, 1); + }else{ + SUser.SendNotiPacketMessage(message, 8); + } + //返还消耗的道具 + SUser.GiveItem(ItemId, 1); + } + function RarityColor(item_id) { + local PvfItemObj = PvfItem.GetPvfItemById(item_id); + if (PvfItemObj == null) { + return; + } + local CItem_get_rarity = PvfItemObj.GetRarity(); // 装备品级 + return _InheritBynangua.rarityColorMap[(CItem_get_rarity).tostring()]; + } + //品级对应的RGB + rarityColorMap = { + "0": [255, 255, 255], // 普通 + "1": [104, 213, 237], // 高级 + "2": [179, 107, 255], // 稀有 + "3": [255, 0, 255], // 神器 + "4": [255, 180, 0], // 史诗 + "5": [255, 102, 102], // 勇者 + "6": [255, 20, 147], // 深粉红色 + "7": [255, 215, 0] // 金色 + }; +} + +//加载入口 +function _Dps_InheritItem_Main_() { + _Dps_InheritItem_Logic_(); +} + +//重载入口 +function _Dps_InheritItem_Main_Reload_(OldConfig) { + local Cofig = GlobalConfig.Get("装备继承券配置_Nangua.json"); + Cb_Use_Item_Sp_Func.rawdelete(OldConfig["继承配置"]["继承券道具ID"].tointeger()); + + //重新注册 + _Dps_InheritItem_Logic_(); +} + +function _Dps_InheritItem_Logic_() { + local Cofig = GlobalConfig.Get("装备继承券配置_Nangua.json"); + // 装备继承 + Cb_Use_Item_Sp_Func[Cofig["继承配置"]["继承券道具ID"]] <- InheritFuncBynangua; +} \ No newline at end of file diff --git a/示例项目/装备继承券/装备继承券_Lenheart.json b/示例项目/装备继承券/装备继承券_Lenheart.json new file mode 100644 index 0000000..8f4df3f --- /dev/null +++ b/示例项目/装备继承券/装备继承券_Lenheart.json @@ -0,0 +1,13 @@ +{ + "继承券id_不可热重载": 17577, + "公告提示1":"请将已强化/增幅/锻造的装备放到\n[装备栏] 的第一格,要继承的装备放到第二格。", + "公告提示2":"继承或要继承的装备不能是称号!", + "公告提示3":"继承失败!\n[武器]\n只能继承至:\n[武器]!", + "公告提示4":"继承失败!\n[首饰]\n只能继承至:\n[首饰]", + "公告提示5":"继承失败!\n[魔法石、辅助装备]只能继承至:\n[魔法石或辅助装备]", + "公告提示6":"继承失败!\n[护肩、上衣、下装、腰带、鞋]\n只能继承至:\n[护肩、上衣、下装、腰带、鞋]", + "公告提示7":"只有大于或者等于 【60级】 的装备才可以继承。", + "公告提示8":"只有 【神器】 以上的品级才能继承。", + "最低可转移等级":60, + "最低可转移品级":3 +} \ No newline at end of file diff --git a/示例项目/装备继承券/装备继承券配置_Nangua.json b/示例项目/装备继承券/装备继承券配置_Nangua.json new file mode 100644 index 0000000..fdf23de --- /dev/null +++ b/示例项目/装备继承券/装备继承券配置_Nangua.json @@ -0,0 +1,30 @@ +{ + "继承配置":{ + "继承券道具ID":123014, + "装备1摆放位置":9, + "装备2摆放位置":10, + "装备等级大于等于多少允许继承":50, + "装备等级跨度超过多少不允许继承":10, + "装备品级低于多少不允许继承":3, + "禁止继承装备ID":[1222, 1111], + "禁止继承装备类型":[11], + "是否允许不同品级的装备继承(true可以,false不可以)":true, + "是否允许继承异界气息(true可以,false不可以)":false, + "继承失败提示1":" 继承失败\n请检查装备栏第一/二格是否存在装备!", + "继承失败提示2":" 继承失败\n请检查装备类型是否正确!", + "继承失败提示3":" 继承失败\n请检查装备等级是否正确!", + "继承失败提示4":" 继承失败\n请检查装备品级是否正确!", + "继承失败提示5":" 继承失败\n特殊/指定装备 [%s] 禁止继承!" + }, + "继承成功提示":{ + "信息发送位置": 37, + "表情": 59, + "信息1": " 已成功将", + "信息2": "属性继承至" + }, + "弹窗提示(true为开启,false为关闭)":false, + "提示1":"开启弹窗提示需在群文件下载 <客户端消息框233.dll> 插件,否则会导致游戏崩溃", + "提示2":"(装备栏9-56,第一排第一格为9,第二格为10以此类推)快捷栏(第一格为3,第二格为4以此类推)", + "提示3":"装备类型继承指武器继承武器(不同职业也可以继承),防具(腰带继承腰带,鞋子继承鞋子以此类推,不同甲类也可继承),首饰同理", + "提示4":"表情可在群文件搜索<表情>下载导入pvf" +} \ No newline at end of file diff --git a/示例项目/装备镶嵌与时装镶嵌/Proj.ifo b/示例项目/装备镶嵌与时装镶嵌/Proj.ifo new file mode 100644 index 0000000..504397d --- /dev/null +++ b/示例项目/装备镶嵌与时装镶嵌/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "装备镶嵌与时装镶嵌", + "ProjectDescribe": "使用之前,请务必保证安装了群内的 \"客户端消息框233.dll\" 插件,这个插件放入你客户端的插件加载目录即可\n如果你是0627的客户端版本还需要安装群内的 \"0627装备镶嵌.dll\" 插件", + "ProjectAuthor": "倾泪寒", + "ProjectVersion": 1.2, + "ProjectConfig": "装备镶嵌与时装镶嵌_Lenheart.json", + "ProjectFiles": [ + "装备镶嵌与时装镶嵌.nut" + ], + "ProjectRunFunc": "_Dps_Equ2AvaJewel_Main_" +} \ No newline at end of file diff --git a/示例项目/装备镶嵌与时装镶嵌/装备镶嵌与时装镶嵌.nut b/示例项目/装备镶嵌与时装镶嵌/装备镶嵌与时装镶嵌.nut new file mode 100644 index 0000000..a67715e --- /dev/null +++ b/示例项目/装备镶嵌与时装镶嵌/装备镶嵌与时装镶嵌.nut @@ -0,0 +1,590 @@ +class EquimentUseJewel { + + ExecUser = null; + + //建库建表 + function CreateMysqlTable() { + local CreateSql1 = "create database if not exists l_equ_jewel default charset utf8;" + local CreateSql2 = "CREATE TABLE l_equ_jewel.equipment ( equ_id int(11) AUTO_INCREMENT, jewel_data blob NOT NULL,andonglishanbai_flag int(11),date VARCHAR(255), PRIMARY KEY (equ_id)) ENGINE=InnoDB DEFAULT CHARSET=utf8,AUTO_INCREMENT = 150;" + local SqlObj = MysqlPool.GetInstance().GetConnect(); + SqlObj.Exec_Sql(CreateSql1); + SqlObj.Exec_Sql(CreateSql2); + MysqlPool.GetInstance().PutConnect(SqlObj); + } + + function api_get_jewel_socket_data(id) { //获取徽章数据,存在返回徽章数据,不存在返回空字节数据 + local CheckSql = "SELECT jewel_data FROM l_equ_jewel.equipment where equ_id = " + id + ";"; + //从池子拿连接 + local SqlObj = MysqlPool.GetInstance().GetConnect(); + local Ret = SqlObj.Select(CheckSql, ["binary"]); + //把连接还池子 + MysqlPool.GetInstance().PutConnect(SqlObj); + //没结婚要返回false + if (Ret.len()< 1 || Ret[0][0] == null) { + return 0; + } else { + return Ret[0][0]; + } + } + + function api_exitjeweldata(id) { //0代表不存在,存在返回1 + local CheckSql = "SELECT andonglishanbai_flag FROM l_equ_jewel.equipment where equ_id = " + id + ";"; + //从池子拿连接 + local SqlObj = MysqlPool.GetInstance().GetConnect(); + local Ret = SqlObj.Select(CheckSql, ["int"]); + //把连接还池子 + MysqlPool.GetInstance().PutConnect(SqlObj); + //没结婚要返回false + if (Ret.len()< 1 || Ret[0][0] == null) { + return 0; + } else { + return Ret[0][0]; + } + } + + function save_equiment_socket(socket_data, id) { //0代表不存在,存在返回1 + local CheckSql = "UPDATE l_equ_jewel.equipment SET jewel_data = 0x" + socket_data + " WHERE equ_id = " + id + ";"; + //从池子拿连接 + local SqlObj = MysqlPool.GetInstance().GetConnect(); + local Ret = SqlObj.Select(CheckSql, ["int"]); + //把连接还池子 + MysqlPool.GetInstance().PutConnect(SqlObj); + //没结婚要返回false + if (Ret.len()< 1 || Ret[0][0] == null) { + return false; + } else { + return true; + } + } + + function CUser_SendCmdErrorPacket(SUser, id, id2) { + local Pack = Packet(); + Pack.Put_Header(1, id); + Pack.Put_Byte(0); + Pack.Put_Byte(id2); + Pack.Finalize(true); + SUser.Send(Pack); + Pack.Delete(); + } + + function api_PacketBuf_get_buf(packet_buf) { + return NativePointer(NativePointer(packet_buf).add(20).readPointer()).add(13); + } + + function add_equiment_socket(equipment_type) { //0代表开孔失败 成功返回标识 + /* + 武器10 + 称号11 + 上衣12 + 头肩13 + 下衣14 + 鞋子15 + 腰带16 + 项链17 + 手镯18 + 戒指19 + 辅助装备20 + 魔法石21 + */ + + /* + 红色:'010000000000010000000000000000000000000000000000000000000000' A + 黄色:'020000000000020000000000000000000000000000000000000000000000' B + 绿色:'040000000000040000000000000000000000000000000000000000000000' C + 蓝色:'080000000000080000000000000000000000000000000000000000000000' D + 白金:'100000000000100000000000000000000000000000000000000000000000' + */ + local DB_JewelsocketData = ""; + switch (equipment_type) { + case 10: //武器10 SS + DB_JewelsocketData = "100000000000000000000000000000000000000000000000000000000000" + break; + case 11: //称号11 SS + DB_JewelsocketData = "100000000000000000000000000000000000000000000000000000000000" + break; + case 12: //上衣12 C + DB_JewelsocketData = "040000000000040000000000000000000000000000000000000000000000" + break; + case 13: //头肩13 B + DB_JewelsocketData = "020000000000020000000000000000000000000000000000000000000000" + break; + case 14: //下衣14 C + DB_JewelsocketData = "040000000000040000000000000000000000000000000000000000000000" + break; + case 15: //鞋子15 D + DB_JewelsocketData = "080000000000080000000000000000000000000000000000000000000000" + break; + case 16: //腰带16 A + DB_JewelsocketData = "010000000000010000000000000000000000000000000000000000000000" + break; + case 17: //项链17 B + DB_JewelsocketData = "020000000000020000000000000000000000000000000000000000000000" + break; + case 18: //手镯18 D + DB_JewelsocketData = "080000000000080000000000000000000000000000000000000000000000" + break; + case 19: //戒指19 A + DB_JewelsocketData = "010000000000010000000000000000000000000000000000000000000000" + break; + case 20: //辅助装备20 S + DB_JewelsocketData = "100000000000000000000000000000000000000000000000000000000000" + break; + case 21: //魔法石21 S + DB_JewelsocketData = "100000000000000000000000000000000000000000000000000000000000" + break; + default: + DB_JewelsocketData = "000000000000000000000000000000000000000000000000000000000000" + break; + } + local date = time(); + local Ct = Sq_GetTimestampString(); + date = date.tostring() + Ct; + + local CheckSql = "INSERT INTO l_equ_jewel.equipment (andonglishanbai_flag,jewel_data,date) VALUES(1,0x" + DB_JewelsocketData + ",\'" + date + "\');"; + local CheckSql1 = "SELECT equ_id FROM l_equ_jewel.equipment where date = \'" + date + "\';"; + //从池子拿连接 + local SqlObj = MysqlPool.GetInstance().GetConnect(); + SqlObj.Select(CheckSql, ["int"]); + local Ret = SqlObj.Select(CheckSql1, ["int"]); + //把连接还池子 + MysqlPool.GetInstance().PutConnect(SqlObj); + if (Ret.len()< 1 || Ret[0][0] == null) { + return 0; + } else { + return Ret[0][0]; + } + return 0; + } + + function CStackableItem_getJewelTargetSocket(C_Object) { + return Sq_CallFunc(S_Ptr("0x0822CA28"), "int", ["pointer"], C_Object); + } + + function CUser_SendUpdateItemList_DB(SUser, Slot, DB_JewelSocketData) { + local Pack = Packet(); + Pack.Put_Header(0, 14); + Pack.Put_Byte(0); + Pack.Put_Short(1); + local InvenObj = SUser.GetInven(); + Sq_CallFunc(S_Ptr("0x084FC6BC"), "int", ["pointer", "int", "int", "pointer"], InvenObj.C_Object, 1, Slot, Pack.C_Object); + Pack.Put_BinaryEx(DB_JewelSocketData.C_Object, 30); + Pack.Finalize(true); + SUser.Send(Pack); + Pack.Delete(); + } + + function GetByte(value) { + local Blob = blob(); + Blob.writen(value, 'w'); + return Blob; + } + + function intToHex(num) { + if (num == 0) { + return "0"; + } + local hexDigits = "0123456789abcdef"; + local hexString = ""; + while (num > 0) { + local remainder = num % 16; + hexString = hexDigits[remainder] + hexString; + num = (num / 16).tointeger(); + } + return hexString; + } + + function lengthCutting(str, ystr, num, maxLength) { + // 如果字符串长度小于最大长度,在前面补0 + local lengthDiff = maxLength - str.len(); + if (lengthDiff > 0) { + local zeroPadding = ""; + for (local i = 0; i< lengthDiff; i++) { + zeroPadding += "0"; + } + str = zeroPadding + str; + } + local strArr = ""; + for (local i = 0; i< str.len(); i += num) { + local endIndex = i + num; + if (endIndex > str.len()) { + endIndex = str.len(); + } + strArr += str.slice(i, endIndex); + } + return ystr + strArr; + } + + + + + + function HackAddSocketToAvatarLogic(Flag) { + if (Flag) { + Sq_WriteByteArr(S_Ptr("821A449"), [0x90, 0x90]); + Sq_WriteByteArr(S_Ptr("0x821A44B"), array(36, 0x90)); + } else { + Sq_WriteByteArr(S_Ptr("821A449"), [0x74, 0x2B]); + } + } + + + + function FixFunction() { + //称号回包 + Cb_CTitleBook_putItemData_Leave_Func.EquimentUseJewel <- function(args) { + local JewelSocketData = api_get_jewel_socket_data(NativePointer(args[3]).add(25).readU32()); + local ret = args.pop(); + if (JewelSocketData && NativePointer(JewelSocketData).add(0).readU8() != 0) { + local Pack = Packet(args[1]); + Pack.Put_BinaryEx(JewelSocketData.C_Object, 30); + } + return null; + }.bindenv(this); + + //设计图继承 + Cb_CUsercopyItemOption_Enter_Func.EquimentUseJewel <- function(args) { + local jewelSocketID = NativePointer(args[2]).add(25).readU32(); + NativePointer(args[1]).add(25).writeU32(jewelSocketID); + return null; + }.bindenv(this); + + //装备开孔 + Cb_AddSocketToAvatar_Enter_Func.EquimentUseJewel <- function(args) { + local SUser = User(args[1]); + local PackCopyBuffer = Memory.alloc(10001); + Memory.copy(PackCopyBuffer, NativePointer(args[2]), 1000); + local Pack = Packet(PackCopyBuffer.C_Object); + local equ_slot = Pack.GetShort(); + local equitem_id = Pack.GetInt(); + local sta_slot = Pack.GetShort(); + local CurCharacInvenW = SUser.GetInven(); + local inven_item = CurCharacInvenW.GetSlot(1, equ_slot); + + if (equ_slot > 56) { //修改后:大于56则是时装装备 原:如果不是装备文件就调用原逻辑 + equ_slot = equ_slot - 57; + local C_PacketBuf = api_PacketBuf_get_buf(args[2]) //获取原始封包数据 + C_PacketBuf.add(0).writeShort(equ_slot) //修改掉装备位置信息 时装类镶嵌从57开始。 + + //执行原逻辑 + return null; + } + //如果已开启镶嵌槽则不执行 + local equ_id = NativePointer(inven_item.C_Object).add(25).readU32(); + if (api_exitjeweldata(equ_id)) { + CUser_SendCmdErrorPacket(SUser, 209, 19); + HackAddSocketToAvatarLogic(true); + return null; + } + + local item = PvfItem.GetPvfItemById(equitem_id); + local ItemType = Sq_CallFunc(S_Ptr("0x08514D26"), "int", ["pointer"], item.C_Object); + + if (ItemType == 10) { + SUser.SendNotiBox("装备为武器类型,不支持打孔!", 1) + CUser_SendCmdErrorPacket(SUser, 209, 0); + HackAddSocketToAvatarLogic(true); + return null; + } else if (ItemType == 11) { + SUser.SendNotiBox("装备为称号类型,不支持打孔!", 1) + CUser_SendCmdErrorPacket(SUser, 209, 0); + HackAddSocketToAvatarLogic(true); + return null; + } + + local id = add_equiment_socket(ItemType); + + Sq_Inven_RemoveItemFormCount(CurCharacInvenW.C_Object, 1, sta_slot, 1, 8, 1); //删除打孔道具 + NativePointer(inven_item.C_Object).add(25).writeU32(id) //写入槽位标识 + SUser.SendUpdateItemList(1, 0, equ_slot); + + local JewelSocketData = api_get_jewel_socket_data(id); + CUser_SendUpdateItemList_DB(SUser, equ_slot, JewelSocketData); //用于更新镶嵌后的装备显示,这里用的是带镶嵌数据的更新背包函数,并非CUser_SendUpdateItemList + + local Pack = Packet(); + Pack.Put_Header(1, 209); + Pack.Put_Byte(1); + Pack.Put_Short(equ_slot + 104); + Pack.Put_Short(sta_slot); + Pack.Finalize(true); + SUser.Send(Pack); + Pack.Delete(); + HackAddSocketToAvatarLogic(true); + return null; + }.bindenv(this); + + Cb_AddSocketToAvatar_Leave_Func.EquimentUseJewel <- function(args) { + HackAddSocketToAvatarLogic(false); + return null; + }.bindenv(this); + + //装备镶嵌和时装镶嵌 + Cb_Dispatcher_UseJewel_Enter_Func.EquimentUseJewel <- function(args) { + local SUser = User(args[1]); + local Pack = Packet(args[2]); + local PackIndex = NativePointer(args[2]).add(4).readInt(); + local State = SUser.GetState(); + if (State != 3) return null; + + local avartar_inven_slot = Pack.GetShort(); + local avartar_item_id = Pack.GetInt(); + local emblem_cnt = Pack.GetByte(); + + //下面是参照原时装镶嵌的思路写的。个别点标记出来。 + if (avartar_inven_slot > 104) { + local equipment_inven_slot = avartar_inven_slot - 104; //取出真实装备所在背包位置值 + local Inven = SUser.GetInven(); + local equipment = Inven.GetSlot(1, equipment_inven_slot); + //校验是否合法 + if (!equipment || equipment.IsEmpty || (equipment.GetIndex() != avartar_item_id) || SUser.CheckItemLock(1, equipment_inven_slot)) return; + + local id = NativePointer(equipment.C_Object).add(25).readU32(); + local JewelSocketData = api_get_jewel_socket_data(id); + if (!JewelSocketData) return; + + local emblems = {}; + if (emblem_cnt <= 3) { + for (local i = 0; i< emblem_cnt; i++) { + local emblem_inven_slot = Pack.GetShort(); + local emblem_item_id = Pack.GetInt(); + local equipment_socket_slot = Pack.GetByte(); + local emblem = Inven.GetSlot(1, emblem_inven_slot); + //校验徽章及插槽数据是否合法 + if (!emblem || emblem.IsEmpty || (emblem.GetIndex() != emblem_item_id) || (equipment_socket_slot >= 3)) return; + + //校验徽章是否满足时装插槽颜色要求 + //获取徽章pvf数据 + local citem = PvfItem.GetPvfItemById(emblem_item_id); + if (!citem) return; + + //校验徽章类型 + if (!citem.IsStackable() || citem.GetItemType() != 20) return; + + //获取徽章支持的插槽 + local emblem_socket_type = CStackableItem_getJewelTargetSocket(citem.C_Object); + //获取要镶嵌的时装插槽类型 + local avartar_socket_type = JewelSocketData.add(equipment_socket_slot * 6).readShort(); + + if (!(emblem_socket_type & avartar_socket_type)) { + + return; + } + + emblems[equipment_socket_slot] <- [emblem_inven_slot, emblem_item_id]; + } + } + + + foreach(equipment_socket_slot, emblemObject in emblems) { + //删除徽章 + local emblem_inven_slot = emblemObject[0]; + Sq_Inven_RemoveItemFormCount(Inven.C_Object, 1, emblem_inven_slot, 1, 8, 1); //删除打孔道具 + //设置时装插槽数据 + local emblem_item_id = emblemObject[1]; + JewelSocketData.add(2 + 6 * equipment_socket_slot).writeU32(emblem_item_id); + } + + local Buf = Sq_Point2Blob(JewelSocketData.C_Object, 30); + local Str = ""; + foreach(Value in Buf) { + Str += format("%02X", Value); + } + + save_equiment_socket(Str, id); + // if (!save_equiment_socket(DB_JewelSocketData, id)) { + // print("写入失败了"); + // return null; + // } + + CUser_SendUpdateItemList_DB(SUser, equipment_inven_slot, JewelSocketData); //用于更新镶嵌后的装备显示,这里用的是带镶嵌数据的更新背包函数,并非CUser_SendUpdateItemList + local Pack = Packet(); + Pack.Put_Header(1, 209); + Pack.Put_Byte(1); + Pack.Put_Short(equipment_inven_slot + 104); + Pack.Finalize(true); + SUser.Send(Pack); + Pack.Delete(); + + return; + } + + AvatarLogic(args, PackIndex); + + return null; + }.bindenv(this); + + Cb_Dispatcher_UseJewel_Leave_Func.EquimentUseJewel <- function(args) { + return -1; + }.bindenv(this); + + //额外数据包,发送装备镶嵌数据给本地处理 + Cb_InterfacePacketBuf_put_packet_Leave_Func.EquimentUseJewel <- function(args) { + local ret = args.pop(); + local Inven_Item = NativePointer(args[1]); + if (Inven_Item.add(1).readU8() == 1) { + local ItemObj = Item(args[1]); + local JewelSocketData = api_get_jewel_socket_data(NativePointer(ItemObj.C_Object).add(25).readU32()); + if (JewelSocketData && JewelSocketData.add(0).readU8() != 0) { + local Pack = Packet(args[0]); + Pack.Put_BinaryEx(JewelSocketData.C_Object, 30); + } + } + return null; + }.bindenv(this); + + L_HookEquimentUseJewel(); + } + + + + + function WongWork_CAvatarItemMgr_getJewelSocketData(a, b) { + return Sq_CallFunc(S_Ptr("0x82F98F8"), "pointer", ["pointer", "int"], a, b); + } + + function CStackableItem_getJewelTargetSocket(C_Object) { + return Sq_CallFunc(S_Ptr("0x0822CA28"), "int", ["pointer"], C_Object); + } + + function CInventory_delete_item(C_Object, Type, Slot, Count, Ps, Log) { + return Sq_CallFunc(S_Ptr("0x850400C"), "int", ["pointer", "int", "int", "int", "int", "int"], C_Object, Type, Slot, Count, Ps, Log); + } + + function api_set_JewelSocketData(jewelSocketData, slot, emblem_item_id) { + if (jewelSocketData) { + NativePointer(jewelSocketData).add(slot * 6 + 2).writeInt(emblem_item_id); + } + } + + function DB_UpdateAvatarJewelSlot_makeRequest(a, b, c) { + return Sq_CallFunc(S_Ptr("0x843081C"), "pointer", ["int", "int", "pointer"], a, b, c); + } + + //获取时装在数据库中的uid + function api_get_avartar_ui_id(avartar) { + return NativePointer(avartar).add(7).readInt(); + } + + + function AvatarLogic(args, PackIndex) { + //角色 + local SUser = User(args[1]); + //包数据 + local Pack = Packet(args[2]); + //还原包读取数据号位 + NativePointer(args[2]).add(4).writeInt(PackIndex); + + //校验角色状态是否允许镶嵌 + if (!SUser || SUser.GetState() != 3) { + return; + } + //时装所在的背包槽 + local Inven_Slot = Pack.GetShort(); + //时装item_id + local Item_Id = Pack.GetInt(); + //本次镶嵌徽章数量 + local Emblem_Count = Pack.GetByte(); + + //获取时装道具 + local InvemObj = SUser.GetInven(); + local AvatarObj = InvemObj.GetSlot(2, Inven_Slot); + + + //校验时装 数据是否合法 + if (!AvatarObj || AvatarObj.IsEmpty || (AvatarObj.GetIndex() != Item_Id) || SUser.CheckItemLock(2, Inven_Slot)) return; + + local Avartar_AddInfo = AvatarObj.GetAdd_Info(); + //获取时装管理器 + local Inven_AvartarMgr = InvemObj.GetAvatarItemMgr(); + + //获取时装插槽数据 + local Jewel_Socket_Data = WongWork_CAvatarItemMgr_getJewelSocketData(Inven_AvartarMgr, Avartar_AddInfo); + if (!Jewel_Socket_Data) return; + + //最多只支持3个插槽 + if (Emblem_Count <= 3) { + local emblems = {}; + + for (local i = 0; i< Emblem_Count; i++) { + //徽章所在的背包槽 + local emblem_inven_slot = Pack.GetShort(); + //徽章item_id + local emblem_item_id = Pack.GetInt(); + //该徽章镶嵌的时装插槽id + local avartar_socket_slot = Pack.GetByte(); + + //获取徽章道具 + local EmblemObje = InvemObj.GetSlot(1, emblem_inven_slot); + + //校验徽章及插槽数据是否合法 + if (!EmblemObje || EmblemObje.IsEmpty || (EmblemObje.GetIndex() != emblem_item_id) || (avartar_socket_slot >= 3)) return; + + //校验徽章是否满足时装插槽颜色要求 + //获取徽章pvf数据 + local citem = PvfItem.GetPvfItemById(emblem_item_id); + if (!citem) return; + + //校验徽章类型 + if (!citem.IsStackable() || citem.GetItemType() != 20) return; + + //获取徽章支持的插槽 + local emblem_socket_type = CStackableItem_getJewelTargetSocket(citem.C_Object); + + //获取要镶嵌的时装插槽类型 + local avartar_socket_type = NativePointer(Jewel_Socket_Data).add(avartar_socket_slot * 6).readShort(); + + if (!(emblem_socket_type & avartar_socket_type)) return; + + emblems[avartar_socket_slot] <- [emblem_inven_slot, emblem_item_id]; + } + + //开始镶嵌 + foreach(avartar_socket_slot, emblemObject in emblems) { + //删除徽章 + local emblem_inven_slot = emblemObject[0]; + CInventory_delete_item(InvemObj.C_Object, 1, emblem_inven_slot, 1, 8, 1); + //设置时装插槽数据 + local emblem_item_id = emblemObject[1]; + api_set_JewelSocketData(Jewel_Socket_Data, avartar_socket_slot, emblem_item_id); + } + + //时装插槽数据存档 + DB_UpdateAvatarJewelSlot_makeRequest(SUser.GetCID(), api_get_avartar_ui_id(AvatarObj.C_Object), Jewel_Socket_Data); + + //通知客户端时装数据已更新 + SUser.SendUpdateItemList(1, 1, Inven_Slot); + + //回包给客户端 + local Pack = Packet(); + Pack.Put_Header(1, 204); + Pack.Put_Int(1); + Pack.Finalize(true); + SUser.Send(Pack); + Pack.Delete(); + } + + return null; + } + + constructor() { + CreateMysqlTable(); + + FixFunction(); + } +} + +function _Dps_Equ2AvaJewel_Main_() { + local Config = GlobalConfig.Get("装备镶嵌与时装镶嵌_Lenheart.json"); + local PoolObj = MysqlPool.GetInstance(); + local Ip = Config["数据库IP 不是外置数据库不要更改"]; + local Port = Config["数据库端口 不懂不要更改"]; + local DbName = Config["数据库用户名 本地用户名不懂不要更改"]; + local Password = Config["数据库密码 本地密码不懂不要更改"]; + //设置数据库连接信息 + PoolObj.SetBaseConfiguration(Ip, Port, DbName, Password); + //连接池大小 + PoolObj.PoolSize = 10; + //初始化 + PoolObj.Init(); + //装备镶嵌修复 + getroottable()._EquimentUseJewel_Object <- EquimentUseJewel(); +} \ No newline at end of file diff --git a/示例项目/装备镶嵌与时装镶嵌/装备镶嵌与时装镶嵌_Lenheart.json b/示例项目/装备镶嵌与时装镶嵌/装备镶嵌与时装镶嵌_Lenheart.json new file mode 100644 index 0000000..9cf2342 --- /dev/null +++ b/示例项目/装备镶嵌与时装镶嵌/装备镶嵌与时装镶嵌_Lenheart.json @@ -0,0 +1,6 @@ +{ + "数据库IP 不是外置数据库不要更改": "127.0.0.1", + "数据库端口 不懂不要更改": 3306, + "数据库用户名 本地用户名不懂不要更改": "game", + "数据库密码 本地密码不懂不要更改": "uu5!^%jg" +} \ No newline at end of file diff --git a/示例项目/觉醒券/Proj.ifo b/示例项目/觉醒券/Proj.ifo new file mode 100644 index 0000000..f4e0f1d --- /dev/null +++ b/示例项目/觉醒券/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "觉醒券", + "ProjectDescribe": "按官方的觉醒券来写,觉醒券道具可以复制 主线完成券 一类。", + "ProjectAuthor": "zz", + "ProjectVersion": 1.1, + "ProjectConfig": "觉醒券.json", + "ProjectFiles": [ + "觉醒券.nut" + ], + "ProjectRunFunc": "_Dps_AWAKENING_Main_" +} \ No newline at end of file diff --git a/示例项目/觉醒券/觉醒券.json b/示例项目/觉醒券/觉醒券.json new file mode 100644 index 0000000..fab9aaf --- /dev/null +++ b/示例项目/觉醒券/觉醒券.json @@ -0,0 +1,55 @@ +{ + "config_meta": { + "comment": "觉醒道具功能配置表 v1.0", + "author": "zz", + "create_date": "2025-05-08" + }, + "awaken_items": { + "8020": { + "job": 7, + "grow": 1, + "comment": "男气功觉醒" + }, + "8021": { + "job": 7, + "grow": 3, + "comment": "男街霸觉醒" + }, + "8022": { + "job": 7, + "grow": 2, + "comment": "男散打觉醒" + }, + "8024": { + "job": 7, + "grow": 4, + "comment": "男柔道觉醒" + }, + "2024051421": { + "job": 9, + "grow": 1, + "comment": "驭剑士觉醒" + }, + "2024051422": { + "job": 9, + "grow": 3, + "comment": "契魔者觉醒" + }, + "2024051423": { + "job": 8, + "grow": 2, + "comment": "冰结师觉醒" + }, + "2024051424": { + "job": 8, + "grow": 1, + "comment": "元素爆破觉醒" + }, + "2024051418": { + "job": 4, + "grow": 4, + "comment": "复仇者觉醒" + } + } + } + \ No newline at end of file diff --git a/示例项目/觉醒券/觉醒券.nut b/示例项目/觉醒券/觉醒券.nut new file mode 100644 index 0000000..14b48f7 --- /dev/null +++ b/示例项目/觉醒券/觉醒券.nut @@ -0,0 +1,57 @@ +// =================== 加载觉醒配置 =================== +local awakenConfig = GlobalConfig.Get("觉醒券.json"); +local AWAKEN_ITEMS = {}; + +// 转换字符串键到整型键 +foreach(keyStr, config in awakenConfig.awaken_items) { + try { + AWAKEN_ITEMS[keyStr.tointeger()] <- config; + } catch(e) { + ::print("[觉醒配置错误] 无效的ID格式: " + keyStr + "\n"); + } +} + +// =================== 觉醒处理函数 =================== +function HandleAwakening(SUser, ItemId) { + // 安全校验 + if (!(ItemId in AWAKEN_ITEMS)) { + return SendClassChangeError(SUser, "无效的觉醒道具", ItemId); + } + + local config = AWAKEN_ITEMS[ItemId]; + local curJob = SUser.GetCharacJob(); + local curGrow = SUser.GetCharacGrowType(); + local curAwaken = SUser.GetCharacSecondGrowType(); + // 获取转职配置 + local config = AWAKEN_ITEMS[ItemId]; + // 条件检查 + if (SUser.GetCharacLevel() < 50) + return SendClassChangeError(SUser, "角色等级不足50级!", ItemId); + + if (curJob != config.job) + return SendClassChangeError(SUser, "职业不符无法觉醒!", ItemId); + + if (curGrow == 0) + return SendClassChangeError(SUser, "请先完成转职!", ItemId); + + if (curGrow == config.grow + 16) + return SendClassChangeError(SUser, "已经处于该觉醒状态!", ItemId); + + // 执行觉醒 + SUser.ChangeGrowType(config.grow, 1); // 假设第二个参数为觉醒标志 + SUser.InitSkillW(1, 1); + SUser.SendNotiPacket(0, 2, 0); + SUser.SendNotiPacketMessage("觉醒成功!", 0); +} + +// =================== 初始化注册 =================== +function _Dps_AWAKENING_Main_() { + if ("awaken_items" in awakenConfig) { + foreach(itemId, _ in AWAKEN_ITEMS) { + Cb_Use_Item_Sp_Func[itemId] <- HandleAwakening; + } + ::print("[觉醒系统] 载入觉醒券数量: " + AWAKEN_ITEMS.len() + "\n"); + } else { + ::print("[觉醒系统错误] 配置缺失 awaken_items 节点\n"); + } +} diff --git a/示例项目/角色初始武器修改/Proj.ifo b/示例项目/角色初始武器修改/Proj.ifo new file mode 100644 index 0000000..bb45fa2 --- /dev/null +++ b/示例项目/角色初始武器修改/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "角色初始武器修改", + "ProjectDescribe": "修改黑暗武士和缔造的初始武器", + "ProjectAuthor": "倾泪寒", + "ProjectVersion": 1.1, + "ProjectConfig": "角色初始武器修改_Lenheart.json", + "ProjectFiles": [ + "角色初始武器修改.nut" + ], + "ProjectRunFunc": "_Dps_SetInitiaweapon_Main_" +} \ No newline at end of file diff --git a/示例项目/角色初始武器修改/角色初始武器修改.nut b/示例项目/角色初始武器修改/角色初始武器修改.nut new file mode 100644 index 0000000..74d29c3 --- /dev/null +++ b/示例项目/角色初始武器修改/角色初始武器修改.nut @@ -0,0 +1,13 @@ +function _Dps_SetInitiaweapon_Logic_() { + local Config = GlobalConfig.Get("角色初始武器修改_Lenheart.json"); + NativePointer("0x081C0CB2").writeInt(Config["黑暗武士武器"].tointeger()); + NativePointer("0x081C0CBB").writeInt(Config["缔造武器"].tointeger()); +} + +function _Dps_SetInitiaweapon_Main_() { + _Dps_SetInitiaweapon_Logic_(); +} + +function _Dps_SetInitiaweapon_Main_Reload_(OldConfig) { + _Dps_SetInitiaweapon_Logic_(); +} \ No newline at end of file diff --git a/示例项目/角色初始武器修改/角色初始武器修改_Lenheart.json b/示例项目/角色初始武器修改/角色初始武器修改_Lenheart.json new file mode 100644 index 0000000..fdb14bc --- /dev/null +++ b/示例项目/角色初始武器修改/角色初始武器修改_Lenheart.json @@ -0,0 +1,4 @@ +{ + "缔造武器": 27600, + "黑暗武士武器":35001 +} \ No newline at end of file diff --git a/示例项目/设置服务器等级上限/Proj.ifo b/示例项目/设置服务器等级上限/Proj.ifo new file mode 100644 index 0000000..2f8f0cc --- /dev/null +++ b/示例项目/设置服务器等级上限/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "设置服务器等级上限", + "ProjectDescribe": "设置服务器等级上限", + "ProjectAuthor": "倾泪寒", + "ProjectVersion": 1.0, + "ProjectConfig": "设置服务器等级上限_Lenheart.json", + "ProjectFiles": [ + "设置服务器等级上限.nut" + ], + "ProjectRunFunc": "_Dps_SetGameMaxLevel_Main_" +} \ No newline at end of file diff --git a/示例项目/设置服务器等级上限/设置服务器等级上限.nut b/示例项目/设置服务器等级上限/设置服务器等级上限.nut new file mode 100644 index 0000000..f2044fd --- /dev/null +++ b/示例项目/设置服务器等级上限/设置服务器等级上限.nut @@ -0,0 +1,12 @@ +function _Dps_SetGameMaxLevel_Logic_() { + local Config = GlobalConfig.Get("设置服务器等级上限_Lenheart.json"); + GameManager.SetGameMaxLevel(Config["等级上限"]); +} + +function _Dps_SetGameMaxLevel_Main_() { + _Dps_SetGameMaxLevel_Logic_(); +} + +function _Dps_SetGameMaxLevel_Main_Reload(OldConfig) { + _Dps_SetGameMaxLevel_Logic_(); +} \ No newline at end of file diff --git a/示例项目/设置服务器等级上限/设置服务器等级上限_Lenheart.json b/示例项目/设置服务器等级上限/设置服务器等级上限_Lenheart.json new file mode 100644 index 0000000..d00d3c8 --- /dev/null +++ b/示例项目/设置服务器等级上限/设置服务器等级上限_Lenheart.json @@ -0,0 +1,3 @@ +{ + "等级上限": 86 +} \ No newline at end of file diff --git a/示例项目/设置装备解锁时间/Proj.ifo b/示例项目/设置装备解锁时间/Proj.ifo new file mode 100644 index 0000000..fb8e94c --- /dev/null +++ b/示例项目/设置装备解锁时间/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "设置装备解锁时间", + "ProjectDescribe": "设置装备的解锁时间", + "ProjectAuthor": "倾泪寒", + "ProjectVersion": 1.1, + "ProjectConfig": "设置装备解锁时间_Lenheart.json", + "ProjectFiles": [ + "设置装备解锁时间.nut" + ], + "ProjectRunFunc": "_Dps_SetEquipmentUnlockTime_Main_" +} \ No newline at end of file diff --git a/示例项目/设置装备解锁时间/设置装备解锁时间.nut b/示例项目/设置装备解锁时间/设置装备解锁时间.nut new file mode 100644 index 0000000..fcfa277 --- /dev/null +++ b/示例项目/设置装备解锁时间/设置装备解锁时间.nut @@ -0,0 +1,12 @@ +function _Dps_SetEquipmentUnlockTime_Logic_() { + local Config = GlobalConfig.Get("设置装备解锁时间_Lenheart.json"); + GameManager.SetItemLockTime(Config["设置装备解锁需要的冷却时间_单位秒"]); +} + +function _Dps_SetEquipmentUnlockTime_Main_() { + _Dps_SetEquipmentUnlockTime_Logic_(); +} + +function _Dps_SetEquipmentUnlockTime_Main_Reload(OldConfig) { + _Dps_SetEquipmentUnlockTime_Logic_(); +} \ No newline at end of file diff --git a/示例项目/设置装备解锁时间/设置装备解锁时间_Lenheart.json b/示例项目/设置装备解锁时间/设置装备解锁时间_Lenheart.json new file mode 100644 index 0000000..aa1fd60 --- /dev/null +++ b/示例项目/设置装备解锁时间/设置装备解锁时间_Lenheart.json @@ -0,0 +1,3 @@ +{ + "设置装备解锁需要的冷却时间_单位秒": 10 +} \ No newline at end of file diff --git a/示例项目/跨界石/Proj.ifo b/示例项目/跨界石/Proj.ifo new file mode 100644 index 0000000..3b31c24 --- /dev/null +++ b/示例项目/跨界石/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "跨界石", + "ProjectDescribe": "通过指定ID的道具,将装备背包第一格的装备跨界。\n配置中修改CrossoverId可修改跨界石的ID 修改NoCrossIdArr可增加不可跨界的ID。", + "ProjectAuthor": "倾泪寒", + "ProjectVersion": 1.4, + "ProjectConfig": "跨界石_Lenheart.json", + "ProjectFiles": [ + "跨界石.nut" + ], + "ProjectRunFunc": "_Dps_CrossBorderStones_Main_" +} \ No newline at end of file diff --git a/示例项目/跨界石/跨界石.nut b/示例项目/跨界石/跨界石.nut new file mode 100644 index 0000000..a8566b0 --- /dev/null +++ b/示例项目/跨界石/跨界石.nut @@ -0,0 +1,92 @@ + + + +function _Dps_CrossBorderStones_Logic_() +{ + local Cofig = GlobalConfig.Get("跨界石_Lenheart.json"); + + Cb_Use_Item_Sp_Func[Cofig.CrossoverId.tointeger()] <- function(SUser, ItemId) { + local Cofig = GlobalConfig.Get("跨界石_Lenheart.json"); + + //获取账号金库对象 + local CargoObj = SUser.GetAccountCargo(); + //获取账号金库中的一个空格子 + local EmptySlot = CargoObj.GetEmptySlot(); + + //如果没有空格子 + if (EmptySlot == -1) { + SUser.SendNotiPacketMessage(Cofig.CrossoverStr2, 8); + //不扣除道具 + Timer.SetTimeOut(function() + { + SUser.GiveItem(ItemId, 1); + },1); + return; + } + + //获取角色背包 + local InvenObj = SUser.GetInven(); + //获取需要转移的装备 这里默认写的装备栏第一个格子 + local ItemObj = InvenObj.GetSlot(Inven.INVENTORY_TYPE_ITEM, 9); + //获取装备ID + local EquipId = ItemObj.GetIndex(); + //获取装备名字 + local ItemName = PvfItem.GetNameById(EquipId); + //获取配置中不可跨界的ID数组 + local NoCrossId = Cofig.NoCrossIdArr; + //如果这个装备不可跨界 + if (NoCrossId.find(EquipId) != null) { + SUser.SendNotiPacketMessage(format(Cofig.CrossoverStr5, ItemName), 7); + //不扣除道具 + Timer.SetTimeOut(function() + { + SUser.GiveItem(ItemId, 1); + },1); + return; + } + //如果没找到这个格子的装备 + if (!ItemName) { + SUser.SendNotiPacketMessage(Cofig.CrossoverStr1, 8); + //不扣除道具 + Timer.SetTimeOut(function() + { + SUser.GiveItem(ItemId, 1); + },1); + return; + } + //跨界 + local Flag = CargoObj.InsertItem(ItemObj, EmptySlot); + if (Flag == -1) { + SUser.SendNotiPacketMessage(Cofig.CrossoverStr3, 8); + //不扣除道具 + Timer.SetTimeOut(function() + { + SUser.GiveItem(ItemId, 1); + },1); + } else { + //销毁背包中的道具 + ItemObj.Delete(); + //刷新玩家背包列表 + SUser.SendUpdateItemList(1, 0, 9); + //刷新账号金库列表 + CargoObj.SendItemList(); + SUser.SendNotiPacketMessage(format(Cofig.CrossoverStr4, ItemName), 7); + } + } +} + + + +//加载入口 +function _Dps_CrossBorderStones_Main_() { + _Dps_CrossBorderStones_Logic_(); +} + +//重载入口 +function _Dps_CrossBorderStones_Main_Reload_(OldConfig) { + //先销毁原来注册的 + Cb_Use_Item_Sp_Func.rawdelete(OldConfig.CrossoverId.tointeger()); + + //重新注册 + _Dps_CrossBorderStones_Logic_(); +} \ No newline at end of file diff --git a/示例项目/跨界石/跨界石_Lenheart.json b/示例项目/跨界石/跨界石_Lenheart.json new file mode 100644 index 0000000..ca1bf9c --- /dev/null +++ b/示例项目/跨界石/跨界石_Lenheart.json @@ -0,0 +1,9 @@ +{ + "CrossoverStr1": "装备栏1号位没有装备..", + "CrossoverStr2": "跨界失败,请检查一下账号金库是否没有开启或者没有空位", + "CrossoverStr3": "跨界失败", + "CrossoverStr4": "跨界成功,已经装备栏第一格的 [%s] 转移至账号金库", + "CrossoverStr5": "跨界失败, [%s] 不可以跨界", + "CrossoverId": 17577, + "NoCrossIdArr" : [26058] +} \ No newline at end of file diff --git a/示例项目/转职券/Proj.ifo b/示例项目/转职券/Proj.ifo new file mode 100644 index 0000000..1fc83d3 --- /dev/null +++ b/示例项目/转职券/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "转职券", + "ProjectDescribe": "按官方的转职券来写,转职券道具可以复制 主线完成券 一类。", + "ProjectAuthor": "zz", + "ProjectVersion": 1.1, + "ProjectConfig": "转职券.json", + "ProjectFiles": [ + "转职券.nut" + ], + "ProjectRunFunc": "_Dps_CLASS_CHANGE_Main_" +} \ No newline at end of file diff --git a/示例项目/转职券/转职券.json b/示例项目/转职券/转职券.json new file mode 100644 index 0000000..39daf5f --- /dev/null +++ b/示例项目/转职券/转职券.json @@ -0,0 +1,53 @@ + + +{ + "config_meta": { + "comment": "转职道具功能配置表 v1.2", + "author": "System", + "create_date": "2024-05-14" + }, + + "class_change_items": { + "8016": { + "job": 7, + "grow": 1, + "comment": "男气功" + }, + "8017": { + "job": 7, + "grow": 3, + "comment": "男街霸" + }, + "8018": { + "job": 7, + "grow": 2, + "comment": "男散打" + }, + "8019": { + "job": 7, + "grow": 4, + "comment": "男柔道" + }, + "2024051416": { + "job": 9, + "grow": 1, + "comment": "驭剑士" + }, + "2024051417": { + "job": 9, + "grow": 3, + "comment": "契魔者" + }, + "2024051419": { + "job": 8, + "grow": 1, + "comment": "元素爆破师" + }, + "2024051420": { + "job": 8, + "grow": 2, + "comment": "冰结师" + } + } + } + \ No newline at end of file diff --git a/示例项目/转职券/转职券.nut b/示例项目/转职券/转职券.nut new file mode 100644 index 0000000..e2e3c2d --- /dev/null +++ b/示例项目/转职券/转职券.nut @@ -0,0 +1,59 @@ + +// =================== 加载 JSON 配置 =================== +local rawConfig = GlobalConfig.Get("转职券.json"); +local CLASS_CHANGE_ITEMS = {}; + +// 转换字符串键到整型键 +foreach(keyStr, config in rawConfig.class_change_items) { + try { + CLASS_CHANGE_ITEMS[keyStr.tointeger()] <- config; + } catch(e) { + ::print("[ERROR] 转换道具ID失败: " + keyStr + "\n"); + } +} + +// =================== 通用转职函数 =================== +function ChangeGrowTypeHandler(SUser, ItemId) { + // 读取角色信息 + local curJob = SUser.GetCharacJob(); + local curGrow = SUser.GetCharacGrowType(); + local curLevel = SUser.GetCharacLevel(); + local curAwaken = SUser.GetCharacSecondGrowType(); + + // 获取转职配置 + local config = CLASS_CHANGE_ITEMS[ItemId]; + + // 条件验证 + if (curLevel < 20) return SendClassChangeError(SUser, "角色等级不足20级!", ItemId); + + if (curJob != config.job) return SendClassChangeError(SUser, "职业不符无法使用!", ItemId); + + if (curAwaken > 0) return SendClassChangeError(SUser, "觉醒后无法转职!", ItemId); + + if (curGrow == config.grow) return SendClassChangeError(SUser, "已处于目标职业分支!", ItemId); + + // 执行转职操作 + SUser.ChangeGrowType(config.grow, 0); + SUser.InitSkillW(1, 0); + SUser.SendNotiPacket(0, 2, 0); + SUser.SendNotiPacketMessage("角色转职成功!", 0); +} + +// =================== 错误处理函数 =================== +function SendClassChangeError(SUser, msg, itemId) { + SUser.SendNotiPacketMessage(msg, 0); + SUser.SendNotiBox(msg, 1); + SUser.GiveItem(itemId, 1); // 退还道具 +} + +//加载入口 +function _Dps_CLASS_CHANGE_Main_() { + if ("class_change_items" in rawConfig) { + foreach (itemId, _ in CLASS_CHANGE_ITEMS) { + Cb_Use_Item_Sp_Func[itemId] <- ChangeGrowTypeHandler; + } + ::print("[SUCCESS] 注册转职道具数量: " + CLASS_CHANGE_ITEMS.len() + "\n"); + } else { + ::print("[ERROR] 转职配置缺失 class_change_items 节点\n"); + } +} \ No newline at end of file diff --git a/示例项目/进入副本自动修理装备/Proj.ifo b/示例项目/进入副本自动修理装备/Proj.ifo new file mode 100644 index 0000000..9611276 --- /dev/null +++ b/示例项目/进入副本自动修理装备/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "进入副本自动修理装备", + "ProjectDescribe": "进入副本时自动修理穿戴栏的装备耐久度,可设置持有指定契约才修理", + "ProjectAuthor": "南瓜", + "ProjectVersion": 1.1, + "ProjectConfig": "自动修理装备配置_nangua.json", + "ProjectFiles": [ + "进入副本自动修理装备.nut" + ], + "ProjectRunFunc": "_Dps_RepairEquipment_Main_" +} \ No newline at end of file diff --git a/示例项目/进入副本自动修理装备/自动修理装备配置_nangua.json b/示例项目/进入副本自动修理装备/自动修理装备配置_nangua.json new file mode 100644 index 0000000..073cb0e --- /dev/null +++ b/示例项目/进入副本自动修理装备/自动修理装备配置_nangua.json @@ -0,0 +1,12 @@ +{ + "自动修理装备开关(true开启/false关闭)":true, + "指定契约才可修理(true开启/false关闭)":true, + "契约ID":12, + "是否开启修理后发送提示(true开启/false关闭)":true, + "是否返还修理费用(true返还/false不返还)":true, + "修理成功提示":"[自动维修]您的装备已自动修理完毕", + "修理失败提示":"[自动维修]金币不足, 自动修理失败", + "修理部分装备提示":"[自动维修]因金币余额不足已维修部分装备", + "发送信息播报位置":1, + "提示":"如果指定契约关闭,则无条件修理,已知黑钻契约ID为12" +} \ No newline at end of file diff --git a/示例项目/进入副本自动修理装备/进入副本自动修理装备.nut b/示例项目/进入副本自动修理装备/进入副本自动修理装备.nut new file mode 100644 index 0000000..6ba49c8 --- /dev/null +++ b/示例项目/进入副本自动修理装备/进入副本自动修理装备.nut @@ -0,0 +1,156 @@ +function _Dps_RepairEquipment_Main_() { + Cb_StartGame_check_error_Enter_Func.info <- function (args) { + local config = GlobalConfig.Get("自动修理装备配置_nangua.json"); + local SUser = User(args[1]); + if(!config["自动修理装备开关(true开启/false关闭)"]) { + return; + } + + local PartyObj = SUser.GetParty(); + local needCheckContract = config["指定契约才可修理(true开启/false关闭)"]; + + if(PartyObj) { + for (local i = 0; i < 4; ++i) { + local TUser = PartyObj.GetUser(i); + if(TUser) { + if(needCheckContract) { + if(_Dps_RepairEquipment.CheckUserPremium(TUser, config["契约ID"])) { + _Dps_RepairEquipment.RepairEquipmentBynangua(TUser); + } + } else { + _Dps_RepairEquipment.RepairEquipmentBynangua(TUser); + } + } + } + } else { + if(needCheckContract) { + if(_Dps_RepairEquipment.CheckUserPremium(SUser, config["契约ID"])) { + _Dps_RepairEquipment.RepairEquipmentBynangua(SUser); + } + } else { + _Dps_RepairEquipment.RepairEquipmentBynangua(SUser); + } + } + } +} + +class _Dps_RepairEquipment { + // 检查用户是否拥有指定契约 + function CheckUserPremium(SUser, contract_id) { + local premium_ptr = Sq_CallFunc(S_Ptr("0x0812CE28"), "pointer", ["pointer"], SUser.C_Object); + local contract_data_ptr = Sq_CallFunc(S_Ptr("0x086ADF52"), "pointer", ["pointer", "int"], premium_ptr, contract_id); + local VIP = NativePointer(contract_data_ptr).readU32() != 0; + return VIP; + } + function RepairEquipmentBynangua(SUser) { + local config = GlobalConfig.Get("自动修理装备配置_nangua.json"); + local InvenObj = SUser.GetInven(); + local anyItemRepaired = false; + local current_money = InvenObj.GetMoney(); + local need_repair_count = 0; + local repaired_count = 0; + local total_repair_cost = 0; + + // 第一次遍历:计算总修理费用和需要修理的装备数量 + for (local i = 10; i <= 16; ++i) { + if (i == 11) continue; + local ItemObj = InvenObj.GetSlot(0, i); + if (ItemObj.GetIndex() == 0) continue; + + local durability = ItemObj.GetDurable(); + local G_CDataManager = Sq_CallFunc(S_Ptr("0x80CC19B"), "pointer", []); + local PVFItemObj = Sq_CallFunc(S_Ptr("0x835FA32"), "pointer", ["pointer", "int"], G_CDataManager, ItemObj.GetIndex()); + local durability_max = Sq_CallFunc(S_Ptr("0x811ED98"), "int", ["pointer"], PVFItemObj); + + if (durability < durability_max) { + need_repair_count++; + local charac_level = SUser.GetCharacLevel(); + local growth_grade = Sq_CallFunc(S_Ptr("0x085137B8"), "int", ["pointer", "int"], PVFItemObj, charac_level); + local growth_repair_cost = Sq_CallFunc(S_Ptr("0x0851381C"), "int", ["pointer", "int"], PVFItemObj, charac_level); + local cost_rate = 1.0; + local repair_cost = Sq_CallFunc(S_Ptr("0x0898C8FC"), "int", ["int", "int", "int", "int", "bool", "float"], growth_repair_cost, durability, durability_max, growth_grade, false, cost_rate); + total_repair_cost += repair_cost; + } + } + + if (need_repair_count == 0) return; + + // 如果开启了返还功能,先给玩家发放金币 + if(config["是否返还修理费用(true返还/false不返还)"]) { + SUser.RechargeMoney(total_repair_cost); + SUser.SendUpdateItemList(1, 0, 0); + current_money = InvenObj.GetMoney(); // 更新当前金币数 + } + + local msg_base = Memory.alloc(32); + local param_base = Memory.alloc(32); + local dispatcher = Memory.alloc(4); + + for (local i = 10; i <= 16; ++i) { + if (i == 11) continue; + + local ItemObj = InvenObj.GetSlot(0, i); + if (ItemObj.GetIndex() == 0) continue; + + local durability = ItemObj.GetDurable(); + local G_CDataManager = Sq_CallFunc(S_Ptr("0x80CC19B"), "pointer", []); + local PVFItemObj = Sq_CallFunc(S_Ptr("0x835FA32"), "pointer", ["pointer", "int"], G_CDataManager, ItemObj.GetIndex()); + local durability_max = Sq_CallFunc(S_Ptr("0x811ED98"), "int", ["pointer"], PVFItemObj); + + if (durability < durability_max) { + local charac_level = SUser.GetCharacLevel(); + local growth_grade = Sq_CallFunc(S_Ptr("0x085137B8"), "int", ["pointer", "int"], PVFItemObj, charac_level); + local growth_repair_cost = Sq_CallFunc(S_Ptr("0x0851381C"), "int", ["pointer", "int"], PVFItemObj, charac_level); + local cost_rate = 1.0; + local repair_cost = Sq_CallFunc(S_Ptr("0x0898C8FC"), "int", ["int", "int", "int", "int", "bool", "float"], growth_repair_cost, durability, durability_max, growth_grade, false, cost_rate); + + if (current_money < repair_cost) { + continue; + } + + local temp_ptr = msg_base; + temp_ptr = temp_ptr.add(13); + temp_ptr.writeU8(3); + temp_ptr = msg_base.add(14); + temp_ptr.writeU16(i); + temp_ptr = msg_base.add(16); + temp_ptr.writeU16(65535); + temp_ptr = param_base.add(12); + temp_ptr.writeU16(0); + + local result = Sq_CallFunc(S_Ptr("0x081C6082"), "int", ["pointer", "pointer", "pointer", "pointer"], dispatcher.C_Object, SUser.C_Object, msg_base.C_Object, param_base.C_Object); + + if(result == 0) { + anyItemRepaired = true; + repaired_count++; + SendRepairPacket(SUser, i, durability_max); + current_money -= repair_cost; + } + } + } + + if (config["是否开启修理后发送提示(true开启/false关闭)"]) { + if (repaired_count == 0) { + SUser.SendNotiPacketMessage(config["修理失败提示"], config["发送信息播报位置"]); + } else if (repaired_count < need_repair_count) { + SUser.SendNotiPacketMessage(config["修理部分装备提示"], config["发送信息播报位置"]); + } else { + SUser.SendNotiPacketMessage(config["修理成功提示"], config["发送信息播报位置"]); + } + } + } +} +function SendRepairPacket(SUser, slot, durability) { + local Pack = Packet(); + Pack.Put_Header(1, 25); + Pack.Put_Byte(1); + local InvenObj = SUser.GetInven(); + local Money = InvenObj.GetMoney(); + Pack.Put_Int(Money); + Pack.Put_Byte(0); + Pack.Put_Short(slot); + Pack.Put_Short(durability); + Pack.Finalize(true); + SUser.Send(Pack); + Pack.Delete(); +} \ No newline at end of file diff --git a/示例项目/道具接取任务/Proj.ifo b/示例项目/道具接取任务/Proj.ifo new file mode 100644 index 0000000..d0408b8 --- /dev/null +++ b/示例项目/道具接取任务/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "道具接取任务", + "ProjectDescribe": "使用道具直接接取特定任务", + "ProjectAuthor": "倾泪寒", + "ProjectVersion": 1.0, + "ProjectConfig": "道具接取任务.json", + "ProjectFiles": [ + "道具接取任务.nut" + ], + "ProjectRunFunc": "_Dps_DJJQRW_Main_" +} \ No newline at end of file diff --git a/示例项目/道具接取任务/道具接取任务.json b/示例项目/道具接取任务/道具接取任务.json new file mode 100644 index 0000000..ab443eb --- /dev/null +++ b/示例项目/道具接取任务/道具接取任务.json @@ -0,0 +1,9 @@ +{ + "道具对应接取任务": [ + [7577,1207], + [7578,1208], + [7579,1209], + [7580,1210] + ], + "提示": "道具请用7577复制出来的道具" +} \ No newline at end of file diff --git a/示例项目/道具接取任务/道具接取任务.nut b/示例项目/道具接取任务/道具接取任务.nut new file mode 100644 index 0000000..a712e03 --- /dev/null +++ b/示例项目/道具接取任务/道具接取任务.nut @@ -0,0 +1,16 @@ + +function _Dps_DJJQRW_Main_() { + + local Config = GlobalConfig.Get("道具接取任务.json"); + foreach( item in Config["道具对应接取任务"]){ + + local a = item[0]; + local b = item[1]; + Cb_Use_Item_Sp_Func[a] <- function(SUser, ItemId) { + print(b); + Sq_CUser_QuestAction(SUser.C_Object, 33, b, 0, 0); + } + + } + +} diff --git a/示例项目/邮件金币去除验证/Proj.ifo b/示例项目/邮件金币去除验证/Proj.ifo new file mode 100644 index 0000000..cd021ea --- /dev/null +++ b/示例项目/邮件金币去除验证/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "邮件金币去除验证", + "ProjectDescribe": "去除了发送邮件中有金币会触发验证码的验证。", + "ProjectAuthor": "倾泪寒", + "ProjectVersion": 1.0, + "ProjectConfig": "", + "ProjectFiles": [ + "邮件金币去除验证.nut" + ], + "ProjectRunFunc": "_Dps_EmailCoinRemovalVerification_Main_" +} \ No newline at end of file diff --git a/示例项目/邮件金币去除验证/邮件金币去除验证.nut b/示例项目/邮件金币去除验证/邮件金币去除验证.nut new file mode 100644 index 0000000..914994e --- /dev/null +++ b/示例项目/邮件金币去除验证/邮件金币去除验证.nut @@ -0,0 +1,7 @@ + + + +function _Dps_EmailCoinRemovalVerification_Main_() +{ + GameManager.FixEmailRemovalVerification(); +} diff --git a/示例项目/重铸券/Proj.ifo b/示例项目/重铸券/Proj.ifo new file mode 100644 index 0000000..c198621 --- /dev/null +++ b/示例项目/重铸券/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "重铸券", + "ProjectDescribe": "使用过后删除装备栏第一格的物品并重新发送一件", + "ProjectAuthor": "倾泪寒", + "ProjectVersion": 1.0, + "ProjectConfig": "重铸券.json", + "ProjectFiles": [ + "重铸券.nut" + ], + "ProjectRunFunc": "_Dps_ReforgeEquipment_Main_" +} \ No newline at end of file diff --git a/示例项目/重铸券/重铸券.json b/示例项目/重铸券/重铸券.json new file mode 100644 index 0000000..00f04f2 --- /dev/null +++ b/示例项目/重铸券/重铸券.json @@ -0,0 +1,4 @@ +{ + "重铸卷的道具ID": 7577, + "是否返还重铸券道具(true代表返还,false代表不返还)": true +} \ No newline at end of file diff --git a/示例项目/重铸券/重铸券.nut b/示例项目/重铸券/重铸券.nut new file mode 100644 index 0000000..c8c33b4 --- /dev/null +++ b/示例项目/重铸券/重铸券.nut @@ -0,0 +1,30 @@ +function _Dps_ReforgeEquipment_Main_() { + local Config = GlobalConfig.Get("重铸券.json"); + //分解券 + Cb_Use_Item_Sp_Func[Config["重铸卷的道具ID"]] <- function(SUser, ItemId) { + local index = 0; + local InvenObj = SUser.GetInven(); + if (InvenObj) { + local ItemObj = InvenObj.GetSlot(1, 9); + //空装备 + if (ItemObj.IsEmpty) { + //发送通知 + SUser.SendNotiPacketMessage("装备不存在 请将装备放置在装备栏第一格", 8); + //返还消耗的道具 + local test = SUser.GiveItem(ItemId, 1); + return; + } + local ItemId1 = ItemObj.GetIndex(); + ItemObj.Delete(); + + local item = SUser.GiveItem(ItemId1, 1); + + + SUser.SendItemSpace(0); + SUser.SendNotiPacketMessage("装备重铸", 8); + if (Config["是否返还分解券道具(true代表返还,false代表不返还)"]) { + SUser.GiveItem(ItemId, 1); + } + } + }; +} \ No newline at end of file diff --git a/示例项目/门票进入副本/Proj.ifo b/示例项目/门票进入副本/Proj.ifo new file mode 100644 index 0000000..813e116 --- /dev/null +++ b/示例项目/门票进入副本/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "门票进入副本", + "ProjectDescribe": "添加副本使用指定道具进入规则", + "ProjectAuthor": "猫咪", + "ProjectVersion": 1.2, + "ProjectConfig": "门票进入副本配置_Maomi.json", + "ProjectFiles": [ + "门票进入副本.nut" + ], + "ProjectRunFunc": "_Dps_TicketAccessToDungeon_Maomi_Main_" +} \ No newline at end of file diff --git a/示例项目/门票进入副本/门票进入副本.nut b/示例项目/门票进入副本/门票进入副本.nut new file mode 100644 index 0000000..2cfe94c --- /dev/null +++ b/示例项目/门票进入副本/门票进入副本.nut @@ -0,0 +1,50 @@ +function get_required_gradeByMaomi(dgn_id, dgn_diff) { + local Config = GlobalConfig.Get("门票进入副本配置_Maomi.json"); + if (Config["门票进入副本"]["副本配置"].rawin(dgn_id.tostring())) { + local requirements = Config["门票进入副本"]["副本配置"][dgn_id.tostring()]; + for (local i = 0; i< requirements.len(); i += 2) { + if (requirements[i] == dgn_diff) { + return requirements[i + 1]; + } + } + } + return null; +} + +function _Dps_TicketAccessToDungeon_Maomi_Main_() { + + Cb_SelectDungeon_Check_Error_Leave_Func.Check_TicketByMaomi <- function(args) { + + local Config = sq_ReadJsonFile("/dp_s/OfficialConfig" + "/" + "门票进入副本配置_Maomi.json"); + // local Config = GlobalConfig.Get("门票进入副本配置_Maomi.json"); + + local SUser = User(args[1]); + local msg_base = args[2]; + local dgn_id = NativePointer(msg_base).add(13).readU16(); // 获取副本ID + local dgn_diff = NativePointer(msg_base).add(15).readU8(); // 获取副本难度 + local PartyObj = SUser.GetParty(); + + local required_Item = get_required_gradeByMaomi(dgn_id, dgn_diff); + local diff_name = Config["副本难度命名"][(dgn_diff).tostring()]; + + if (!required_Item) return null; + + if (PartyObj && Config["门票进入副本"]["门票进入副本开关(true为开启,false为关闭)"]) { + for (local i = 0; i< 4; ++i) { + local Tuser = PartyObj.GetUser(i); + if (Tuser) { + local InvenObj = Tuser.GetInven(); + if (InvenObj) { + if (InvenObj.CheckItemCount(required_Item, 1)){ + InvenObj.DeleteItemCount(required_Item, 1); + return null; + } + } + } + } + + SUser.SendNotiBox("队伍中所有玩家均无副本所需门票,无法进入!", 2) + return 1; + } + } +} \ No newline at end of file diff --git a/示例项目/门票进入副本/门票进入副本配置_Maomi.json b/示例项目/门票进入副本/门票进入副本配置_Maomi.json new file mode 100644 index 0000000..1201349 --- /dev/null +++ b/示例项目/门票进入副本/门票进入副本配置_Maomi.json @@ -0,0 +1,29 @@ +{ + "门票进入副本": { + "提示": "门票进入副本配置(副本ID[普通级,门票,冒险级,门票,勇士级,门票,王者级,门票,地狱级,门票])", + "门票进入副本开关(true为开启,false为关闭)": true, + "副本配置": { + "1": [ + 0, + 3037, + 1, + 3037, + 2, + 3037, + 3, + 3037, + 4, + 3037, + 5, + 3037 + ] + } + }, + "副本难度命名": { + "0": "普通级", + "1": "冒险级", + "2": "勇士级", + "3": "王者级", + "4": "地狱级" + } +} \ No newline at end of file diff --git a/示例项目/防脱机制裁/Proj.ifo b/示例项目/防脱机制裁/Proj.ifo new file mode 100644 index 0000000..073a28d --- /dev/null +++ b/示例项目/防脱机制裁/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "防脱机制裁", + "ProjectDescribe": "本项目只能够防止脱机外挂,配置可以更改", + "ProjectAuthor": "南瓜", + "ProjectVersion": 1.7, + "ProjectConfig": "防脱机制裁_Nangua.json", + "ProjectFiles": [ + "防脱机制裁.sut" + ], + "ProjectRunFunc": "_Dps_AntiOfflineSanctions_Main_" +} \ No newline at end of file diff --git a/示例项目/防脱机制裁/防脱机制裁.sut b/示例项目/防脱机制裁/防脱机制裁.sut new file mode 100644 index 0000000000000000000000000000000000000000..61350e70b30078295bd53f2cb79962f04831ea19 GIT binary patch literal 12226 zcmcJVcTgN@n&vaJ(r0C5`7D!Fg+Rgx5FkJ}sYFs)Rau6@6q4}Bpl@wiAOS68kasO_?#n7rn;FH%?cV%J z5Sf*qd*A2zJ)bWq%I&jvKl}8?&g-uS_n+?`yT9$o{7Caur{Z;wE%`(rXU)v_4;(i`mI|; zDcpZ<@{5t_KfO6MFnMU_vH#Nj{OI8GgY7TIJ4TN89T?kw`02p-aL=35y*FMCO`qIi zCs5j?W}sx&7JH|!EnMessOH#6qtx-@QUCdi9p8)6BfY2YZvM?jTvb)G zTvK0H6_CL1<@7wc_{q(4og*jvUmW{v*P)%a54Ar&P~LHVYR9>mo_(XAzI@*QY@+|! z^Uwa<#hthAcb*yU-t(aM+MR;#%U65P?ddport8G{-+lV<*B`8t8bS@;)y)l!K2dB8 zXMcV9Wyi~*FHU^gJ9}YZ;#mLnBb^Tpl@3l!?K*emo1r6v4@SSb{2zL6-Ws@iu7CDr z`?=G7C!X}axwGu+>o5ApZtXm_t7qcmjy+E|Y-;m0237~7&7MYo8(0T-_p^sXd&j!Y z?CE+q*?wtg@XCRn2a}~c?%(|M%*~$3Q%4^k7;7!}%$dQNho77~+;{Rq*S9wc zyWdP7o_O7UYNG$(kb7bd!uENLyU&}zMPI5BLm;v-G1s|&+M7O$NRQVAL+R>27hwm zM*qajj$?Z|pI!Xq)!Ck#GlR#@Z=ZVk$&nL72d;F^%oOzBy4OB@wfD?;@5G`0n@?KT zS;~L)5f=sP=Lq^D-gm6+nwG!P+OBH&E3Iv1!(VA_KdSsIt!<_M|Nq)<<|22{2uems ztHr=LELoX4My?gjraH8tJleJa)8H5qGI5X)tqNsn1slZS*HKqbq$Q-woXymg?qzk7O*7NSG|*1(q=_1K-jFQLd^Dn<$2*p&$k0&d|@i ztb;f3wz@@nEh{6qk#A{T4`B%B3ge3yy8ww5H4!6hW=Urzq=QRw46Sed5JIz1L^h*3 zy9bNb$(YM-fM{D5>qJW-iu~o))^(IdXA(sdV`CVH34#{W7EX*tY1B<}Lb+#+WW{z~ zobc2)U|mnIMjC6e-kHOpzgg9&Zjs7-friyJ^@!{h1(>sz2Gy{`qFU>vDWSYtC^N#c z%%}*X4lWD1I0SZ(Wxw3|z6~-FxaY^eSKq36ZylJk9p|5XG4hYykIoN1ys&}L(yR-^ zxJX9)TXIO3uGyrgED-`?Db}IkFph@2p@OXu@BvPH1ae83DOe+tEJ9#blob$eWpgX| zU$vzS(UY=?qcsfV2vr3GDkvSBB3xrLbye<(i@dYQh@7FNGkU=*2VvaYgj!LN=13Qx3Rj> ztHI57pEnS#Dlc`ZRGiJsXx!!g=vJ}1hNT3ZO_?*6v0Gh1DI(TZ`eO45`d`of;lb0l zIwl`?%%1-A$i?j^FUQ6Z|4taT8*qnMD}@7V){2p6*qUaxqdKuJz(UxHK>5WP}F@&tRHo%{63aQn>7RVi(0zA*(>kzz5ty4$^&uJukmtoarAmZ+IE zxiFKrA{vtB_L5hUS1bEPPncnn6=iQlB(R{!vnm=^zC{j3s)6qL;{I6Y@cru88d*>W z4!PPQV2$)Yc&=GlKk%J%&w>4^{GReHpoe$v*}eVv!`5Fb?-W>_5wfucGw-0C)tfC_ zT3IWK*la4StvIs~HQ?;eakI97;ZPN1&LLGU&eo8Xry@`mH#O9(-u$7N)w}GRx4@N` z#jr@e70W7LQIKzf8o4T_rZO5$t&A=Tmz4%Rgu@b8>&*|EGnR9x)@qV#-hDbnF6_#!1M$l1{{JFwMc~Yatt55b@|YgS8DMV7QNb zFE%e=e5;3M5A+|sCPzhoh_>fiBZ$CAU*8^UKRET}g+FwkxbLGejVCr|Jovqh(gttu zsk^W+JeO#$wMWnPK2shWP=D4LeP8&f@+ds#$is?W}!6YWP?qfDD8}op}H|_ zF!G{Lw?WyT${Ha8M<;k46ocT7GA@H4NHv6~I3OBfjX-yW7z(CQHplKEY%1RZ&Jpkr zJU-uj=Tz_AbA$Vbw;i2vFE@E`9P_EIIOi{41vDl}My23N6k>Fw!}fDTpfwzuZecJ; zZDoqXJ7+Hb@!;Ko^V7TT|HlAfcBpoooo;`$hh=d+vvYRrPY?Eq{^hZ?{a~Gua~OGA zd8g3&;YK0}=TL8AwyDnKf(sHSw^MVH)^QjkWfP+aS;d~d_1_`J{#2IuV_ zH_vJyP(zlPa#C5GO~nziby-fK&QQPFVf8fDCPb?$lYM@FSSBGN;%{?@Ei@@WvZ1O- z@};FiIxV8f%^(?9tUvtlJC~^;O099`BQTD*+G-;O$vFnR&XS>G_4z1D;*2$y(%2mq zkAMn-ute)rC1#}4YK}I6T0S?|C7HZUSFGl(6)0T7P%gsct&h}ugElZ{>)nve!E=F+ z7jDvQSvRK(QkO6$n~Tb$)eH)|ONjSK7w9PI7@G+rM7qTY4Tae5Vs=BNdY3(+9_nswebAW$G@O*7;m1P&t>p;`v?zop>qsd255XKzvK zwSr2cXa--|nzftX{}5x_$}<6OB(Nk!}Y)VXyK+0S&gr(IL{0V^UrzJ zq+G5y*ditygBMXar(&rz6AD2_E6w}2oqW>w;^3Clg0B1HJD%NYpM4R?kqE*;dR*BK zmaoXy)g=}ulva2GrL_{|gz&hgZF_%R8{)9lf!@J3tzEkbi~(rVP2f90UD|%)TF_1^a)uK#Iw7|vix$-Gr747LXi8%BX$gzU%X34Oez92&EAu(^ zwEOIvq1#u053i5^o-Y0^@jYR6h^!e(qwxhOMW$I*E}bI?kGHRI|WSp(;%(OJx8*mT6FGh()bpy-gK0b)*ws zuCb`~EQ4@!b;;ZIegecgb6YgH(Vho%D-Y!t7FkiDDP2>ToRU#4XVu`1(N$5O*C)0g zyf=9DNZ;;#edo@1?LFM}XfLQ|VEydzy^GMnKr*N=3=ca@`UG7>14Fs_D{UwrOw(A> zQWrOPm$fxSWiur3b)-NSU>HHDRT+AO;V7KV-qL>c(!kz_fB1)6pPxVI>l~eIzjbL~ z_tBmg`@->k)^la={zu2cfX$S83RgBS`+pdNE*!0ikKxwC!K2gN-(KlH@UngQRoC(1 z-~Nx&fFXgsw8@M?q(!BHQk-;_hOzyny~N^9YZ_x)u2%CE3hQbgy3jvyuVeUW-{o7| zMqYOv80~#C+VQb255jPk;WLSZOa`Tr zA$zS3%7z)PG^TC-l6G5+X+Sx#c_KSz$G$pwV+(M*op;AS>l#f}v~a9udDA&lrk%BB z;t3Rq);B3yIKS)u;o`3Qmj+&BX$0-MA;SS(%30Y9B@DO2gmbPDB2MA>I$o}QbRx`sx2A5C^Y-+N&1gOX$d z)fmwrYEhvczYJJ{Qp-zWlLsMKg3JeA4I$J^3>@cRoSE+M!0pj5NB^<=#(~Fm>ju%k6Sik)qu|%@Q;gmjP1VX#r;w*yb=B&7+J??Gi#0SsgA` zc!(#Hr|j;ktS0m34`W=TG+#N>i&SV-%fDM+FQ?r)K80W!3x2*tr(rQ?sn{U3%=IH{ zn$B)xlwKu(y-Q;>L`iDe#pT#w;3?BNLCy0EV}8?ERs``{exU}c$jnFrTZ|QA>lbG; zv_=bjqq70{a1Ff@atnNgK^q7FnA+^|8z78!x6}q=b4I}o%2xAbM&MzMwZJ#xoP{bj z2~8}QT3*{&8EvRxvet|HBYs47KT7ou5Jn`ocaQLnAxrGJ-VpMjz zK<+yXO3Z6eKRchE{Oo_<0-RPV@Dpm9_N-i~XW+7oG+pL`1%ZYpwlpH)1{|CFmQC+# z0q;>m4k)fetm~B;qDCS=d9Fs{qC6EpE5%lpq8T_`{NMx%%>_smnXYPFDZ^cauAB0T(sYb8jRvI5W|A{DH#fUyPP- zJ9`!NELmAsGCwbKY^^!gtEgCi=hT6K6kg$WafZ@7%Ee_YE)9!z8|&sl2Vm8pJ)3}r z2E8$t%Op7tDvs&1V3x|cICVkTzbw2u9Pz|h*TC(UyN3U3_nQa(mrm8ibWgm8Ba6Y< zX`@nK%A{2d!8*}X7K!;(v$L+PHY%@DdLts}a6MUGZ7|3`)jd)F z>b9Cy>y>_63C``httw6|FQI8bD4EnB>#TfVP0ic6&fw5Q6~U!ggqR=8m3B;z4jj7M zIeTr}@WpV~gYoTCXL~N4J^E;$G;n94WABW@;NM*GeSY-WMt^SZs(H_~b;B|Yr8qT} zv$O=Ftm_jQAvtwfBf4D4Z0f5OPBb|4r2FvGzH1i-c8|3KR@t!|Fo9T2{@^D$P+H2b z^k%c-8xu1Ud}TR`7v-F8p22nb;EdHPSwZKa)9vS944!`0eeHGku}cHjE_6NK|A&7* z`^ByCHicdMlqIZL#^tU}uVLA$%5j9#2^;t;{*AgfKyeL@3^b{UPITIWfBT^QPF%XpkW3&oa&QYts z=iX@F;{!)u9gg?#;>8>Wg27U&3r+OLyqY5{*Oul=ONA`^EiOE zm#D3v=fw1Gy{QF0;=w{cFJ z0iy|vymgU6M}8vbf-;L3wse_05o!rk)o52&*QG2-No|OD$|F@6R_HI2-5$g)aRh9W z3cMm}rYr>w%YWGaY7H=q>)ntV0$jtQQZo(SN?+UiD2*5_@!s(xUI18nPI`<5xs9+; zM5xsSEP!x)2F7SpARdi#J#rjUvtj$}c-X?yGnN zhC&@Aqhl1N1Npv9G+{qe*XPxogn8GJg{ z@%n1twMV`$?|=8r(D8wb=Xbs8?72HMc>QGmorC?S@Aci?-#2@G@W7GYM~CIgG!p@lkflNc`IUo-J9_R=CU0Q)Q_p_TJd_^nZPF^iplSuNdNeI(}{!8hF!38YN>WTCRu8r5ThnK`^r= zNoJqe9A9@<6G^wDXdZ0w#`()UCyGBe_rC(UFuX)zNb`FliLv`?0?}19%^|H>bPKM~ z-_=Iy=W2-J)e<-Z>p(O(nxCL>o6;=El(XV*0`*05`IIA@Bd7w=bOa+S=;3{+wDHXZhgwwNSmASTQO-|01I=@d0216vH4g146 z9;X261SEilSc%5o0+&)Wg`ia`RU$egAu8&DVFyi`>tA8J<+sO-T-|-6rcP{KW zzQ6OucsTBFzj-j$aqm(+$UEwN`77%4-sWoDf{eSIJaz5v&0xIc9hW;BQO^`4}Uy*X<+QpuEYNaytjSN(~j$-@%t0KFD7Go z)erKOw|Xg2V7!9XR_c#?QJ>_K2k##1*nP1;7QNB$b)*-9F(#Ek(em<0X;@yq{ra)& zZ|+?JC2rTyKXo6Ss99B$*L~*Pw!??!d_T}c^BhIVgn>K=7a+2dX(;0}#nx8whB3AV zJi5|vV`o~)LO#FzRqxEH!J*0iYg1iAH$FYIulMO0pnLkJk9ALfH+1LOz}+W1PET$9 zAd||?L~X?u&4S`g2tussn+z~xD@s;F1~?}ZWJIM7m3+*p;lDj%yKoC63?9iVmxQoa}Lh6h=vMqji8tC90wlr=%81n;~KRdN=;%&7*V)s zplaaWNayWST~9~iz9V;0tUpv(h_X3dN2(-W(8X{pfuV_jB`}2Du+`$gaM(o_`#Y{) z>^?lT{p78|!{>YgZ{$DwYFSIyt!PDIE2trH-x%YO zfF~==I7}H?o5C@_&nD)w@_fLdeQN?HHN|+mRda0n9eGY_r0eFBZPTNPCD})wp6Gab zbH~ghe^Dump_uQ-^{ia>uL*Ek68Ve1U-4DTavU0`!mqOuNsu?HxoRTJX|*tgt}vhk zrf~-Sa#Whj`;#4|Xf*J)9~D|4)dN5#iS|bq!hOeH zdpxBH^^~zS+8AfeOLMrJ)aF`tvuqY{JdBuU5$> zK;Psyw|?l6+Tx7!xBoo)<->{g{S)1nr~8iI?mu?yt8XX19)Gsu(w(m^{in?zrqD&1 z;GHGu1|ff`Gb2suIR!~ZfmdY-Nfd{wt)^st5sTt>eHb)O68H`!H;l&ir&1&5bKf}I ze#aO9+fHO1<}jPh;E+nAXa-ND>)FIt*DrkYY$VRX+$&<|sDk5REaCz8JT@v`J1}2+ z-obE{Gd4XQaoUYg$C;ab&rgtg$dcB6bG-lYWD_^PCwSQAbSig8fF69C8^-!3;7iKB zE`tQ4EBoIU{vdanyH~OjBT#UUMPZetfM)>?ZA0Y9JG@hIRsYFHom0cfnTz|s8|{Am zy6@_Ff38nO3fef+d8_&4NOe^xQH!8|*{>*h)ru8n6srGQlt@{SSXL^k>9>B7wfork+K;O5c+fLo?Jbb{0Q{Bfsl@F*11M=#EEcrR|q5bndB zW?Qasb|Dpoi7;*sYbe0}bm4h+k(Wh78a1BHdLW0&K}e_`(eiexwjA8Mz%$hlATdfk ztC!`bp_+->#XG5*)vq<*}Go;DVEuPDuv(+pHFI&i)VQX$O z%3&bC)j~`t%K?Gf1KSoFQeq5HVOAz^4??-`CdPcRqP^#JL7X|7x%gcN;PI66mn6_J z{|@$bMgpy-sKnSBDI84n^uL_wx<20X;BepN=L5%Qi?a;2piv)0Oc>z-8FW1VE1&l( z@6cJ>N3L`~IfR*D9ouzow)?xIBn?^7?T;rr$A|pELZwdwulBvS;NN=mWr}YEB17>O zfq#`R2254);q%#2oEQ8#6R!_z6>rF+INZX?AVX2IK?Jsj%i?pU23)u*P$P*IQe#uF zEU2F26Y<=)aQ-_B35&x5?v*H6h{DfW)v+29^S6aDUp)6*EQ9`6^y#GUwVRT)ioQ^d z$mkH_Z6*bDh{52HAjFKN`!a<1pX|GaPWk9R589BlfJGcI~=QhV+;C(2( zGCqdybG41$>Q!&O=%3}&+oy*6PJg$5=5^QEp?yy*dFr-n<^XI$qjw z{Dtf<%CFA>x8~wHGR`#&W(xEia3?C8z^NbHW5B4(i8#BDWv!mKns&KS>BZ*! zlAro%B`W~)xip(+tYt+g#3!PKYBz<`JhT@41!5%;Fn<+oDSL;;45}=Q)8aI@fQ{|mLG`@8@E literal 0 HcmV?d00001 diff --git a/示例项目/防脱机制裁/防脱机制裁_Nangua.json b/示例项目/防脱机制裁/防脱机制裁_Nangua.json new file mode 100644 index 0000000..a1337b3 --- /dev/null +++ b/示例项目/防脱机制裁/防脱机制裁_Nangua.json @@ -0,0 +1,12 @@ +{ + "制裁开关(true开启false关闭)": true, + "制裁配置 达到次数 制裁时长(秒)": [2, 300], + "角色CID白名单": [1, 2], + "副本ID白名单(非正常副本接口进入的副本)": [11111,222222], + "温馨提示1":"副本ID白名单并不是加了就关闭检测,只是关闭其中一项检测", + "温馨提示2": "角色ID可以查看数据库中 taiwan_cain → charac_info → charac_no代表角色ID", + + "msgtype1": "你已经被限制获取道具和金币,剩余:[%d]秒...", + "msgtype2": "你已经被限制获取经验,剩余:[%d]秒...", + "msgtype3": "玩家[%s]因使用第三方软件已被限制进入副本,剩余:[%d]秒..." +} \ No newline at end of file diff --git a/示例项目/黄金品级调整箱/Proj.ifo b/示例项目/黄金品级调整箱/Proj.ifo new file mode 100644 index 0000000..b661fe9 --- /dev/null +++ b/示例项目/黄金品级调整箱/Proj.ifo @@ -0,0 +1,11 @@ +{ + "ProjectName": "黄金品级调整箱", + "ProjectDescribe": "将一个品级调整箱替换为黄金品级调整箱", + "ProjectAuthor": "凌众", + "ProjectVersion": 1.1, + "ProjectConfig": "黄金品级调整箱_Lenheart.json", + "ProjectFiles": [ + "黄金品级调整箱.nut" + ], + "ProjectRunFunc": "_Dps_SetEquinherit_Main_" +} \ No newline at end of file diff --git a/示例项目/黄金品级调整箱/黄金品级调整箱.nut b/示例项目/黄金品级调整箱/黄金品级调整箱.nut new file mode 100644 index 0000000..95bbb0c --- /dev/null +++ b/示例项目/黄金品级调整箱/黄金品级调整箱.nut @@ -0,0 +1,29 @@ + +function _Dps_SetEquinherit_Main_() { + Cb_ModItemattr_Enter_Func.addinfoMax <- function(args) { + //print("触发回调"); + local Config = GlobalConfig.Get("黄金品级调整箱_Lenheart.json"); + local SUser = User(args[1]); + local pack = NativePointer(args[2]); + local a = NativePointer(pack.add(20).readPointer()); + local itemSold = a.add(13).add(6).readShort(); + local equSold = a.add(13).add(0).readShort(); + local InvenObj = SUser.GetInven(); + print("InvenObj = " + InvenObj); + if (InvenObj) { + local ItemObj = InvenObj.GetSlot(1, itemSold); + local equObj = InvenObj.GetSlot(1, equSold); + //print("ItemObj = " + ItemObj); + //print("equObj = " + equObj); + //print("ItemObj.GetIndex() = " + ItemObj.GetIndex()); + if (ItemObj.GetIndex() == Config["黄金品级调整箱id"]) { + Timer.SetTimeOut(function() { + equObj.SetAdd_Info(999999998); + equObj.Flush(); + SUser.SendUpdateItemList(1, 0, equSold); + //print("调整成功"); + }, 1); + } + } + } +} \ No newline at end of file diff --git a/示例项目/黄金品级调整箱/黄金品级调整箱_Lenheart.json b/示例项目/黄金品级调整箱/黄金品级调整箱_Lenheart.json new file mode 100644 index 0000000..3c955e2 --- /dev/null +++ b/示例项目/黄金品级调整箱/黄金品级调整箱_Lenheart.json @@ -0,0 +1,4 @@ +{ + "黄金品级调整箱id": 897 + +} \ No newline at end of file