13674307070 发表于 2026-8-20 08:47:39

沙巴克系统是传奇的灵魂,VV 引擎用四个触发加二十个接口

一、攻城战的生命周期:引擎管核心,脚本管节点
理解沙巴克系统,先要理解攻城战的生命周期。一场攻城战从开始到结束,大致经历四个阶段:备战、开战、争夺、结算。

备战阶段是攻城开始之前,行会需要报名参加攻城,进入攻城列表。这个阶段引擎做的是管理报名名单,脚本可以做的是控制报名条件、收取报名费、设置攻城时间。

开战阶段是攻城战正式开始,地图进入战争状态,攻守双方进入战斗。引擎在这个时候触发 OnCastleWarStart,脚本可以在这里做开战公告、刷出攻城怪物、开启特殊机制、给参战玩家加 buff。

争夺阶段是攻城战的核心,双方在城堡内外厮杀,争夺宫殿的归属权。当一方行会占领宫殿达到一定时间(这个时间引擎底层配置),就触发 OnBeforeCaptureCastle 和 OnCaptureCastle,城堡易主。这个阶段引擎管的是占领判定和归属切换,脚本可以在易主的瞬间做自定义逻辑 —— 比如新占领方获得临时增益、旧占领方获得复仇 buff、宫殿刷新、全服公告。

结算阶段是攻城战结束, OnCastleWarEnd 触发,最终占领城堡的行会成为沙巴克主人,享受直到下次攻城前的所有权和福利。脚本在这里做结算奖励、统计战绩、发放奖励、关闭战争状态。

四个阶段,引擎管的是最核心的机制 —— 战争状态的切换、占领判定、归属切换、城门城墙的血量和修复。脚本管的是外围的自定义逻辑 —— 公告、奖励、buff、特殊机制、统计。这种分工很合理,核心机制引擎保证稳定和公平,外围逻辑脚本保证灵活和可定制。

二、触发体系:四个节点,覆盖攻城战的关键时刻
沙巴克的触发只有四个,但每一个都卡在了最关键的节点上。

OnCastleWarStart 是攻城开始的触发,无参数。这个触发的意义是 "战争状态开启了",你可以在这里做所有开战相关的逻辑 —— 全服公告 "沙巴克攻城战开始"、给攻城地图里的玩家加战争 buff、刷出攻城专属怪物或 NPC、开启攻城期间的特殊规则(比如死亡不掉装备、允许全服 PK)。

无参数的设计说明这个触发是全局的,不针对某个特定行会或玩家,就是 "攻城开始了" 这个事件本身。你的逻辑如果需要知道哪些行会参加,可以在触发里调 GetCastleWarList 拿名单。

OnCastleWarEnd 是攻城结束的触发,也无参数。和开始对应,做收尾逻辑 —— 发奖励、统计战绩、关闭战争状态、公告最终结果。同样是全局事件,需要知道最终谁赢了就调 GetCastleGuildName 查当前占领行会。

OnBeforeCaptureCastle 是占领城堡前的触发,带一个参数 —— 新占领行会。这个触发的 "Before" 很关键,它是在城堡易主之前触发的,意味着你可以在这里做拦截或者预处理。虽然文档里没有明确说这个触发能不能阻止占领,但按 "Before" 系列触发的惯例,应该是可以通过返回值来阻止的(和行会系统的 OnBeforeJoinGuild 类似)。如果你的版本有特殊规则 —— 比如 "某个行会在特定条件下不能占领沙巴克",就可以在这里拦截。

即使不做拦截,这个触发也很有用 —— 你可以在易主前给旧占领方发提示 "城堡即将失守",或者给新占领方做一些前置准备。

OnCaptureCastle 是占领城堡的触发,带两个参数 —— 原占领行会和新占领行会。这是沙巴克最核心的事件,城堡易主的瞬间触发。新旧行会都传给你,你可以做对比逻辑 —— 比如 "如果是从 A 行会转到 B 行会,就触发某个特殊事件",或者 "连续占领的行会获得额外奖励"。

