Kzeroko KTM2

NPC 战斗

目标意图、动作选择、攻击时序、武器近战、伤害限制与战斗状态。

更新于 2026/9/26 20 分钟阅读

NPC 战斗由 entity.CombatNpcEntity 与内部的 internal.ai.action 实现负责。这些类型供项目内部扩展,不是公开战斗门面。本页说明自主行动的 NPC;玩家控制的变身仍使用玩家的战斗输入。

它做什么

战斗 NPC 通过威胁传感器选择目标。NpcCombatController 调度法术与支援动作,NpcWeaponMeleeController 则在 supportsNpcCombat() 与 NpcConfig.normalMeleeAttacks() 允许时持续驱动武器近战。普通近战没有动作索引、编写的执行时序或额外冷却;特殊动作冷却期间仍可挥击。

Better Combat 与 Better Mob Combat 是外部依赖。本模组调用它们的武器注册表、就绪检查、攻击范围与起手接口,不在这里定义其武器 JSON 默认值。本地兼容层还会将 BaseNpcEntity 和 SimpleCloneEntity 的 Better Mob Combat 生物额外冷却替换为 0。

运行时模型

目标意图与保护

NpcThreatSensor 在传感器回调中,以 20 tick 为快速校验间隔、40 tick 为完整选择间隔。这些是内部扫描门槛,不代表大脑每 tick 都调用此传感器。近期攻击者记忆持续 600 tick(30 秒),最多保存 6 个 UUID;超量时按插入顺序移除首项。刷新已有项只延长有效期,不改变插入位置。未加载、无效或已不允许反击的实体会被移除。

意图优先级支援追击距离限制
HOSTILE1无
FAVOR_HIT2有
PROTECT3有
REVENGE4无
COMMAND5无

高优先级意图取代低优先级意图。同级候选通常必须满足 newDistanceSquared + 1 < oldDistanceSquared,比较的是距离平方。新的 FAVOR_HIT 候选可直接替换另一个协助攻击目标,不要求上述距离优势。指令目标始终通过保护模式的意图检查,但仍须满足目标有效性和伤害限制。

跟随时,ASSIST 接受全部普通意图,DEFEND 排除 FAVOR_HIT,PASSIVE 只接受 REVENGE。非跟随模式不受这些模式限制。跟随者的常规自卫扫描不会主动选择周围尚未交战的敌对生物;正在攻击 NPC 的生物仍可入选。主动敌对关系由阵营规则决定,NPC 的目标访问器会排除友好阵营实体。

保护对象包括 protectRange 内可见的友好玩家与阵营盟友,以及 max(protectRange, followRange) 内仍存活的跟随主人;主人无需出现在可见列表中。威胁必须同时接近保护对象和 NPC。令 A = max(protectRange, aggroRange),威胁距保护对象不得超过 A,距 NPC 不得超过 max(followRange, protectRange, aggroRange) + A。持续支援时仍受这个 NPC 距离限制。

保护评分输入数值
阵营盟友 / 友好玩家 / 跟随主人优先级1 / 2 / 3
优先级乘数1000000
正在以保护对象为目标的生物10000
保护对象记录的攻击者9000
记录的攻击者也正在瞄准保护对象额外 80
距离扣分减去威胁与保护对象的距离平方

FAVOR_HIT 要求被跟随或受信任玩家的最近攻击时间有效,且距今不超过 100 tick(5 秒)。它取玩家攻击的实体,使用相同的距离范围,并按 priority × 1000000 + 9000 - targetToPlayerSquared - npcToTargetSquared × 0.25 评分。

当前目标必须存活、同世界、可被选中且符合意图;创造模式和旁观模式玩家被排除。只要目标可见且仍有效,普通仇恨距离检查不会将其清除。不可见的复仇目标可保留至攻击者记忆到期;其他不可见目标必须仍在 aggroRange 内。目标变化或可见时更新最后已知位置。追踪不可见目标时使用当前维度内记录的方块位置,没有记录则停留。

动作选择与时序

动作集仅根据子类注册内容延迟构建,空动作集也能进行纯武器近战。空白或重复 ID 抛出异常,不再设默认动作索引。客户端与服务端必须按相同顺序注册相同动作。

