37 Commits

Author SHA1 Message Date
a71fb179ef 11 2026-04-27 15:15:16 +08:00
e82f15f1b0 11 2026-04-22 23:09:05 +08:00
fe5b77e1bf 1 2026-04-22 21:52:16 +08:00
d29985fbbb 1 2026-04-22 21:45:39 +08:00
aec3ef9738 111 2026-04-22 21:39:59 +08:00
96ee9c24c0 1 2026-04-22 20:47:49 +08:00
9d145656a7 1 2026-04-22 20:37:29 +08:00
5c5a92e2d2 1 2026-04-22 18:09:07 +08:00
841663e827 1 2026-04-22 18:06:09 +08:00
acd48ed2a0 1 2026-04-22 17:21:23 +08:00
ef99801739 feat: 添加服务器IP打印和解除角色创建限制功能
在SocketBase.nut中添加打印本服务器IP的功能,便于调试
在A.nut中添加解除角色创建限制的回调函数
2026-04-22 10:45:39 +08:00
be156a2559 11 2026-04-18 14:47:36 +08:00
5319f2cac9 11 2026-04-18 14:44:54 +08:00
991050c720 fix(偷窃系统): 修复NPC列表标签解析问题并更新.gitignore
修复StealSystem中NPC列表标签解析逻辑,确保正确识别[npc list]标签
添加加载成功提示信息并排除libAurora.so文件
2026-04-18 14:11:31 +08:00
721fb5a992 添加示例项目 2026-04-16 16:27:53 +08:00
69a2141804 feat(偷窃系统): 添加新的偷窃系统功能
- 在FileConfig.json中添加偷窃系统配置
- 实现偷窃系统核心逻辑,包括物品消耗、奖励随机获取和每日重置
- 优化_PVF_Data_类的Seek和Get方法
- 修改Use_Item_Sp.nut中的回调函数为异步执行
- 更新测试命令功能,替换物品掉落为窗口通知
2026-04-14 13:15:07 +08:00
aac9cd20c7 feat: 添加露露定制内容系统和开发技能文档
新增露露定制内容系统,实现稀有物品掉落功能
添加DP-S游戏服务器插件开发技能文档,包含框架架构、模块开发规范和常用API参考
2026-04-09 23:48:15 +08:00
e4061fab4c docs(_Core): 修正钩子函数的注释描述
将物品删除钩子的错误注释更正为"向区域添加用户",以准确反映实际功能
2026-03-19 11:12:18 +08:00
046f22b3cf refactor(数据库): 优化换装系统表结构
将cid字段长度从128改为64,info字段类型从VARCHAR改为TEXT
2026-03-18 17:22:04 +08:00
2815d5e898 refactor(装备拓展): 移除已注释的购买装备后刷新金币逻辑 2026-03-04 16:04:59 +08:00
5baa2cff0a fix(装备拓展): 修复购买装备时金币显示错乱问题
修改装备镶嵌相关代码后导致购买装备时金币显示异常,通过添加定时刷新金币操作解决该问题
2026-03-04 15:42:44 +08:00
ee09eb5efc refactor(装备系统): 注释掉装备属性对比的条件判断
移除装备属性对比的条件判断,直接执行后续逻辑
2026-03-03 20:47:20 +08:00
36c144dc6d 更新换装系统插件 2026-03-03 15:45:14 +08:00
b2881499c6 1.3基础 2026-02-27 15:40:20 +08:00
616a254c31 修复誉名录BUG 2026-02-18 21:11:31 +08:00
b5fba5bed3 寒服更新 2026-02-18 21:06:10 +08:00
c9d989f8b8 寒服更新 2026-02-14 04:23:30 +08:00
868a9f3c9a 11 2026-02-14 03:31:30 +08:00
4de459884c 寒服更新 2026-02-13 20:20:00 +08:00
lenheart
513caa5347 异界装备升级 2026-02-02 19:27:29 +08:00
lenheart
58b5ebfcc2 更新HOOK 2026-01-26 15:36:13 +08:00
lenheart
877ab0b8bc HOOK新增 2026-01-22 20:02:26 +08:00
lenheart
3cf9eaaa29 新的hook接口 2026-01-22 19:59:35 +08:00
lenheart
b2e4be7d06 添加装备拓展功能,创建新表以管理唯一ID,并实现UUID生成逻辑;更新ItemClass和PacketClass以支持新功能 2026-01-19 21:59:05 +08:00
lenheart
a1e5af4ef9 更新hook 2026-01-16 22:59:19 +08:00
lenheart
153de52d6b 更新回调 2026-01-11 19:16:56 +08:00
lenheart
a162ecee48 更新HttpServer类以支持服务ID管理,添加MD5类的hex_encode函数和改进base64_encode函数,修改A.nut文件以增加注释和功能 2026-01-03 14:38:50 +08:00
229 changed files with 13759 additions and 68 deletions

2
.gitignore vendored
View File

@@ -1,2 +1,4 @@
OfficialProject/
OfficialConfig/
lib/libAurora.so
lib/libAurora.so

22
AGENTS.md Normal file
View File

@@ -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.

View File

@@ -1,8 +1,10 @@
function sqr_main() {
GameManager.OpenHotFix("/dp_s/OfficialConfig");
GameManager.OpenHotFix("/dp_s/OfficialProject");
GameManager.OpenHotFix("/dp_s/_DPS_/_Core");
GameManager.OpenHotFix("/dp_s/_DPS_/_BuiltProject");
GameManager.OpenHotFix("/dp_s/MyProject");
GameManager.SetGameMaxLevel(86);
// GameManager.SetGameMaxLevel(86);
// Cb_History_MileageSet_Func["TW_reach_game_world"] <- function(SUser, Data) {
// //第一次上线
// if (SUser.GetCharacLevel() > 70) {

View File

@@ -22,6 +22,7 @@ Gm_InputFunc_Handle["给"] <- function(SUser, CmdString) {
}
}
Gm_InputFunc_Handle["转职"] <- function(SUser, CmdString) {
local count = -1;
local pos = 0;
@@ -69,6 +70,8 @@ Gm_InputFunc_Handle["完成任务"] <- function(SUser, CmdString) {
SUser.ClearQuest_Gm(221);
}
Gm_InputFunc_Handle["升级"] <- function(SUser, CmdString) {
local count = -1;
local pos = 0;
@@ -109,15 +112,71 @@ Gm_InputFunc_Handle["点券"] <- function(SUser, CmdString) {
}
}
Gm_InputFunc_Handle["test"] <- function(SUser, CmdString) {
SUser.SendItemWindowNotification([[33900, 1]]);
}
Timer.SetTimeOut(function() {
// _Dps_Equ2AvaJewel_Main_()
// local Pack = Packet();
// print("包位置: " + Pack.GetPos());
// Pack.Put_Int(666);
// print("包位置: " + Pack.GetPos());
// // Sq_Rsa_GenerateKey();
// local str = format("%08x010101010101010101010101010101010101010101010101010101010101010155914510010403030101", 1);
// local Byte = Sq_Rsa_Private_Encrypt(str);
// local EnStr = _base64_encode(Byte);
// print(EnStr);
// local passwd = "1";
// Sq_WriteByteArr(S_Ptr("0x081E907E"), [0x90, 0x90, 0x90, 0x90, 0x90]);
// Sq_WriteByteArr(S_Ptr("0x0081E9085"), [0x0F, 0x94, 0xC0]);
// Sq_WriteByteArr(S_Ptr("0x81731F2"), Haker.AsmGenerateMcd(
// "mov eax,0",
// "ret" ));
}, 1);
Gm_InputFunc_Handle["我要点券"] <- function(SUser, CmdString) {
SUser.RechargeCera(500000);
}
Cb_CreateCharac_CheckLimitCreateNewCharac_Leave_Func["解除创建角色限制"] <- function(args) {
print(args.pop());
return true;
}
// // //玩家新增道具时
// Cb_wdzsdfge_Enter_Func <- {};
// Cb_wdzsdfge_Leave_Func <- {};
// _Hook_Register_Currency_Func_("0x81731F2", ["pointer", "pointer", "pointer", "int"], Cb_wdzsdfge_Enter_Func, Cb_wdzsdfge_Leave_Func);
// Cb_wdzsdfge_Leave_Func["11"] <- function(args) {
// print("登录请求: ");
// print("args[0]: " + args[0]);
// NativePointer(args[0]).Output(512);
// print("args[1]: " + args[1]);
// //NativePointer(args[1]).Output(512);
// print("args[2]: " + args[2]);
// NativePointer(args[2]).Output(512);
// }
Gm_InputFunc_Handle["我要升级"] <- function(SUser, CmdString) {
SUser.SetCharacLevel(85);
}
Gm_InputFunc_Handle["南瓜村"] <- function(SUser, CmdString) {
getroottable()._NewTitle_.AddTitle(SUser, 1);
}

View File

@@ -0,0 +1,396 @@
/*
文件名:灵魂救赎武器.nut
路径:_DPS_/_BuiltProject/灵魂救赎武器/灵魂救赎武器.nut
创建日期:2026-01-20 19:24
文件用途:
*/
class SoulSalvationC {
MysqlObject = null;
//灵魂救赎武器标识 (阶段1)
Equipment1List = null;
//1阶段所需灵魂数量
Stage1SoulCount = 1000;
//1阶段进阶所需材料
Equipment1Upgrade = null;
//灵魂救赎武器标识 (阶段2)
Equipment2List = null;
//2阶段所需灵魂数量
Stage2SoulCount = 100;
//灵魂救赎武器标识 (阶段3)
Equipment3List = null;
//数据
data = null;
constructor() {
Equipment1List = {};
Equipment1Upgrade = {};
Equipment2List = {};
Equipment3List = {};
data = {};
//创建数据库
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"));
local check_table_sql = "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'soulsalvation';"
local Ret = MysqlObject.Select(check_table_sql, ["int"]);
if (Ret.len() == 0 || Ret[0][0] == 0) {
local sql = "CREATE TABLE soulsalvation (`uid` INT(11) NOT NULL COMMENT '主键ID',`soul_id` INT(11) NOT NULL DEFAULT 0 COMMENT '灵魂ID', `soul_count` INT(11) NOT NULL DEFAULT 0 COMMENT '数值字段默认值0',PRIMARY KEY (`uid`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='soulsalvation表';";
MysqlObject.Exec_Sql(sql);
}
//读取PVF
InitPvf();
//注册客户端收包
RegisterClient();
//读取数据库数据到缓存
InitMysqlData();
}
function RegisterClient() {
//测试用客户端主动查询灵魂数
ClientSocketPackFuncMap.rawset(21011001, function(SUser, Jso) {
local SendInfo = GetUserData(SUser);
SUser.SendJso({
op = 21011002,
Info = SendInfo
})
}.bindenv(this));
Cb_CParty_OnKillMonster_Leave_Func.rawset("SoulSalvationC", function(args) {
local SUser = User(args[1]);
//获取佩戴的武器
local Weapon = GetUserWeapon(SUser);
local WeaponIndex = Weapon.GetIndex();
//可积累灵魂数量
if (Equipment1List.rawin(WeaponIndex) || Equipment2List.rawin(WeaponIndex)) {
local uuid = Weapon.GetUuid();
//有数据则+1 无数据则新增
if (data.rawin(uuid)) {
if (data[uuid].soul_count< Stage1SoulCount)
data[uuid].soul_count += 1;
} else {
data[uuid] <- {
soul_count = 1,
soul_id = 0
};
}
SUser.SendJso({
op = 21011004,
uuid = uuid,
info = data[uuid]
})
}
}.bindenv(this));
//上线时 获取自己的灵魂武器信息
Cb_reach_game_world_Func.rawset("SoulSalvationC", function(SUser) {
local SendInfo = GetUserData(SUser);
SUser.SendJso({
op = 21011002,
Info = SendInfo
})
}.bindenv(this));
//查看他人信息的时候 获取灵魂武器信息
Cb_GetUserInfo_Leave_Func.rawset("SoulSalvationC", function(args) {
local SUser = User(args[1])
local CheckWorldId = NativePointer(args[2]).add(13).readShort();
local CheckUser = World.GetUserBySession(CheckWorldId);
if (CheckUser) {
local SendInfo = GetUserData(CheckUser);
SUser.SendJso({
op = 21011006,
Info = SendInfo
})
}
}.bindenv(this));
//每5分钟固化一次数据到数据库
Timer.RemoveCronTask("SoulSalvationC");
Timer.SetCronTask(function() {
local DataList = [];
foreach(uuid, value in data) {
local str = format("(%d,%d,%d),", uuid, value.soul_id, value.soul_count);
DataList.push(str);
}
local sql = "REPLACE INTO soulsalvation (uid, soul_id, soul_count) VALUES";
foreach(value in DataList) {
sql += value;
}
//去除最后一个,
sql = sql.slice(0, -1);
sql += ";";
MysqlObject.Exec_Sql(sql);
// }.bindenv(this), {
// Cron = "0 */5 * * * *",
// Name = "SoulSalvationC"
// });
}.bindenv(this), {
Cron = "*/5 * * * * *",
Name = "SoulSalvationC"
});
ClientSocketPackFuncMap.rawset(21011007, function(SUser, Jso) {
//获取背包
local InvenObj = SUser.GetInven();
//装备ID
local Id = Jso.item.ItemId;
//判断是否为灵魂武器一阶段
if (!Equipment1List.rawin(Id)) {
SUser.SendJso({
op = 21011008,
error = 1
});
return;
}
// //判断灵魂数量是否已经达到要求
// local SlotItem = InvenObj.GetSlot(Inven.INVENTORY_TYPE_ITEM, 9 + Jso.item.Pos);
// local uuid = SlotItem.GetUuid();
// local SoulCount = 0;
// if(data.rawin(uuid)){
// SoulCount = data[uuid].soul_count;
// }
// if(SoulCount < Stage1SoulCount){
// SUser.SendJso({
// op = 21011008,
// error = 2
// });
// return;
// }
local MgArr = [];
foreach(Index, Count in Equipment1Upgrade) {
//查询自己有多少个道具
MgArr.push({
Item = Index,
RealCount = InvenObj.GetCountById(Index),
NeedCount = Count.tointeger()
});
}
SUser.SendJso({
op = 21011008,
MgInfo = MgArr
});
}.bindenv(this));
ClientSocketPackFuncMap.rawset(21011009, function(SUser, Jso) {
//获取背包
local InvenObj = SUser.GetInven();
//装备ID
local Id = Jso.item.ItemId;
//判断是否为灵魂武器一阶段
if (!Equipment1List.rawin(Id)) {
SUser.SendJso({
op = 21011008,
error = 1
});
return;
}
//要给予的装备ID
local giveid = Id + 1;
//获取对应槽位的装备
local SlotItem = InvenObj.GetSlot(Inven.INVENTORY_TYPE_ITEM, 9 + Jso.item.Pos);
local uuid = SlotItem.GetUuid();
if (SlotItem.GetIndex() != Id) {
SUser.SendNotiBox(" 升级失败 升级时请不要操作背包", 1);
return;
} else {
local MgArr = [];
foreach(Index, Count in Equipment1Upgrade) {
//查询自己有多少个道具
MgArr.push({
Id = Index,
Count = Count.tointeger()
});
}
local Flag = InvenObj.CheckArrItemCount(MgArr);
if (!Flag) {
print("扣除失败");
SUser.SendNotiBox(" 升级失败 升级时请不要操作背包", 1);
return;
} else {
local SlogInfo = SUser.GiveItem(giveid, 1);
if (!SlogInfo) {
SUser.SendNotiBox(" 升级失败 请确定背包有空余空间", 1);
return;
}
local giveItemObj = InvenObj.GetSlot(1, SlogInfo[1])
//扣除道具
InvenObj.DeleteArrItemCount(MgArr);
local forging = SlotItem.GetForging(); //锻造
local upgrade = SlotItem.GetUpgrade(); //强化
local amplification = SlotItem.GetAmplification(); //增幅
local enchanting = SlotItem.GetEnchanting(); //附魔
if (forging > 0) {
giveItemObj.SetForging(forging);
}
if (upgrade > 0) {
giveItemObj.SetUpgrade(upgrade);
}
if (amplification != 0) {
giveItemObj.SetAmplification(amplification);
}
giveItemObj.SetEnchanting(enchanting);
giveItemObj.SetAdd_Info(SlotItem.GetAdd_Info()); //品级
giveItemObj.Flush();
//销毁背包中的道具
SlotItem.Delete();
//刷新玩家背包列表
SUser.SendUpdateItemList(1, 0, 9 + Jso.item.Pos);
SUser.SendUpdateItemList(1, 0, SlogInfo[1]);
//刷新
SUser.SendNotiBox(" 升级成功", 1);
SUser.SendJso({
op = 21011010
});
//从缓存中移除这个uuid
data.rawdelete(uuid);
local Sql = "DELETE FROM soulsalvation WHERE uid = " + uuid + ";";
MysqlObject.Exec_Sql(Sql);
}
}
}.bindenv(this));
};
//获取角色的武器
function GetUserWeapon(SUser) {
local InvenObj = SUser.GetInven();
if (!InvenObj) return null;
return InvenObj.GetSlot(Inven.INVENTORY_TYPE_BODY, 10);
}
//获取角色的灵魂武器信息
function GetUserData(SUser) {
//获取背包对象
local InvenObj = SUser.GetInven();
if (!InvenObj) return {};
local UUIDList = [];
//遍历身上
for (local i = 0; i< 26; i++) {
local Inven_Item = InvenObj.GetSlot(Inven.INVENTORY_TYPE_BODY, i);
if (Inven_Item) {
local Index = Inven_Item.GetIndex();
if (Equipment1List.rawin(Index) || Equipment2List.rawin(Index)) {
local uuid = Inven_Item.GetUuid();
UUIDList.push(uuid);
}
}
}
//遍历物品栏
for (local i = 0; i< 70; i++) {
local Inven_Item = InvenObj.GetSlot(Inven.INVENTORY_TYPE_ITEM, i);
if (Inven_Item) {
local Index = Inven_Item.GetIndex();
if (Equipment1List.rawin(Index) || Equipment2List.rawin(Index)) {
local uuid = Inven_Item.GetUuid();
UUIDList.push(uuid);
}
}
}
local SendInfo = {};
foreach(uuid in UUIDList) {
if (data.rawin(uuid)) {
SendInfo.rawset(uuid, data[uuid]);
} else {
data[uuid] <- {
soul_count = 0,
soul_id = 0
};
}
}
return SendInfo;
}
function InitPvf() {
Script();
ScriptData.GetFileData("etc/rindro/expandequipment/soulsalvation.etc", function(DataTable, Data) {
while (!Data.Eof()) {
local Fragment = Data.Get();
if (Fragment == "[EQUIPMENT1]") {
Equipment1List = {};
while (true) {
local Fbuf = Data.Get();
if (Fbuf == "[/EQUIPMENT1]") {
break;
}
Equipment1List.rawset(Fbuf, true);
}
} else if (Fragment == "[EQUIPMENT2]") {
Equipment2List = {};
while (true) {
local Fbuf = Data.Get();
if (Fbuf == "[/EQUIPMENT2]") {
break;
}
Equipment2List.rawset(Fbuf, true);
}
} else if (Fragment == "[EQUIPMENT3]") {
Equipment3List = {};
while (true) {
local Fbuf = Data.Get();
if (Fbuf == "[/EQUIPMENT3]") {
break;
}
Equipment3List.rawset(Fbuf, true);
}
} else if (Fragment == "[STAGE1 VALUE]") {
Stage1SoulCount = Data.Get();
} else if (Fragment == "[STAGE2 VALUE]") {
Stage2SoulCount = Data.Get();
} else if (Fragment == "[STAGE1 NEED ITEM]") {
Equipment1Upgrade = {};
while (true) {
local Fbuf = Data.Get();
if (Fbuf == "[/STAGE1 NEED ITEM]") {
break;
}
Equipment1Upgrade.rawset(Fbuf, Data.Get());
}
}
}
}.bindenv(this));
}
function InitMysqlData() {
local Sql = "SELECT * FROM soulsalvation"
local Ret = MysqlObject.Select(Sql, ["int", "int", "int"]);
foreach(Row in Ret) {
local uuid = Row[0];
local soul_id = Row[1];
local soul_count = Row[2];
data.rawset(uuid, {
soul_id = soul_id,
soul_count = soul_count
})
}
}
}
Timer.SetTimeOut(function() {
getroottable()._SoulSalvation_ <- SoulSalvationC();
print("双端插件·灵魂救赎武器 - 已加载");
}, 1);

View File

@@ -0,0 +1,95 @@
/*
文件名:装备拓展.nut
路径:MyProject/装备拓展.nut
创建日期:2026-01-15 10:02
文件用途:
*/
Cb_ItemGloballyUniqueIdentifier_t_SetServerId_Enter_Func <- {};
Cb_ItemGloballyUniqueIdentifier_t_SetServerId_Leave_Func <- {};
_Hook_Register_Currency_Func_("0x08894782", ["pointer", "int", "pointer"], Cb_ItemGloballyUniqueIdentifier_t_SetServerId_Enter_Func, Cb_ItemGloballyUniqueIdentifier_t_SetServerId_Leave_Func);
// Cb_CInventory_MakeItemPacket_Enter_Func <- {};
// Cb_CInventory_MakeItemPacket_Leave_Func <- {};
// _Hook_Register_Currency_Func_("0x084FC6BC", ["pointer", "int", "int", "pointer", "int"], Cb_CInventory_MakeItemPacket_Enter_Func, Cb_CInventory_MakeItemPacket_Leave_Func);
class EquipmentExpandC {
MysqlObject = null;
constructor() {
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"));
local check_table_sql = "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'equip_only_id';"
local Ret = MysqlObject.Select(check_table_sql, ["int"]);
if (Ret.len() == 0 || Ret[0][0] == 0) {
local sql = "CREATE TABLE equip_only_id (id INT(11) NOT NULL AUTO_INCREMENT COMMENT '32位自增唯一ID初始值10000', PRIMARY KEY (id)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT '纯自增ID表' AUTO_INCREMENT=10000;";
MysqlObject.Exec_Sql(sql);
}
//HOOKServerId生成uuid
Cb_ItemGloballyUniqueIdentifier_t_SetServerId_Enter_Func["_装备拓展_"] <- function(args) {
args[1] = GenerateUUID();
// print("赋予唯一id " + args[1]);
return args;
}.bindenv(this);
// //补充数据包
// Cb_CInventory_MakeItemPacket_Leave_Func["_装备拓展_"] <- function(args) {
// // local ItemObj = Item(Sq_CallFunc(S_Ptr("0x084FC1DE"), "int", ["pointer", "int", "int", "pointer"], args[0], args[1], args[2]));
// // print("装备编号: " + ItemObj.GetIndex());
// // local Pack = Packet(args[3]);
// // local Buffer = Memory.alloc(30);
// // //无需重置为0 需要写数据的地方自己会覆写
// // Buffer.add(18).writeInt(NativePointer())
// // Pack.Put_BinaryEx(DB_JewelSocketData.C_Object, 30);
// }.bindenv(this);
//额外数据包,发送装备数据给本地处理
Cb_InterfacePacketBuf_put_packet_Leave_Func["_装备拓展_"] <- function(args) {
local Inven_Item = NativePointer(args[1]);
if (Inven_Item.add(1).readU8() == 1) {
local Uuid = Inven_Item.add(21).readInt();
if (Uuid< 10000) {
local uuid = GenerateUUID();
Inven_Item.add(21).writeInt(uuid);
}
}
}.bindenv(this);
}
function GenerateUUID() {
MysqlObject.Exec_Sql("INSERT INTO equip_only_id VALUES ();");
local Ret = MysqlObject.Select("SELECT LAST_INSERT_ID() AS new_auto_id;", ["int"]);
return Ret[0][0];
}
// Cb_CInventory_insertItemIntoInventory_Leave_Func["_装备拓展_"] <- function(args) {
// local SUser = User(NativePointer(args[0]).readPointer());
// local ItemObject = Item(NativePointer(Haker.CpuContext.ebp).add(12).C_Object);
// local ServerId = NativePointer(ItemObject.C_Object).add(21).readInt();
// //数据库主键从10000开始自增
// if (ServerId< 10000) {
// local NewUuid = GenerateUUID();
// NativePointer(ItemObject.C_Object).add(21).writeInt(NewUuid);
// }
// }.bindenv(this);
}
Timer.SetTimeOut(function() {
getroottable()._EquipmentExpand_ <- EquipmentExpandC();
print("特殊插件·装备拓展 - 已加载");
}, 1);

View File

@@ -83,6 +83,11 @@
"毁梦定制战力系统/毁梦定制战力系统.nut"
]
},
"毁梦定制装备继承": {
"Script": [
"毁梦定制装备继承/毁梦定制装备继承.nut"
]
},
"卢克": {
"Script": [
"卢克/LukeClass.nut"
@@ -107,6 +112,41 @@
"Script": [
"装备跨界/装备跨界.nut"
]
},
"灵魂救赎武器": {
"Script": [
"灵魂救赎武器/灵魂救赎武器.nut"
]
},
"装备拓展": {
"Script": [
"装备拓展/装备拓展.nut"
]
},
"异界装备升级": {
"Script": [
"异界装备升级/异界装备升级.nut"
]
},
"黄金哥布林袖珍罐": {
"Script": [
"黄金哥布林袖珍罐/黄金哥布林袖珍罐.nut"
]
},
"誉名录": {
"Script": [
"誉名录/誉名录.nut"
]
},
"换装系统": {
"Script": [
"换装系统/换装系统.nut"
]
},
"偷窃系统": {
"Script": [
"偷窃系统/偷窃系统.nut"
]
}
}
}

