Files
Rindro-Sqr/Project/Steal/Steal.nut
Lenheart fe05643f72 feat(偷窃系统): 实现偷窃系统基础功能
- 添加偷窃系统配置文件及脚本入口
- 实现NPC交互菜单和偷窃功能逻辑
- 添加网络通信协议处理
- 初始化偷窃配置数据并注册NPC交互
- 完善偷窃解锁动画和状态管理
2026-04-14 13:20:57 +08:00

516 lines
15 KiB
Plaintext

/*
文件名:Steal.nut
路径:Project/Steal/Steal.nut
创建日期:2026-04-07 05:19
文件用途:
*/
class StealC_Password_Box extends LenheartNewUI_CommonUi {
NumberSeq = null;
ScrollPixelOffset = 0;
ScrollBaseIndex = 0;
InitBaseIndex = 0;
ScrollStartTimeMs = 0;
RowDurationMs = 250;
RowHeight = 24;
ColumnYOffset = 0;
CorrectIndex = 0;
IsStopped = false;
IsLockedCorrect = false;
StopSnapOffsetY = 0;
PassStr = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"];
constructor(X, Y, ...) {
LenheartNewUI_CommonUi.constructor(X, Y, 30, 120);
local PhaseIndex = 0;
if (vargc > 0) {
PhaseIndex = vargv[0];
}
ColumnYOffset = -(PhaseIndex * 8);
NumberSeq = [];
for (local i = 0; i< 10; i++) {
local Info = {
num = PassStr[sq_getRandom(0, PassStr.len() - 1)],
CorrectFlag = false,
}
NumberSeq.append(Info);
}
CorrectIndex = sq_getRandom(0, 9);
NumberSeq[CorrectIndex].CorrectFlag = true;
local SeqLen = NumberSeq.len();
if (SeqLen > 0) {
local Phase = PhaseIndex % SeqLen;
if (Phase< 0) {
Phase += SeqLen;
}
ScrollBaseIndex = Phase;
InitBaseIndex = Phase;
}
ScrollStartTimeMs = Clock();
}
function GetLoopIndex(Index) {
local SeqLen = NumberSeq.len();
if (SeqLen <= 0) {
return 0;
}
local Value = Index % SeqLen;
if (Value< 0) {
Value += SeqLen;
}
return Value;
}
function UpdateScrollByClock() {
if (RowDurationMs <= 0) {
RowDurationMs = 1;
}
local RunTimeMs = Clock() - ScrollStartTimeMs;
if (RunTimeMs< 0) {
RunTimeMs = 0;
}
local PassedRows = (RunTimeMs / RowDurationMs).tointeger();
local CycleMs = RunTimeMs % RowDurationMs;
ScrollPixelOffset = sq_GetUniformVelocity(0, RowHeight - 1, CycleMs, RowDurationMs).tointeger();
ScrollBaseIndex = GetLoopIndex(InitBaseIndex - PassedRows);
}
function GetCorrectCenterY(RefY) {
local SeqLen = NumberSeq.len();
if (SeqLen <= 0) {
return Y;
}
local Delta = GetLoopIndex(CorrectIndex - ScrollBaseIndex);
local TextY = Y + 10;
local BaseY = TextY + Delta * RowHeight + ScrollPixelOffset + ColumnYOffset + 8;
local CycleHeight = SeqLen * RowHeight;
local BestY = BaseY;
local BestDist = abs(BestY - RefY);
for (local k = -2; k <= 2; k++) {
local CY = BaseY + (k * CycleHeight);
local Dist = abs(CY - RefY);
if (Dist< BestDist) {
BestY = CY;
BestDist = Dist;
}
}
return BestY;
}
function TryLockByRect(LockTopY, LockHeight, SnapExtraOffsetY) {
if (IsStopped) {
return false;
}
UpdateScrollByClock();
local LockCenterY = LockTopY + (LockHeight / 2).tointeger();
local CorrectCenterY = GetCorrectCenterY(LockCenterY);
if (CorrectCenterY >= LockTopY && CorrectCenterY <= (LockTopY + LockHeight)) {
StopSnapOffsetY = (LockCenterY - CorrectCenterY) + SnapExtraOffsetY;
IsStopped = true;
IsLockedCorrect = true;
return true;
}
return false;
}
function Show(obj) {
Parent.Img.DrawExPng(10, X, Y, 0, sq_RGBA(255, 255, 255, 90), 1.0, 1.0);
local SeqLen = NumberSeq.len();
if (SeqLen <= 0) {
return;
}
if (!IsStopped) {
UpdateScrollByClock();
}
local DrawCount = (Height / RowHeight).tointeger() + 4;
local TextY = Y + 10;
for (local i = -2; i< DrawCount - 2; i++) {
local Info = NumberSeq[GetLoopIndex(ScrollBaseIndex + i)];
local NumStr = Info.num.tostring();
local Color = Info.CorrectFlag ? sq_RGBA(157, 245, 10, 255) : sq_RGBA(150, 150, 150, 255);
local DrawY = TextY + i * RowHeight + ScrollPixelOffset + ColumnYOffset + StopSnapOffsetY;
L_sq_DrawCode(NumStr, X + 15 - LenheartTextClass.GetStringLength(NumStr) / 2, DrawY, Color, 0, 1);
}
}
}
class StealC extends LenheartNewUI_Windows {
//调试模式
// DeBugMode = true;
//不是窗口
// NoWindow = true;
//是否可见
Visible = false;
Img = Rindro_Image("steal/widget.img");
//解锁完成度
UnlockComplete = 0.0;
CorrectLockedCount = 0;
LockBoxHeight = 24;
LockSnapOffsetY = -6;
AllUnlockedTriggered = false;
AniQueue = null;
UnlockSuccessAni = null;
UnlockAniPlaying = false;
UnlockAniFinished = false;
UnlockFinalizeDone = false;
UnlockAniFrameDelay = 60;
NPCList = null;
RequestNpcId = -1;
NpcEachRegistered = false;
constructor(gObjectId, gX, gY, gWidth, gHeight, gTitleH) {
Childrens = [];
AniQueue = QuestQueue();
//注册控件
RegisterWidget();
LenheartNewUI_Windows.constructor(gObjectId, gX, gY, gWidth, gHeight, gTitleH);
RefreshUnlockComplete();
InitStealConfig();
RegisterStealNpcEach();
RegisterPack(21017002, function(Chunk) {
local Jso = Json.Decode(Chunk);
if (Jso.rawin("ret") && Jso.ret == true) {
ResetStealSession();
Visible = true;
ResetFocus();
} else {
Visible = false;
}
}.bindenv(this));
}
function RegisterWidget() {
// //关闭按钮
// local CloseButton = LenheartNewUI_BaseButton(278, 0, 11, 12, "interface/lenheartwindowcommon.img", 276);
// CloseButton.OnClick = function() {
// this.Visible = false;
// }.bindenv(this);
// Childrens.append(CloseButton);
for (local i = 0; i< 8; i++) {
local PasswordBox = StealC_Password_Box(280 + i * 30, 268, i);
AddChild(PasswordBox);
}
}
//绘制主界面
function InitStealConfig() {
NPCList = [];
Rindro_Script.GetFileData("etc/steal.etc", function(DataTable, Data) {
while (!Data.Eof()) {
local Fragment = Data.Get();
if (Fragment == "[npc list]") {
while (true) {
local Value = Data.Get();
if (Value == "[/npc list]") {
break;
}
local NpcId = Value;
try {
NpcId = Value.tointeger();
} catch (exception) {}
NPCList.append(NpcId);
}
}
}
}.bindenv(this));
}
function RegisterStealNpcEach() {
if (NpcEachRegistered) {
return;
}
if (!NPCList || NPCList.len() <= 0) {
return;
}
local RootTab = getroottable();
local EachManager = null;
if (RootTab.rawin("L_Each_Obj")) {
EachManager = RootTab["L_Each_Obj"];
} else if (RootTab.rawin("Lenheart_Get_EachObj")) {
EachManager = RootTab["Lenheart_Get_EachObj"]();
}
if (!EachManager) {
return;
}
foreach(id in NPCList) {
EachManager.AddEachForNpc(id, function(SThis) {
local StealButton = LenheartNewUI_Each_BaseButton(0, 0, 100, 21, "interface2/popup_menu/popup_back.img", 3);
StealButton.Icon = "interface2/popup_menu/popup_icon_cn.img";
StealButton.IconIdx = 27;
StealButton.Str = "偷窃";
StealButton.OnClick = function(Button) {
local NpcId = Button.Parent.NPC_Index;
RequestNpcId = NpcId;
Button.Parent.CloseAllEach();
Button.Parent.CloseWindow();
SendPackEx({
op = 21017001,
npcid = NpcId
});
}.bindenv(this);
StealButton.SetCallBackFunc(function(Button) {})
SThis.AddChild(StealButton);
}.bindenv(this));
}
NpcEachRegistered = true;
}
function ResetStealSession() {
UnlockComplete = 0.0;
CorrectLockedCount = 0;
AllUnlockedTriggered = false;
UnlockAniPlaying = false;
UnlockAniFinished = false;
UnlockFinalizeDone = false;
UnlockSuccessAni = null;
AniQueue = QuestQueue();
Childrens = [];
RegisterWidget();
RefreshUnlockComplete();
}
function GetLockTopY() {
return Y + 268 + 60;
}
function GetFocusPasswordBox() {
foreach(Child in Childrens) {
if ((Child instanceof StealC_Password_Box) && !Child.IsStopped) {
return Child;
}
}
return null;
}
function GetPasswordBoxTotal() {
local Total = 0;
foreach(Child in Childrens) {
if (Child instanceof StealC_Password_Box) {
Total += 1;
}
}
return Total;
}
function RefreshUnlockComplete() {
local Total = GetPasswordBoxTotal();
if (Total <= 0) {
UnlockComplete = 0.0;
return;
}
UnlockComplete = CorrectLockedCount.tofloat() / Total.tofloat();
}
function BuildUnlockFrameArr() {
local FrameArr = [];
local Delay = UnlockAniFrameDelay;
if (Delay <= 0) {
Delay = 60;
}
for (local i = 0; i< 13; i++) {
FrameArr.append({
ImgIndex = i,
Delay = Delay,
Pos = [0, 0]
});
}
return FrameArr;
}
function StartUnlockSuccessSequence() {
if (UnlockAniPlaying || UnlockAniFinished) {
return;
}
local FrameArr = BuildUnlockFrameArr();
UnlockSuccessAni = Lenheart_Ani("", FrameArr, [X + 269, Y + 260]);
UnlockSuccessAni.ImgPath = "steal/unlock.img";
UnlockSuccessAni.LoopFlag = false;
UnlockSuccessAni.Reset();
UnlockAniPlaying = true;
UnlockAniFinished = false;
if (AniQueue) {
AniQueue.RemoveQuest("StealUnlockSuccessAni");
AniQueue.AddQuest("StealUnlockSuccessAni", function(Name, Time) {
if (!UnlockSuccessAni) {
UnlockAniPlaying = false;
UnlockAniFinished = true;
AniQueue.RemoveQuest(Name);
FinalizeUnlockSuccess();
return;
}
UnlockSuccessAni.X = X + 324;
UnlockSuccessAni.Y = Y + 109;
UnlockSuccessAni.Show(Duration);
if (UnlockSuccessAni.State == 0) {
UnlockAniPlaying = false;
UnlockAniFinished = true;
AniQueue.RemoveQuest(Name);
FinalizeUnlockSuccess();
}
}.bindenv(this));
} else {
UnlockAniPlaying = false;
UnlockAniFinished = true;
FinalizeUnlockSuccess();
}
}
function FinalizeUnlockSuccess() {
if (UnlockFinalizeDone) {
return;
}
UnlockFinalizeDone = true;
SendStealUnlockPack();
CloseWindow();
Visible = false;
}
function SendStealUnlockPack() {
if (RequestNpcId == null || RequestNpcId <= 0) {
return;
}
SendPackEx({
op = 21017003,
npcid = RequestNpcId
});
}
function OnAllUnlockedInternal() {
R_Utils.PlaySound("GAMBLE_RESULT_GOOD");
StartUnlockSuccessSequence();
}
function CheckAllUnlocked() {
if (AllUnlockedTriggered) {
return;
}
local Total = GetPasswordBoxTotal();
if (Total <= 0) {
return;
}
if (CorrectLockedCount >= Total) {
AllUnlockedTriggered = true;
OnAllUnlockedInternal();
}
}
function CheckSpaceLock() {
if (!Visible) {
return;
}
if (!KeyPressNB.isKeyPress(7, "StealC")) {
return;
}
local FocusBox = GetFocusPasswordBox();
if (!FocusBox) {
return;
}
if (FocusBox.TryLockByRect(GetLockTopY(), LockBoxHeight, LockSnapOffsetY)) {
R_Utils.PlaySound("TRADE_OK");
CorrectLockedCount += 1;
RefreshUnlockComplete();
CheckAllUnlocked();
} else {
R_Utils.PlaySound("ALERT_2");
}
}
function DrawMain(obj) {
//灰色背景
DrawNineBoxAlpha(X, Y, 800, 600, "interface2/common/mypopup/popup.img", 0, 150);
//宝箱动画
local Ani = DrawAniEx(X + 425, Y + 230, "npc/animation/storagediamond.ani");
Ani.setImageRateFromOriginal(1.7, 1.7);
//如果未解锁
if (UnlockComplete< 1.0) {
//宝箱锁图像
Img.DrawPng(12, X + 335, Y + 120);
}
//完成度文字
local StealCompleteStr = "解锁完成度: " + UnlockComplete * 100.0 + "%";
L_sq_DrawCode(StealCompleteStr, X + 410 - LenheartTextClass.GetStringLength(StealCompleteStr) / 2, Y + 406, sq_RGBA(255, 255, 255, 255), 0, 1);
//完成度进度条
Img.DrawPng(2, X + 162, Y + 425);
setClip(X + 168, Y + 429, X + 168 + (463 * UnlockComplete).tointeger(), Y + 429 + 6);
Img.DrawPng(3, X + 168, Y + 429);
releaseClip(); //裁切结束
//解锁界面
Img.DrawPng(9, X + 269, Y + 260);
//解锁锁定条
Img.DrawPng(11, X + 269 + 8, Y + 260 + 60);
}
function Show(obj) {
DrawMain(obj);
if (AniQueue) {
AniQueue.Run();
}
//裁切遮罩密码框
setClip(X + 280, Y + 268, X + 280 + 8 * 30, Y + 268 + 120);
LenheartNewUI_Windows.Show(obj);
releaseClip(); //裁切结束
}
//逻辑入口
function Proc(obj) {
LenheartNewUI_Windows.SyncPos(X, Y);
if (!NpcEachRegistered) {
RegisterStealNpcEach();
}
CheckSpaceLock();
}
}
L_Windows_List <- [];
getroottable().rawdelete("LenheartPluginsInitFlag");
getroottable().rawdelete("EventList_Obj")
getroottable().rawdelete("Steal_Obj");
getroottable().rawdelete("L_Each_Obj");
function Lenheart_Steal_Fun(obj) {
local RootTab = getroottable();
if (!RootTab.rawin("Steal_Obj")) {
RootTab.rawset("Steal_Obj", true);
LenheartNewUI_CreateWindow(StealC, "Steal_Window", 0, 0, 800, 600, 0);
}
}
getroottable()["LenheartFuncTab"].rawset("StealFuncN", Lenheart_Steal_Fun);