这个触发的应用场景非常多:全服公告 "XX 行会占领了沙巴克"、给新占领行会的成员发临时 buff、给旧占领行会发复仇 buff、刷新宫殿里的 NPC 或怪物、记录占领历史、统计战绩。基本上所有和 "城堡易主" 相关的逻辑都挂在这里。

四个触发,覆盖了 "开始、结束、易主前、易主后" 四个最关键的节点。攻城战中其他的时刻(比如玩家死亡、击杀敌人、进入地图),引擎没有专门给触发,因为那些可以通过通用的死亡触发、击杀触发、地图进入触发来处理,不需要沙巴克系统单独给。

这种 "只给最关键的节点,其余通过通用触发组合" 的设计思路,和整个 VV 引擎的风格一致 —— 不做冗余的接口,能复用的就复用。

三、信息查询:城堡状态和玩家身份,两套接口各管一摊
沙巴克的信息查询分两套:一套是查城堡本身的状态,一套是查玩家的沙巴克身份。

城堡状态的查询有两种风格。 CastleInfo 是统一接口,传一个索引(1 到 6),返回对应的信息 —— 城堡名称、占领行会名称、会长名字、占领天数、是否在攻城状态、副会长名字。一个接口搞定所有基础信息,靠索引区分。

另一套是具名接口 —— GetCastleName、GetCastleGuildName、GetCastleMasterName、GetCastleViceMasterName、GetCastleCaptureDay、IsUnderWar。每个接口只做一件事,接口名就是它的用途,不需要记索引。

两套接口并存是引擎迭代的常见现象 —— 早期可能只有 CastleInfo,后来为了方便使用又加了具名接口。实际用的时候推荐用具名接口,代码可读性好,不用查索引对应关系。但如果需要同时查多个信息, CastleInfo 可能更高效(一次调用拿多个值,虽然每次只能拿一个…… 其实也差不多)。

这里有个细节: CastleInfo 的返回值类型不统一 —— 名称类返回 string,占领天数返回 number,是否攻城返回 Bool。用的时候要注意类型,不要把布尔值当字符串处理。

玩家身份的查询用 CastleIdentity,传玩家对象,返回一个整数:0 是非城堡成员,1 是城堡成员,2 是城堡老大。这个接口的应用场景很广 —— 判断玩家能不能进入沙巴克专属地图、能不能领取沙巴克奖励、能不能使用沙巴克专属功能、显示沙巴克称号或标识。

三档身份的设计很清晰 —— 不是成员、是成员、是老大。大部分情况下区分 "是不是沙巴克的人" 就够了,需要区分老大和普通成员的时候(比如只有老大能领城主奖励),用 2 来判断。

这里有个心得:判断玩家的沙巴克身份,优先用 CastleIdentity,不要自己去查玩家所在行会然后和 GetCastleGuildName 对比。因为 CastleIdentity 是引擎直接返回的身份判定,可能考虑了一些边界情况(比如代理会长、临时权限),自己对比行会名可能会漏。

四、攻城列表:谁能打沙巴克,报名机制的设计
不是所有行会都能自动参加攻城战,需要先进入攻城列表。沙巴克系统给了一套管理攻城列表的接口。

AddToCastleWarList 是基础的报名接口,传行会名和天数。这个 "天数" 参数很有意思 —— 它意味着报名不是永久的,而是有有效期的。报名一次可以参加 N 天内的攻城战,过期了需要重新报名。这个设计适合做 "报名消耗" 的机制 —— 比如报名需要消耗行会金币或道具,报一次名管几天,到期再报。

AddToCastleWarListEx 是强制报名接口,支持传星号表示所有行会。文档里的示例就是用这个接口把所有行会加进列表,然后强制开启攻城战。"Ex" 版本和基础版的区别在于 "强制"—— 基础版可能有一些条件限制(比如需要满足报名条件),Ex 版本直接加,不检查条件。做 GM 命令或活动强制攻城的时候用 Ex。

GetCastleWarList 获取当前的攻城列表,返回行会列表。做报名查询、攻城名单显示的时候用。