View File

@@ -0,0 +1,376 @@
/*
File: 偷窃系统.nut
Path: _DPS_/_BuiltProject/偷窃系统/偷窃系统.nut
*/
class StealSystem {
RandomCountMin = 0;
RandomCountMax = 0;
NpcConfig = null;
RequestCache = null;
Loaded = false;
UseItem = null;
constructor() {
NpcConfig = {};
RequestCache = {};
Loaded = InitPvf();
if (Loaded) {
RegisterClient();
InitDailyResetTask();
}
}
function RegisterClient() {
ClientSocketPackFuncMap.rawset(21017001, function(SUser, Jso) {
local ret = false;
local npcId = null;
try {
npcId = Jso.npcid;
} catch (exception) {
npcId = null;
}
local cid = SUser.GetCID();
if (typeof npcId == "integer" && NpcConfig.rawin(npcId)) {
if (!RequestCache.rawin(cid)) {
RequestCache[cid] <- {};
}
if (!RequestCache[cid].rawin(npcId)) {
ret = true;
} else {
SUser.SendNotiBox(" 偷窃失败 今日已对该他实施过偷窃", 1);
SUser.SendJso({
op = 21017002,
ret = ret
});
return;
}
} else {
print("StealSystem: 21017001 invalid npcid.");
SUser.SendJso({
op = 21017002,
ret = ret
});
return;
}
local InvenObj = SUser.GetInven();
local itemSlot = InvenObj.GetSlotById(UseItem);
if (itemSlot == -1) {
ret = false;
SUser.SendNotiBox(" 偷窃失败 您没有该偷窃消耗物品!", 1);
SUser.SendJso({
op = 21017002,
ret = ret
});
return;
} else if (!InvenObj.DeleteItemCount(UseItem, 1)) {
ret = false;
SUser.SendJso({
op = 21017002,
ret = ret
});
return;
}
if (ret) {
RequestCache[cid].rawset(npcId, 1);
}
SUser.SendJso({
op = 21017002,
ret = ret
});
}.bindenv(this));
ClientSocketPackFuncMap.rawset(21017003, function(SUser, Jso) {
local npcId = null;
try {
npcId = Jso.npcid;
} catch (exception) {
npcId = null;
}
if (typeof npcId != "integer") {
print("StealSystem: 21017003 invalid npcid.");
return;
}
local cid = SUser.GetCID();
if (!RequestCache.rawin(cid) || !RequestCache[cid].rawin(npcId) || RequestCache[cid][npcId] != 1) {
print(format("StealSystem: 21017003 denied for cid=%d npc=%d because 21017001 is not passed.", cid, npcId));
return;
}
if (!NpcConfig.rawin(npcId)) {
print(format("StealSystem: 21017003 npc config not found for npc=%d.", npcId));
return;
}
local groupName = GetRandomGroupName();
local groupData = NpcConfig[npcId][groupName];
if (groupData.len() == 0) {
print(format("StealSystem: 21017003 npc=%d picked empty group %s.", npcId, groupName));
return;
}
local drawCount = MathClass.Rand(RandomCountMin, RandomCountMax);
if (drawCount <= 0) {
print(format("StealSystem: 21017003 draw count <= 0 for npc=%d.", npcId));
return;
}
local rewardMap = BuildRewardMap(groupData, drawCount);
if (!rewardMap) {
print(format("StealSystem: 21017003 failed to build rewards for npc=%d group=%s.", npcId, groupName));
return;
}
if (!GiveRewardMap(SUser, rewardMap)) {
print(format("StealSystem: 21017003 failed to give rewards for cid=%d npc=%d.", cid, npcId));
return;
}
local notiadd = [];
foreach(Index, Count in rewardMap) {
notiadd.append([Index, Count]);
}
SUser.SendItemWindowNotification(notiadd);
RequestCache[cid].rawset(npcId, 2);
}.bindenv(this));
}
function InitDailyResetTask() {
Timer.RemoveCronTask("StealSystemDailyReset");
Timer.SetCronTask(function() {
RequestCache = {};
print("StealSystem: daily request cache reset.");
}.bindenv(this), {
Cron = "0 0 6 * * *",
Name = "StealSystemDailyReset"
});
}
function ParseRewardGroup(Data, CloseTag, OutList, Path, NpcId, GroupName) {
while (true) {
local first = Data.Get();
if (first == CloseTag) {
return true;
}
if (Data.Eof()) {
print(format("StealSystem: npc %d %s block missing close tag %s in %s.", NpcId, GroupName, CloseTag, Path));
return false;
}
local second = Data.Get();
if (Data.Eof()) {
print(format("StealSystem: npc %d %s block has incomplete entry in %s.", NpcId, GroupName, Path));
return false;
}
local third = Data.Get();
if (typeof first != "integer" || typeof second != "integer" || typeof third != "integer") {
print(format("StealSystem: npc %d %s block has non-integer entry in %s.", NpcId, GroupName, Path));
return false;
}
OutList.append({
item = first,
rate = second,
count = third
});
}
return false;
}
function GetRandomGroupName() {
local groupNames = ["normal", "rarity", "epic"];
return groupNames[MathClass.Rand(0, groupNames.len() - 1)];
}
function RollRewardFromGroup(GroupData) {
local totalRate = 0;
foreach(_idx, info in GroupData) {
if (info.rate > 0) {
totalRate += info.rate;
}
}
if (totalRate <= 0) {
return null;
}
local randNum = MathClass.Rand(0, totalRate);
foreach(_idx, info in GroupData) {
if (info.rate <= 0) {
continue;
}
if (randNum< info.rate) {
return info;
}
randNum -= info.rate;
}
return GroupData[GroupData.len() - 1];
}
function BuildRewardMap(GroupData, DrawCount) {
local rewardMap = {};
for (local i = 0; i< DrawCount; i++) {
local rewardInfo = RollRewardFromGroup(GroupData);
if (!rewardInfo) {
return null;
}
if (!rewardMap.rawin(rewardInfo.item)) {
rewardMap[rewardInfo.item] <- 0;
}
rewardMap[rewardInfo.item] += rewardInfo.count;
}
return rewardMap;
}
function GiveRewardMap(SUser, RewardMap) {
foreach(itemId, itemCount in RewardMap) {
if (itemCount <= 0) {
continue;
}
local giveRet = SUser.GiveItem(itemId, itemCount);
if (!giveRet) {
return false;
}
}
return true;
}
function LoadNpcConfig(NpcId) {
local path = format("etc/stealconfig/%d.etc", NpcId);
local parseFailed = false;
local foundNormal = false;
local foundRarity = false;
local foundEpic = false;
local config = ScriptData.GetFileData(path, function(DataTable, Data) {
DataTable.normal <- [];
DataTable.rarity <- [];
DataTable.epic <- [];
while (!Data.Eof()) {
local fragment = Data.Get();
if (fragment == "[normal]") {
foundNormal = true;
if (!ParseRewardGroup(Data, "[/normal]", DataTable.normal, path, NpcId, "normal")) {
parseFailed = true;
return;
}
} else if (fragment == "[rarity]") {
foundRarity = true;
if (!ParseRewardGroup(Data, "[/rarity]", DataTable.rarity, path, NpcId, "rarity")) {
parseFailed = true;
return;
}
} else if (fragment == "[epic]") {
foundEpic = true;
if (!ParseRewardGroup(Data, "[/epic]", DataTable.epic, path, NpcId, "epic")) {
parseFailed = true;
return;
}
}
}
}.bindenv(this));
if (!config) {
print(format("StealSystem: npc %d config file not found: %s.", NpcId, path));
return null;
}
if (parseFailed) {
return null;
}
if (!foundNormal || !foundRarity || !foundEpic) {
print(format("StealSystem: npc %d config missing required tags in %s.", NpcId, path));
return null;
}
return config;
}
function InitPvf() {
Script();
local foundRandomCount = false;
local foundNpcTag = false;
local parseFailed = false;
local minCount = 0;
local maxCount = 0;
local npcList = [];
local fileData = ScriptData.GetFileData("etc/steal.etc", function(_n, Data) {
while (!Data.Eof()) {
local fragment = Data.Get();
if (fragment == "[random count]") {
local minValue = Data.Get();
local maxValue = Data.Get();
if (minValue > maxValue) {
print("StealSystem: [random count] min is greater than max in etc/steal.etc.");
parseFailed = true;
return;
}
minCount = minValue;
maxCount = maxValue;
foundRandomCount = true;
} else if (fragment == "[use item]") {
UseItem = Data.Get();
} else if (fragment == "[npc list]") {
foundNpcTag = true;
while (true) {
local npcId = Data.Get();
if (npcId == "[/npc list]") {
break;
}
if (Data.Eof()) {
print("StealSystem: [npc list] block missing [/npc list] in etc/steal.etc.");
parseFailed = true;
return;
}
if (typeof npcId != "integer") {
print("StealSystem: [npc] contains non-integer npc id in etc/steal.etc.");
parseFailed = true;
return;
}
npcList.append(npcId);
}
}
}
}.bindenv(this));
if (!fileData) {
print("StealSystem: failed to load etc/steal.etc.");
return false;
}
if (parseFailed) {
return false;
}
RandomCountMin = minCount;
RandomCountMax = maxCount;
NpcConfig = {};
foreach(_idx, npcId in npcList) {
local npcConfig = LoadNpcConfig(npcId);
if (!npcConfig) {
print(format("StealSystem: skip npc %d due to invalid config.", npcId));
continue;
}
NpcConfig.rawset(npcId, npcConfig);
}
return true;
}
}
Timer.SetTimeOut(function() {
local stealSystem = StealSystem();
if (!stealSystem.Loaded) {
print("StealSystem: initialization failed, object not registered.");
return;
}
getroottable()._StealSystem_ <- stealSystem;
print("偷窃系统 - 已加载");
}, 1);

