Compare commits
2 Commits
4814840b2c
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
de679f5dd1 | ||
|
|
1e9616d06d |
45
Plugin.cs
45
Plugin.cs
@@ -14,11 +14,19 @@ public class CNText : BaseUnityPlugin
|
|||||||
{
|
{
|
||||||
static ConfigEntry<bool> needReplaceTextComp;
|
static ConfigEntry<bool> needReplaceTextComp;
|
||||||
static ConfigEntry<int> customTextFontSize;
|
static ConfigEntry<int> customTextFontSize;
|
||||||
|
static ConfigEntry<bool> removeUnsupportedTags;
|
||||||
|
static ConfigEntry<bool> closeUnclosedTags;
|
||||||
|
|
||||||
void Awake()
|
void Awake()
|
||||||
{
|
{
|
||||||
needReplaceTextComp = Config.Bind<bool>("OnlinePlayerUI", "NeedReplaceTextComp", true, "是否显示替换的头顶玩家名称文本组件");
|
needReplaceTextComp = Config.Bind<bool>("OnlinePlayerUI", "NeedReplaceTextComp", true, "是否显示替换的头顶玩家名称文本组件");
|
||||||
customTextFontSize = Config.Bind<int>("OnlinePlayerUI", "CustomTextFontSize", 38, "替换后头顶玩家名称字号");
|
customTextFontSize = Config.Bind<int>("OnlinePlayerUI", "CustomTextFontSize", 38, "替换后头顶玩家名称字号");
|
||||||
Harmony.CreateAndPatchAll(typeof(OnlinePlayerUIUpdatePatch));
|
removeUnsupportedTags = Config.Bind<bool>("OnlinePlayerUI", "RemoveUnsupportedTags", true, "是否移除不支持的标签");
|
||||||
|
closeUnclosedTags = Config.Bind<bool>("OnlinePlayerUI", "CloseUnclosedTags", true, "是否自动闭合未闭合的标签");
|
||||||
|
if (needReplaceTextComp.Value)
|
||||||
|
{
|
||||||
|
Harmony.CreateAndPatchAll(typeof(OnlinePlayerUIUpdatePatch));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
static Font LoadCustomFont()
|
static Font LoadCustomFont()
|
||||||
{
|
{
|
||||||
@@ -44,27 +52,6 @@ public class CNText : BaseUnityPlugin
|
|||||||
return font;
|
return font;
|
||||||
}
|
}
|
||||||
|
|
||||||
static public string ReformatName(string name)
|
|
||||||
{
|
|
||||||
name = name.Replace("<#", "</Color><Color=#");
|
|
||||||
if (name.StartsWith("</Color>"))
|
|
||||||
{
|
|
||||||
name = name.Substring(8);
|
|
||||||
}
|
|
||||||
if (name.LastIndexOf("</Color>") < name.LastIndexOf("<Color=#"))
|
|
||||||
{
|
|
||||||
name += "</Color>";
|
|
||||||
}
|
|
||||||
if (name.IndexOf("</Color>") < name.LastIndexOf("<Color=#"))
|
|
||||||
{
|
|
||||||
name = name.Substring(0, name.IndexOf("</Color>")) + name.Substring(name.IndexOf("</Color>") + 8);
|
|
||||||
}
|
|
||||||
if (name.LastIndexOf("</I>") < name.LastIndexOf("<I>") || name.LastIndexOf("</i>") < name.LastIndexOf("<i>"))
|
|
||||||
{
|
|
||||||
name += "</i>";
|
|
||||||
}
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
[HarmonyPatch(typeof(OnlinePlayerUI), "Update")]
|
[HarmonyPatch(typeof(OnlinePlayerUI), "Update")]
|
||||||
class OnlinePlayerUIUpdatePatch
|
class OnlinePlayerUIUpdatePatch
|
||||||
{
|
{
|
||||||
@@ -100,17 +87,9 @@ public class CNText : BaseUnityPlugin
|
|||||||
customText.alignment = TextAnchor.MiddleCenter;
|
customText.alignment = TextAnchor.MiddleCenter;
|
||||||
}
|
}
|
||||||
customText.color = tmpPlayerText.color;
|
customText.color = tmpPlayerText.color;
|
||||||
customText.text = ReformatName(tmpPlayerText.text);
|
customText.text = TMPToFontTextConverter.Convert(tmpPlayerText.text, removeUnsupportedTags.Value, closeUnclosedTags.Value);
|
||||||
if (needReplaceTextComp.Value)
|
tmpPlayerText.enabled = false;
|
||||||
{
|
customText.enabled = true;
|
||||||
tmpPlayerText.enabled = false;
|
|
||||||
customText.enabled = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
tmpPlayerText.enabled = true;
|
|
||||||
customText.enabled = false;
|
|
||||||
}
|
|
||||||
customText.supportRichText = true;
|
customText.supportRichText = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
153
TMPToFontTextConverter.cs
Normal file
153
TMPToFontTextConverter.cs
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
public class TMPToFontTextConverter
|
||||||
|
{
|
||||||
|
private static readonly Dictionary<string, string> ColorNameToHex = new Dictionary<string, string>()
|
||||||
|
{
|
||||||
|
{"white", "#FFFFFF"}, {"black", "#000000"},
|
||||||
|
{"red", "#FF0000"}, {"green", "#00FF00"},
|
||||||
|
{"blue", "#0000FF"}, {"yellow", "#FFFF00"},
|
||||||
|
{"cyan", "#00FFFF"}, {"magenta", "#FF00FF"},
|
||||||
|
{"gray", "#808080"}, {"grey", "#808080"}
|
||||||
|
};
|
||||||
|
|
||||||
|
public static string Convert(string input, bool removeUnsupportedTags = true, bool closeUnclosedTags = true)
|
||||||
|
{
|
||||||
|
var output = PreprocessColorTags(input);
|
||||||
|
output = ProcessColorTags(output);
|
||||||
|
output = RemoveUnsupportedTags(output, removeUnsupportedTags);
|
||||||
|
return closeUnclosedTags ? CloseUnclosedTags(output) : output;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string PreprocessColorTags(string input)
|
||||||
|
{
|
||||||
|
// 转换简写颜色标签格式(带#和不带#)
|
||||||
|
return Regex.Replace(input, @"<(#?[0-9a-fA-F]{3,8})>", match =>
|
||||||
|
{
|
||||||
|
string hex = match.Groups[1].Value;
|
||||||
|
return $"<color={(hex.StartsWith("#") ? hex : "#" + hex)}>";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ProcessColorTags(string input)
|
||||||
|
{
|
||||||
|
// 匹配所有颜色标签进行标准化处理
|
||||||
|
return Regex.Replace(input, @"<color=(.*?)>", match =>
|
||||||
|
{
|
||||||
|
string colorValue = match.Groups[1].Value.Trim();
|
||||||
|
|
||||||
|
// 处理颜色名称
|
||||||
|
if (ColorNameToHex.TryGetValue(colorValue.ToLower(), out var hex))
|
||||||
|
return $"<color={hex}>";
|
||||||
|
|
||||||
|
// 处理十六进制格式
|
||||||
|
if (colorValue.StartsWith("#"))
|
||||||
|
{
|
||||||
|
string cleanHex = colorValue.Replace("#", "");
|
||||||
|
return FormatHexColor(cleanHex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理RGBA格式
|
||||||
|
if (colorValue.StartsWith("rgba"))
|
||||||
|
return ConvertRGBA(colorValue);
|
||||||
|
|
||||||
|
return $"<color={colorValue}>"; // 保留原始格式
|
||||||
|
}, RegexOptions.IgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string FormatHexColor(string hex)
|
||||||
|
{
|
||||||
|
hex = hex.ToUpper();
|
||||||
|
switch (hex.Length)
|
||||||
|
{
|
||||||
|
case 3: // #RGB → RRGGBB
|
||||||
|
return $"<color=#{new string(hex[0], 2)}{new string(hex[1], 2)}{new string(hex[2], 2)}>";
|
||||||
|
case 6: // RRGGBB → RRGGBBFF
|
||||||
|
return $"<color=#{hex}FF>";
|
||||||
|
case 8: // RRGGBBAA → 保持原样
|
||||||
|
return $"<color=#{hex}>";
|
||||||
|
default: // 无效格式保持原样
|
||||||
|
return $"<color=#{hex}>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string RemoveUnsupportedTags(string input, bool removeUnsupported)
|
||||||
|
{
|
||||||
|
if (!removeUnsupported) return input;
|
||||||
|
|
||||||
|
// 删除不支持的标签及其内容(保留内容)
|
||||||
|
string[] unsupportedTags = { "font", "s", "u", "align" };
|
||||||
|
foreach (var tag in unsupportedTags)
|
||||||
|
{
|
||||||
|
input = Regex.Replace(input, $@"<{tag}=.*?>", "", RegexOptions.IgnoreCase);
|
||||||
|
input = Regex.Replace(input, $@"<\/{tag}>", "", RegexOptions.IgnoreCase);
|
||||||
|
}
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string CloseUnclosedTags(string input)
|
||||||
|
{
|
||||||
|
Stack<string> tagStack = new Stack<string>();
|
||||||
|
StringBuilder output = new StringBuilder();
|
||||||
|
int index = 0;
|
||||||
|
|
||||||
|
while (index < input.Length)
|
||||||
|
{
|
||||||
|
if (input[index] == '<')
|
||||||
|
{
|
||||||
|
int tagEnd = input.IndexOf('>', index);
|
||||||
|
if (tagEnd == -1) break;
|
||||||
|
|
||||||
|
string fullTag = input.Substring(index, tagEnd - index + 1);
|
||||||
|
bool isClosing = fullTag.StartsWith("</");
|
||||||
|
string tagType = Regex.Match(fullTag, @"</?(\w+)").Groups[1].Value.ToLower();
|
||||||
|
|
||||||
|
output.Append(fullTag);
|
||||||
|
|
||||||
|
// 处理标签栈
|
||||||
|
if (!isClosing && !tagType.EndsWith("/"))
|
||||||
|
{
|
||||||
|
tagStack.Push(tagType); // 记录基础标签类型
|
||||||
|
}
|
||||||
|
else if (isClosing)
|
||||||
|
{
|
||||||
|
// 正确匹配最近打开的标签
|
||||||
|
if (tagStack.Count > 0 && tagStack.Peek() == tagType)
|
||||||
|
tagStack.Pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
index = tagEnd + 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
output.Append(input[index++]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 补全剩余标签(保持打开顺序)
|
||||||
|
while (tagStack.Count > 0)
|
||||||
|
{
|
||||||
|
string tag = tagStack.Pop();
|
||||||
|
output.Append($"</{tag}>");
|
||||||
|
}
|
||||||
|
|
||||||
|
return output.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ConvertRGBA(string rgba)
|
||||||
|
{
|
||||||
|
var matches = Regex.Matches(rgba, @"\d+\.?\d*");
|
||||||
|
if (matches.Count < 3) return "#FFFFFFFF";
|
||||||
|
|
||||||
|
StringBuilder hex = new StringBuilder("#");
|
||||||
|
for (int i = 0; i < 3; i++)
|
||||||
|
hex.Append((int.Parse(matches[i].Value) * 255).ToString("X2"));
|
||||||
|
|
||||||
|
hex.Append(matches.Count > 3 ?
|
||||||
|
((int)(float.Parse(matches[3].Value) * 255)).ToString("X2") : "FF");
|
||||||
|
|
||||||
|
return $"<color={hex}>";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -27,10 +27,16 @@
|
|||||||
└─StreamingAssets
|
└─StreamingAssets
|
||||||
```
|
```
|
||||||
|
|
||||||
**其中 `StreamingAssets`为新增的目录**
|
**其中 `StreamingAssets`为新增的目录**
|
||||||
|
|
||||||
## 更新日志
|
## 更新日志
|
||||||
|
|
||||||
|
### v1.0.2
|
||||||
|
|
||||||
|
fix:
|
||||||
|
|
||||||
|
- 标签解析,重构了标签转换器
|
||||||
|
|
||||||
### v1.0.1
|
### v1.0.1
|
||||||
|
|
||||||
fix:
|
fix:
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<TargetFramework>net35</TargetFramework>
|
<TargetFramework>net35</TargetFramework>
|
||||||
<AssemblyName>stick.plugins.cntext</AssemblyName>
|
<AssemblyName>stick.plugins.cntext</AssemblyName>
|
||||||
<Product>cntext</Product>
|
<Product>cntext</Product>
|
||||||
<Version>1.0.1</Version>
|
<Version>1.0.2</Version>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
<LangVersion>latest</LangVersion>
|
<LangVersion>latest</LangVersion>
|
||||||
<RestoreAdditionalProjectSources>
|
<RestoreAdditionalProjectSources>
|
||||||
@@ -24,13 +24,14 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFramework.TrimEnd(`0123456789`))' == 'net'">
|
<ItemGroup Condition="'$(TargetFramework.TrimEnd(`0123456789`))' == 'net'">
|
||||||
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.2" PrivateAssets="all" />
|
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.2"
|
||||||
|
PrivateAssets="all" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="Assembly-CSharp">
|
<Reference Include="Assembly-CSharp">
|
||||||
<HintPath>lib/Assembly-CSharp.dll</HintPath>
|
<HintPath>lib/Assembly-CSharp.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -39,13 +40,13 @@
|
|||||||
</Reference>
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="UnityEngine.UI">
|
<Reference Include="UnityEngine.UI">
|
||||||
<HintPath>lib/UnityEngine.UI.dll</HintPath>
|
<HintPath>lib/UnityEngine.UI.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="TextMeshPro">
|
<Reference Include="TextMeshPro">
|
||||||
<HintPath>lib/TextMeshPro-1.0.55.56.0b9.dll</HintPath>
|
<HintPath>lib/TextMeshPro-1.0.55.56.0b9.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
|||||||
Reference in New Issue
Block a user