AddAttackSabukAll 是一个特殊接口,无参数,效果是 "所有行会在当晚同时攻城"。这个接口和 AddToCastleWarListEx("*") 的区别可能在于时效 ——Ex 是加进列表(可能管几天),而这个接口只针对 "当晚"。做 "全服攻城日" 这种活动的时候用这个,所有行会当天晚上都能打沙巴克,过了就恢复正常。

攻城列表的设计,本质上是给攻城战加了一个 "准入机制"。不是谁都能来打,得先报名。这个机制让攻城战更有组织性 —— 行会需要提前准备、提前报名,而不是临时凑人就冲。同时报名可以附带条件(等级、金币、道具),增加攻城的门槛和仪式感。

实际开发中的心得:如果你的版本是 "所有行会都能打沙巴克" 的宽松模式,直接在攻城开始前调 AddToCastleWarListEx("*") 就行,不用管理报名。如果是 "需要报名" 的模式,就用 AddToCastleWarList 做报名逻辑,配合 NPC 或界面让行会主动报名。 两种模式选一种,不要混着来,不然报名逻辑会很乱。

五、城堡防御:城门、城墙、弓箭手,守方的工具
攻城战不只是攻方的事,守方也有防御工具。沙巴克系统给了城门城墙修复和弓箭手雇佣两套防御机制。

修复城门城墙用 RepairCastle,传一个索引指定修复类型 ——0 是城门,1 到 3 是三段城墙。文档里特别注明 "攻沙期间无法修复城门 / 城墙",这个限制很关键 —— 如果攻城期间还能无限修城门,攻方根本打不进来,攻城战就变成了消耗战。所以修复只能在非攻城时间用,守方在攻城开始前把城门城墙修好,战斗中就只能靠人守了。

这个设计体现了对攻城战平衡性的考虑。修复是守方的准备工作,不是战斗中的救命稻草。攻城一旦开始,城门破了就是破了,守方得用人命填,不能靠修。

雇佣弓箭手 / 卫士用 CastleArcherGen,传怪物 ID 和类型(0 是弓箭手,1 是卫士)。这个接口需要配合配置文件 SabukW.txt 使用 —— 配置文件里定义了每个弓箭手和卫士的位置、名字、血量。接口只是根据 ID 把配置里的 NPC 刷出来。

文档里有两个注意事项。第一,雇佣需要消耗城堡金币,可以在 M2 参数里设置消耗为 0(免费雇佣)。第二,弓箭手 / 卫士的尸体消失后才能再次雇佣 —— 也就是说不能在同一个位置重复刷,得等上一个的尸体消失。

雇佣弓箭手和卫士是守方的重要防御手段。攻城开始前,守方可以花城堡金币在关键位置雇佣一批弓箭手和卫士,帮助防守。这些 NPC 会自动攻击进入范围的敌人,相当于固定的防御塔。配置文件里可以自定义它们的位置和属性,做版本的时候可以根据地图设计调整防御布局。

城堡防御这一块的设计思路是:守方在战前有准备时间,可以修城门、雇弓箭手,战斗中靠这些防御设施和玩家自己防守。攻方在战斗中需要攻破城门、清理弓箭手、冲进宫殿。 这种 "战前准备 + 战中对抗" 的结构,让攻城战有了策略性,不只是人数碾压。

六、强制控制:脚本直接干预攻城进程
除了正常的攻城流程,沙巴克系统还提供了一套强制控制接口,让脚本可以直接干预攻城战的进程。

ForceStartCastleWar 强制开始攻城, ForceStopCastleWar 强制停止攻城。这两个接口不需要满足任何条件,调用就生效。文档里的示例是先把所有行会加进攻城列表,然后强制开始;停止前先判断 CastleInfo(5) 是否在攻城状态,再停止。

强制控制的应用场景很多。GM 命令手动开启 / 关闭攻城、活动期间临时开启攻城、脚本逻辑判断后自动开启(比如某个条件满足了就触发攻城)、紧急情况下停止攻城(比如出 bug 了)。