View File

@@ -0,0 +1,204 @@
/*
文件名:升级券.nut
路径:MyProject/升级券/升级券.nut
创建日期:2025-12-14 21:32
文件用途:
*/
class ItemEquipmentUpgrade {
//配置表
Info = null;
//用户状态表
UserState = null;
//基础所需材料
BasicMaterials = null;
//等级倍率
LevelRate = 1.0;
//品级倍率
RarityRate = 1.0;
//禁止升级Map
BanEquipmentMap = null;
//特殊装备Map
SpecialEquipmentMap = null;
//特殊材料组
SpecialMaterialsGroup = null;
constructor() {
Info = {};
UserState = {};
//读取PVF
InitPvf();
//注册客户端收包
RegisterClient();
}
function RegisterClient() {
ClientSocketPackFuncMap.rawset(21010001, function(SUser, Jso) {
//获取背包
local InvenObj = SUser.GetInven();
//装备ID
local Id = Jso.item.ItemId;
local pvfItem = PvfItem.GetPvfItemById(Id)
local Level = pvfItem.GetUsableLevel();
//判断是否为异界装备 不是就返回 或者等级已经是85了也返回
if (pvfItem.GetRarity() != 5 || Level == 85) {
SUser.SendJso({
op = 21010002,
error = 1
});
return;
}
//普通升级装备
else {
local equ = PvfItem.GetPvfItemById(Id);
local Rarity = equ.GetRarity();
local Level = equ.GetUsableLevel();
local RarityCalculateRate = Rarity * RarityRate;
local LevelCalculateRate = Level * LevelRate;
local MgArr = [];
foreach(object in BasicMaterials) {
//查询自己有多少个道具
MgArr.push({
Item = object.Item,
RealCount = InvenObj.GetCountById(object.Item),
NeedCount = (object.Count * RarityCalculateRate * LevelCalculateRate).tointeger()
});
}
SUser.SendJso({
op = 21010002,
MgInfo = MgArr
});
}
}.bindenv(this));
ClientSocketPackFuncMap.rawset(21010003, function(SUser, Jso) {
//获取背包
local InvenObj = SUser.GetInven();
//装备ID
local Id = Jso.item.ItemId;
local equ = PvfItem.GetPvfItemById(Id);
local Level = equ.GetUsableLevel();
local giveid = Id + 220000000;
if (Level == 75 || Level == 80) {
giveid = Id + 1000000;
}
//获取对应槽位的装备
local SlotItem = InvenObj.GetSlot(Inven.INVENTORY_TYPE_ITEM, 9 + Jso.item.Pos);
if (SlotItem.GetIndex() != Id) {
SUser.SendNotiBox(" 升级失败 升级时请不要操作背包", 1);
return;
} else {
local equ = PvfItem.GetPvfItemById(Id);
local Rarity = equ.GetRarity();
local Level = equ.GetUsableLevel();
local RarityCalculateRate = Rarity * RarityRate;
local LevelCalculateRate = Level * LevelRate;
local MgArr = [];
foreach(object in BasicMaterials) {
//查询自己有多少个道具
MgArr.push({
Id = object.Item,
Count = (object.Count * RarityCalculateRate * LevelCalculateRate).tointeger()
});
}
local Flag = InvenObj.CheckArrItemCount(MgArr);
if (!Flag) {
print("扣除失败");
SUser.SendNotiBox(" 升级失败 升级时请不要操作背包", 1);
return;
} else {
local SlogInfo = SUser.GiveItem(giveid, 1);
if (!SlogInfo) {
SUser.SendNotiBox(" 升级失败 请确定背包有空余空间", 1);
return;
}
local giveItemObj = InvenObj.GetSlot(1, SlogInfo[1])
//扣除道具
InvenObj.DeleteArrItemCount(MgArr);
local forging = SlotItem.GetForging(); //锻造
local upgrade = SlotItem.GetUpgrade(); //强化
local amplification = SlotItem.GetAmplification(); //增幅
local enchanting = SlotItem.GetEnchanting(); //附魔
if (forging > 0) {
giveItemObj.SetForging(forging);
}
if (upgrade > 0) {
giveItemObj.SetUpgrade(upgrade);
}
if (amplification != 0) {
giveItemObj.SetAmplification(amplification);
}
giveItemObj.SetEnchanting(enchanting);
giveItemObj.SetAdd_Info(SlotItem.GetAdd_Info()); //品级
giveItemObj.Flush();
//销毁背包中的道具
SlotItem.Delete();
//刷新玩家背包列表
SUser.SendUpdateItemList(1, 0, 9 + Jso.item.Pos);
SUser.SendUpdateItemList(1, 0, SlogInfo[1]);
//刷新账号金库列表
SUser.SendNotiBox(" 升级成功", 1);
SUser.SendJso({
op = 21010004
});
}
}
}.bindenv(this));
};
function InitPvf() {
// getroottable()._Script_Data_Init_ <- false;
Script();
ScriptData.GetFileData("etc/rindro/itemtool/itemotherworldupgrade/itemotherworldupgrade.etc", function(DataTable, Data) {
while (!Data.Eof()) {
local Fragment = Data.Get();
if (Fragment == "[BASIC MATERIALS]") {
BasicMaterials = [];
while (true) {
local Fbuf = Data.Get();
if (Fbuf == "[/BASIC MATERIALS]") {
break;
}
local T = {
Item = Fbuf,
Count = Data.Get()
}
BasicMaterials.push(T);
}
} else if (Fragment == "[LEVEL RATE]") {
LevelRate = Data.Get()
} else if (Fragment == "[RARITY RATE]") {
RarityRate = Data.Get()
}
}
}.bindenv(this));
}
}
Timer.SetTimeOut(function() {
getroottable()._ItemEquipmentUpgrade_ <- ItemEquipmentUpgrade();
print("装备升级 - 已加载");
}, 1);

View File

@@ -0,0 +1,211 @@
/*
文件名:换装系统.nut
路径:_DPS_/_BuiltProject/换装系统/换装系统.nut
创建日期:2026-03-02 04:16
文件用途:
*/
class OutfitSystem {
MysqlObject = null;
ItemSlot = [
[0, 13], //护肩
[0, 14], //下装
[0, 15], //鞋子
[0, 12], //上衣
[0, 16], //腰带
[0, 10], //武器
[0, 18], //手镯
[0, 20], //辅助装备
[-1, -1], //耳环
[0, 11], //称号
[0, 17], //项链
[0, 19], //戒指
[0, 21], //魔法石
[-1, -1], //武器装扮
[1, 9], //光环
[1, 1], //头部
[1, 6], //胸部
[1, 7], //腰部
[1, 0], //帽子
[1, 3], //上衣
[1, 4], //下装
[1, 2], //脸部
[1, 8], //皮肤
[1, 5], //鞋
[2, 22] //宠物
];
constructor() {
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"));
local check_table_sql = "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'OutfitSystem';"
local Ret = MysqlObject.Select(check_table_sql, ["int"]);
if (Ret.len() == 0 || Ret[0][0] == 0) {
local sql = "CREATE TABLE OutfitSystem (cid VARCHAR(64) NOT NULL COMMENT '角色CID', info TEXT NOT NULL COMMENT '信息字符串', PRIMARY KEY (cid)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色换装信息表';";
MysqlObject.Exec_Sql(sql);
}
//记录装备
ClientSocketPackFuncMap.rawset(21015001, function(SUser, Jso) {
local Slot = Jso.Slot;
local EquipInfo = Json.Encode(Jso.EquipInfo);
local SetKey = format("%d_%d", SUser.GetCID(), Slot);
MysqlObject.Exec_Sql(format("INSERT INTO OutfitSystem (cid, info) VALUES ('%s', '%s') ON DUPLICATE KEY UPDATE info = '%s';", SetKey, EquipInfo, EquipInfo));
}.bindenv(this));
//清空装备
ClientSocketPackFuncMap.rawset(21015005, function(SUser, Jso) {
local Slot = Jso.Slot;
local SetKey = format("%d_%d", SUser.GetCID(), Slot);
MysqlObject.Exec_Sql(format("DELETE FROM OutfitSystem WHERE cid = '%s';", SetKey));
}.bindenv(this));
//查询配置
ClientSocketPackFuncMap.rawset(21015003, function(SUser, Jso) {
local Slot = Jso.Slot;
local SetKey = format("%d_%d", SUser.GetCID(), Slot);
local Ret = MysqlObject.Select(format("SELECT info FROM OutfitSystem WHERE cid = '%s';", SetKey), ["string"]);
if (Ret.len() == 0) {
SUser.SendJso({
op = 21015004,
Slot = Slot,
EquipInfo = null
});
} else {
SUser.SendJso({
op = 21015004,
Slot = Slot,
EquipInfo = Json.Decode(Ret[0][0])
});
}
}.bindenv(this));
//换装请求
Gm_InputFunc_Handle["HZ"] <- 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) return;
local Slot = handler[1].tointeger() - 1;
local SetKey = format("%d_%d", SUser.GetCID(), Slot);
local Ret = MysqlObject.Select(format("SELECT info FROM OutfitSystem WHERE cid = '%s';", SetKey), ["string"]);
if (Ret.len() != 0) {
local EquipInfoList = Json.Decode(Ret[0][0]);
local ChangeFlag = true;
foreach(_Index, EquipInfo in EquipInfoList) {
local EquipIndex = EquipInfo[0];
//没有装备
if (EquipIndex == 0) continue;
//耳环和武器装扮
if (ItemSlot[_Index][0] == -1) continue;
//寻找符合的装备
local Flag = ChanageEquip(SUser, ItemSlot[_Index], EquipInfo);
if(!Flag) ChangeFlag = false;
}
Sq_CallFunc(S_Ptr("0x0867ba5c"), "int", ["pointer", "int", "int", "int"], SUser.C_Object, 0, 2, 0);
Sq_CallFunc(S_Ptr("0x0867ba5c"), "int", ["pointer", "int", "int", "int"], SUser.C_Object, 1, 2, 1);
Sq_CallFunc(S_Ptr("0x0867ba5c"), "int", ["pointer", "int", "int", "int"], SUser.C_Object, 7, 2, 7);
SUser.SendItemSpace(7);
SUser.SendItemSpace(1);
SUser.SendItemSpace(0);
if(ChangeFlag) {
SUser.SendNotiPacketMessage("换装成功!", 8);
}
} else {
}
}.bindenv(this);
}
function ChanageEquip(SUser, SlotInfo, EquipInfo) {
local InvenObj = SUser.GetInven();
local EquipIndex = EquipInfo[0];
local Upgrade = EquipInfo[1];
local Amplification = EquipInfo[2];
local Forging = EquipInfo[3];
local Enchanting = EquipInfo[4];
local StartIndex = 0;
local EndIndex = 0;
local FindType = 0;
//装备
if (SlotInfo[0] == 0) {
StartIndex = 3;
EndIndex = 56;
FindType = 1;
}
//时装
else if (SlotInfo[0] == 1) {
StartIndex = 0;
EndIndex = 104;
FindType = 2;
}
//宠物
else if (SlotInfo[0] == 2) {
StartIndex = 0;
EndIndex = 104;
FindType = 3;
}
for (local i = StartIndex; i< EndIndex; i++) {
local Inven_Item = InvenObj.GetSlot(FindType, i);
if (Inven_Item) {
local Index = Inven_Item.GetIndex();
if (Index == EquipIndex) {
//对比属性
local EquipUpgrade = Inven_Item.GetUpgrade();
local EquipAmplification = Inven_Item.GetAmplification();
local EquipForging = Inven_Item.GetForging();
local EquipEnchanting = Inven_Item.GetEnchanting();
// if (EquipUpgrade == Upgrade && EquipAmplification == Amplification && EquipForging == Forging && EquipEnchanting == Enchanting) {
//对比成功
if (SlotInfo[0] == 2) {
Sq_CallFunc(S_Ptr("0x08500688"), "int", ["pointer", "int", "int", "int", "int"], InvenObj.C_Object, 3, i, 0, 22);
return true;
} else {
//装备和时装
Sq_CallFunc(S_Ptr("0x0865EED2"), "int", ["pointer", "int", "short", "int"], SUser.C_Object, SlotInfo[0], i, SlotInfo[1]);
return true;
}
// }
}
}
}
}
}
Timer.SetTimeOut(function() {
getroottable()._OutfitSystem_ <- OutfitSystem();
print("换装系统 - 已加载");
/*
对于宠物的更换 走的不是装备的变更逻辑
//move item
Sq_CallFunc(S_Ptr("0x08500688"), "int", ["pointer", "int", "int", "int", "int"], InvenObj.C_Object, 3, 0, 0, 22);
//刷新宠物
Sq_CallFunc(S_Ptr("0x0867ba5c"), "int", ["pointer", "int", "int", "int"], SUser.C_Object, 1, 2, 1);
//刷新宠物背包
SUser.SendItemSpace(7);
*/
}, 1);

View File

@@ -0,0 +1,81 @@
class HM_InheritC {
constructor() {
//注册客户端收包
RegisterClient();
}
function RegisterClient() {
ClientSocketPackFuncMap.rawset(21013001, function(SUser, Jso) {
//获取背包
local InvenObj = SUser.GetInven();
local Equip1 = InvenObj.GetSlot(Inven.INVENTORY_TYPE_ITEM, 9 + Jso.item.Pos);
local Equip2 = InvenObj.GetSlot(Inven.INVENTORY_TYPE_ITEM, 9 + Jso.item2.Pos);
local Level1 = PvfItem.GetPvfItemById(Equip1.GetIndex()).GetUsableLevel();
local Level2 = PvfItem.GetPvfItemById(Equip2.GetIndex()).GetUsableLevel();
if (Level1< 50 || Level2< 50) {
SUser.SendNotiBox(" 继承失败 装备等级不可低于50", 1);
return;
}
local Rarity1 = PvfItem.GetPvfItemById(Equip1.GetIndex()).GetRarity();
local Rarity2 = PvfItem.GetPvfItemById(Equip2.GetIndex()).GetRarity();
if (Rarity1< 4 || Rarity2< 4) {
SUser.SendNotiBox(" 继承失败 装备品级不可低于史诗", 1);
return;
}
if (Equip1.GetIndex() != Jso.item.ItemId || Equip2.GetIndex() != Jso.item2.ItemId) {
SUser.SendNotiBox(" 继承失败 继承时请不要操作背包", 1);
return;
}
local itemType1 = NativePointer(PvfItem.GetPvfItemById(Jso.item.ItemId).C_Object).add(141 * 4).readU32();
local itemType2 = NativePointer(PvfItem.GetPvfItemById(Jso.item2.ItemId).C_Object).add(141 * 4).readU32();
if (itemType1 != itemType2) {
SUser.SendNotiBox(" 继承失败 继承时需要同类型装备", 1);
return;
}
local forging = Equip1.GetForging(); //锻造
local upgrade = Equip1.GetUpgrade(); //强化
local amplification = Equip1.GetAmplification(); //增幅
local enchanting = Equip1.GetEnchanting(); //附魔
local Jewel = NativePointer(Equip1.C_Object).add(25).readU32(); //镶嵌
Equip2.SetForging(forging);
Equip2.SetUpgrade(upgrade);
Equip2.SetAmplification(amplification);
Equip2.SetEnchanting(enchanting);
Equip2.Flush();
NativePointer(Equip2.C_Object).add(25).writeU32(Jewel);
Equip1.Delete();
SUser.SendUpdateItemList(1, 0, 9 + Jso.item.Pos);
SUser.SendUpdateItemList(1, 0, 9 + Jso.item2.Pos);
SUser.GiveItem(Jso.item.ItemId, 1);
SUser.SendNotiBox(" 继承成功", 1);
SUser.SendJso({
op = 21013002
});
}.bindenv(this));
}
}
Timer.SetTimeOut(function() {
getroottable()._HM_InheritC_ <- HM_InheritC();
print("双端插件·毁梦定制继承系统 - 已加载");
}, 1);

View File