每次控制器 tick 先递减各动作冷却与正值全局冷却。NPC 死亡、睡眠、使用物品、忙于 POI 或播放表情动作时,会停止导航,并以 BLOCKED 结束当前动作。目标效果接管大脑时改为调用 interrupt(),不执行控制器 tick,因此该路径不会推进这些冷却。

空闲且全局冷却结束时,控制器逐个评估未冷却动作:满足最低已完成挥击数,选择同世界的存活目标,调用 isInRange、canStart,并要求评分为有限正数。先比较优先级,再比较评分;完全相同则保留先注册的动作。rangeOverride() 为正时替代 attackRange,否则使用配置范围。选中的动作预留下一个允许衔接的近战时点:等待期间不发起新挥击,每 tick 重新检查动作资格。

字段类型默认值作用
castTicksinteger必填CASTING 阶段的非负控制器 tick 数。为零则直接进入 EXECUTING。
executeTicksinteger必填非负;控制器至少执行 1 tick。
triggerTickinteger必填从 0 开始的执行 tick,范围为 0 至 max(0, executeTicks - 1);越界抛出异常。

NpcActionTiming.instant() 为 (0, 1, 0)。开始动作时立即调用 onStart,执行阶段的处理要等后续控制器 tick。施法的最后一个 tick 切换阶段后直接返回,不在同 tick 触发执行。执行期间,onTrigger 在指定 tick 仅调用一次;ATTACK 还会向情绪管理器报告攻击。动作冷却在触发后开始,全局冷却在动作完成或停止时开始。触发前停止也会施加动作冷却,避免被取消的施法每 tick 重试。

控制器保存选定目标的 UUID 与引用。动作进行中检查目标存活、移除状态及世界,以 45 度转向限值注视目标并推进时序。它不会逐 tick 重新执行当前动作的 selectTarget、canStart、距离、可见性或评分检查。当前动作的 interruptible() 为 true 且全局冷却已结束时,更高优先级的合格动作可以在其允许的近战衔接时点抢占;同优先级不能抢占。阻塞、显式中断和目标丢失仍可停止不可抢占动作。

导航锁在开始及施法、执行阶段生效。移动锁只在执行阶段将前进和侧向输入清零,不会重置速度。停止原因为 FINISHED、TARGET_LOST、INTERRUPTED、BLOCKED。onStop 收到保存的目标(可能为空),随后清除跟踪状态。触发前停止会消耗编写的动作冷却;已施加的动作冷却保留。这些动作计时器不会延迟基础武器攻击。

没有动作启动时,接近逻辑处理敌对目标。武器命中框已能触及目标时,即使特殊动作仍在冷却也会停留;针对该敌人的 ATTACK、DEBUFF 已入选且正在等待近战衔接时点时也会停留,否则继续接近。HEAL、BUFF、CLEANSE、SHIELD 不影响攻击接近停距。可见目标路径的目标容差为零,每 4–6 个控制器 tick 刷新一次,期间继续更新导航速度;法术的长射程不会让近战者停在武器范围外。

支援动作

NpcSupportSensor 请求 20 tick 的感知间隔。可见生物缓存不存在或为空时,会先清空支援列表,不再处理主人。否则,protectRange 内存活且受信任的跟随主人排在首位;随后加入范围内可见、友好且非创造/旁观模式的玩家,以及受阵营保护的存活实体,主人不重复添加。

NpcActions.builder 默认提供以下目标策略:

类型选择方式
ATTACK、DEBUFF当前敌对目标
HEAL自身与支援候选中,受伤存活实体里生命比例最低者;全部满血则无目标
SHIELD同样优先选择受伤者,没有时选自身
BUFF随机支援候选;列表为空则选自身
CLEANSE自身与支援候选中首个存活且带有有害状态效果者

控制器校验存活、世界与距离;构造器的默认条件不增加可见性或阵营限制。默认锁定导航但不锁定移动输入,不允许抢占,评分为 1 + random × 0.001。生命周期回调默认不执行操作,显式传入的回调函数不得为空。通过 target、可组合的 condition 与 score 可替换选取逻辑、添加战术条件并表达紧急程度;触发时的命中有效性由动作自行校验。

持续武器近战