这里有个心得:强制开始攻城之前,确保攻城列表里有行会。如果列表是空的,强制开启了也没人打,虽然不会报错但攻城战没有意义。文档里的示例很规范 —— 先 AddToCastleWarListEx("*") 把所有行会加进去,再 ForceStartCastleWar。这个顺序不要反。

SetCastleGuild 是另一个强制控制接口,脚本直接设置沙巴克的归属行会。传行会名和一个参数 —— 是否忽略 OnBeforeCaptureCastle 触发。0 是不忽略(正常触发),1 是忽略(直接设置不触发)。

这个接口的应用场景是:GM 手动指定沙巴克归属、活动奖励直接把沙巴克给某个行会、脚本逻辑判定后自动转移归属。忽略触发的参数很实用 —— 如果你是 GM 手动调整,不想触发占领事件的一系列逻辑(发公告、发 buff),就设为 1,静默设置。如果是正常的脚本逻辑转移,需要触发事件,就设为 0。

强制控制接口是把双刃剑。用好了可以做很多灵活的功能,用不好会破坏攻城战的公平性和仪式感。建议只在 GM 命令、活动脚本、异常处理这些场景使用,正常的攻城流程还是让引擎按规则走。

七、几个实际使用中的心得和坑
把沙巴克系统过完,结合传奇类游戏的开发经验,总结几个心得和容易踩的坑。

第一,攻城开始和结束的触发是全局的,逻辑里要自己查具体信息。 OnCastleWarStart 和 OnCastleWarEnd 都不带参数,你需要在触发函数里自己调 GetCastleWarList、GetCastleGuildName、CastleInfo 这些接口来获取具体信息。不要指望触发参数告诉你谁参加了、谁赢了。

第二,占领触发的行会参数类型要注意。 文档里 OnBeforeCaptureCastle 的 GuildName 参数类型写的是 object, OnCaptureCastle 的 OldGuild 和 NewGuild 也是 object。但实际可能是行会名称字符串,也可能是行会对象。用的时候先打印一下确认类型,不要直接当字符串用或者当对象调方法。

第三,CastleInfo 的返回值类型不统一。 名称是 string,天数是 number,是否攻城是 Bool。用的时候注意类型转换,尤其是在做字符串拼接的时候,number 和 Bool 要转成 string。

第四,攻沙期间不能修城门城墙,这个限制要提前告诉玩家。 如果守方不知道这个限制,攻城开始了还在想修城门,发现修不了就会困惑。在 NPC 说明或者游戏提示里讲清楚,避免玩家误解。

第五,雇佣弓箭手需要配置文件支持。 CastleArcherGen 不是随便传个 ID 就能刷的,需要 SabukW.txt 里配置了对应 ID 的弓箭手 / 卫士信息(位置、名字、血量)。做版本的时候先把配置文件配好,再用接口调用。而且雇佣消耗城堡金币,如果城堡金币不够会雇佣失败,要做好失败处理。

第六,强制开始攻城之前先加攻城列表。 这个前面说了,再强调一次 —— 空列表开启攻城没有意义。规范的顺序是:先加列表(所有行会用 AddToCastleWarListEx("*"),指定行会用 AddToCastleWarList),再 ForceStartCastleWar。

第七,SetCastleGuild 的忽略触发参数根据场景选。 GM 手动调整设为 1(静默),脚本正常转移设为 0(触发事件)。不要一概而论,不然要么该触发的没触发(公告没发、buff 没加),要么不该触发的触发了(GM 调个测试结果全服公告了)。

第八,沙巴克身份的判断用 CastleIdentity,不要自己对比行会名。 前面也说了,引擎的身份判定可能考虑了边界情况,自己对比容易漏。而且 CastleIdentity 直接返回三档身份,比 "查玩家行会→查沙巴克行会→对比" 三步要简洁高效得多。


13674307070 发表于 2026-8-20 08:48:18

挠头的500贡献

13674307070 发表于 2026-8-20 08:58:09

挠头的500贡献
页: [1]
查看完整版本: 沙巴克系统是传奇的灵魂,VV 引擎用四个触发加二十个接口