@@ -0,0 +1,419 @@
/*
文件名:灵魂救赎武器.nut
路径:_DPS_/_BuiltProject/灵魂救赎武器/灵魂救赎武器.nut
创建日期:2026-01-20 19:24
文件用途:
*/
class SoulSalvationC {
MysqlObject = null;
//灵魂救赎武器标识 (阶段1)
Equipment1List = null;
//1阶段所需灵魂数量
Stage1SoulCount = 1000;
//1阶段进阶所需材料
Equipment1Upgrade = null;
//灵魂救赎武器标识 (阶段2)
Equipment2List = null;
//2阶段通关镇魂曲副本的ID
Stage2DgnList = null;
//灵魂救赎武器标识 (阶段3)
Equipment3List = null;
//数据
data = null;
constructor() {
Equipment1List = {};
Equipment1Upgrade = {};
Equipment2List = {};
Stage2DgnList = {};
Equipment3List = {};
data = {};
//创建数据库
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"));
local check_table_sql = "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'soulsalvation';"
local Ret = MysqlObject.Select(check_table_sql, ["int"]);
if (Ret.len() == 0 || Ret[0][0] == 0) {
local sql = "CREATE TABLE soulsalvation (`uid` INT(11) NOT NULL COMMENT '主键ID',`soul_id` INT(11) NOT NULL DEFAULT 0 COMMENT '灵魂ID', `soul_count` INT(11) NOT NULL DEFAULT 0 COMMENT '数值字段默认值0',PRIMARY KEY (`uid`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='soulsalvation表';";
MysqlObject.Exec_Sql(sql);
}
//读取PVF
InitPvf();
//注册客户端收包
RegisterClient();
//读取数据库数据到缓存
InitMysqlData();
}
function RegisterClient() {
//测试用客户端主动查询灵魂数
ClientSocketPackFuncMap.rawset(21011001, function(SUser, Jso) {
local SendInfo = GetUserData(SUser);
SUser.SendJso({
op = 21011002,
Info = SendInfo
})
}.bindenv(this));
Cb_CParty_OnKillMonster_Leave_Func.rawset("SoulSalvationC", function(args) {
local SUser = User(args[1]);
//获取佩戴的武器
local Weapon = GetUserWeapon(SUser);
local WeaponIndex = Weapon.GetIndex();
//可积累灵魂数量
if (Equipment1List.rawin(WeaponIndex)) {
local uuid = Weapon.GetUuid();
//有数据则+1 无数据则新增
if (data.rawin(uuid)) {
if (data[uuid].soul_count< Stage1SoulCount)
data[uuid].soul_count += 1;
} else {
data[uuid] <- {
soul_count = 1,
soul_id = 0
};
}
SUser.SendJso({
op = 21011004,
uuid = uuid,
info = data[uuid]
})
}
}.bindenv(this));
//上线时 获取自己的灵魂武器信息
Cb_reach_game_world_Func.rawset("SoulSalvationC", function(SUser) {
local SendInfo = GetUserData(SUser);
SUser.SendJso({
op = 21011002,
Info = SendInfo
})
}.bindenv(this));
//查看他人信息的时候 获取灵魂武器信息
Cb_GetUserInfo_Leave_Func.rawset("SoulSalvationC", function(args) {
local SUser = User(args[1])
local CheckWorldId = NativePointer(args[2]).add(13).readShort();
local CheckUser = World.GetUserBySession(CheckWorldId);
if (CheckUser) {
local SendInfo = GetUserData(CheckUser);
SUser.SendJso({
op = 21011006,
Info = SendInfo
})
}
}.bindenv(this));
//每5分钟固化一次数据到数据库
Timer.RemoveCronTask("SoulSalvationC");
Timer.SetCronTask(function() {
local DataList = [];
foreach(uuid, value in data) {
local str = format("(%d,%d,%d),", uuid, value.soul_id, value.soul_count);
DataList.push(str);
}
local sql = "REPLACE INTO soulsalvation (uid, soul_id, soul_count) VALUES";
foreach(value in DataList) {
sql += value;
}
//去除最后一个,
sql = sql.slice(0, -1);
sql += ";";
MysqlObject.Exec_Sql(sql);
}.bindenv(this), {
Cron = "0 */5 * * * *",
Name = "SoulSalvationC"
});
//1阶段晋级逻辑
ClientSocketPackFuncMap.rawset(21011007, function(SUser, Jso) {
//获取背包
local InvenObj = SUser.GetInven();
//装备ID
local Id = Jso.item.ItemId;
//判断是否为灵魂武器一阶段
if (!Equipment1List.rawin(Id)) {
SUser.SendJso({
op = 21011008,
error = 1
});
return;
}
//判断灵魂数量是否已经达到要求
local SlotItem = InvenObj.GetSlot(Inven.INVENTORY_TYPE_ITEM, 9 + Jso.item.Pos);
local uuid = SlotItem.GetUuid();
local SoulCount = 0;
if (data.rawin(uuid)) {
SoulCount = data[uuid].soul_count;
}
if (SoulCount< Stage1SoulCount) {
SUser.SendJso({
op = 21011008,
error = 2
});
return;
}
local MgArr = [];
foreach(Index, Count in Equipment1Upgrade) {
//查询自己有多少个道具
MgArr.push({
Item = Index,
RealCount = InvenObj.GetCountById(Index),
NeedCount = Count.tointeger()
});
}
SUser.SendJso({
op = 21011008,
MgInfo = MgArr
});
}.bindenv(this));
//1阶段晋级逻辑
ClientSocketPackFuncMap.rawset(21011009, function(SUser, Jso) {
//获取背包
local InvenObj = SUser.GetInven();
//装备ID
local Id = Jso.item.ItemId;
//判断是否为灵魂武器一阶段
if (!Equipment1List.rawin(Id)) {
SUser.SendJso({
op = 21011008,
error = 1
});
return;
}
//要给予的装备ID
local giveid = Id + 1;
//获取对应槽位的装备
local SlotItem = InvenObj.GetSlot(Inven.INVENTORY_TYPE_ITEM, 9 + Jso.item.Pos);
local uuid = SlotItem.GetUuid();
if (SlotItem.GetIndex() != Id) {
SUser.SendNotiBox(" 升级失败 升级时请不要操作背包", 1);
return;
} else {
local MgArr = [];
foreach(Index, Count in Equipment1Upgrade) {
//查询自己有多少个道具
MgArr.push({
Id = Index,
Count = Count.tointeger()
});
}
local Flag = InvenObj.CheckArrItemCount(MgArr);
if (!Flag) {
SUser.SendNotiBox(" 升级失败 升级时请不要操作背包", 1);
return;
} else {
local SlogInfo = SUser.GiveItem(giveid, 1);
if (!SlogInfo) {
SUser.SendNotiBox(" 升级失败 请确定背包有空余空间", 1);
return;
}
local giveItemObj = InvenObj.GetSlot(1, SlogInfo[1])
//扣除道具
InvenObj.DeleteArrItemCount(MgArr);
local forging = SlotItem.GetForging(); //锻造
local upgrade = SlotItem.GetUpgrade(); //强化
local amplification = SlotItem.GetAmplification(); //增幅
local enchanting = SlotItem.GetEnchanting(); //附魔
if (forging > 0) {
giveItemObj.SetForging(forging);
}
if (upgrade > 0) {
giveItemObj.SetUpgrade(upgrade);
}
if (amplification != 0) {
giveItemObj.SetAmplification(amplification);
}
giveItemObj.SetEnchanting(enchanting);
giveItemObj.SetAdd_Info(SlotItem.GetAdd_Info()); //品级
giveItemObj.Flush();
//销毁背包中的道具
SlotItem.Delete();
//刷新玩家背包列表
SUser.SendUpdateItemList(1, 0, 9 + Jso.item.Pos);
SUser.SendUpdateItemList(1, 0, SlogInfo[1]);
//刷新
SUser.SendNotiBox(" 升级成功", 1);
SUser.SendJso({
op = 21011010
});
//从缓存中移除这个uuid
data.rawdelete(uuid);
local Sql = "DELETE FROM soulsalvation WHERE uid = " + uuid + ";";
MysqlObject.Exec_Sql(Sql);
}
}
}.bindenv(this));
//2阶段通关镇魂曲获取璀璨灵魂结晶
Cb_ClearDungeon_Enter_Func.rawset("SoulSalvationC", function(args) {
local PartyObj = Party(args[0]);
if (PartyObj) {
local Bfobj = PartyObj.GetBattleField();
local DgnObj = Bfobj.GetDgn();
if (DgnObj) {
local Dungeon_Id = DgnObj.GetId();
if (Stage2DgnList.rawin(Dungeon_Id)) {
//对小队每一个人进行操作
PartyObj.ForeachMember(function(SUser, Pos) {
//获取佩戴的武器
local Weapon = GetUserWeapon(SUser);
local WeaponIndex = Weapon.GetIndex();
//释魂武器
if (Equipment2List.rawin(WeaponIndex)) {
SUser.GiveItem(92000 + Dungeon_Id + 1, 1);
}
print("通关镇魂曲副本");
}.bindenv(this));
}
}
}
}.bindenv(this));
};
//获取角色的武器
function GetUserWeapon(SUser) {
local InvenObj = SUser.GetInven();
if (!InvenObj) return null;
return InvenObj.GetSlot(Inven.INVENTORY_TYPE_BODY, 10);
}
//获取角色的灵魂武器信息
function GetUserData(SUser) {
//获取背包对象
local InvenObj = SUser.GetInven();
if (!InvenObj) return {};
local UUIDList = [];
//遍历身上
for (local i = 0; i< 26; i++) {
local Inven_Item = InvenObj.GetSlot(Inven.INVENTORY_TYPE_BODY, i);
if (Inven_Item) {
local Index = Inven_Item.GetIndex();
if (Equipment1List.rawin(Index) || Equipment3List.rawin(Index)) {
local uuid = Inven_Item.GetUuid();
UUIDList.push(uuid);
}
}
}
//遍历物品栏
for (local i = 0; i< 70; i++) {
local Inven_Item = InvenObj.GetSlot(Inven.INVENTORY_TYPE_ITEM, i);
if (Inven_Item) {
local Index = Inven_Item.GetIndex();
if (Equipment1List.rawin(Index) || Equipment3List.rawin(Index)) {
local uuid = Inven_Item.GetUuid();
UUIDList.push(uuid);
}
}
}
local SendInfo = {};
foreach(uuid in UUIDList) {
if (data.rawin(uuid)) {
SendInfo.rawset(uuid, data[uuid]);
} else {
data[uuid] <- {
soul_count = 0,
soul_id = 0
};
}
}
return SendInfo;
}
function InitPvf() {
Script();
ScriptData.GetFileData("etc/rindro/expandequipment/soulsalvation.etc", function(DataTable, Data) {
while (!Data.Eof()) {
local Fragment = Data.Get();
if (Fragment == "[EQUIPMENT1]") {
Equipment1List = {};
while (true) {
local Fbuf = Data.Get();
if (Fbuf == "[/EQUIPMENT1]") {
break;
}
Equipment1List.rawset(Fbuf, true);
}
} else if (Fragment == "[EQUIPMENT2]") {
Equipment2List = {};
while (true) {
local Fbuf = Data.Get();
if (Fbuf == "[/EQUIPMENT2]") {
break;
}
Equipment2List.rawset(Fbuf, true);
}
} else if (Fragment == "[EQUIPMENT3]") {
Equipment3List = {};
while (true) {
local Fbuf = Data.Get();
if (Fbuf == "[/EQUIPMENT3]") {
break;
}
Equipment3List.rawset(Fbuf, true);
}
} else if (Fragment == "[STAGE1 VALUE]") {
Stage1SoulCount = Data.Get();
} else if (Fragment == "[STAGE1 NEED ITEM]") {
Equipment1Upgrade = {};
while (true) {
local Fbuf = Data.Get();
if (Fbuf == "[/STAGE1 NEED ITEM]") {
break;
}
Equipment1Upgrade.rawset(Fbuf, Data.Get());
}
} else if (Fragment == "[STAGE2 DUNGEON]") {
Stage2DgnList = {};
while (true) {
local Fbuf = Data.Get();
if (Fbuf == "[/STAGE2 DUNGEON]") {
break;
}
Stage2DgnList.rawset(Fbuf, true);
}
}
}
}.bindenv(this));
}
function InitMysqlData() {
local Sql = "SELECT * FROM soulsalvation"
local Ret = MysqlObject.Select(Sql, ["int", "int", "int"]);
foreach(Row in Ret) {
local uuid = Row[0];
local soul_id = Row[1];
local soul_count = Row[2];
data.rawset(uuid, {
soul_id = soul_id,
soul_count = soul_count
})
}
}
}
Timer.SetTimeOut(function() {
getroottable()._SoulSalvation_ <- SoulSalvationC();
print("双端插件·灵魂救赎武器 - 已加载");
}, 1);

View File

@@ -0,0 +1,103 @@
/*
文件名:装备拓展.nut
路径:MyProject/装备拓展.nut
创建日期:2026-01-15 10:02
文件用途:
*/
Cb_ItemGloballyUniqueIdentifier_t_SetServerId_Enter_Func <- {};
Cb_ItemGloballyUniqueIdentifier_t_SetServerId_Leave_Func <- {};
_Hook_Register_Currency_Func_("0x08894782", ["pointer", "int", "pointer"], Cb_ItemGloballyUniqueIdentifier_t_SetServerId_Enter_Func, Cb_ItemGloballyUniqueIdentifier_t_SetServerId_Leave_Func);
// Cb_CInventory_MakeItemPacket_Enter_Func <- {};
// Cb_CInventory_MakeItemPacket_Leave_Func <- {};
// _Hook_Register_Currency_Func_("0x084FC6BC", ["pointer", "int", "int", "pointer", "int"], Cb_CInventory_MakeItemPacket_Enter_Func, Cb_CInventory_MakeItemPacket_Leave_Func);
class EquipmentExpandC {
MysqlObject = null;
constructor() {
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"));
local check_table_sql = "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'equip_only_id';"
local Ret = MysqlObject.Select(check_table_sql, ["int"]);
if (Ret.len() == 0 || Ret[0][0] == 0) {
local sql = "CREATE TABLE equip_only_id (id INT(11) NOT NULL AUTO_INCREMENT COMMENT '32位自增唯一ID初始值10000', PRIMARY KEY (id)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT '纯自增ID表' AUTO_INCREMENT=10000;";
MysqlObject.Exec_Sql(sql);
}
//HOOKServerId生成uuid
Cb_ItemGloballyUniqueIdentifier_t_SetServerId_Enter_Func["_装备拓展_"] <- function(args) {
args[1] = GenerateUUID();
return args;
}.bindenv(this);
// //补充数据包
// Cb_CInventory_MakeItemPacket_Leave_Func["_装备拓展_"] <- function(args) {
// // local ItemObj = Item(Sq_CallFunc(S_Ptr("0x084FC1DE"), "int", ["pointer", "int", "int", "pointer"], args[0], args[1], args[2]));
// // print("装备编号: " + ItemObj.GetIndex());
// // local Pack = Packet(args[3]);
// // local Buffer = Memory.alloc(30);
// // //无需重置为0 需要写数据的地方自己会覆写
// // Buffer.add(18).writeInt(NativePointer())
// // Pack.Put_BinaryEx(DB_JewelSocketData.C_Object, 30);
// }.bindenv(this);
//额外数据包,发送装备数据给本地处理
// Cb_InterfacePacketBuf_put_packet_Enter_Func["_装备拓展_"] <- function(args) {
// local Inven_Item = NativePointer(args[1]);
// if (Inven_Item.add(1).readU8() == 1) {
// local Uuid = Inven_Item.add(21).readInt();
// if (Uuid< 10000) {
// local uuid = GenerateUUID();
// Inven_Item.add(21).writeInt(uuid);
// }
// }
// }.bindenv(this);
//因为装备镶嵌那边更改的数据导致 购买装备时金币会错乱 任意方式刷新一下金币就可以了
// Cb_BuyItem_send_Enter["_装备拓展_"] <- function(args) {
// local SUser = User(args[1]);
// Timer.SetTimeOut(function() {
// print("刷新");
// SUser.SendItemSpace(0);
// }, 1);
// }.bindenv(this);
}
function GenerateUUID() {
MysqlObject.Exec_Sql("INSERT INTO equip_only_id VALUES ();");
local Ret = MysqlObject.Select("SELECT LAST_INSERT_ID() AS new_auto_id;", ["int"]);
return Ret[0][0];
}
// Cb_CInventory_insertItemIntoInventory_Leave_Func["_装备拓展_"] <- function(args) {
// local SUser = User(NativePointer(args[0]).readPointer());
// local ItemObject = Item(NativePointer(Haker.CpuContext.ebp).add(12).C_Object);
// local ServerId = NativePointer(ItemObject.C_Object).add(21).readInt();
// //数据库主键从10000开始自增
// if (ServerId< 10000) {
// local NewUuid = GenerateUUID();
// NativePointer(ItemObject.C_Object).add(21).writeInt(NewUuid);
// }
// }.bindenv(this);
}
Timer.SetTimeOut(function() {
getroottable()._EquipmentExpand_ <- EquipmentExpandC();
print("特殊插件·装备拓展 - 已加载");
}, 1);

View File

