using System.Collections.Generic;
using System.Globalization;
namespace AibisDream.MiniGame.Language
{
///
/// 将玩家可见文字按 Unicode 文本元素拆分。空白保留在布局中,但不生成粒子。
/// 随机粒子字符池不使用本类,维持现有行为。
///
public static class ExpressionTextTokenizer
{
public readonly struct Layout
{
public Layout(List visibleElements, List offsets)
{
VisibleElements = visibleElements;
Offsets = offsets;
}
public IReadOnlyList VisibleElements { get; }
public IReadOnlyList Offsets { get; }
public int Count => VisibleElements?.Count ?? 0;
}
public static List GetVisibleElements(string text)
{
var result = new List();
if (string.IsNullOrEmpty(text))
return result;
TextElementEnumerator enumerator = StringInfo.GetTextElementEnumerator(text);
while (enumerator.MoveNext())
{
string element = enumerator.GetTextElement();
if (!string.IsNullOrWhiteSpace(element))
result.Add(element);
}
return result;
}
public static Layout BuildLayout(
string text,
float characterSpacing,
float whitespaceSpacingMultiplier)
{
var visible = new List();
var positions = new List();
if (string.IsNullOrEmpty(text))
return new Layout(visible, positions);
float spacing = characterSpacing > 0f ? characterSpacing : 1f;
float whitespaceAdvance = spacing * whitespaceSpacingMultiplier;
float cursor = 0f;
TextElementEnumerator enumerator = StringInfo.GetTextElementEnumerator(text);
while (enumerator.MoveNext())
{
string element = enumerator.GetTextElement();
if (string.IsNullOrWhiteSpace(element))
{
if (visible.Count > 0)
cursor += whitespaceAdvance;
continue;
}
visible.Add(element);
positions.Add(cursor);
cursor += spacing;
}
if (positions.Count > 0)
{
float center = (positions[0] + positions[positions.Count - 1]) * 0.5f;
for (int i = 0; i < positions.Count; i++)
positions[i] -= center;
}
return new Layout(visible, positions);
}
public static int FindVisibleSequence(
IReadOnlyList source,
IReadOnlyList fragment)
{
if (source == null || fragment == null || fragment.Count == 0 || fragment.Count > source.Count)
return -1;
int lastStart = source.Count - fragment.Count;
for (int start = 0; start <= lastStart; start++)
{
bool matches = true;
for (int i = 0; i < fragment.Count; i++)
{
if (!string.Equals(source[start + i], fragment[i], System.StringComparison.Ordinal))
{
matches = false;
break;
}
}
if (matches)
return start;
}
return -1;
}
}
}