using System; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using AibisDream.Framework; using AibisDream.Kit; using AibisDream.Utility; using UnityEngine; using Yarn.Unity; namespace AibisDream { public class LineSyncToken { private Action _nextStep; private readonly ITextShown _textShown; public LineInfo lineInfo; public LineSyncState state; public CancellationTokenSource AdvanceDelayToken { get; private set; } public event Action SkipTextAnima; public event Action OnAdvance; public static LineSyncToken CreateAuto(LocalizedLine lineInfo, Action nextStep) { return new LineSyncToken(lineInfo, nextStep, new AutoNextTextShown()); } public static LineSyncToken CreateImmediate(LocalizedLine lineInfo, Action nextStep) { return new LineSyncToken(lineInfo, nextStep, new ImmediateTextShown()); } public static LineSyncToken CreateDefault(LocalizedLine lineInfo, Action nextStep) { return new LineSyncToken(lineInfo, nextStep, new DefaultTextShown()); } public LineSyncToken(LocalizedLine lineInfo, Action nextStep, ITextShown textShown) { this.lineInfo = LineInfo.Generate(lineInfo); _nextStep = nextStep; _textShown = textShown; } public void TrySkip() { if (state == LineSyncState.PlayText) { SkipTextAnima?.Invoke(); SkipTextAnima = null; } else { Debug.Log($"line{lineInfo.lineId} 状态为{state}, 不能跳过文本"); } } public void TryAdvance() { if (state == LineSyncState.WaitForAdvance || state == LineSyncState.WaitForAutoAdvance) { AdvanceDelayToken?.Cancel(); Advance(); Dispose(); } else { Debug.Log($"line{lineInfo.lineId} 状态为{state}, 不能向前推进"); } } public void ForceAdvance() { if (state != LineSyncState.Advanced) { // 先中断延时 AdvanceDelayToken?.Cancel(); // 推进 Advance(); Dispose(); } } private void Advance() { EnumEventSystem.Global.Send(DialogEventEnum.LineEnd); OnAdvance?.Invoke(); OnAdvance = null; _nextStep?.Invoke(); state = LineSyncState.Advanced; } private void Dispose() { _nextStep = null; SkipTextAnima = null; } public async void TextShown() { AdvanceDelayToken = new CancellationTokenSource(); EnumEventSystem.Global.Send(DialogEventEnum.LineShown); await _textShown.OnTextShown(this); } public void TextStart() { if (state != LineSyncState.Default) { Debug.LogWarning($"line{lineInfo.lineId} 状态为{state}, 不能开始文本"); return; } state = LineSyncState.PlayText; EnumEventSystem.Global.Send(DialogEventEnum.LineStart, lineInfo); } } public struct LineInfo { public string lineId; public string lineText; public CharacterVo character; public bool isAutoSkip; public bool isOption; public static LineInfo Generate(LocalizedLine dialogueLine) { return new LineInfo { lineId = dialogueLine.TextID, lineText = dialogueLine.TextWithoutCharacterName.Text, character = dialogueLine.GetCharacterVo(), isAutoSkip = dialogueLine.IsAutoSkipLine(), isOption = false }; } public string GetRecordLine() { const string allowedTagsPattern = @"(<|{)(?!\/?(b|i|color)(?=>|\s|=))[^(>|})]+(>|})"; var res = Regex.Replace(lineText, allowedTagsPattern, ""); return res; } public int CalcAutoDelayTime() { var delayTime = lineText.RemoveRichTextTags().Length * ConstRef.CharacterDelayTime; return CommonUtil.Limit(delayTime, ConstRef.LineMaxDelay, ConstRef.LineMinDelay); } public int CalcDelayTime() { return ConstRef.FixedDelay; } } public interface ITextShown { Task OnTextShown(LineSyncToken token); } public class DefaultTextShown : ITextShown { public async Task OnTextShown(LineSyncToken token) { try { token.state = LineSyncState.AdvanceDelay; await Task.Delay(token.lineInfo.CalcDelayTime(), token.AdvanceDelayToken.Token); token.state = LineSyncState.WaitForAdvance; await Task.Delay(token.lineInfo.CalcAutoDelayTime(), token.AdvanceDelayToken.Token); token.state = LineSyncState.WaitForAutoAdvance; } catch (TaskCanceledException) { // 跳过,无事发生 } } } public class AutoNextTextShown : ITextShown { public async Task OnTextShown(LineSyncToken token) { try { token.state = LineSyncState.AdvanceDelay; await Task.Delay(token.lineInfo.CalcDelayTime(), token.AdvanceDelayToken.Token); token.state = LineSyncState.WaitForAutoAdvance; token.TryAdvance(); } catch (TaskCanceledException) { // 跳过,无事发生 } } } public class ImmediateTextShown : ITextShown { public async Task OnTextShown(LineSyncToken token) { token.state = LineSyncState.WaitForAdvance; } } public enum LineSyncState { Default, PlayText, AdvanceDelay, WaitForAdvance, WaitForAutoAdvance, Advanced, // 仅限DialogController使用 InOption } }