package methods // // 骑乘 / 厕所 QTE(马桶鸵鸟)契约 — 处理器在 GameRouter.ToiletQte.cs。 // ★★ 触发链(2026-09-30 从客户端反查,三条证据都实锤) // // Lua/LuaFiles/LX6/SGUI/StoreDefine/ToiletOstrichGamePanelStore.lua 就是那个 QTE 面板: // 音游式三键判定,本地常量 QTE_DURATION=2 / PERFECT_WINDOW=0.1 / // GREAT_WINDOW=0.2,满分 100(perfect 30 / normal 20)。判定纯客户端, // 服务端不参与打分。 // 面板由 C#→lua 回调打开,不是服务端推的: // LX6/Manager/CsToLuaHandler.lua 里 // module.OnStartRideQTE(riderPid, beRidenPid) ⇒ // gPanelManager:CheckShow(gPanelId.TOILET_OSTRICH_GAME_PANEL, { npcPid = beRidenPid })。 // 所以 C# 只在「骑乘成立」时才 fire ⇒ 我们要做的是让骑乘坐实。 // 面板里 SceneDataMgr.GetUnit(self.npcPid) ⇒ 被骑的那只必须是**世界里真实存在的 unit**。 // // ★★ 马桶鸵鸟 = 标准 Agent,不是 Gadget / SceneItem // NpcCatalog.json 里 5 条同名(40122028 / 40652007 / 40922011 / 42046401 / 45000446), // 同一具模型 GeneralModelId=86963029,EnemyClassType=1(与普通敌人同类), // faction 18000300「通用怪物」;AgentConfig.ToiletOstrich = 42046401 是客户端自己的常量。 // 骑乘能力由**客户端 AI 状态树**给出(MassAI.Npc.MassAetherAI.Tasks.TryOnRideAgent / // TryDownRideAgent,走 BehaviorActionConfigId 40214321 + AISetting 40213146), // 服务端不需要给任何「可骑」标记 ⇒ 现有的 NPC/敌人生成通道(GameAdminBridge.SpawnAgentAsync) // 直接就能出。 // // // AskRideAgent(67575069) —— 玩家请求骑上 agentId 这只单位。 // 形状(LuaGen/AutoGen/ClientToGameSceneDelegate.lua:3314,C2S Invoke): // SerializeBase.WritePrimitive(writer, agentid, writer.WriteUInt64, 0) ⇒ 单个 u64。 // 回包(RPCDeserializeAuto.lua:15516 midToReturnMessageReader[67575069]) // 函数体是空的 ⇒ 服务端必须回 0 字节;ReturnEmptyOkAsync 正是这个形状。 // ⛑ DefaultReturnCatalog4229938 里 没有 这个 mid ⇒ 不注册的话框架会走 // ReturnEmptyOkAsync 兜底,**形状恰好也是对的**,所以这一条不注册也未必报错; // 注册的意义是:能观测(打日志)、以及把「骑上去了」这件事变成服务端可推的状态。 // // // AskToiletOstrichFlee(67154315) —— QTE 失败时**客户端主动上报**「鸵鸟跑了」。 // 形状(ClientToGameSceneDelegate.lua:945-951):self.Notify(self, 67154315, …) // ⇒ Notify,不是 Invoke(无回包);载荷单个 u64 agentid。 // 调用点是 ToiletOstrichGamePanelStore.lua:485 的失败分支: // // gCS.BaseUnitUtils.DismountCheckScore() // gCS.LogicStateMachineManager.SendGameplayInwardSignal(unit, 5754) // gClientToGameSceneDelegate:AskToiletOstrichFlee(self.npcPid) // // 成功分支则是 SendGameplayInwardSignal(unit, 5753),**不发这条**。 // ⛑ 因为它是 Notify,不注册只会落进 DefaultUnknownNotify(无害),但服务端就永远不知道 // QTE 失败过 ⇒ 这条必须接住,否则「鸵鸟该跑不跑」。 // // // SyncRideAgent(68906846) —— 服务端权威的骑乘状态(IGameSceneToClient.SyncRideAgent // (ulong rider, ulong ridee, bool hasRideOn))。 // 形状:RPCDeserializeAuto.lua:7142 midToReader[68906846] 读 // UInt64 + UInt64 + Boolean = 17 字节,无对象 marker ⇒ Inline = true; // 且 midToExportOption[68906846] = LuaAndCSharp(lua 侧也有 reader)。 // ⛑ 是否必需尚未实测:OnStartRideQTE 也可能由 AskRideAgent 的回包路径直接 fire。 // 所以这条挂在开关 gameplay.toiletQte.pushRideState 后面,**默认关**; // 若只注册骑乘回执时面板不出,把它打开(这与自动驾驶那条「服务端推状态」是同一个模式, // 见 GameRouter.VehicleNav.cs 的包序教训:状态包要**早于**动作包)。 // // // SyncToiletPosture(68122107) —— 马桶鸵鸟的「蹲 / 起」姿态。 // 形状:ulong agentId + bool isSquatting = 9 字节(Inline = true)。 // ⛑ RPCDeserializeAuto.lua:714 把它标成 CSharpOnly ⇒ lua 侧没有 reader, // 只由 C# 表现层消费。**别指望在 lua 日志里看到它**,判据只能在实机画面上。 // ⛔ 官方还有一个「直接生成一只蹲着的马桶」的 GM 入口 // GmCreateToilet / GmCreateToiletSquatting(69686776 / 69250152,**无参** C2S GM Invoke), // 那是开发调试用的,我们不走那条路(我们的生成都在服务端,见面板的 // /api/agent/spawn)⇒ 这条 S2C 挂在开关 gameplay.toiletQte.pushPosture 后面,默认关。 //