你碰到的这个“脚本死循环”报错,根源在 `[@GetExp]` 这个触发段里。系统每获得一次经验,就触发一次这个段,而段里连着三个 `goto` 命令,其中 `goto @宗派经验` 这一跳,跳过去执行完,正常情况下应该用 `break` 或 `return` 把流程断掉,但你没有,导致程序逻辑乱套,在特定条件下疯狂循环,M2检测到1秒内执行了多次相同脚本,就直接报错中断。下面把修复步骤拆开,每一步干什么、为什么这么干,全给你讲清楚。
**第一步:先看报错信息,定位问题在哪**
报错原文是:
`[脚本死循环] NPC:QFunction 位置:0(0:0) 命令:GOTO @宗派经验 1秒1次`
翻译成人话:
- **NPC:QFunction**:出问题的脚本在 `QFunction-0.txt` 文件里。
- **位置:0(0:0)**:脚本坐标显示0,说明不是地图上的NPC,是功能触发段。
- **命令:GOTO @宗派经验**:循环的元凶是 `GOTO @宗派经验` 这条命令。
- **1秒1次**:系统检测到这段脚本在1秒内反复执行,判定为死循环,强制中断了。
**第二步:分析 `[@GetExp]` 段的错误写法**
你的原脚本是这样的:
```
[@GetExp]
#act
goto @宗派经验
goto @烽火001
goto @冲级赛
break
```
错误点在于:
1. **`goto` 命令的逻辑**:`goto` 是跳转命令,跳到指定段执行,执行完会**返回**原跳转点的下一行继续执行。
2. **流程举例**:假设执行到 `goto @宗派经验`,程序跳去 `[@宗派经验]` 段,执行完里面的所有命令,遇到 `break` 或执行完最后一行,它会**返回到 `[@GetExp]` 段**,接着执行下一行 `goto @烽火001`。
3. **问题在哪**:问题不在返回,而在 `[@宗派经验]` 段里**可能又触发了 `[@GetExp]`**。如果 `[@宗派经验]` 里的操作(比如加经验、改文件)再次触发了经验获得事件,就会再次进入 `[@GetExp]`,形成一个套娃式的循环。
**第三步:分析 `[@宗派经验]` 段的隐患**
你的 `[@宗派经验]` 段写了文件读写操作:
```
GetRandomName ..\QuestDiary\宗师系统\经验\<$USERNAME>.txt S28
mov d21 <$STR(S28)>
MOV d22 <$GETEXP>
INC d21 <$STR(d22)>
MOV S27 <$STR(d21)>
DelTextList <$STR(S28)> ..\QuestDiary\宗师系统\经验\<$USERNAME>.txt
AddTextList <$STR(S27)> ..\QuestDiary\宗师系统\经验\<$USERNAME>.txt
```
这里面藏着两个可能触发死循环的坑:
1. **`<$GETEXP>` 这个变量**:`<$GETEXP>` 是当前获得的经验值。在 `[@GetExp]` 触发时,这个值已经产生了。但你在 `[@宗派经验]` 里又去读取它,本身没问题,问题在于后面你**删文件、加文件**这些操作,会不会间接导致系统重新计算经验或触发其他事件?某些引擎在修改文件时会触发文件监控,如果文件监控关联了经验相关事件,就可能再次进入 `[@GetExp]`。
2. **文件操作本身**:`DelTextList` 和 `AddTextList` 是对文本文件进行读写。如果这个文件被其他脚本监控,或者宗师系统本身有定时检测机制,在你修改文件的一瞬间触发了其他事件,那个事件里又有加经验的逻辑,那就会二次触发 `[@GetExp]`。
**第四步:给出正确的修改方案**
核心原则:**在 `[@GetExp]` 里不要用 `goto` 跳转,改用 `call` 或者直接写命令**,并且确保每个被调用的段都执行完就彻底退出。
**方案一:最简单粗暴的改法(推荐)**
把 `goto` 换成 `call`。`call` 命令执行完子段后会返回,但你可以用 `break` 控制流程,或者在子段里不加可能触发循环的操作。
修改 `[@GetExp]` 为:
```
[@GetExp]
#act
call @宗派经验
call @烽火001
call @冲级赛
break
```
并且在 `[@宗派经验]` 段末尾明确加上 `return` 或 `break`:
```
[@宗派经验]
#if
CHECKNAMELIST ..\QuestDiary\宗师系统\宗主名单.txt
#ACT
GetRandomName ..\QuestDiary\宗师系统\经验\<$USERNAME>.txt S28
mov d21 <$STR(S28)>
MOV d22 <$GETEXP>
INC d21 <$STR(d22)>
MOV S27 <$STR(d21)>
DelTextList <$STR(S28)> ..\QuestDiary\宗师系统\经验\<$USERNAME>.txt
AddTextList <$STR(S27)> ..\QuestDiary\宗师系统\经验\<$USERNAME>.txt
break
```
**方案二:合并命令,减少跳转**
如果 `[@宗派经验]`、`[@烽火001]`、`[@冲级赛]` 这三个段逻辑简单,可以直接把它们的命令合并到 `[@GetExp]` 里,用一个段执行完所有操作,彻底消除跳转。
```
[@GetExp]
#if
CHECKNAMELIST ..\QuestDiary\宗师系统\宗主名单.txt
#ACT
GetRandomName ..\QuestDiary\宗师系统\经验\<$USERNAME>.txt S28
mov d21 <$STR(S28)>
MOV d22 <$GETEXP>
INC d21 <$STR(d22)>
MOV S27 <$STR(d21)>
DelTextList <$STR(S28)> ..\QuestDiary\宗师系统\经验\<$USERNAME>.txt
AddTextList <$STR(S27)> ..\QuestDiary\宗师系统\经验\<$USERNAME>.txt
#IF
#ACT
; 这里是烽火001的代码
#IF
#ACT
; 这里是冲级赛的代码
break
```
**第五步:检查宗师系统其他脚本**
有时候死循环不在明处,在暗处。你需要检查:
1. **宗师系统里有没有其他 `[@GetExp]` 触发**:在整个 `QFunction-0.txt` 里搜索 `[@GetExp]`,确保只有一个,没有重复定义。
2. **文件操作是否被监控**:看看 `.\QuestDiary\宗师系统\` 文件夹下有没有其他脚本在监听文件变化,比如有脚本每隔几秒读一次这个经验文件,读到的内容变化时又触发加经验逻辑。
3. **`<$GETEXP>` 的用法**:确认这个变量在你引擎里是只读还是可写。如果是可写的,你在 `INC d21 <$STR(d22)>` 这一步修改了数值,会不会导致经验值被重新写入数据库,从而再次触发 `[@GetExp]`。
**第六步:终极测试方法**
修改完脚本后,保存文件,在M2上重新加载所有脚本(或者重启M2)。然后找个号进游戏,打一只怪获得经验,观察M2控制台有没有再次报错。如果还报,看报错内容是不是同一个位置。如果是,说明问题没根除,需要把 `[@宗派经验]` 里的代码逐行注释掉,用排除法找到具体哪一行命令触发了循环。
比如先注释掉文件删除和添加那两行,只保留变量操作,测试还循环不循环。如果不循环了,问题就出在 `DelTextList` 和 `AddTextList` 这两行上,需要研究这两个命令在你引擎里的具体机制,或者换一种方式读写文件。