@@ -0,0 +1,219 @@
/*
文件名:誉名录.nut
路径:_DPS_/_BuiltProject/誉名录/誉名录.nut
创建日期:2026-02-01 02:19
文件用途:
*/
class NewTitleC {
MysqlObject = null;
//数据
data = null;
constructor() {
//创建数据库
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"));
local check_table_sql = "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'newtitle';"
local Ret = MysqlObject.Select(check_table_sql, ["int"]);
if (Ret.len() == 0 || Ret[0][0] == 0) {
local sql = "CREATE TABLE `taiwan_cain`.`newtitle` (`cid` int(11) NOT NULL,`data` text NULL,PRIMARY KEY (`cid`));";
MysqlObject.Exec_Sql(sql);
}
//读取数据库数据到缓存
InitMysqlData();
//固化缓存数据到数据库
SaveCacheData();
//注册客户端收包
RegisterClient();
//注册GM命令
Gm_InputFunc_Handle["Title"] <- 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) {
AddTitle(SUser, handler[1]);
}
}.bindenv(this);
//注册区域移动添加角色的Hook
Cb_Area_insert_user_Leave.rawset("NewTitleC", function(args) {
local UserList = World.GetAreaUserList(args[0]);
if (UserList) {
local ShowList = GenerateDrawInfo(UserList);
foreach(AreaUser in UserList) {
AreaUser.SendJso({
op = 21012004,
ShowData = ShowList
});
}
}
}.bindenv(this));
//上线时 下发自己的渲染信息
Cb_reach_game_world_Func.rawset("NewTitleC", function(SUser) {
local ShowCidList = [SUser];
local ShowList = GenerateDrawInfo(ShowCidList);
SUser.SendJso({
op = 21012004,
ShowData = ShowList
});
}.bindenv(this));
}
function RegisterClient() {
//查询自身称号簿数据
ClientSocketPackFuncMap.rawset(21012001, function(SUser, Jso) {
local SendInfo = {};
local Cid = SUser.GetCID().tostring();
print(data.rawin(Cid))
if (data.rawin(Cid)) {
SendInfo = data.rawget(Cid);
}
SUser.SendJso({
op = 21012002,
Info = SendInfo
})
}.bindenv(this));
//保存称号簿配置
ClientSocketPackFuncMap.rawset(21012003, function(SUser, Jso) {
local SaveInfo = Jso.SendInfo;
local SaveId = Jso.Id;
local Cid = SUser.GetCID().tostring();
//因为json传输过程会导致INT key变成string 所以这里的缓存里一律要使用string 的编号
data[Cid].rawset(SaveId.tostring(), SaveInfo);
RefreshAreaDrawInfo(SUser);
}.bindenv(this));
}
//给玩家添加称号
function AddTitle(SUser, TitleId) {
local Cid = SUser.GetCID().tostring();
if (!data.rawin(Cid)) {
data.rawset(Cid, {});
}
data[Cid].rawset(TitleId, {
IsDisplay = false,
Scale = 1.0,
XOffset = 0,
YOffset = 0,
ZOrder = 1
})
}
//给玩家移除称号
function RemoveTitle(SUser, TitleId) {
local Cid = SUser.GetCID().tostring();
if (!data.rawin(Cid)) {
data.rawset(Cid, {});
}
data[Cid].rawdelete(TitleId);
}
//刷新角色区域内的渲染信息
function RefreshAreaDrawInfo(SUser) {
local Location = SUser.GetLocation();
local Area = World.GetArea(Location.Town, Location.Area);
local UserList = World.GetAreaUserList(Area);
if (UserList) {
local ShowList = GenerateDrawInfo(UserList);
foreach(AreaUser in UserList) {
AreaUser.SendJso({
op = 21012004,
ShowData = ShowList
});
}
}
}
//生成指定List的渲染信息
function GenerateDrawInfo(ShowCidList) {
local ShowList = {};
foreach(SUserObj in ShowCidList) {
local Cid = SUserObj.GetCID().tostring();
local ShowData = null;
if (data.rawin(Cid)) {
ShowData = data.rawget(Cid);
}
local Data = [];
if (ShowData) {
foreach(TitleId, RealData in ShowData) {
if (!RealData.IsDisplay) continue;
RealData.Id <- TitleId;
Data.push(RealData);
}
}
Data.sort(function(a, b) {
return a.ZOrder <=> b.ZOrder;
});
ShowList.rawset(SUserObj.GetUniqueId(), Data);
}
return ShowList.len() > 0 ? ShowList : null;
}
function InitMysqlData() {
data = {};
local Sql = "SELECT * FROM newtitle"
local Ret = MysqlObject.Select(Sql, ["int", "string"]);
foreach(Row in Ret) {
local cid = Row[0];
local jso = Json.Decode(Row[1]);
data.rawset(cid.tostring(), jso);
}
}
function SaveCacheData() {
//每5分钟固化一次数据到数据库
Timer.RemoveCronTask("NewTitleC");
Timer.SetCronTask(function() {
local DataList = [];
foreach(cid, value in data) {
local str = format("(%s,'%s'),", cid.tostring(), Json.Encode(value));
DataList.push(str);
}
local sql = "REPLACE INTO newtitle (cid, data) VALUES";
foreach(value in DataList) {
sql += value;
}
//去除最后一个,
sql = sql.slice(0, -1);
sql += ";";
MysqlObject.Exec_Sql(sql);
}.bindenv(this), {
Cron = "* */5 * * * *",
Name = "NewTitleC"
});
}
}
Timer.SetTimeOut(function() {
getroottable()._NewTitle_ <- NewTitleC();
print("誉名录 - 已加载");
}, 1);

View File

@@ -0,0 +1,53 @@
/*
文件名:露露定制内容.nut
路径:_DPS_/_BuiltProject/露露定制内容/露露定制内容.nut
创建日期:2026-03-23 11:05
文件用途:
*/
class LuluCustomizedSystemC {
Data = null;
constructor() {
InitPvf();
ClientSocketPackFuncMap.rawset(21016001, function(SUser, Jso) {
SUser.DropItem(Jso.id, Jso.posX, Jso.posY);
}.bindenv(this));
ClientSocketPackFuncMap.rawset(21016003, function(SUser, Jso) {
if (Data.rawin(Jso.rarity)) {
local Index = MathClass.Rand(0, Data[Jso.rarity].len());
local ItemId = Data[Jso.rarity][Index];
SUser.DropItem(ItemId, Jso.posX, Jso.posY);
}
}.bindenv(this));
}
function InitPvf() {
// getroottable()._Script_Data_Init_ <- false;
Script();
Data = {};
ScriptData.GetFileData("etc/uniquehellpartydroptable.etc", function(_n, DataA) {
while (!DataA.Eof()) {
local Fragment = DataA.Get();
if (Fragment.find("[") >= 0 && Fragment.find("/") == null) {
local key = Fragment.slice(1, -1);
Data[key] <- [];
while (true) {
local Id = DataA.Get();
if (typeof Id == "string" && Id.find("[/") != null) {
break;
}
Data[key].push(Id);
}
}
}
}.bindenv(this));
print(Data);
}
}
Timer.SetTimeOut(function() {
getroottable()._LuluCustomizedSystemC_ <- LuluCustomizedSystemC();
}, 1);

View File

@@ -0,0 +1,127 @@
/*
文件名:哥布林袖珍罐.nut
路径:_DPS_/_BuiltProject/哥布林袖珍罐/哥布林袖珍罐.nut
创建日期:2026-02-06 09:37
文件用途:哥布林袖珍罐
*/
class GoblinSmallBox {
//记录玩家缓存
playerCache = null;
//数据
data = null;
constructor() {
playerCache = {};
data = {};
//读取PVF数据
InitPvf();
//注册道具使用回调
RegistUseItemCallBack();
ClientSocketPackFuncMap.rawset(21014003, function(SUser, Jso) {
local CID = SUser.GetCID();
if (playerCache.rawin(CID)) {
local ItemArr = playerCache.rawget(CID);
local ItemId = ItemArr[0].ItemId;
local ItemCount = ItemArr[0].ItemCount;
SUser.GiveItem(ItemId, ItemCount);
ItemArr.remove(0);
if (ItemArr.len() == 0) {
playerCache.rawdelete(CID);
}
}
}.bindenv(this));
}
function InitPvf() {
Script();
ScriptData.GetFileData("etc/rindro/goldgoblin/goldgoblin.lst", function(_n, DataA) {
while (!DataA.Eof()) {
local Id = DataA.Get();
local Path = DataA.Get();
local Info = ScriptData.GetFileData("etc/" + Path, function(DataTable, Data) {
while (!Data.Eof()) {
local Fragment = Data.Get();
if (Fragment == "[production]") {
DataTable.production <- [Data.Get(), Data.Get()];
} else if (Fragment == "[trigger]") {
DataTable.trigger <- Data.Get();
} else if (Fragment == "[item]") {
DataTable.item <- [];
DataTable.item_total_probability <- 0;
while (true) {
local Ret = Data.Get();
if (Ret == "[/item]") {
break;
}
local ItemInfo = {};
ItemInfo.ItemId <- Ret;
ItemInfo.ItemCount <- Data.Get();
ItemInfo.Probability <- Data.Get();
DataTable.item_total_probability += ItemInfo.Probability;
ItemInfo.IsNoti <- Data.Get();
ItemInfo.Grade <- GetItemGrade(ItemInfo.ItemId);
DataTable.item.push(ItemInfo);
}
}
}
}.bindenv(this));
data.rawset(Id, Info);
}
}.bindenv(this));
}
function RegistUseItemCallBack() {
foreach(id, info in data) {
if ("trigger" in info) {
Cb_Use_Item_Sp_Func[info.trigger] <- function(SUser, Item_id) {
local Count = MathClass.Rand(data[id].production[0], data[id].production[1]);
local ItemArr = [];
for (local i = 0; i< Count; i++) {
local RandNum = MathClass.Rand(0, data[id].item_total_probability);
local ItemInfo = null;
for (local j = 0; j< data[id].item.len(); j++) {
if (RandNum< data[id].item[j].Probability) {
ItemInfo = clone(data[id].item[j]);
break;
}
RandNum -= data[id].item[j].Probability;
}
ItemInfo.rawdelete("Probability");
if (ItemInfo != null) {
ItemArr.push(ItemInfo);
}
}
playerCache[SUser.GetCID()] <- ItemArr;
SUser.SendJso({
op = 21014002,
RewardPackage = ItemArr,
BoxId = info.trigger,
})
}.bindenv(this);
}
}
}
function GetItemGrade(ItemId) {
local Equ = PvfItem.GetPvfItemById(ItemId);
local Rarity = Equ.GetRarity();
return Rarity;
}
}
Timer.SetTimeOut(function() {
getroottable()._GoblinSmallBox_ <- GoblinSmallBox();
print("哥布林袖珍罐 - 已加载");
}, 1);

View File

