功能脚本组织
什么是功能脚本
功能脚本可以简单理解成“组件化的脚本”:把一整套玩法、任务、活动、检测、奖励或公共逻辑拆出来,做成可以重复调用的功能模块。
这样做的目的通常有三个:
- 把大功能拆开,避免单个脚本越来越长
- 让多个入口复用同一套逻辑
- 把“入口脚本”和“功能实现”分层维护
如果你在多个地方都要用同一套判断、发奖或结算流程,就很适合把它整理成功能脚本,而不是每个入口都复制一份。
如果一个功能已经开始出现多个入口、多个标签、多个判断分支,继续全塞在同一个 NPC 文件里,后面会越来越难改。
QuestDiary 是什么
MirServer\Mir200\Envir\QuestDiary\ 是各类自定义游戏功能的脚本目录,里面通常放游戏玩法、任务、功能和数据相关脚本。
这意味着:
QuestDiary不是单一任务目录- 它更像“自定义功能脚本总目录”
- 需要把公共功能抽出来时,优先考虑往这里组织
实际项目里,常见会按功能继续细分子目录,例如活动、排行、登录逻辑、公共模块等。
功能脚本和普通脚本的区别
使用 #CALL 前,最好先把这两类脚本分清。
| 脚本类型 | 常见位置 | 主要作用 | 常见写法 |
|---|---|---|---|
| 普通脚本 | Market_Def、NPC 对应脚本 | 接玩家点击、显示对话、提供按钮入口 | 常直接从 [@Main] 开始 |
| 功能脚本 | QuestDiary 功能目录 | 处理一整套功能逻辑,供多个入口复用 | 常先写一个总入口,再在内部拆多个标签 |
可以直接这样理解:
- 普通脚本更像入口层
- 功能脚本更像实现层
在服务端样本里,Market_Def 下不少 NPC 脚本本身只负责入口,进入后马上用 #CALL 转到 QuestDiary 里的目标脚本。QManage 里也能看到一组连续的 #CALL,把不同功能挂到各自的功能脚本中。
功能脚本里常见的组织方式如下:
[@功能脚本]
{
; 脚本主流程
[@标签1]
[@标签2]
}
这种写法的意思是:
- 外部通常先调用
@功能脚本这个总入口 - 具体判断、发奖、提示、跳转,再继续拆到内部标签里
- 一个功能脚本文件中,可以集中维护整套功能流程
#CALL 调用功能脚本
#CALL 用来调用 QuestDiary 功能脚本目录中的脚本。
语法
#CALL <脚本路径> <@标签名>
示例
[@Main]
#SAY
<每日奖励/@领取每日奖励>
[@领取每日奖励]
#ACT
#CALL [\每日奖励\每日奖励.txt] @每日奖励
[@每日奖励]
{
#IF
CheckLevelEx > 50
#ACT
GOTO @发放奖励
#ELSEACT
GOTO @等级不足
[@发放奖励]
#ACT
SENDMSG 7 你领取了今日奖励
[@等级不足]
#SAY
等级不足,暂时无法领取
}
使用这类写法时,可以重点关注这几点:
- 入口按钮仍然可以写在当前 NPC 脚本里
- 真正的功能实现放到
QuestDiary下独立维护 - 后续其他 NPC、QManage、活动入口也能继续调用同一份脚本
#CALL 的组织约定
下面这类写法,表示执行到当前标签时才触发外部脚本:
[@领取每日奖励]
#ACT
#CALL [\每日奖励\每日奖励.txt] @每日奖励
这类写法更适合按钮点击后才进入的功能,或者满足条件后才继续执行的流程。
下面这类写法,表示把公共脚本集中放到当前脚本的公共加载入口:
[@打开活动面板]
#ACT
GOTO @活动入口
[@领取活动奖励]
#ACT
GOTO @活动入口
[@自动加载]
#CALL [\活动系统\活动入口.txt] @活动入口
这种组织方式更适合公共脚本、公共配置或常量文件。重点不是在多个标签里反复 #CALL 同一目标,而是先集中加载一次,再让各个标签继续走当前脚本自己的流转。
DELAYCALL 延时调用
DELAYCALL 用于延迟一段时间后,再去执行目标标签。
语法
DELAYCALL <毫秒> <@标签名> [参数1] [参数2] [参数3] [参数4] [参数5]
作用
它适合处理“先等待一下,再进入后续逻辑”的场景。
和 DELAYGOTO 一样,DELAYCALL 也支持最多 5 个上下文参数传输。这 5 个参数都是可选的;需要传参时再补上,不需要传参时只写前面的毫秒和标签名即可。传入后的参数可通过 <$STR(S_CONTEXT[0-4])> 读取,避免临时变量被覆盖。
示例
[@Main]
#SAY
1 秒后进入每日奖励结算...
#ACT
DELAYCALL 1000 @每日奖励结算 <$STR(S_奖励类型)> <$USERNAME>
[@每日奖励结算]
{
#ACT
SENDMSG 7 "即将为 <$STR(S_CONTEXT[1])> 处理 <$STR(S_CONTEXT[0])> 奖励"
}
如果你的需求是“延时后跳到当前脚本中的另一个标签继续菜单流转”,优先看 DELAYGOTO。
如果你的需求是“延时后进入一段功能逻辑”,更适合使用 DELAYCALL。
GOTO 和 CALL 怎么区分
使用这两组命令时,最容易混淆的是“当前脚本内跳转”和“跨脚本调用”的边界。
可以直接按下面的方式区分:
GOTO/DELAYGOTO:解决当前脚本里的标签流转问题#CALL/DELAYCALL:解决功能拆分和脚本复用问题
如果目标仍然是当前脚本里的其他标签,优先用 GOTO。
如果目标已经是独立功能脚本,尤其是 QuestDiary 目录下的功能模块,优先用 #CALL。
一个常见组织方式
比较稳妥的功能组织方式通常是:
- 当前 NPC 脚本只负责入口对话和按钮
- 按钮点击后,用
#CALL进入QuestDiary中的功能脚本 - 功能脚本内部再继续拆成多个标签
- 需要延时执行时,再决定用
DELAYGOTO还是DELAYCALL
这种分法能把“入口显示”和“功能实现”拆开,后续维护会轻很多。
使用建议
- 一个功能只有一个入口、逻辑也很短时,不必强行拆到
QuestDiary - 一个功能开始被多个入口复用时,就应尽早拆成功能脚本
- 跨脚本调用前,先统一目录结构和标签命名,避免后续调用目标越来越乱
- 需要当前脚本内跳转时,用
GOTO系列;需要功能模块化时,用CALL系列 - 公共加载入口如果只负责统一
#CALL,通常放在脚本后部会更清晰,便于和前面的流程标签区分开