NpcCombatAttributes统一定义适配武器的攻击速度基值4.0,所有NPC属性构造器均继承它。服务端生成初始化和NBT加载在原版读完保存属性后恢复此基值,不清除装备、服装或效果修饰符。现有原型NPC加载时自动使用正确基准,无须重新生成或执行属性命令;伤害、生命与其他保存属性保留原值。

个体攻击节奏通过属性修饰符调整。直接修改攻击速度基值会在下次生成初始化或加载时被覆盖。这是当前统一的属性规则,不按版本迁移,也不回退到旧数值。

武器就绪时,驱动器像分身近战一样直接调用 bettermobcombat$startUpswing。攻击蓄力、连击进度、命中框、动画数据包和武器恢复时间由 Better Mob Combat 负责。此路径不调用延迟起手,不占用调度器执行窗口,也不添加动作结束后的停顿。编写的动作结束后,只要武器恢复时间允许,同一个控制器 tick 即可恢复近战。

启动要求目标存活、可见、同世界且通过 canAttack,主手武器具有非空注册攻击列表,并存在当前攻击。NpcWeaponMeleeController.isInRange 委托 MobCombatHelper.isWithinAttackRange,不会用中心距离排除已相交的命中框。NpcCombatFacing 按分身攻击目标的方式对齐朝向与俯仰,俯仰限制为 -75–75 度。实际攻击速度必须为有限正数,最终就绪检查仍由依赖负责。

BetterMobCombatNpcSwingMixin 在推进待命中的挥击前检查:自主 NPC 未阻塞、不在编写的动作中、不受目标效果接管大脑,且仍在攻击同一个有效目标。取消会清除待命中计时、延迟任务和回调,停止武器动画,但保留已有武器恢复时间。此限定范围的桥接修正了 Better Mob Combat 1.3.1 取消后仍保留命中倒计时的行为;桥接通过合并后生成的 @Accessor 方法访问依赖字段,并调用公开的 MobAttackWindup、PlayerAttackProperties 接口。它不对其他 mixin 注入的成员声明 shadow,因为 shadow 校验早于这些成员的合并。分身及玩家控制的变身实体不进入该 NPC 桥接。伤害回调还会拒绝无效攻击者、跨世界或死亡/移除的受击者,以及过期的取消令牌。玩家须通过 canApplyOutgoingDamageToPlayer,其他存活实体须通过 canAttack,随后调用 tryAttack。

动作穿插策略

每个编写的动作通过 meleePolicy() 指定策略。NpcMeleePolicy.DEFAULT 等于 afterSwings(0):不要求挥击次数,但等待当前命中时点后再进入动作。

策略字段类型默认值含义
transitionenumAFTER_IMPACTAFTER_IMPACT 等待待执行的命中;AFTER_RECOVERY 还等待武器恢复;INTERRUPT 取消未完成的挥击。三者都不会清除武器恢复时间。
minimumSwingsinteger0自上一个编写的动作开始以来,已提交的挥击次数。必须非负,负值抛出异常。

NpcMeleePolicy.afterSwings(2) 要求完成两次挥击后才允许动作。挥空也算完成攻击;一次挥击不论命中多少目标都只计一次,被取消的起手不计数。任何编写的动作开始时都会消耗这份共享计数。敌对目标变化时重置本次交战计数,并使排队中的旧目标伤害失效。状态按 NPC 独立保存,仅在运行时存在,不新增网络或 NBT 字段。

正数门槛明确要求先近战,不应套在必须随时可用的纯远程法术或紧急治疗上;这类动作应设零门槛,并配合优先级、生命或距离条件。中断策略可取消武器起手,但若要替换正在施放的法术,还要求该法术允许 interruptible()。所有编写的动作都会在自身持续期间暂停新的基础挥击。快捷物品自动使用会等待命中时点,并要求没有编写的动作正在执行。

伤害许可与闪避

玩家敌意按攻击尝试累计,而非只按生命损失累计。普通伤害路径先拒绝无敌、非正伤害和友军伤害,再记录攻击尝试,之后才检查无敌帧与闪避。弹射物穿透回避路径仅在成功回避时记录尝试。

对 CombatNpcEntity,来自存活、非创造/旁观模式且可被选中玩家的尝试会刷新 600 tick(30 秒)记忆。当前传入伤害决定本次用于比较累计次数的门槛:

当前潜在伤害所需尝试次数
不超过 25
大于 2 且不超过 72
大于 71

一旦解锁复仇,状态保留至该玩家记忆到期。不同伤害档位没有独立计数器。敌意表不持久化;NPC 的普通目标和攻击者访问器会隐藏受信任玩家。

对玩家造成的伤害仅允许命中保存的战斗目标,或与该目标玩家同阵营的玩家。没有保存目标时,普通目标必须为玩家,再采用同样规则。创造模式、旁观模式、死亡及其他不可被选中的玩家均被拒绝。NpcTargetDamageGuard 会同时检查攻击者与直接来源所归属的 NPC;此限制独立于队伍友军伤害保护。

闪避是通过 setDodgeableNpc 明确启用的实体能力。默认闪避要求实体存活、清醒、未使用物品、未骑乘,伤害来源可回避,并符合依赖的空中翻滚许可。它朝远离来源的方向移动,无法确定时向后,叠加翻滚速度并授予翻滚回避窗口。成功闪避设置 dodgeCooldownTick,再用刚授予的窗口检查当前伤害。

本地闪避计算数值或行为
基础翻滚距离3 加 Combat Roll 配置增量,最小 1
水平速度距离 × 0.475
水中乘数min(waterHeight, 1) × 0.5
熔岩乘数0.3
比草方块更滑的地面乘以草方块与当前地面滑度比的平方
窗口时长Combat Roll 时长 × 速度比,向上取整,最少 1 tick
速度比限制0.5–2.5,基准为 3 × 0.475
获取依赖时长失败回退为 8 tick

本地窗口在到期 tick 仍然有效。绕过无敌或效果的伤害,以及虚空、溺水、饥饿、摔落、窒息和火焰伤害,不能通过此工具回避;可回避伤害还必须有攻击者或直接来源。表中描述的是本地适配,不是外部 Combat Roll 的配置默认值。

约定

配置与持久化

字段类型默认值作用
normalMeleeAttacksbooleantrue仅对具备战斗能力的 NPC 开启持续武器近战。
attackRangedouble2.4默认动作范围,单位为方块;构造器不限制。
protectRangedouble12保护和支援半径,单位为方块;构造器不限制。
aggroRangedouble12普通敌对及不可见目标范围,单位为方块;构造器不限制。
dodgeCooldownTickinteger30成功闪避后的冷却;限制为非负。

这些范围是配置输入,不是经过安全校验的半径;平方计算不会拒绝负值。表中列出 NpcConfig 默认值,具体 NPC 可以覆盖。

战斗冷却字段直接存入实体 NBT:

字段类型默认值作用
CombatGlobalCDinteger0剩余特殊动作冷却;加载的负值归 0,不阻止近战。
CombatActionCDlist of compounds空已知动作 ID 的正值冷却;忽略未知 ID 和非正 tick。
CombatActionCD[].ActionIdstring空精确匹配当前动作集 ID;稳定 ID 可在动作重排后保留冷却对应关系。
CombatActionCD[].Ticksinteger0剩余正值 tick;重复的有效 ID 后项覆盖前项。

控制器不保存动作阶段、已执行时长、捕获目标及是否已触发标记,加载不会续播中断攻击。跟踪器同步动作索引、阶段、序号、目标 UUID、施法开始时间和时长;阶段变化会递增序号。因此两端必须以确定且一致的顺序注册动作。

动画归属

普通动作默认使用 charge_<id>、接续的 charge_loop_<id> 和 release_<id>,显式动画键按对应循环或保持标记播放。usesExternalAnimation() 可抑制编写动作的 Gecko 动画。持续近战本身不持有 Gecko 动作动画,而由 Better Mob Combat 播放;编写的动作开始时会停止残余武器收势动画,但不会缩短服务端的武器恢复计时。

模型姿态处理末尾、体型塑形之前,HumanoidPlayerPoseBridge 先检查已采样的变身宿主姿态。否则,自主 NPC 路径要求 Player Animator 正在播放,普通近战已启用且 Better Mob Combat 战斗动画活跃;屏幕内姿态是例外。躯干、头、四肢分别映射到 torso、head、right_arm、left_arm、right_leg、left_leg,缺少的骨骼被跳过。自主动画通道以已求得的 Gecko 姿态回退,并将弯曲近似为刚体旋转。