@@ -262,12 +262,17 @@ class HttpServer {
//处理函数
Handler = null;
//服务ID
ServerId = null;
constructor(host, service = "80") {
Host = host;
Service = service;
if (getroottable().rawin("HttpServer_" + Host + "_" + Service)){
throw "端口被占用";
}
getroottable()["HttpServer_" + Host + "_" + Service] <- this;
}
@@ -275,15 +280,21 @@ class HttpServer {
//记录处理函数
Handler = Func;
local success = Sq_CreateHttpServer(Host, Service, this);
if (success) {
ServerId = Sq_CreateHttpServer(Host, Service, this);
if (ServerId) {
::print("Server started successfully.");
} else {
::print("Failed to start server.");
}
}
function Event(SocketObject, Msg) {
Timer.SetTimeOut(Handler, 1, HttpResponse(SocketObject), Msg);
function Stop()
{
Sq_StopHttpServer(ServerId);
}
function Event(SocketObject, Header, Msg) {
print("Event");
Timer.SetTimeOut(Handler, 1, HttpResponse(SocketObject), Header, Msg);
}
}

View File

@@ -146,7 +146,11 @@ class Item extends Base_C_Object {
this = null;
}
//获取UUID
function GetUuid() {
Attribute.seek(21);
return Attribute.readn('i');
}
}
//是否可打包

View File

@@ -21,31 +21,78 @@ class MD5 {
Sq_CallFunc(MD5_Final_ptr, "void", ["pointer", "pointer"], Ctx, Result);
}
// 16进制编码函数
// 参数: input - 可以是字符串或字节数组或Memory对象
// 返回: 32位16进制小写字符串默认格式
function hex_encode(input) {
local byteArray = [];
// 如果输入是字符串,转换为字节数组
if (typeof input == "string") {
for (local i = 0; i < input.len(); i++) {
byteArray.append(input[i].tointeger() & 0xFF);
}
}
// 如果输入是数组,直接使用
else {
byteArray = input;
}
local encoded = "";
for (local i = 0; i < byteArray.len(); i++) {
local byte = byteArray[i] & 0xFF;
encoded += format("%02x", byte);
}
return encoded;
}
// Base64 编码函数
// 参数: input - 可以是字符串或字节数组
// 返回: base64 编码后的字符串
function base64_encode(input) {
local base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
local inputLength = input.len();
// 如果输入是字符串,转换为字节数组
local byteArray = [];
if (typeof input == "string") {
for (local i = 0; i< input.len(); i++) {
byteArray.append(input[i].tointeger() & 0xFF);
}
} else {
// 假设是数组,直接使用
byteArray = input;
}
local inputLength = byteArray.len();
local i = 0;
local j = 0;
local charArray3 = array(3);
local charArray4 = array(4);
local encoded = "";
while (inputLength--) {
charArray3[i++] = input[inputLength];
// 处理每3个字节为一组
local pos = 0;
while (pos< inputLength) {
charArray3[i++] = byteArray[pos++];
if (i == 3) {
// 将3个字节转换为4个base64字符
charArray4[0] = (charArray3[0] & 0xfc) >> 2;
charArray4[1] = ((charArray3[0] & 0x03) << 4) + ((charArray3[1] & 0xf0) >> 4);
charArray4[2] = ((charArray3[1] & 0x0f) << 2) + ((charArray3[2] & 0xc0) >> 6);
charArray4[3] = charArray3[2] & 0x3f;
for (i = 0; i< 4; i++) {
encoded += base64_chars[charArray4[i]];
encoded += base64_chars.slice(charArray4[i], charArray4[i] + 1);
}
i = 0;
}
}
if (i) {
// 处理剩余的字节不足3个字节的情况
if (i > 0) {
for (j = i; j< 3; j++) {
charArray3[j] = 0;
}
@@ -56,9 +103,10 @@ class MD5 {
charArray4[3] = charArray3[2] & 0x3f;
for (j = 0; j< i + 1; j++) {
encoded += base64_chars[charArray4[j]];
encoded += base64_chars.slice(charArray4[j], charArray4[j] + 1);
}
// 添加填充字符 '='
while (i++<3) {
encoded += "=";
}
@@ -67,6 +115,7 @@ class MD5 {
return encoded;
}
function GetFile(FileName) {
local Io = IO(FileName, "r+");

View File

@@ -101,9 +101,9 @@ class Packet extends Base_C_Object {
}
function GetString(a3,a4) {
function GetString(a3, a4) {
local data = Memory.alloc(a3);
if (Sq_CallFunc(S_Ptr("0x858D2BC"), "int", ["pointer", "pointer","int","int","int"], this.C_Object, data.C_Object,a3,a4)) {
if (Sq_CallFunc(S_Ptr("0x858D2BC"), "int", ["pointer", "pointer", "int", "int", "int"], this.C_Object, data.C_Object, a3, a4)) {
return data;
}
return null;
@@ -114,4 +114,16 @@ class Packet extends Base_C_Object {
Sq_Delete_Point(this.C_Object);
// Sq_Packet_Delete(this.C_Object);
}
function GetPos() {
return Sq_CallFunc(S_Ptr("0x8110B4C"), "int", ["pointer"], this.C_Object);
}
function SetPos(pos) {
Sq_CallFunc(S_Ptr("0x822B7B0"), "int", ["pointer", "int"], this.C_Object, pos);
}
function GetLength() {
return Sq_CallFunc(S_Ptr("0x848F438"), "int", ["pointer"], this.C_Object);
}
}

View File

@@ -24,7 +24,6 @@ class Script {
getroottable()._Script_Data_Init_ = true;
}
function GetFileInfo(Path) {
local size = Asset_GetPvfFileSize(C_Object, Path);
if (size) {
@@ -64,40 +63,38 @@ class _PVF_Data_ {
function Last() {
if (Pos > 0) {
Pos--;
return Get();
return Data[Pos];
}
return null;
}
function Seek(i) {
if (Pos > 0 && Pos<(Max - 1)) {
Pos = i;
}
if (i < 0)
i = 0;
else if (i > Max)
i = Max;
Pos = i;
}
function Seekg(i) {
Pos += i;
Seek(Pos + i);
}
function Get() {
local Ret = Data[Pos];
if (Pos<(Max - 1)) {
Pos++;
if (Pos >= Max) {
return null;
}
local Ret = Data[Pos];
Pos++;
return Ret;
}
function Eof() {
if (Pos == Max - 1)
return true;
return Pos >= Max;
}
function Next() {
if (Pos<(Max - 1)) {
Pos++;
return Get();
}
return null;
return Get();
}
}

View File

@@ -28,12 +28,6 @@ if (!getroottable().rawin("OnGatewaySocketConnectFunc")) OnGatewaySocketConnectF
function OnGatewaySocketConnect() {
print("\x1b[96m" + "############双端插件连接建立############" + "\x1b[0m");
//获得本服务器的IP
GatewaySocketPackFuncMap.rawset(10002, function(Jso) {
getroottable().Dps_Self_Ip <- Jso.myip;
print("\x1b[96m" + "############双端插件初始化成功############" + "\x1b[0m");
});
Timer.SetTimeOut(function() {
local Jso = {
op = 10001,

View File

@@ -272,4 +272,30 @@ class World {
Sq_Delete_Point(end);
return PlayerArr;
}
//获取区域
function GetArea(TownId, AreaId) {
local GameWorld = Sq_CallFunc(S_Ptr("0x080DA3A7"), "pointer", []);
local Town = Sq_CallFunc(S_Ptr("0x086D1764"), "pointer", ["pointer", "int"], GameWorld, TownId);
local Area = Sq_CallFunc(S_Ptr("0x086C3BA2"), "pointer", ["pointer", "int"], Town, AreaId);
return Area;
}
//获取区域的玩家列表
function GetAreaUserList(Area) {
local List = [];
local VectorM = Memory.alloc(4 * 3);
Sq_CallFunc(S_Ptr("0x08168420"), "int", ["pointer"], VectorM.C_Object);
local Flag = Sq_CallFunc(S_Ptr("0x086C305E"), "bool", ["pointer", "pointer"], Area, VectorM.C_Object);
local start = VectorM.readPointer();
local finish = VectorM.add(4).readPointer();
local Size = Sq_Ptr2Int(Sq_PointerOperationPointer(finish, start, "-")) / 2;
for (local i = 0; i< Size; i++) {
local UserId = NativePointer(start).add(i * 2).readUShort();
local SUser = GetUserBySession(UserId);
if (SUser) List.push(SUser);
}
return List.len() > 0 ? List : null;
}
}

View File

@@ -19,3 +19,12 @@ function Cb_GameWorld_move_position(C_User, a3, a4, a5, a6) {
}
Socket.SendGateway(evv);
}
Cb_GameWorld_move_position_Enter <- {};
Cb_GameWorld_move_position_Leave <- {};
_Hook_Register_Currency_Func_("0x086C5706", ["pointer", "pointer", "int", "int", "int", "short", "void"], Cb_GameWorld_move_position_Enter, Cb_GameWorld_move_position_Leave);
Cb_GameWorld_move_position_Enter["Rindro_BaseCallBack"] <- function(args) {
Cb_GameWorld_move_position(args[1], args[2], args[3], args[4], args[5]);
}

View File

@@ -14,3 +14,7 @@ function Cb_giveup_dgn(C_User) {
}
}
}
Cb_Party_giveup_game_Enter_Func["Rindro_BaseCallBack"] <- function(args) {
Cb_giveup_dgn(args[1]);
}

View File

@@ -5,3 +5,7 @@ function Cb_insert_user(C_Area, C_User) {
Func(C_Area, C_User);
}
}
Cb_Area_insert_user_Enter["Rindro_BaseCallBack"] <- function(args) {
Cb_insert_user(args[0], args[1]);
}

View File

@@ -12,3 +12,13 @@ function Cb_reach_game_world(C_User) {
Func(SUser);
}
}
Cb_GameWorld_reach_game_world_Enter_Func <- {};
Cb_GameWorld_reach_game_world_Leave_Func <- {};
_Hook_Register_Currency_Func_("0x086C4E50", ["pointer", "pointer", "char"], Cb_GameWorld_reach_game_world_Enter_Func, Cb_GameWorld_reach_game_world_Leave_Func);
Cb_GameWorld_reach_game_world_Enter_Func["Rindro_BaseCallBack"] <- function(args) {
Cb_reach_game_world(args[1]);
}

View File

@@ -12,3 +12,11 @@ function Cb_timer_dispatch() {
Func();
}
}
Cb_TimerQueue_GetTimerMess_Enter <- {};
Cb_TimerQueue_GetTimerMess_Leave <- {};
_Hook_Register_Currency_Func_("0x08630ECC", ["pointer", "pointer", "int"], Cb_TimerQueue_GetTimerMess_Enter, Cb_TimerQueue_GetTimerMess_Leave);
Cb_TimerQueue_GetTimerMess_Enter["Rindro_BaseCallBack"] <- function(args) {
Cb_timer_dispatch();
}

View File

@@ -6,15 +6,26 @@
*/
if (!("Cb_Use_Item_Sp_Func" in getroottable())) Cb_Use_Item_Sp_Func <- {};
// function Cb_use_item_sp(C_User, ItemId) {
// if (ItemId in Cb_Use_Item_Sp_Func) {
// local SUser = User(C_User);
// if (SUser) {
// local Ret = Cb_Use_Item_Sp_Func[ItemId](SUser, ItemId);
// if (Ret == false) return false;
// }
// }
// return true;
// }
function Cb_use_item_sp(C_User, ItemId) {
if (ItemId in Cb_Use_Item_Sp_Func) {
local SUser = User(C_User);
if (SUser) {
local Ret = Cb_Use_Item_Sp_Func[ItemId](SUser, ItemId);
if (Ret == false) return false;
Cb_History_ItemDown_Func["UseSharedEffectItem"] <- function(SUser, Data) {
if (Data[18] == "3") {
local ItemId = Data[15].tointeger();
if (ItemId in Cb_Use_Item_Sp_Func) {
if (SUser) {
Timer.SetTimeOut(function() {
Cb_Use_Item_Sp_Func[ItemId](SUser, ItemId);
}, 1);
}
}
}
return true;
}

View File

@@ -19,8 +19,7 @@ Cb_MySQL_Open_Enter_Func.info <- function(args) {
try {
dofile("/dp_s/Front_main.nut");
Front_main();
} catch (exception){
}
} catch (exception) {}
function sqr_main() {
if (getroottable().rawin("DP_S_VERSION") && DP_S_VERSION >= 25.332) {
@@ -72,6 +71,7 @@ function sqr_main() {
// 输出到控制台
print(banner);
print("Sersion: 1.3");
dofile("/dp_s/Main.nut");
@@ -206,14 +206,21 @@ if (getroottable().rawin("DP_S_VERSION") && DP_S_VERSION >= 25.329 && DP_S_VERSI
);
}
//获得本服务器的IP
GatewaySocketPackFuncMap.rawset(10002, function(Jso) {
getroottable().Dps_Self_Ip <- Jso.myip;
print("\x1b[96m" + "############双端插件初始化成功############" + "\x1b[0m");
});
if (!getroottable().rawin("Dps_Self_Ip")) Dps_Self_Ip <- "";
Haker.LoadHook("0x081E8C78", ["pointer", "pointer", "pointer", "int"],
function(args) {
local SUser = User(args[1]);
local SNU = Memory.allocUtf8String(getroottable().Dps_Self_Ip);
SUser.SendBlob(SNU);
return null;
},
function(args) {
return null;
});
Haker.LoadHook("0x081E8C78", ["pointer", "pointer", "pointer", "int"],
function(args) {
local SUser = User(args[1]);
local SNU = Memory.allocUtf8String(getroottable().Dps_Self_Ip);
SUser.SendBlob(SNU);
return null;
},
function(args) {
return null;
});

View File

@@ -686,7 +686,7 @@ _Hook_Register_Currency_Func_("0x81f92ca", ["pointer", "int", "int", "int", "poi
//移动物品
Cb_DisPatcher_MoveItem_Enter_Func <- {};
Cb_DisPatcher_MoveItem_Leave_Func <- {};
_Hook_Register_Currency_Func_("0x81C5B76", ["pointer", "pointer", "pointer", "void"],Cb_DisPatcher_MoveItem_Enter_Func, Cb_DisPatcher_MoveItem_Leave_Func);
_Hook_Register_Currency_Func_("0x81C5B76", ["pointer", "pointer", "pointer", "void"], Cb_DisPatcher_MoveItem_Enter_Func, Cb_DisPatcher_MoveItem_Leave_Func);
// 注册道具转职Hook
Cb_ChangeGrowType_Item_Enter_Func <- {};
@@ -699,6 +699,57 @@ Cb_ItemVendingMachine_ProcessIPG_ResultOutput_Leave_Func <- {};
_Hook_Register_Currency_Func_("0x8178676", ["pointer", "pointer", "int", "int", "pointer", "int"], Cb_ItemVendingMachine_ProcessIPG_ResultOutput_Enter_Func, Cb_ItemVendingMachine_ProcessIPG_ResultOutput_Leave_Func);
//商城购买道具收包
Cb_ItemVendingMachine_BuyAuctionItem_Enter_Func <- {};
Cb_ItemVendingMachine_BuyAuctionItem_Leave_Func <- {};
_Hook_Register_Currency_Func_("0x81769f6", ["pointer", "pointer", "int", "pointer"], Cb_ItemVendingMachine_BuyAuctionItem_Enter_Func, Cb_ItemVendingMachine_BuyAuctionItem_Leave_Func);
// 构建地图
Cb_CBattle_Field_ConsistMap_Enter_Func <- {};
Cb_CBattle_Field_ConsistMap_Leave_Func <- {};
_Hook_Register_Currency_Func_("0x083031D2", ["pointer", "void"], Cb_CBattle_Field_ConsistMap_Enter_Func, Cb_CBattle_Field_ConsistMap_Leave_Func);
// 击杀怪物时
Cb_CBattle_Field_kill_monster_Enter_Func <- {};
Cb_CBattle_Field_kill_monster_Leave_Func <- {};
_Hook_Register_Currency_Func_("0x0830BC78", ["pointer", "pointer", "pointer", "pointer", "pointer", "int"], Cb_CBattle_Field_kill_monster_Enter_Func, Cb_CBattle_Field_kill_monster_Leave_Func);
// 副本中角色死亡
Cb_CParty_die_user_Enter_Func <- {};
Cb_CParty_die_user_Leave_Func <- {};
_Hook_Register_Currency_Func_("0x085A7828", ["pointer", "pointer", "void"], Cb_CParty_die_user_Enter_Func, Cb_CParty_die_user_Leave_Func);
//NPC商店购买发包
Cb_BuyItem_send_Enter <- {};
Cb_BuyItem_send_Leave <- {};
_Hook_Register_Currency_Func_("0x081BE20A", ["pointer", "pointer", "pointer", "void"], Cb_BuyItem_send_Enter, Cb_BuyItem_send_Leave);
//获取深渊派对
Cb_GetHellPartyDifficulty_Enter_Func <- {};
Cb_GetHellPartyDifficulty_Leave_Func <- {};
_Hook_Register_Currency_Func_("0x082FFA2E", ["pointer", "int", "int"], Cb_GetHellPartyDifficulty_Enter_Func, Cb_GetHellPartyDifficulty_Leave_Func);
// 初始化并启动活动
Cb_CEventManager_TriggerEventStart_Enter <- {};
Cb_CEventManager_TriggerEventStart_Leave <- {};
_Hook_Register_Currency_Func_("0x08115CC6", ["pointer", "int", "int", "int"], Cb_CEventManager_TriggerEventStart_Enter, Cb_CEventManager_TriggerEventStart_Leave);
// 装备红字打书检查
Cb_CItemAmplifier_checkInvestableItem_Enter <- {};
Cb_CItemAmplifier_checkInvestableItem_Leave <- {};
_Hook_Register_Currency_Func_("0x08234980", ["pointer", "pointer", "pointer", "int", "bool"], Cb_CItemAmplifier_checkInvestableItem_Enter, Cb_CItemAmplifier_checkInvestableItem_Leave);
// 物品删除 CInventory::delete_item
Cb_CInventory_delete_item_Enter <- {};
Cb_CInventory_delete_item_Leave <- {};
_Hook_Register_Currency_Func_("0x0850400C", ["pointer", "int", "int", "int", "int", "int", "int"], Cb_CInventory_delete_item_Enter, Cb_CInventory_delete_item_Leave);
// 向区域添加用户
Cb_Area_insert_user_Enter <- {};
Cb_Area_insert_user_Leave <- {};
_Hook_Register_Currency_Func_("0x086C25A6", ["pointer", "pointer", "void"], Cb_Area_insert_user_Enter, Cb_Area_insert_user_Leave);
function _Hook_Register_Currency_DelayHook_() {
//五国时的热点函数
//获取Item Rarity

View File

@@ -1,11 +1,9 @@
getroottable().DebugModelFlag <- false;
//初始化插件
function InitPluginInfo() {
try {
local Io = IO("/java_plugin/Restart", "r");
Io.Close();
Sq_CreatCConnectPool(2, 4, "127.0.0.1", 3306, "game", "uu5!^%jg");
Sq_CreatSocketConnect("127.0.0.1", "65109");
} catch (exception) {
}
@@ -17,3 +15,6 @@ function InitPluginInfo() {
function main() {
InitPluginInfo();
}

957
dp_s_development.skill Normal file
View File

@@ -0,0 +1,957 @@
# DP-S Game Server Plugin Development Skill
## 🎯 框架架构概述
### 整体架构层次
```
MyProject/ (用户自定义脚本层) - GM命令、自定义功能、逻辑重写
_DPS_/_BuiltProject/ (内置功能层) - 团本系统、装备系统、交易系统等
_DPS_/_Core/ (核心框架层) - BaseClass类库、回调系统、钩子系统
Native C++ 服务器 (游戏原生代码) - 原生游戏逻辑
```
### 核心类体系
- User: 玩家对象核心类
- GameManager: 游戏管理器
- Packet Dungeon: 副本对象类
- Mysql/MysqlPool: 数据库连接池
- Timer: 定时器类
- NativePointer: 内存操作类
---
## 📐 官方模块开发规范
### OfficialProject/ 目录规范
**OfficialProject** 是官方功能模块目录,包含标准化的服务器功能模块。
#### 已有官方模块列表
| 模块名称 | 功能描述 | 配置文件 |
|----------|----------|----------|
| 二狗宝珠继承 | 装备宝珠属性继承系统 | 无 |
| 交易邮件播报 | 交易和邮件系统播报 | 交易邮件播报.json |
| 任务清除卷 | 任务相关功能 | 任务相关配置_南瓜.json |
| 副本播报 | 副本通关/放弃播报 | 副本播报配置_Nangua.json |
| 史诗掉落奖励 | 史诗/神器/稀有掉落奖励 | 史诗掉落奖励配置_南瓜.json |
| 心悦播报 | 心悦会员登录播报 | 心悦播报配置_Nangua.json |
| 战力榜 | 战力排行榜功能 | 战力榜配置_南瓜.json |
| 杂项功能 | 多种游戏功能开关 | 杂项功能开关_Nangua.json |
| 根据任务进入不同副本 | 任务控制副本进入 | 根据任务进入不同副本_Lenheart.json |
| 深渊爆率实时控制 | 深渊爆率调整 | 深渊爆率实时控制_Nangua.json |
| 独立掉落 | 自定义掉落系统 | 无 |
| 自动刷新GM工具的邮件 | GM工具邮件管理 | 无 |
| 装备镶嵌与时装镶嵌 | 装备和时装镶嵌扩展 | 装备镶嵌与时装镶嵌_Lenheart.json |
| 记录服务器聊天内容 | 聊天内容记录 | 无 |
| 门票进入副本 | 门票控制副本进入 | 门票进入副本配置_Maomi.json |
#### OfficialConfig/ 目录规范
**OfficialConfig** 是官方配置文件目录,存储所有官方模块的配置。
##### 配置文件命名规范
```
功能名称.json # 通用配置
功能名称_服务器名.json # 服务器特定配置
功能名称_管理员昵称.json # 管理员自定义配置
```
##### 通用配置结构
```json
{
"功能配置": {
"开关控制": true,
"提示信息": "这是提示信息",
"配置项1": "值1",
"配置项2": ["数组值1", "数组值2"],
"配置项3": {
"子项1": "子值1",
"子项2": "子值2"
}
},
"数据库配置": {
"IP": "127.0.0.1",
"端口": 3306,
"用户名": "game",
"密码": "password"
}
}
```
---
## 🏢 官方模块开发模式
### 标准模块结构
OfficialProject/ 目录下的每个模块应遵循以下结构:
```
功能模块名/
├── 功能模块名.nut # 主脚本文件
└── Proj.ifo # 项目信息文件(可选)
```
#### 主脚本文件模板
```nut
/*
文件名:功能模块名.nut
路径:OfficialProject/功能模块名/功能模块名.nut
创建日期:YYYY-MM-DD HH:MM
文件用途:功能模块描述
*/
function _功能模块名_Main_() {
// 加载配置
local config = GlobalConfig.Get("功能模块名配置.json");
// 读取配置开关
if (config["功能配置"]["开关控制"]) {
// 执行功能初始化
// 注册回调
RegisterCallbacks();
// 初始化数据
InitData();
}
// 注册回调和钩子
function RegisterCallbacks() {
// 注册GM命令
Gm_InputFunc_Handle["命令名"] <- function(SUser, CmdString) {
// 命令处理逻辑
}.bindenv(this);
// 注册事件回调
Cb_reach_game_world_Func["功能模块名"] <- function(SUser) {
// 事件处理逻辑
}.bindenv(this);
// 注册Hook回调
Cb_HookName_Enter_Func["功能模块名"] <- function(args) {
// Hook处理逻辑
return value;
}.bindenv(this);
}
// 初始化数据
function InitData() {
// 初始化数据库表
// 加载初始数据
}
}
```
### 配置文件管理
#### 读取配置
```nut
// 读取配置文件
local config = GlobalConfig.Get("配置文件名.json");
// 访问配置项
local switchState = config["功能配置"]["开关控制"];
local someValue = config["功能配置"]["配置项1"];
local arrayValue = config["功能配置"]["配置项2"][0];
```
#### 配置热重载
```nut
// 在Main.nut中注册重载函数
GlobalOfficial_Project.ReloadProjectMap <- {
"配置文件名" = "_功能模块名_Main_"
};
// 重载函数
function _功能模块名_Main_(OldConfig) {
// 使用旧配置清理资源(如果有)
if (OldConfig) {
// 清理逻辑
}
// 重新加载配置
local config = GlobalConfig.Get("配置文件名.json");
// 重新初始化
// ...
}
```
### 官方模块最佳实践
#### 1. 战力榜模块模式
```nut
// 获取战力值
function GetRankNumber(charac_no) {
local Config = GlobalConfig.Get("战力榜配置_服务器名.json");
local currentDB = Config["战力榜配置"]["当前选择"];
local dbConfig = Config["战力榜配置"]["数据库选择"][currentDB];
local SqlObj = MysqlPool.GetInstance().GetConnect();
local query = "SELECT " + dbConfig["字段名"] +
" FROM " + dbConfig["数据库名"] + "." + dbConfig["表名"] +
" WHERE " + dbConfig["条件字段"] + "=" + charac_no;
local result = SqlObj.Select(query, ["string"]);
MysqlPool.GetInstance().PutConnect(SqlObj);
if (result.len() > 0) {
return result[0][0].tointeger();
}
return 0;
}
```
#### 2. 副本播报模块模式
```nut
// 配置驱动的播报系统
function ReportDungeonClear(SUser, dgnName, timeUsed, monsterCount) {
local config = GlobalConfig.Get("副本播报配置_服务器名.json");
// 检查开关
if (!config["副本播报开关"]) return;
// 检查排除列表
local excludeDgns = config["不需要播报的副本ID"];
local excludeCIDs = config["指定角色CID不播报"];
if (excludeDgns.find(dgnName) != null) return;
if (excludeCIDs.find(SUser.GetCID()) != null) return;
// 构建播报消息
local msg = format(config["通关播报信息"],
SUser.GetCharacName(),
dgnName,
GetDungeonDifficulty(dgnName),
timeUsed,
monsterCount
);
// 发送播报
AdMsg().PutEquipment(msg, config["发送信息位置"]);
}
```
#### 3. 杂项功能模块模式
```nut
// 多功能开关控制
function _杂项功能_Main_() {
local config = GlobalConfig.Get("杂项功能开关_服务器名.json");
// 功能1: 设置最大等级
if (config["杂项功能开关"]["设置最高等级"][0]) {
local level = config["杂项功能开关"]["设置最高等级"][1];
GameManager.SetGameMaxLevel(level);
}
// 功能2: 装备解锁时间
if (config["杂项功能开关"]["装备解锁(单位:秒)"][0]) {
local time = config["杂项功能开关"]["装备解锁(单位:秒)"][1];
GameManager.SetItemLockTime(time);
}
// 功能3: 创建缔造者
if (config["杂项功能开关"]["创建缔造者"]) {
GameManager.OpenCreateJob_CreatorMage();
}
// Hook回调和Leave Hook返回值拦截
Cb_Item_IsBanRedeemItem_Leave_Func.BanRedeemItem <- function(args) {
if (config["杂项功能开关"]["关闭商店回购"]) {
return 1; // 返回1拦截默认行为
}
}
}
```
#### 4. 史诗掉落奖励模块模式
```nut
// 配置驱动的奖励系统
function GiveEpicReward(SUser, epicCount) {
local config = GlobalConfig.Get("史诗掉落奖励配置_服务器名.json");
// 检查开关
if (!config["奖励控制"]["多件史诗奖励控制开关"]) return;
// 获取奖励配置
local rewardConfig = config["奖励控制"]["多件SS对应奖励"][epicCount.tostring()];
if (rewardConfig) {
foreach(reward in rewardConfig) {
local itemId = reward[0];
local quantity = reward[1];
if (itemId == 0) {
// 点券
SUser.RechargeCera(quantity);
} else {
// 道具
SUser.GiveItem(itemId, quantity);
}
}
// 发送播报
local title = config["信息播报"]["标题"];
local contentColor = config["信息播报"]["内容颜色"];
local ending = config["信息播报"]["结尾"];
AdMsg().PutEquipment(title, 14);
AdMsg().PutEquipment(ending, 14);
}
}
```
#### 5. 心悦播报模块模式
```nut
// 任务驱动的播报系统
function CheckXinyueStatus(SUser) {
local config = GlobalConfig.Get("心悦播报配置_服务器名.json");
local xinyueConfig = config["心悦播报配置"]["心悦会员登录播报"];
// 检查开关
if (!xinyueConfig["开关"]) return;
// 检查任务完成情况
local taskConfigs = config["心悦播报配置"]["任务等级配置"];
foreach(taskConfig in taskConfigs) {
local taskId = taskConfig["任务ID"];
if (SUser.CheckQuest(taskId)) {
// 构建播报消息
local msg = format("%s[%s]%s%s",
xinyueConfig["标题"],
taskConfig["心悦等级"],
SUser.GetCharacName(),
xinyueConfig["结尾信息内容"]
);
// 发送播报
AdMsg().PutEquipment(msg, xinyueConfig["信息播报发送位置"]);
break;
}
}
}
```
### 游戏管理器常用方法
```nut
// 设置游戏最大等级
GameManager.SetGameMaxLevel(85);
// 设置装备解锁时间(秒)
GameManager.SetItemLockTime(1);
// 开启缔造者创建
GameManager.OpenCreateJob_CreatorMage();
// 自动解除魔法封印
GameManager.OpenRandomAutomaticUnblocking();
// 开启装备与时装镶嵌
GameManager.FixEquipUseJewel();
// 修复下线卡城镇
GameManager.FixSaveTown();
// 修复绝望之塔金币异常
GameManager.FixDespairGold();
// 设置每日可交易金币上限
GameManager.FixGlodTradeDaily(50000000);
// 强化13以上免刷新
GameManager.Fix_13Upgrade();
// 开启14格技能栏
GameManager.Fix14Skill();
// 修复拍卖行消耗品上架
GameManager.Fix_Auction_Regist_Item();
// 设置副本内可丢弃物品品级
GameManager.FixDungeonDropGrade(3);
// 修复邮件验证
GameManager.FixEmailRemovalVerification();
```
### 公告系统配置
```nut
// 登录器配置示例
{
"noticeclass": ["公告", "活动", "玩法"],
"notice": [
{
"type": "img",
"category": 1,
"text": "公告标题",
"link": "https://example.com",
"content": "http://192.168.200.25:3000/image.png"
},
{
"type": "link",
"category": 1,
"text": "链接标题",
"link": "https://example.com"
}
],
"scriptMd5": "",
"pluginsMd5": [],
"updateUrl": "www.baidu.com"
}
```
---
## 🔧 钩子系统
### Hook 注册方法
```nut
// 步骤1: 声明回调表
Cb_HookName_Enter_Func <- {};
Cb_HookName_Leave_Func <- {};
// 步骤2: 注册Hook
_Hook_Register_Currency_Func_(
"0x内存地址",
["参数类型数组"],
Cb_HookName_Enter_Func,
Cb_HookName_Leave_Func
);
// 步骤3: 添加处理函数
Cb_HookName_Enter_Func["模块名"] <- function(args) {
local SUser = User(args[0]); // args是参数数组
// 处理逻辑
return value; // 可返回null或修改后的值
}.bindenv(this);
```
### 常用钩子点
| 钩子名称 | 触发时机 | 参数说明 |
|----------|----------|----------|
| Cb_reach_game_world | 玩家进入游戏世界 | (C_User) |
| Cb_player_exit | 玩家下线 | (C_User) |
| Cb_User_Insert_Item | 玩家获得道具 | (C_User, ItemId, 数量, ...) |
| Cb_Move_Area | 玩家区域移动 | (C_User, TownIndex, AreaIndex) |
| Cb_base_input | 玩家普通输入 | (C_User, CmdString) |
| Cb_gm_input | GM命令输入 | (C_User, CmdString) |
| Cb_Use_Item_Sp | 特殊道具使用 | (C_User, ItemId) |
| Cb_CInventory_ChangeEquip | 切换装备 | (C_User, EquipSlot) |
| Cb_CParty_OnKillMonster | 击杀怪物 | (C_Party, C_User, MonsterId) |
---
## 📡 回调系统
### 回调注册模式
```nut
// 简单回调
if (!("Cb_event_name_Func" in getroottable())) Cb_event_name_Func <- {};
Cb_event_name_Func["模块名"] <- function(SUser) {
// 处理逻辑
}.bindenv(this);
```
### 常用回调类型
```nut
// 玩家上线
Cb_reach_game_world_Func["模块名"] <- function(SUser) {
local name = SUser.GetCharacName();
local level = SUser.GetCharacLevel();
}
// 玩家下线
Cb_player_exit_Func["模块名"] <- function(SUser) {
// 清理玩家数据
}
// 每帧执行
Cb_timer_dispatch_Func["模块名"] <- function() {
// 每帧执行逻辑
}
// 副本通关
Cb_ClearDungeon_Enter_Func["模块名"] <- function(args) {
local SUser = User(args[0]);
// 奖励处理
}
```
---
## 💻 GM 命令开发
### 基本命令注册
```nut
Gm_InputFunc_Handle["命令名"] <- function(SUser, CmdString) {
// 参数解析
local handler = [];
local pos = 0;
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));
} while (pos != null);
// 业务逻辑
if (handler.len() >= 1) {
local itemId = handler[1].tointeger();
local quantity = (handler.len() >= 2) ? handler[2].tointeger() : 1;
SUser.GiveItem(itemId, quantity);
SUser.SendNotiPacketMessage("道具已发放", 8);
}
}
```
### 快捷命令
```nut
Gm_InputFunc_Handle["满级"] <- function(SUser, CmdString) {
SUser.SetCharacLevel(85);
SUser.SendNotiPacketMessage("已满级", 8);
}
Gm_InputFunc_Handle["回城"] <- function(SUser, CmdString) {
SUser.ChangeTown(0, 1, 0, 0); // 城镇, 区域, X, Y
}
```
---
## 🗄️ 数据库操作
### 基本查询
```nut
// 单连接模式
MysqlObject = Mysql(Str_Ptr("127.0.0.1"), 3306,
Str_Ptr("数据库名"), Str_Ptr("账号"), Str_Ptr("密码"));
MysqlObject.Exec_Sql("SET NAMES 'latin1'");
// 查询
local Ret = MysqlObject.Select("SELECT id, name, level FROM users WHERE id = " + uid,
["int", "string", "int"]);
foreach(Row in Ret) {
local id = Row[0];
local name = Row[1];
local level = Row[2];
}
// 插入/更新
MysqlObject.Exec_Sql(format("UPDATE users SET level = %d WHERE id = %d", newLevel, id));
// 获取自增ID
MysqlObject.Exec_Sql("INSERT INTO users (name) VALUES ('test')");
local Ret = MysqlObject.Select("SELECT LAST_INSERT_ID()", ["int"]);
local newId = Ret[0][0];
```
### 连接池模式
```nut
// 获取连接
local SqlObj = MysqlPool.GetInstance().GetConnect();
// 查询
local Ret = SqlObj.Select("SELECT * FROM table", ["int", "string"]);
foreach(row in Ret) {
// 处理数据
}
// 执行
SqlObj.Exec_Sql("UPDATE table SET field = value");
// 归还连接
MysqlPool.GetInstance().PutConnect(SqlObj);
```
---
## 🎒 装备操作
### 装备基本操作
```nut
local InvenObj = SUser.GetInven();
// 获取装备
local Equip = InvenObj.GetSlot(Inven.INVENTORY_TYPE_BODY, 槽位);
ifif (!Equip) return;
// 读取属性
local upgrade = Equip.GetUpgrade(); // 强化等级
local amplification = Equip.GetAmplification(); // 增幅等级
local enchanting = Equip.GetEnchanting(); // 附魔ID
local forging = Equip.GetForging(); // 镶嵌数据
local itemId = Equip.GetIndex(); // 道具ID
// 修改属性
Equip.SetUpgrade(12);
Equip.SetAmplification(3);
Equip.SetEnchanting(1001);
Equip.Flush(); // 必须调用
// 刷新客户端
SUser.SendUpdateItemList(1, 3, 槽位);
```
### 装备继承模式
```nut
// 读取源装备属性
local sourceEquip = InvenObj.GetSlot(Inven.INVENTORY_TYPE_ITEM, 9);
local forging = sourceEquip.GetForging();
local upgrade = sourceEquip.GetUpgrade();
local amplification = sourceEquip.GetAmplification();
local enchanting = sourceEquip.GetEnchanting();
// 应用到目标装备
local targetEquip = InvenObj.GetSlot(Inven.INVENTORY_TYPE_BODY, 10);
targetEquip.SetForging(forging);
targetEquip.SetUpgrade(upgrade);
targetEquip.SetAmplification(amplification);
targetEquip.SetEnchanting(enchanting);
targetEquip.Flush();
// 刷新客户端
SUser.SendUpdateItemList(1, 0, 9);
SUser.SendUpdateItemList(1, 3, 10);
```
---
## ⏰ 定时器系统
### 延迟任务
```nut
// 延迟执行单位游戏tick60tick≈1秒
Timer.SetTimeOut(function() {
SUser.SendNotiPacketMessage("延迟执行", 8);
}, 300); // 5秒后执行
// 带参数的延迟执行
Timer.SetTimeOut(function(arg1, arg2) {
print("参数1: " + arg1 + ", 参数2: " + arg2);
}, 300, "hello", 123);
```
### 定时任务
```nut
// 移除旧任务
Timer.RemoveCronTask("任务名称");
// 创建定时任务
Timer.SetCronTask(function() {
// 定时执行的代码
print("定时任务执行");
return true; // 返回false取消下次执行
}.bindenv(this), {
Cron = "0 */5 * * * *", // Cron表达式每5分钟
Name = "任务名称"
});
// Cron表达式格式秒 分 时 日 月 周
// 示例:
// "0 0 * * * *" - 每小时
// "0 */5 * * * *" - 每5分钟
// "0 0 0 * * *" - 每天0点
```
---
## 🔍 内存操作
### 基本读写
```nut
// 写入操作
NativePointer("0x8360C38").add(3).writeU8(MaxLevel);
NativePointer("0x081E907E").writeInt(value);
// 读取操作
local value = NativePointer("0x8360C38").add(3).readS8();
local value = NativePointer(address).readInt();
local value = NativePointer(address).readPointer();
local str = NativePointer(address).readUtf8String();
```
### 字节数组操作
```nut
// 读取字节数组
local arr = NativePointer(address).readByteArray(size);
// 写入字节数组(内存补丁)
Sq_WriteByteArr(S_Ptr("0x081E907E"), [0x90, 0x90, 0x90, 0x90, 0x90]);
```
### 原生函数调用
```nut
// 调用原生函数
local result = Sq_CallFunc(
S_Ptr("0x082947A4"), // 函数地址
"pointer", // 返回类型
["pointer", "int"], // 参数类型
arg1, arg2 // 参数值
);
// 常用原生函数
Sq_CallFunc(S_Ptr("0x0858EFDE"), "int", ["pointer", "pointer", "int"],
S_Ptr("0x0"), SUser.C_Object, newLevel);
```
---
## 🏗️ 模块开发模板
### 标准模块结构
```nut
/*
文件名:模块名.nut
路径:MyProject/模块名.nut
创建日期:2026-03-16 HH:MM
文件用途:模块功能描述
*/
Timer.SetTimeOut(function() {
// 创建模块实例并挂载到根表
getroottable()._模块名_ <- ModuleClass();
print("模块名 - 已加载");
}, 1);
class ModuleClass {
MysqlObject = null;
data = null;
constructor() {
// 初始化数据库
MysqlObject = Mysql(Str_Ptr("127.0.0.1"), 3306,
Str_Ptr("数据库名"), Str_Ptr("账号"), Str_Ptr("密码"));
// 初始化数据
data = {};
// 注册回调
RegisterCallbacks();
// 初始化数据
InitData();
}
function RegisterCallbacks() {
// 注册GM命令
Gm_InputFunc_Handle["模块命令"] <- function(SUser, CmdString) {
// 命令处理
}.bindenv(this);
// 注册事件回调
Cb_reach_game_world_Func["模块名"] <- function(SUser) {
// 玩家上线处理
}.bindenv(this);
// 注册Hook回调
Cb_HookName_Enter_Func["模块名"] <- function(args) {
// Hook处理
}.bindenv(this);
}
function InitData() {
// 初始化配置和数据
try {
MysqlObject.Exec_Sql("CREATE TABLE IF NOT EXISTS table_name (...)");
print("数据表初始化成功");
} catch (exception) {
print("数据表初始化失败");
}
}
function SaveData() {
// 保存数据到数据库
}
function LoadData() {
// 从数据库加载数据
}
}
```
---
## 🐛 调试与测试
### 调试命令
```
// 在游戏中输入
T // 显示当前玩家调试信息
ResetScript // 重载所有脚本
```
### 调试输出
```nut
// 启用调试模式
getroottable().DebugModelFlag <- true;
// 启用包调试
getroottable().PacketDebugModel <- true;
// 输出调试信息
print("调试信息: " + value);
// 输出表格
printT(TableObject);
// 查看回调注册
print(Cb_reach_game_world_Func);
```
### 错误处理
```nut
try {
// 可能失败的操作
local result = SomeOperation();
} catch (exception) {
print("Error: " + exception);
if (SUser) {
SUser.SendNotiPacketMessage("操作失败", 8);
}
}
// 数据验证
if (!SUser) return;
if (!InvenObj) return;
if (!Equip) return;
```
---
## 📋 常见任务清单
### 任务1: 添加GM命令
- 创建命令处理函数
- 解析参数
- 实现功能逻辑
- 发送反馈消息
### 任务2: 监听游戏事件
- 确定事件类型
- 找到对应回调表
- 注册处理函数
- 处理事件数据
### 任务3: 修改游戏配置
- 找到配置内存地址
- 使用NativePointer写入
- 测试修改效果
### 任务4: 创建自定义道具
- 注册道具使用回调
- 实现道具效果
- 验证和反馈
### 任务5: 实现新功能模块
- 创建类结构
- 初始化数据库
- 注册回调和Hook
- 实现业务逻辑
- 添加定时任务
---
## 🎓 开发建议
### 通用开发规范
1. **命名规范**: 使用 `_模块名_` 作为全局表前缀,避免冲突
2. **延迟加载**: 使用 `Timer.SetTimeOut` 避免初始化顺序问题
3. **错误处理**: 关键操作使用 try-catch 包裹
4. **内存管理**: 及时删除 Packet 对象,释放内存
5. **数据验证**: 检查 `rawin()` 和对象有效性
6. **性能优化**: 高频查询使用连接池,定时任务批量保存
7. **代码注释**: 添加文件头注释说明用途
8. **版本检查**: 使用 `DP_S_VERSION` 判断服务器版本
### OfficialProject 开发规范
#### 1. 模块命名
- 使用中文命名,清晰表达功能
- 避免使用特殊字符和空格
- 示例: `杂项功能`, `战力榜`, `副本播报`
#### 2. 文件结构
- 主脚本文件名与目录名一致
- 必须包含标准文件头注释
- 配置文件放在 OfficialConfig/ 目录
#### 3. 主函数命名
- 使用 `_模块名_Main_` 格式
- 用于模块初始化和重载
- 示例: `_杂项功能_Main_()`, `_战力榜_Main_()`
#### 4. 配置管理
- 使用 GlobalConfig.Get() 读取配置
- 配置文件使用 JSON 格式
- 支持配置热重载
#### 5. Hook 回调命名
- 使用 `模块名.功能名` 格式
- 示例: `杂项功能.BanRedeemItem`, `战力榜.UpdateRank`
#### 6. 数据库操作
- 使用连接池模式
- 使用 try-catch 处理异常
- 及时归还连接
#### 7. 播报系统
- 使用 AdMsg() 类发送公告
- 支持富文本和链接
- 注意公告频率控制
### OfficialConfig 开发规范
#### 1. 配置文件命名
- `功能名.json` - 通用配置
- `功能名_服务器名.json` - 服务器特定配置
- `功能名_管理员昵称.json` - 管理员自定义配置
#### 2. JSON 结构规范
- 使用中文键名,提高可读性
- 包含必要的注释字段(如 "提示", "说明"
- 支持嵌套结构
#### 3. 数据库配置
- 使用单独的配置节
- 包含 IP、端口、用户名、密码
- 提供默认值和提示
#### 4. 开关控制
- 使用布尔值控制功能开关
- 数组类型的配置使用 `[开关, 值]` 格式
- 提供清晰的说明
#### 5. 排除列表
- 使用数组存储排除项
- 支持 CID、道具ID、副本ID 等
- 提供注释说明如何获取
---
## 📞 快速参考
### User常用方法
- GetArea(), GetLocation(), GetUID(), GetCID()
- GetCharacLevel(), SetCharacLevel()
- GetCharacName(), GetCharacJob()
- GiveItem(itemId, quantity)
- SendNotiPacketMessage(msg, type)
- ChangeTown(town, area, x, y)
### Packet方法
- Put_Header(op, sub), Put_Int(v), Put_Str(s)
- Send(SUser)
### 数据类型
- 整数: `tointeger()`
- 字符串: `tostring()`
- 布尔: `true` / `false`
- 表: `{key = value}`
- 数组: `[value1, value2]`

BIN
lib/libAurora.so Executable file → Normal file

Binary file not shown.

BIN
lib/libAurora.so.bak Executable file → Normal file

Binary file not shown.

0
lib/strip_other_funcs_unix.sh Executable file → Normal file
View File

0
temp_content.txt Normal file
View File

1
开发文档/dp-s_doc Submodule

Submodule 开发文档/dp-s_doc added at d7c0a4983a

View File

@@ -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");
}

View File

@@ -0,0 +1,11 @@
{
"ProjectName": "DPS启动器",
"ProjectDescribe": "DPS作为网关,QT编写的纯净登录器",
"ProjectAuthor": "倾泪寒",
"ProjectVersion": 1.0,
"ProjectConfig": "登录器配置.json",
"ProjectFiles": [
"DPS启动器.nut"
],
"ProjectRunFunc": "_DPS_Login_Gateway_Init_"
}

View File

@@ -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"
}

View File

@@ -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_();
}

View File

@@ -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 倾泪寒'",
"说明" : "所有指令都可更改 相对于的触发方式输入自己设定的指令即可"
}

View File

@@ -0,0 +1,11 @@
{
"ProjectName": "GM便捷操作台",
"ProjectDescribe": "本项目提供了许多GM的便捷操作 方便平时调试或写代码 \n输入'//' + 设定的指令即可执行逻辑 ",
"ProjectAuthor": "倾泪寒",
"ProjectVersion": 1.0,
"ProjectConfig": "GM便捷操作台_Lenheart.json",
"ProjectFiles": [
"GM便捷操作台.nut"
],
"ProjectRunFunc": "_Dps_GmConvenienceConsole_Main_"
}

View File

@@ -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);
}

View File

@@ -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"
}

View File

@@ -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_"
}

View File

@@ -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_"
}

View File

@@ -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();
}

View File

@@ -0,0 +1,7 @@
{
"提示":"挂载后只有新使用的sp,tp,属性石会返还,之前使用过的不会返还",
"数据库IP 不是外置数据库不要更改": "127.0.0.1",
"数据库端口 不懂不要更改": 3306,
"数据库用户名 本地用户名不懂不要更改": "game",
"数据库密码 本地密码不懂不要更改": "uu5!^%jg"
}

View File

@@ -0,0 +1,11 @@
{
"ProjectName": "一键分解卷",
"ProjectDescribe": "使用道具将需要分解的装备分解,如未开启副职业及开启摆摊分解则调用诺顿分解机分解",
"ProjectAuthor": "南瓜",
"ProjectVersion": 1.3,
"ProjectConfig": "一键分解卷_Lenheart.json",
"ProjectFiles": [
"一键分解卷.nut"
],
"ProjectRunFunc": "_Dps_OneClickDisassemblyOfRoll_Main_"
}

View File

@@ -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_();
}

View File

@@ -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)"
}

View File

@@ -0,0 +1,11 @@
{
"ProjectName": "一键存入个人金库",
"ProjectDescribe": "使用之前,请务必保证安装了群内的 \"客户端消息框233.dll\" 插件,这个插件放入你客户端的插件加载目录即可,在游戏中输入 //yjcc 即可一键存入个人金库,也可以在游戏中的快捷喊话中添加为快捷键 配置中可更改 yjcc 关键字",
"ProjectAuthor": "倾泪寒 & 南瓜",
"ProjectVersion": 1.3,
"ProjectConfig": "一键存入个人金库_Lenheart.json",
"ProjectFiles": [
"一键入库.nut"
],
"ProjectRunFunc": "_Dps_OneClickStorage_Main_"
}

View File

@@ -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_();
}

View File

@@ -0,0 +1,4 @@
{
"一键存仓命令": "yjcc",
"指定道具ID不存入": [3037, 3038]
}

View File

@@ -0,0 +1,11 @@
{
"ProjectName": "上线自动完成任务",
"ProjectDescribe": "上线自动完成任务",
"ProjectAuthor": "倾泪寒",
"ProjectVersion": 1.0,
"ProjectConfig": "上线自动完成任务_Lenheart.json",
"ProjectFiles": [
"上线自动完成任务.nut"
],
"ProjectRunFunc": "_Dps_AutomaticallyCompleteTasksOnline_Main_"
}

View File

@@ -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);
}
}
}

View File

@@ -0,0 +1,4 @@
{
"需要完成的任务编号" : [674,649,675,650],
"注意事项": "请确保你的任务需求等级是1级,否则可能无法完成。请在PVF中更改任务需要的等级。"
}

View File

@@ -0,0 +1,11 @@
{
"ProjectName": "交易邮件播报",
"ProjectDescribe": "交易邮件播报",
"ProjectAuthor": "巅峰 & 倾泪寒",
"ProjectVersion": 1.2,
"ProjectConfig": "交易邮件播报.json",
"ProjectFiles": [
"交易邮件播报.nut"
],
"ProjectRunFunc": "_Dps_JYYJBB_Main_"
}

View File

@@ -0,0 +1,4 @@
{
"提示": "暂时没写配置"
}

View File

@@ -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);
}

