feat(偷窃系统): 添加新的偷窃系统功能

- 在FileConfig.json中添加偷窃系统配置
- 实现偷窃系统核心逻辑,包括物品消耗、奖励随机获取和每日重置
- 优化_PVF_Data_类的Seek和Get方法
- 修改Use_Item_Sp.nut中的回调函数为异步执行
- 更新测试命令功能,替换物品掉落为窗口通知
This commit is contained in:
2026-04-14 13:15:07 +08:00
parent aac9cd20c7
commit 69a2141804
5 changed files with 398 additions and 20 deletions

View File

@@ -116,13 +116,11 @@ Gm_InputFunc_Handle["点券"] <- function(SUser, CmdString) {
Gm_InputFunc_Handle["test"] <- function(SUser, CmdString) {
SUser.DropItem(26058, 200, 200);
SUser.SendItemWindowNotification([[33900, 1]]);
}
Timer.SetTimeOut(function() {
// _Dps_Equ2AvaJewel_Main_()
// local Pack = Packet();

View File

@@ -142,6 +142,11 @@
"Script": [
"换装系统/换装系统.nut"
]
},
"偷窃系统": {
"Script": [
"偷窃系统/偷窃系统.nut"
]
}
}
}

View File

@@ -0,0 +1,375 @@
/*
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 {
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;
}, 1);

View File

@@ -63,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

@@ -22,7 +22,9 @@ Cb_History_ItemDown_Func["UseSharedEffectItem"] <- function(SUser, Data) {
local ItemId = Data[15].tointeger();
if (ItemId in Cb_Use_Item_Sp_Func) {
if (SUser) {
Cb_Use_Item_Sp_Func[ItemId](SUser, ItemId);
Timer.SetTimeOut(function() {
Cb_Use_Item_Sp_Func[ItemId](SUser, ItemId);
}, 1);
}
}
}