手持物品偏移通过矩阵施加:位置除以 16,再按 Z/Y/X 顺序旋转,不累积到物品骨骼上。变身的 body 通道使用独立外层矩阵;应用宿主各部位前,桥接器会恢复 Gecko body 的绑定变换。扩展渲染器时,应分别处理动画归属与服务器命中权限。

扩展方式

在 NPC 子类覆盖 registerNpcActions(NpcActionSet.Builder),保持 ID 稳定、唯一,注册顺序确定。实现 NpcAction,或在目标策略合适时使用 NpcActions.builder。

成员组职责
id、type、priority、score标识、动作类别及空闲选择顺序
selectTarget、canStart、rangeOverride启动许可;命中阶段特有条件须在触发时重查
timing、cooldownTicks、globalCooldownTicks时序与两种冷却生命周期
locksNavigation、locksMovement、interruptible导航与移动锁,以及是否允许更高优先级动作抢占
castAnimationKey、executeAnimationKey、loopCastAnimation、holdExecuteAnimation、usesExternalAnimation动画归属与播放
meleePolicy所需挥击数与近战衔接方式
visualSpell可选法术表现引用
onStart、onTrigger、onStop服务器回调;所有停止原因都应释放外部资源

动作上下文提供服务器世界、NPC、当前敌对目标、可见生物及支援候选。回调同步执行,控制器不捕获异常进行恢复,应在服务器 tick 路径调用。不要将 canStart 通过等同于稍后的命中仍然合法。

构造器生成不可变定义。默认时序为 instant,优先级 0,两种特殊动作冷却均为 0,范围覆盖为 0,无视觉法术、无显式动画键,施法循环为 false,执行保持为 true,外部动画为 false。设置方法拒绝负冷却,以及非有限或负的范围覆盖。重复调用 condition 会按短路 AND 组合;再次修改构造器不会改变已生成的动作。

以下示例供已开启普通近战、并具备对应 Gecko 蓄力与释放动画的战斗 NPC 使用:至少两次武器挥击后允许穿插现有雷电法术;冷却尚未结束时会继续挥击。示例动作 ID 需要在该 NPC 上提供对应动画。

builder.add(NpcActions.builder("short_cast_lightning", NpcActionType.ATTACK)
        .timing(NpcActionTiming.of(20, 8, 7))
        .cooldownTicks(22).globalCooldownTicks(22)
        .meleePolicy(NpcMeleePolicy.afterSwings(2))
        .visualSpell(LightningStrikeSpell.INSTANCE)
        .onTrigger((ctx, target) -> LightningStrikeSpell.execute(ctx.npc(), target, 12.0F))
        .build());

伊芙莉娜的定义已采用此构造器,保留原有法术时序、伤害以及禁用普通近战的设定。新原型自行选择循环策略,构造器不会自动将施法者变成近战者。

验证

运行./gradlew.bat compileJava。在多人游戏中验证属性存档加载、持久修饰符、武器切换、动作取消和各 NPC 状态独立。

  1. 给战斗 NPC 装备已注册近战武器且不注册特殊动作,检查连续挥击节奏;再加入两次挥击后施法的动作,分别检查命中后、恢复后及中断衔接策略。
  2. 检查评分完全相同、冷却到期和零施法时长。确认只触发一次,提前取消会消耗动作冷却,特殊动作冷却期间仍继续近战。
  3. 起手期间杀死或移除目标、开始使用物品、睡眠、表情动作及目标效果接管,检查各停止路径且无残留依赖起手。
  4. 启动自定义动作后,让目标离开范围或移到墙后,确认触发回调执行预期的命中规则。
  5. 测试协助、防御、被动模式,以及受信任玩家、阵营盟友和进入挥击范围的旁观者;攻击怪物时不能伤害无关玩家。
  6. 测试低、中、高伤害尝试,包含被闪避攻击和记忆到期;被拒绝的友军伤害不得增加敌意次数。
  7. 冷却中保存,保留 ID 但重排动作后加载,确认冷却正确对应,且进行中的动作不会续播。
  8. 从另一客户端观察近战的基础及替换服装,检查手臂、物品和身体变换没有重复 Gecko 攻击动画;另外验证玩家变身仍由玩家输入控制。

相关页面