View File

@@ -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_"
}

View File

@@ -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;
}

View File

@@ -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]
}

View File

@@ -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"
}

View File

@@ -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;
}
}

View File

@@ -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]
}

View File

@@ -0,0 +1,11 @@
{
"ProjectName": "修复卡NPC商店道具",
"ProjectDescribe": "修复了客户端通过BUG卡NPC商店道具的问题",
"ProjectAuthor": "倾泪寒",
"ProjectVersion": 1.0,
"ProjectConfig": "",
"ProjectFiles": [
"修复卡NPC商店道具.nut"
],
"ProjectRunFunc": "_Dps_RepairCardNpcStoreProps_Main_"
}

View File

@@ -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;
}
}
}

View File

@@ -0,0 +1,12 @@
{
"ProjectName": "全职业通用转职书",
"ProjectDescribe": "全职业都可用的转职书。每个转职职业是个数组,可以写多个转职卷编号仿照7577的道具类型即可使用\",\"隔开。",
"ProjectAuthor": "倾泪寒",
"ProjectVersion": 1.0,
"ProjectConfig": "",
"ProjectConfig": "全职业通用转职书_Lenheart.json",
"ProjectFiles": [
"全职业通用转职书.nut"
],
"ProjectRunFunc": "_Dps_GeneralJobTransferCertificateForAllProfessions_Main_"
}

