package routers import ( "context" "sync" ) var _ = sync.Mutex{} var _ = context.Background // // 敌人警戒状态机 —— 敌人 HUD(血条 + 挂它上面的警戒条)什么时候出现。 // 移植自捐赠端 7k-v0.5 的 Drmk.Handlers/Game/GameRouter.EnemyWarning.cs(2026-09-26)。 // 为什么需要它:客户端的 EnemyHUDCtrl 把潜行警戒条绑在 unitDataSet.detectionState 上 // (:85 → OnShowDetectionEffect),而 detectionState 只由 // SyncEnemyDetectStatus(68126766) 写。我们原来在 EngageSpawnedEnemyAsync 里 // 一 engage 就发 Fight(4),于是敌人一生成警戒条就在那; // 捐赠端是先注册 Idle(1),靠距离推进到 Alert(3) / Fight(4)。 // ⛑⛑⛑ 2026-09-26 02:1x 订正(重要):这套动不了血条。 // 血条的判据是 EnemyHUDCtrl.lua:551 RefreshHpVisible: // canShowHp = cfg.ShowHPBar(模板配置,服务端改不了), // shouldShowHp = ... and not HasState(unit, UnitStateConfig.HideHp) and ...。 // 整条链上没有任何一处读 detectionState。 // ⇒ "血条默认不显示、打了才显示" 要走 GameRouter.EnemyHpBar.cs(HideHp 10266)。 // 实测「改了状态机血条还是一直挂着」就是这个原因,不是状态机没生效。 // 设计要点(照抄捐赠端,别自创): // · 状态不变不发包:每 2 s 才允许重估一次,且状态相同就直接跳过。 // · 注册后两拍必须重发(Warmup=2):模型/occur 重建会把早期那份 HUD 快照丢掉, // 只发一次的话血条就永远绑不上。 // · 单轮最多 8 个:一次 tick 里不能给几十个敌人刷包。 // · stealthValue 跟 state 走:Fight=100 / Alert=50 / 其余 0(捐赠端原值)。 // · 死亡/清账必须 clear:留在 Fight 上的尸体会一直挂着一根空血条。 // ⛑⛑ 这套只改 HUD 显示时机,不碰战斗激活:engage 那五件套 // (LockTarget / FightEdict / DisarmState / BattleAiS / SyncEnemyDetect)原样不动, // 敌人会不会打人跟这里无关。关掉开关就整体回到"一生成就有血条"。 // // // 距离平方。用账本里记的敌人坐标(LocalEnemyRecord.Position)对 // 玩家最后上报的坐标;任一侧没有有效坐标时按"够远"处理(回落到 Idle)。 // func GameRouter_EnemyWarning_LocalEnemyWarningDistanceSquared(ctx context.Context, record any, state any) float32 { // Router implementation for GameRouter_EnemyWarning_LocalEnemyWarningDistanceSquared return 0 } // // 当前该显示的 detectionState。0 = 不参与(死人/已被骇成友军/坐标没就绪)。 // 2026-09-26 Wave 4:25 m 内按 AttackRange 分 Alert(3) / Fight(4), // 移植 7K EnemyWantsCloseCombat(GameRouter.EnemyAiActions.cs:23-25)的判据: // 有攻击技能且距离² ≤ AttackRange² ⇒ Fight,否则 Alert(stealthValue=50)。 // AttackRange 随 Wave 2 的目录列进来(近战 2.8 / 远程 18)——「远程单位站 20 m 外 // 开枪是 Alert 不是 Fight」现在能表达了。距离用 LocalEnemyRecord.AiPosition // 估算(追击中的敌人会贴近才升 Fight);未接敌时 = 出生点,与旧值一致。 // func GameRouter_EnemyWarning_SelectLocalEnemyWarningState(ctx context.Context, record any, state any) byte { // Router implementation for GameRouter_EnemyWarning_SelectLocalEnemyWarningState return 0 } // 同 LocalEnemyWarningDistanceSquared,但用 AI 估算位置 // (追击中的敌人 LocalEnemyRecord.AiPosition 会被 worker 逐步推向玩家)。 func GameRouter_EnemyWarning_LocalEnemyWarningAiDistanceSquared(ctx context.Context, record any, state any) float32 { // Router implementation for GameRouter_EnemyWarning_LocalEnemyWarningAiDistanceSquared return 0 } func GameRouter_EnemyWarning_SendLocalEnemyWarningStateAsync(ctx context.Context, session *GameSession, id uint64, warningState byte) any { // Router implementation for GameRouter_EnemyWarning_SendLocalEnemyWarningStateAsync var zero any; return zero } // // 注册一个敌人进状态机(spawn 之后调一次)。发 SyncEnemyDetect + Idle(1)。 // 幂等:已经注册过就直接返回。 // func GameRouter_EnemyWarning_PrimeLocalEnemyWarningAsync(ctx context.Context, session *GameSession, uid uint64) any { // Router implementation for GameRouter_EnemyWarning_PrimeLocalEnemyWarningAsync var zero any; return zero } // // 周期重估。挂在 RunEnemyAttackAiAsync 的 tick 上(100 ms 一次,内部自己节流到 2 s)。 // func GameRouter_EnemyWarning_RefreshLocalEnemyWarningsAsync(ctx context.Context, session *GameSession, state any, now int64) any { // Router implementation for GameRouter_EnemyWarning_RefreshLocalEnemyWarningsAsync var zero any; return zero } // // 死亡 / 拆账本时清掉警戒状态:发 None(0),血条收起。 // 不清的话尸体上会一直挂一根空血条。 // func GameRouter_EnemyWarning_ClearLocalEnemyWarningAsync(ctx context.Context, session *GameSession, uid uint64) any { // Router implementation for GameRouter_EnemyWarning_ClearLocalEnemyWarningAsync var zero any; return zero }