using Ananta.SDK.Network;
using Ananta.SDK.Serialization;
using Ananta.Server.ClientData.Client4229938;
using Ananta.Server.Protocol.Client4229938;
using GameMethods = Ananta.Server.RpcTypes.Client4229938.Methods.Game;
namespace Ananta.Server.Handlers.Game;
///
/// 剧情对话 / 台詞播放(2026-10-02 从捐赠端 PrivateServerPor 移植)。
///
/// ★ 链路极短:服务端只发一个 SyncShowDialog(64967628),里面就 3 样东西
/// —— dialogId、beginDialog(默认同 id)、param(15 字段,实测**全填 0** 也能播)。
/// 台词文本、镜头、语音都由客户端按自己的 DialogConfig 演,服务端不搬运内容。
///
/// ⛑ 为什么 param 敢全填 0:SyncShowDialog 是 LuaAndCSharp(有真 reader,
/// RPCDeserializeAuto.lua:4924),param 是 complex(可选,空则 null)。
/// 填全零等价于「不给上下文」,客户端按 dialogId 自己演 —— 这正是 GM 面板要的效果。
///
/// ⛑ 表查不到也能下发: 只是「列清单 + 取文案」用,
/// 客户端有自己的 DialogConfig,id 不在我们索引里照样会播。
///
internal sealed partial class GameRouter
{
/// 推一条台词(含过场镜头)。
internal static async Task<(bool Ok, string Message)> PushStoryDialogAsync(uint dialogId)
{
var target = GameAdminBridge.RequirePlayerSession();
if (target is null)
return (false, "还没有玩家会话(先用游戏客户端进世界)");
if (dialogId == 0)
return (false, "缺少 dialogId");
var session = target.Value.Session;
try
{
await session.NotifyAsync(MethodId.SyncShowDialog,
UxSerializer.Serialize(new GameMethods.SyncShowDialog4229938
{
dialogId = dialogId,
beginDialog = dialogId,
param = new GameMethods.DialogParam4229938
{
Reason = 0,
NpcTemplateId = 0,
NpcInstanceId = 0,
BlackContinue = false,
FromTaskId = 0,
FromEventId = 0,
// ★ 必须是 false:true 表示「客户端自己发起的对话」,会走另一条校验路径。
FromClient = false,
DialogCameraSpawnId = 0,
SpoonNodeId = 0,
},
}), CancellationToken.None);
var entry = StoryDialogue4229938.Find(dialogId);
session.Log.Info($"[STORY] 已下发 SyncShowDialog dialogId={dialogId} " +
$"spk={entry?.Speaker ?? "(索引里没有)"} cutscene={(entry?.IsCutscene ?? false)}");
return (true, $"已下发台词 {dialogId}{(entry is null ? "(索引里没有这条,客户端按自己的表播)" : $":{entry.Speaker} — {entry.Message}")}");
}
catch (Exception ex)
{
return (false, $"下发失败:{ex.Message}");
}
}
/// 只推语音(不带画面/字幕),配合 用。
internal static async Task<(bool Ok, string Message)> PushStoryDialogVoiceAsync(uint dialogId)
{
var target = GameAdminBridge.RequirePlayerSession();
if (target is null)
return (false, "还没有玩家会话(先用游戏客户端进世界)");
if (dialogId == 0)
return (false, "缺少 dialogId");
var session = target.Value.Session;
try
{
await session.NotifyAsync(MethodId.SyncShowDialogVoice,
UxSerializer.Serialize(new GameMethods.SyncShowDialogVoice4229938
{
// ⛑ 硬规则 120:回给客户端的标识填 pid,不是服务端 uid。
pid = Profile.PlayerPid,
dialogId = dialogId,
}), CancellationToken.None);
session.Log.Info($"[STORY] 已下发 SyncShowDialogVoice dialogId={dialogId}");
return (true, $"已下发语音 {dialogId}");
}
catch (Exception ex)
{
return (false, $"下发失败:{ex.Message}");
}
}
}