View File

@@ -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);
}
}
}
}

View File

@@ -0,0 +1,8 @@
{
"转职初始职业":[7577],
"转职第一职业":[7577],
"转职第二职业":[7577],
"转职第三职业":[7577],
"转职第四职业":[7577],
"转职第五职业":[7577]
}

View File

@@ -0,0 +1,11 @@
{
"ProjectName": "副本使用道具奖励",
"ProjectDescribe": "特定副本使用特定道具会返还自定义的奖励",
"ProjectAuthor": "至尚 & 倾泪寒",
"ProjectVersion": 1.0,
"ProjectConfig": "副本使用道具奖励.json",
"ProjectFiles": [
"副本使用道具奖励.nut"
],
"ProjectRunFunc": "_Dps_ReturnDuplicateItems_Main_"
}

View File

@@ -0,0 +1,7 @@
{
"副本返还配置列表": [
[1,1106,3037,10],
[2,1108,3038,10]
],
"提示": "第一个为副本id 第二个为使用的道具id 第三个为返还的道具id 第四个为返还的数量"
}

View File

@@ -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());
}
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
{
"ProjectName": "副本内禁止丢弃物品",
"ProjectDescribe": "副本内禁止丢弃物品及金币",
"ProjectAuthor": "倾泪寒&南瓜",
"ProjectVersion": 1.0,
"ProjectConfig": "",
"ProjectFiles": [
"副本内禁止丢弃物品.nut"
],
"ProjectRunFunc": "_Dps_DgnCannotDropItem_Main_"
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,11 @@
{
"ProjectName": "副本播报",
"ProjectDescribe": "通关及未通关时播报耗时时长",
"ProjectAuthor": "南瓜",
"ProjectVersion": 1.3,
"ProjectConfig": "副本播报配置_Nangua.json",
"ProjectFiles": [
"副本播报.nut"
],
"ProjectRunFunc": "_Dps_send_dungeon_msg_Main_"
}

View File

@@ -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);
}
}

View File

@@ -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": "地狱级"
}
}

View File

@@ -0,0 +1,11 @@
{
"ProjectName": "副本难度解锁券",
"ProjectDescribe": "使用指定道具解锁当前账号副本所有难度",
"ProjectAuthor": "南瓜",
"ProjectVersion": 1.0,
"ProjectConfig": "副本难度解锁_Nangua.json",
"ProjectFiles": [
"副本难度解锁券.nut"
],
"ProjectRunFunc": "_Dps_unlock_all_dgn_diff_Main_"
}

View File

@@ -0,0 +1,4 @@
{
"解锁成功提示信息": "已将该账号的副本难度开启,将自动返回选择角色界面...",
"副本难度解锁券道具ID": 7577
}

View File

@@ -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);
}
}

View File

@@ -0,0 +1,11 @@
{
"ProjectName": "副本需要持有道具进入",
"ProjectDescribe": "副本需要持有道具进入",
"ProjectAuthor": "凌众",
"ProjectVersion": 1.0,
"ProjectConfig": "副本需要持有道具进入_Lenheart.json",
"ProjectFiles": [
"副本需要持有道具进入.nut"
],
"ProjectRunFunc": "_Dps_MapNeedItem_Main_"
}

View File

@@ -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; // 禁止进入副本
}
}
}
}
}
}

View File

@@ -0,0 +1,7 @@
{
"公告": "缺少进入凭证:",
"副本需要持有道具才允许进入":{
"65":3037,
"64":3038
}
}

View File

@@ -0,0 +1,11 @@
{
"ProjectName": "史诗免确认",
"ProjectDescribe": "史诗免确认",
"ProjectAuthor": "倾泪寒",
"ProjectVersion": 1.1,
"ProjectConfig": "",
"ProjectFiles": [
"史诗免确认.nut"
],
"ProjectRunFunc": "_Dps_EpicNoConfirmationRequired_Main_"
}

View File

@@ -0,0 +1,7 @@
function _Dps_EpicNoConfirmationRequired_Main_() {
NativePointer("0x085A56CE").add(2).writeU8(9);
Cb_CItem_IsRoutingItem_Leave_Func["DPSOFFICIAL"] <- function (args)
{
return 0
}
}

View File

@@ -0,0 +1,11 @@
{
"ProjectName": "史诗掉落奖励",
"ProjectDescribe": "获取指定数量获得奖励以及指定道具获得奖励",
"ProjectAuthor": "南瓜",
"ProjectVersion": 1.1,
"ProjectConfig": "史诗掉落奖励配置_南瓜.json",
"ProjectFiles": [
"史诗掉落奖励.nut"
],
"ProjectRunFunc": "_Dps_SSDL_nangua_Main_"
}

View File

@@ -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] // 金色
};
}

View File

@@ -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]]
}
}

View File

@@ -0,0 +1,11 @@
{
"ProjectName": "史诗药剂",
"ProjectDescribe": "在单人深渊中使用指定ID的道具使玩家获得爆率加成。(组队无效),史诗药剂道具的冷却时间和持续时间需一致!!",
"ProjectAuthor": "倾泪寒 & 南瓜",
"ProjectVersion": 1.7,
"ProjectConfig": "史诗药剂配置文件.json",
"ProjectFiles": [
"史诗药剂.nut"
],
"ProjectRunFunc": "_Dps_EpicPotion_Main_"
}

View File

@@ -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;
}
}

View File

@@ -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,用此道具有图标提示以及时间提示,但是具体字符串相关自行解决"
}

View File

@@ -0,0 +1,11 @@
{
"ProjectName": "在线泡点",
"ProjectDescribe": "根据设定时间间隔给与点券或代币活跃奖励",
"ProjectAuthor": "南瓜",
"ProjectVersion": 1.0,
"ProjectConfig": "在线泡点配置_Nangua.json",
"ProjectFiles": [
"在线泡点.nut"
],
"ProjectRunFunc": "_Dps_Online_rewardsBynangua_Main_"
}

View File

@@ -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());
}
}

View File

@@ -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"
}

View File

@@ -0,0 +1,11 @@
{
"ProjectName": "多彩蜜蜡改跨界石",
"ProjectDescribe": "将物品跨界存放至账号仓库,需dll或启动器支持,否则无法放入物品",
"ProjectAuthor": "南瓜",
"ProjectVersion": 1.0,
"ProjectConfig": "多彩蜜蜡改跨界石_Nangua.json",
"ProjectFiles": [
"多彩蜜蜡改跨界石.nut"
],
"ProjectRunFunc": "_Dps_Cross_Stone_Main_"
}

View File

@@ -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();
}
}

Some files were not shown because too many files have changed in this diff Show More