Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of LCTC Thunderstore v1.3.1
BepInEx/plugins/LCTC_Terminal.dll
Decompiled a week agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using HarmonyLib; using LCTC; using Microsoft.CodeAnalysis; using Patchers; using TMPro; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("LCTC_Terminal")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyDescription("A template for Lethal Company")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("LCTC_Terminal")] [assembly: AssemblyTitle("LCTC_Terminal")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } internal static class Dictionaries { public static readonly Dictionary<string, string> Regex = new Dictionary<string, string>(); public static readonly Dictionary<string, string> TextPostProcess = new Dictionary<string, string> { { "Welcome to the exomoons catalogue.", "歡迎使用星球目錄。" }, { "\\[There was no action supplied with the word.\\]", "[該字詞並無附帶任何動作]" }, { "\\[There was no object supplied with the action, or your word was typed incorrectly or does not exist.\\]", "[該動作未傳入任何參數,或是你輸入的字詞有誤,或是該字詞不存在]" }, { "To route the autopilot to a moon, use the word ROUTE.", "要將自動駕駛導航至星球,請輸入「ROUTE」。" }, { "To learn about any moon, use the word INFO.", "要查看任意星球資訊,請輸入「INFO」。" }, { "Buying at", "公司收購價格:" }, { "Other commands:\n\n", "其他指令:\n\n" }, { "To toggle on AND off the main monitor's map cam\n\n", "開啟/關閉主監視器的地圖攝影機\n\n" }, { ">SWITCH [Player name\\]", ">SWITCH [玩家名稱]" }, { "To switch view to a player on the main monitor\n\n", "將主監視器畫面切換至指定玩家\n\n" }, { ">PING [Radar booster name\\]", ">PING [雷達增幅器名稱]" }, { "To make a radar booster play a noise.\n\n", "讓雷達增幅器發出聲音\n\n" }, { ">TRANSMIT [message\\]", ">TRANSMIT [訊息]" }, { "To transmit a message with the signal translator\n\n", "透過訊號轉換器傳送訊息\n\n" }, { "To scan for the number of items left on the current planet.", "掃描目前星球上剩餘物品數量。\n\n>FLASH [雷達增幅器名稱]\n啟用雷達增幅器閃光。\n\n>EJECT\n緊急彈射。" }, { "Routing autopilot to (.+?)\nYour new balance is [playerCredits\\].\n\nPlease enjoy your flight.\n\n", "正在將自動駕駛設定至 $1\n你的新餘額為 [playerCredits]\n\n祝你旅途愉快。\n\n" }, { "Routing autopilot to (.+?)\nYour new balance is [playerCredits\\].\n\nGood luck.\n\n", "正在將自動駕駛設定至 $1\n你的新餘額為 [playerCredits]\n\n祝你好運。\n\n" }, { "The cost to route to (.+?) is (.+?). It is \ncurrently (.+?) on this moon.\n\nPlease CONFIRM or DENY.\n\n\n", "前往 $1 的費用為 $2。\n目前該星球狀態為 $3。\n\n請輸入「CONFIRM」或「DENY」。\n\n" }, { "You could not afford these items!", "你無法負擔這些物品!" }, { "Your balance is (\\$[0-9]+). Total cost of these items is (\\$[0-9]+).", "你的餘額為 $1,這些物品的總價格為 $2。" }, { "\\[No items stored. While moving an object with B, press X to store it.\\]", "[目前沒有儲存物品。使用 B 搬運物品時,按下 X 可將其存入倉庫]" }, { "There are ([0-9]+) objects outside the ship, totalling at an approximate value of (\\$[0-9]+).", "船外共有 $1 件物品,總價值約為 $2" }, { "There are ([0-9]+) objects in the ship, totalling at (\\$[0-9]+).", "船內共有 $1 件物品,總價值為 $2" }, { "Exception occured on terminal while setting node planet info; current node displayPlanetInfo:(.+?)", "終端設定星球資訊時發生錯誤,目前節點 displayPlanetInfo:$1" }, { "Unable to route the ship currently. It must be in orbit around a moon to route the autopilot.", "目前無法設定航線。必須先進入星球軌道才能使用自動駕駛。" }, { "Use the main lever at the front desk to enter orbit.", "請使用前方控制台的主操縱桿進入軌道。" }, { "To learn about any moon, use INFO.", "要查看星球資訊,請輸入 INFO。" }, { "The ship cannot be leaving or landing!", "飛船目前無法起飛或降落!" }, { "\\[No items in stock!\\]", "[目前沒有庫存!]" }, { "\\[ALL DATA HAS BEEN CORRUPTED OR OVERWRITTEN\\]", "[所有資料已損毀或被覆寫]" }, { "([0-9]+) purchased items on route.", "已有 $1 件購買物品正在運送中。\n" }, { "The delivery vehicle cannot hold more than 12 items\nat a time. Please pick up your items when they land!", "運輸載具一次最多只能運送 12 件物品。\n請在物品送達後儘快領取!" }, { "[No items available\\]", "[沒有可用物品]" }, { "This item is not in stock!", "此物品目前缺貨!" }, { "No data collected on wildlife. Scans are required.", "尚未收集任何野生生物資料,請先進行掃描。" }, { "No data has been collected on this creature.", "尚未收集此生物的資料。" }, { "A scan is required.", "需要進行掃描。" }, { "([0-9]+)% OFF!", "$1% 折扣!" }, { "\\(Locked\\)", "(鎖定)" }, { "\\(NEW\\)", "(新)" }, { "Price:", "價格:" }, { "mild weather", "溫和天氣" }, { "DustClouds", "塵雲" }, { "Rainy", "雨天" }, { "Stormy", "暴風" }, { "Foggy", "濃霧" }, { "Flooded", "淹水" }, { "Eclipsed", "日蝕" }, { "dustclouds", "塵雲" }, { "rainy", "雨天" }, { "stormy", "暴風" }, { "foggy", "濃霧" }, { "flooded", "淹水" }, { "eclipsed", "日蝕" }, { "Please CONFIRM or DENY.", "請輸入「CONFIRM」或「DENY」。" } }; public static readonly Dictionary<string, string> Chat = new Dictionary<string, string> { { "(.*) joined the ship\\.", "$1 已加入飛船。" }, { "(.*) started the ship\\.", "$1 已啟動飛船。" }, { "(.*) was left behind\\.", "$1 被留在外面。" }, { "(.*) disconnected\\.", "$1 已離線。" }, { "(.*) was kicked\\.", "$1 已被踢出。" } }; public static readonly Dictionary<string, string> Notes = new Dictionary<string, string> { { "Most profitable.", "最賺錢員工。" }, { "Sustained the most injuries.", "受傷最多的員工。" }, { "The laziest employee.", "最偷懶的員工。" }, { "The most paranoid employee.", "最神經質的員工。" } }; public static readonly Dictionary<string, string> Loading = new Dictionary<string, string> { { "Random seed", "隨機種子" }, { "LOADING WORLD...", "載入世界中..." } }; public static readonly Dictionary<string, string> ShipTeleporter = new Dictionary<string, string> { { "[Cooldown: (.+?) sec\\.\\]", "[冷卻時間:$1 秒]" } }; public static readonly Dictionary<string, string> PlanetInfo = new Dictionary<string, string> { { "A competitive ecosystem supports aggressive lifeforms.", "競爭激烈的生態系孕育出具攻擊性的生命體。" }, { "Arid. Thick haze, worsened by industrial artifacts.", "乾燥。濃厚霧霾,受到工業殘留影響而加劇。" }, { "Dangerous entities have been rumored to take residence in the vast network of tunnels.", "據傳廣大的地下隧道網絡中棲息著危險生物。" }, { "Dominated by a few species.", "由少數物種主導。" }, { "Ecosystem supports territorial behaviour.", "生態系支持強烈的領域性行為。" }, { "Expansive. Constant rain.", "廣闊。持續降雨。" }, { "Frozen, rocky. Its planet orbits a white dwarf star.", "冰冷且多岩。其所屬行星圍繞白矮星運行。" }, { "Humid. Rough terrain. Teeming with plant-life.", "潮濕。地形崎嶇。植物生長茂密。" }, { "Jagged and weathered terrain.", "崎嶇且風化嚴重的地形。" }, { "No land masses. Continual storms.", "無陸地。持續風暴。" }, { "Unlikely for complex life to exist", "不太可能存在複雜生命體" }, { "Where the Company resides.", "公司所在地。" }, { "Abandoned", "已廢棄" }, { "Diverse", "多樣" }, { "Eclipsed", "日蝕" }, { "Flooded", "淹水" }, { "Foggy", "濃霧" }, { "None", "無" }, { "Rainy", "雨天" }, { "Safe", "安全" }, { "Stormy", "暴風" }, { "Unknown", "未知" }, { "CELESTIAL BODY", "天體" }, { "Orbiting", "軌道運行" }, { "POPULATION", "人口" }, { "CONDITIONS", "環境" }, { "FAUNA", "生態" }, { "Weather", "天氣" } }; public static readonly Dictionary<string, string> ItemNames = new Dictionary<string, string> { { "Walkie-talkie", "$& (對講機)" }, { "Flashlight", "$& (手電筒)" }, { "Shovel", "$& (鏟子)" }, { "Lockpicker", "$& (開鎖器)" }, { "Pro-flashlight", "$& (高級手電筒)" }, { "Stun grenade", "$& (震撼彈)" }, { "Boombox", "$& (音響)" }, { "TZP-Inhalant", "$& (TZP 吸入劑)" }, { "Zap gun", "$& (電擊槍)" }, { "Jetpack", "$& (噴射背包)" }, { "Extension ladder", "$& (伸縮梯)" }, { "Radar-booster", "$& (雷達增幅器)" }, { "Spray paint", "$& (噴漆)" }, { "Weed killer", "$& (除草劑)" }, { "Belt bag", "$& (腰包)" }, { "Cruiser", "$& (巡航車)" } }; } namespace LCTC_Terminal { public static class MyPluginInfo { public const string PLUGIN_GUID = "LCTC_Terminal"; public const string PLUGIN_NAME = "LCTC_Terminal"; public const string PLUGIN_VERSION = "1.0.0"; } } namespace NewTerminalDetected { [HarmonyPatch] internal static class UIPatches { [HarmonyPatch] internal static class UniversalUIPatches { [HarmonyPostfix] [HarmonyPatch(typeof(PreInitSceneScript), "Start")] private static void OnPreInitMenuShown() { //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) if (Plugin.Flags.HasFlag(Flags.NewTerminalDetected)) { TextMeshProUGUI component = Object.Instantiate<GameObject>(GameObject.Find("Canvas/GameObject/LANOrOnline/OnlineButton/Text (TMP) (1)").gameObject).GetComponent<TextMeshProUGUI>(); ((TMP_Text)component).transform.parent = GameObject.Find("Canvas/GameObject").transform; ((TMP_Text)component).transform.localPosition = new Vector3(200f, -170f, 0f); ((TMP_Text)component).transform.localScale = Vector3.one; ((TMP_Text)component).text = "偵測到已安裝 NewTerminal,可能與 LCTC 終端機翻譯衝突。"; ((TMP_Text)component).autoSizeTextContainer = true; ((Graphic)component).color = new Color(0.9434f, 0.0434f, 0.0434f, 1f); ((TMP_Text)component).alignment = (TextAlignmentOptions)514; ((TMP_Text)component).fontSize = 18f; ((Graphic)component).raycastTarget = false; } } } } } namespace Patchers { internal static class ExternalTextTranslator { private sealed class RegexRule { public Regex Pattern { get; } public string Replacement { get; } public RegexRule(Regex pattern, string replacement) { Pattern = pattern; Replacement = replacement; } } [CompilerGenerated] private sealed class <GetBuiltInRegexRules>d__26 : IEnumerable<KeyValuePair<string, string>>, IEnumerable, IEnumerator<KeyValuePair<string, string>>, IEnumerator, IDisposable { private int <>1__state; private KeyValuePair<string, string> <>2__current; private int <>l__initialThreadId; private Dictionary<string, string>.Enumerator <>s__1; private KeyValuePair<string, string> <item>5__2; private Dictionary<string, string>.Enumerator <>s__3; private KeyValuePair<string, string> <item>5__4; private Dictionary<string, string>.Enumerator <>s__5; private KeyValuePair<string, string> <item>5__6; private Dictionary<string, string>.Enumerator <>s__7; private KeyValuePair<string, string> <item>5__8; private Dictionary<string, string>.Enumerator <>s__9; private KeyValuePair<string, string> <item>5__10; private Dictionary<string, string>.Enumerator <>s__11; private KeyValuePair<string, string> <item>5__12; private Dictionary<string, string>.Enumerator <>s__13; private KeyValuePair<string, string> <item>5__14; private Dictionary<string, string>.Enumerator <>s__15; private KeyValuePair<string, string> <item>5__16; KeyValuePair<string, string> IEnumerator<KeyValuePair<string, string>>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <GetBuiltInRegexRules>d__26(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { switch (<>1__state) { case -3: case 1: try { } finally { <>m__Finally1(); } break; case -4: case 2: try { } finally { <>m__Finally2(); } break; case -5: case 3: try { } finally { <>m__Finally3(); } break; case -6: case 4: try { } finally { <>m__Finally4(); } break; case -7: case 5: try { } finally { <>m__Finally5(); } break; case -8: case 6: try { } finally { <>m__Finally6(); } break; case -9: case 7: try { } finally { <>m__Finally7(); } break; case -10: case 8: try { } finally { <>m__Finally8(); } break; } <>s__1 = default(Dictionary<string, string>.Enumerator); <item>5__2 = default(KeyValuePair<string, string>); <>s__3 = default(Dictionary<string, string>.Enumerator); <item>5__4 = default(KeyValuePair<string, string>); <>s__5 = default(Dictionary<string, string>.Enumerator); <item>5__6 = default(KeyValuePair<string, string>); <>s__7 = default(Dictionary<string, string>.Enumerator); <item>5__8 = default(KeyValuePair<string, string>); <>s__9 = default(Dictionary<string, string>.Enumerator); <item>5__10 = default(KeyValuePair<string, string>); <>s__11 = default(Dictionary<string, string>.Enumerator); <item>5__12 = default(KeyValuePair<string, string>); <>s__13 = default(Dictionary<string, string>.Enumerator); <item>5__14 = default(KeyValuePair<string, string>); <>s__15 = default(Dictionary<string, string>.Enumerator); <item>5__16 = default(KeyValuePair<string, string>); <>1__state = -2; } private bool MoveNext() { try { switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>s__1 = Dictionaries.Regex.GetEnumerator(); <>1__state = -3; goto IL_0194; case 1: <>1__state = -3; <item>5__2 = default(KeyValuePair<string, string>); goto IL_0194; case 2: <>1__state = -4; <item>5__4 = default(KeyValuePair<string, string>); goto IL_0210; case 3: <>1__state = -5; <item>5__6 = default(KeyValuePair<string, string>); goto IL_028c; case 4: <>1__state = -6; <item>5__8 = default(KeyValuePair<string, string>); goto IL_0308; case 5: <>1__state = -7; <item>5__10 = default(KeyValuePair<string, string>); goto IL_0384; case 6: <>1__state = -8; <item>5__12 = default(KeyValuePair<string, string>); goto IL_0400; case 7: <>1__state = -9; <item>5__14 = default(KeyValuePair<string, string>); goto IL_047c; case 8: <>1__state = -10; <item>5__16 = default(KeyValuePair<string, string>); goto IL_04f8; case 9: <>1__state = -1; <>2__current = new KeyValuePair<string, string>("loud horn", "$& (大聲喇叭)"); <>1__state = 10; return true; case 10: <>1__state = -1; <>2__current = new KeyValuePair<string, string>("Signal Translator", "$& (訊號翻譯器)"); <>1__state = 11; return true; case 11: <>1__state = -1; <>2__current = new KeyValuePair<string, string>("signal translator", "$& (訊號翻譯器)"); <>1__state = 12; return true; case 12: <>1__state = -1; <>2__current = new KeyValuePair<string, string>("Teleporter", "$& (傳送器)"); <>1__state = 13; return true; case 13: <>1__state = -1; <>2__current = new KeyValuePair<string, string>("teleporter", "$& (傳送器)"); <>1__state = 14; return true; case 14: <>1__state = -1; <>2__current = new KeyValuePair<string, string>("Inverse Teleporter", "$& (反向傳送器)"); <>1__state = 15; return true; case 15: <>1__state = -1; <>2__current = new KeyValuePair<string, string>("inverse teleporter", "$& (反向傳送器)"); <>1__state = 16; return true; case 16: <>1__state = -1; <>2__current = new KeyValuePair<string, string>("Happy Monday", "祝你星期一愉快"); <>1__state = 17; return true; case 17: <>1__state = -1; <>2__current = new KeyValuePair<string, string>("Happy Tuesday", "祝你星期二愉快"); <>1__state = 18; return true; case 18: <>1__state = -1; <>2__current = new KeyValuePair<string, string>("Happy Wednesday", "祝你星期三愉快"); <>1__state = 19; return true; case 19: <>1__state = -1; <>2__current = new KeyValuePair<string, string>("Happy Thursday", "祝你星期四愉快"); <>1__state = 20; return true; case 20: <>1__state = -1; <>2__current = new KeyValuePair<string, string>("Happy Friday", "祝你星期五愉快"); <>1__state = 21; return true; case 21: <>1__state = -1; <>2__current = new KeyValuePair<string, string>("Happy Saturday", "祝你星期六愉快"); <>1__state = 22; return true; case 22: <>1__state = -1; <>2__current = new KeyValuePair<string, string>("Happy Sunday", "祝你星期日愉快"); <>1__state = 23; return true; case 23: <>1__state = -1; <>2__current = new KeyValuePair<string, string>("\\bMonday\\b", "星期一"); <>1__state = 24; return true; case 24: <>1__state = -1; <>2__current = new KeyValuePair<string, string>("\\bTuesday\\b", "星期二"); <>1__state = 25; return true; case 25: <>1__state = -1; <>2__current = new KeyValuePair<string, string>("\\bWednesday\\b", "星期三"); <>1__state = 26; return true; case 26: <>1__state = -1; <>2__current = new KeyValuePair<string, string>("\\bThursday\\b", "星期四"); <>1__state = 27; return true; case 27: <>1__state = -1; <>2__current = new KeyValuePair<string, string>("\\bFriday\\b", "星期五"); <>1__state = 28; return true; case 28: <>1__state = -1; <>2__current = new KeyValuePair<string, string>("\\bSaturday\\b", "星期六"); <>1__state = 29; return true; case 29: <>1__state = -1; <>2__current = new KeyValuePair<string, string>("\\bSunday\\b", "星期日"); <>1__state = 30; return true; case 30: { <>1__state = -1; return false; } IL_0194: if (<>s__1.MoveNext()) { <item>5__2 = <>s__1.Current; <>2__current = <item>5__2; <>1__state = 1; return true; } <>m__Finally1(); <>s__1 = default(Dictionary<string, string>.Enumerator); <>s__3 = Dictionaries.TextPostProcess.GetEnumerator(); <>1__state = -4; goto IL_0210; IL_047c: if (<>s__13.MoveNext()) { <item>5__14 = <>s__13.Current; <>2__current = <item>5__14; <>1__state = 7; return true; } <>m__Finally7(); <>s__13 = default(Dictionary<string, string>.Enumerator); <>s__15 = Dictionaries.ItemNames.GetEnumerator(); <>1__state = -10; goto IL_04f8; IL_0210: if (<>s__3.MoveNext()) { <item>5__4 = <>s__3.Current; <>2__current = <item>5__4; <>1__state = 2; return true; } <>m__Finally2(); <>s__3 = default(Dictionary<string, string>.Enumerator); <>s__5 = Dictionaries.Chat.GetEnumerator(); <>1__state = -5; goto IL_028c; IL_028c: if (<>s__5.MoveNext()) { <item>5__6 = <>s__5.Current; <>2__current = <item>5__6; <>1__state = 3; return true; } <>m__Finally3(); <>s__5 = default(Dictionary<string, string>.Enumerator); <>s__7 = Dictionaries.Notes.GetEnumerator(); <>1__state = -6; goto IL_0308; IL_04f8: if (<>s__15.MoveNext()) { <item>5__16 = <>s__15.Current; <>2__current = <item>5__16; <>1__state = 8; return true; } <>m__Finally8(); <>s__15 = default(Dictionary<string, string>.Enumerator); <>2__current = new KeyValuePair<string, string>("Loud horn", "$& (大聲喇叭)"); <>1__state = 9; return true; IL_0308: if (<>s__7.MoveNext()) { <item>5__8 = <>s__7.Current; <>2__current = <item>5__8; <>1__state = 4; return true; } <>m__Finally4(); <>s__7 = default(Dictionary<string, string>.Enumerator); <>s__9 = Dictionaries.Loading.GetEnumerator(); <>1__state = -7; goto IL_0384; IL_0400: if (<>s__11.MoveNext()) { <item>5__12 = <>s__11.Current; <>2__current = <item>5__12; <>1__state = 6; return true; } <>m__Finally6(); <>s__11 = default(Dictionary<string, string>.Enumerator); <>s__13 = Dictionaries.PlanetInfo.GetEnumerator(); <>1__state = -9; goto IL_047c; IL_0384: if (<>s__9.MoveNext()) { <item>5__10 = <>s__9.Current; <>2__current = <item>5__10; <>1__state = 5; return true; } <>m__Finally5(); <>s__9 = default(Dictionary<string, string>.Enumerator); <>s__11 = Dictionaries.ShipTeleporter.GetEnumerator(); <>1__state = -8; goto IL_0400; } } catch { //try-fault ((IDisposable)this).Dispose(); throw; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; ((IDisposable)<>s__1).Dispose(); } private void <>m__Finally2() { <>1__state = -1; ((IDisposable)<>s__3).Dispose(); } private void <>m__Finally3() { <>1__state = -1; ((IDisposable)<>s__5).Dispose(); } private void <>m__Finally4() { <>1__state = -1; ((IDisposable)<>s__7).Dispose(); } private void <>m__Finally5() { <>1__state = -1; ((IDisposable)<>s__9).Dispose(); } private void <>m__Finally6() { <>1__state = -1; ((IDisposable)<>s__11).Dispose(); } private void <>m__Finally7() { <>1__state = -1; ((IDisposable)<>s__13).Dispose(); } private void <>m__Finally8() { <>1__state = -1; ((IDisposable)<>s__15).Dispose(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator<KeyValuePair<string, string>> IEnumerable<KeyValuePair<string, string>>.GetEnumerator() { if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; return this; } return new <GetBuiltInRegexRules>d__26(0); } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable<KeyValuePair<string, string>>)this).GetEnumerator(); } } private static readonly object SyncRoot = new object(); private static readonly Regex PlaceholderTokenRegex = new Regex("\\[[^\\]\\r\\n]+\\]", RegexOptions.Compiled); private static readonly Dictionary<string, string> NodeExactRules = new Dictionary<string, string>(StringComparer.Ordinal); private static readonly List<RegexRule> RegexRules = new List<RegexRule>(); private static FileSystemWatcher _watcher; private static string _filePath; private static DateTime _lastWriteUtc = DateTime.MinValue; private static bool _initialized; private static volatile bool _dirty = true; public static void Initialize() { lock (SyncRoot) { if (!_initialized) { _filePath = Path.Combine(Paths.ConfigPath, PatchSettings.ExternalTranslationFileName ?? "LCTC.Terminal.Translations.txt"); EnsureDefaultFile(); AppendMissingBuiltInRegexRulesToExternalFile(); SetupWatcher(); ReloadRules(force: true); _initialized = true; } } } public static string Translate(string input, string nodeName = null) { if (string.IsNullOrEmpty(input)) { return input; } Initialize(); ReloadRules(force: false); string text = input; if (!string.IsNullOrEmpty(nodeName) && NodeExactRules.TryGetValue(nodeName, out var value)) { text = AlignPlaceholderTokens(value, input); } for (int i = 0; i < RegexRules.Count; i++) { text = RegexRules[i].Pattern.Replace(text, RegexRules[i].Replacement); } return text; } public static string TranslateNodeTemplate(string templateText, string nodeName) { if (string.IsNullOrEmpty(templateText) || string.IsNullOrEmpty(nodeName)) { return templateText; } if (IsDynamicHubNode(nodeName)) { return templateText; } if (ContainsDynamicListToken(templateText)) { return templateText; } Initialize(); ReloadRules(force: false); if (NodeExactRules.TryGetValue(nodeName, out var value)) { return AlignPlaceholderTokens(value, templateText); } return templateText; } public static string TranslatePostProcess(string processedText) { if (string.IsNullOrEmpty(processedText)) { return processedText; } Initialize(); ReloadRules(force: false); string text = processedText; for (int i = 0; i < RegexRules.Count; i++) { text = RegexRules[i].Pattern.Replace(text, RegexRules[i].Replacement); } return RemoveOverlappingItemTranslations(text); } private static string RemoveOverlappingItemTranslations(string text) { if (string.IsNullOrEmpty(text)) { return text; } text = Regex.Replace(text, "\\b(Inverse\\s+Teleporter)\\b(\\s*\\([^)]+\\))\\s*\\([^)]+\\)", "$1$2", RegexOptions.CultureInvariant); text = Regex.Replace(text, "\\b(inverse\\s+teleporter)\\b(\\s*\\([^)]+\\))\\s*\\([^)]+\\)", "$1$2", RegexOptions.CultureInvariant); text = Regex.Replace(text, "(\\s*\\([^)]+\\))\\s*\\1+", "$1", RegexOptions.CultureInvariant); return text; } public static bool HasNodeRule(string nodeName) { if (string.IsNullOrEmpty(nodeName)) { return false; } Initialize(); ReloadRules(force: false); lock (SyncRoot) { return NodeExactRules.ContainsKey(nodeName); } } public static bool TryGetNodeRule(string nodeName, out string translatedNodeText) { translatedNodeText = null; if (string.IsNullOrEmpty(nodeName)) { return false; } Initialize(); ReloadRules(force: false); lock (SyncRoot) { return NodeExactRules.TryGetValue(nodeName, out translatedNodeText); } } public static int AppendMissingNodeRules(IEnumerable<TerminalNode> nodes) { if (nodes == null) { return 0; } Initialize(); ReloadRules(force: false); List<string> list = new List<string>(); lock (SyncRoot) { foreach (TerminalNode node in nodes) { if (!((Object)(object)node == (Object)null) && !string.IsNullOrEmpty(((Object)node).name) && !NodeExactRules.ContainsKey(((Object)node).name)) { string value = node.displayText ?? string.Empty; string item = "NODE\t" + Escape(((Object)node).name) + "\t" + Escape(value); list.Add(item); NodeExactRules[((Object)node).name] = value; } } } if (list.Count == 0) { return 0; } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine(); stringBuilder.AppendLine("# ===== Auto-added missing NODE rules ====="); stringBuilder.AppendLine("# Added at: " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); foreach (string item2 in list) { stringBuilder.AppendLine(item2); } File.AppendAllText(_filePath, stringBuilder.ToString(), Encoding.UTF8); _lastWriteUtc = File.GetLastWriteTimeUtc(_filePath); _dirty = true; return list.Count; } private static void ReloadRules(bool force) { lock (SyncRoot) { if (string.IsNullOrEmpty(_filePath) || !File.Exists(_filePath)) { return; } DateTime lastWriteTimeUtc = File.GetLastWriteTimeUtc(_filePath); if (!force && !_dirty && PatchSettings.ReloadExternalTranslationOnChange && lastWriteTimeUtc == _lastWriteUtc) { return; } NodeExactRules.Clear(); RegexRules.Clear(); string[] array = File.ReadAllLines(_filePath, Encoding.UTF8); string[] array2 = array; for (int i = 0; i < array2.Length; i++) { string text = array2[i]?.TrimEnd() ?? string.Empty; if (string.IsNullOrWhiteSpace(text) || text.StartsWith("#")) { continue; } string[] array3 = text.Split('\t'); if (array3.Length < 3) { continue; } string a = array3[0].Trim(); StringBuilder stringBuilder = new StringBuilder(array3[2]); for (int j = 3; j < array3.Length; j++) { stringBuilder.Append('\t').Append(array3[j]); } if (string.Equals(a, "NODE", StringComparison.OrdinalIgnoreCase)) { string text2 = Unescape(array3[1]); string value = Unescape(stringBuilder.ToString()); if (!string.IsNullOrEmpty(text2)) { NodeExactRules[text2] = value; } } else { if (!string.Equals(a, "REGEX", StringComparison.OrdinalIgnoreCase)) { continue; } string text3 = Unescape(array3[1]); string replacement = Unescape(stringBuilder.ToString()); if (!string.IsNullOrEmpty(text3)) { try { Regex pattern = new Regex(text3, RegexOptions.CultureInvariant); RegexRules.Add(new RegexRule(pattern, replacement)); } catch { } } } } HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal); for (int k = 0; k < RegexRules.Count; k++) { hashSet.Add(RegexRules[k].Pattern.ToString()); } foreach (KeyValuePair<string, string> builtInRegexRule in GetBuiltInRegexRules()) { if (!string.IsNullOrEmpty(builtInRegexRule.Key) && !hashSet.Contains(builtInRegexRule.Key)) { try { Regex pattern2 = new Regex(builtInRegexRule.Key, RegexOptions.CultureInvariant); RegexRules.Add(new RegexRule(pattern2, builtInRegexRule.Value ?? string.Empty)); hashSet.Add(builtInRegexRule.Key); } catch { } } } _lastWriteUtc = lastWriteTimeUtc; _dirty = false; } } private static void EnsureDefaultFile() { if (!string.IsNullOrEmpty(_filePath)) { string directoryName = Path.GetDirectoryName(_filePath); if (!string.IsNullOrEmpty(directoryName) && !Directory.Exists(directoryName)) { Directory.CreateDirectory(directoryName); } if (!File.Exists(_filePath)) { File.WriteAllText(_filePath, BuildDefaultContent(), Encoding.UTF8); } } } private static void AppendMissingBuiltInRegexRulesToExternalFile() { if (string.IsNullOrEmpty(_filePath) || !File.Exists(_filePath)) { return; } HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal); string[] array = File.ReadAllLines(_filePath, Encoding.UTF8); string[] array2 = array; for (int i = 0; i < array2.Length; i++) { string text = array2[i]?.TrimEnd() ?? string.Empty; if (string.IsNullOrWhiteSpace(text) || text.StartsWith("#")) { continue; } string[] array3 = text.Split('\t'); if (array3.Length < 3) { continue; } string a = array3[0].Trim(); if (string.Equals(a, "REGEX", StringComparison.OrdinalIgnoreCase)) { string text2 = Unescape(array3[1]); if (!string.IsNullOrEmpty(text2)) { hashSet.Add(text2); } } } List<string> list = new List<string>(); foreach (KeyValuePair<string, string> builtInRegexRule in GetBuiltInRegexRules()) { if (!string.IsNullOrEmpty(builtInRegexRule.Key) && !hashSet.Contains(builtInRegexRule.Key)) { list.Add("REGEX\t" + Escape(builtInRegexRule.Key) + "\t" + Escape(builtInRegexRule.Value)); hashSet.Add(builtInRegexRule.Key); } } if (list.Count == 0) { return; } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine(); stringBuilder.AppendLine("# ===== Auto-added missing REGEX rules ====="); stringBuilder.AppendLine("# Added at: " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); foreach (string item in list) { stringBuilder.AppendLine(item); } File.AppendAllText(_filePath, stringBuilder.ToString(), Encoding.UTF8); } private static void SetupWatcher() { if (_watcher == null && !string.IsNullOrEmpty(_filePath)) { string directoryName = Path.GetDirectoryName(_filePath); string fileName = Path.GetFileName(_filePath); if (!string.IsNullOrEmpty(directoryName) && !string.IsNullOrEmpty(fileName)) { _watcher = new FileSystemWatcher(directoryName, fileName) { NotifyFilter = (NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite) }; _watcher.Changed += OnTranslationFileChanged; _watcher.Created += OnTranslationFileChanged; _watcher.Renamed += OnTranslationFileChanged; _watcher.EnableRaisingEvents = true; } } } private static void OnTranslationFileChanged(object sender, FileSystemEventArgs e) { _dirty = true; } private static string BuildDefaultContent() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("# LCTC translation rules"); stringBuilder.AppendLine("# Format:"); stringBuilder.AppendLine("# NODE<TAB>nodeName<TAB>translatedFullText"); stringBuilder.AppendLine("# REGEX<TAB>regexPattern<TAB>replacementText"); stringBuilder.AppendLine("# Escape supports: \\\\, \\t, \\r, \\n"); stringBuilder.AppendLine(); stringBuilder.AppendLine("# NODE rules are auto-appended from runtime terminal nodes"); stringBuilder.AppendLine("# when AutoAppendMissingNodeRules=true."); stringBuilder.AppendLine("# Example:"); stringBuilder.AppendLine("# NODE\t0_Bestiary\t..."); stringBuilder.AppendLine(); stringBuilder.AppendLine("# ===== Default REGEX rules ====="); foreach (KeyValuePair<string, string> builtInRegexRule in GetBuiltInRegexRules()) { stringBuilder.Append("REGEX\t"); stringBuilder.Append(Escape(builtInRegexRule.Key)); stringBuilder.Append('\t'); stringBuilder.Append(Escape(builtInRegexRule.Value)); stringBuilder.AppendLine(); } return stringBuilder.ToString(); } private static bool ContainsDynamicListToken(string text) { if (string.IsNullOrEmpty(text)) { return false; } return text.Contains("[currentScannedEnemiesList]"); } private static bool IsDynamicHubNode(string nodeName) { return string.Equals(nodeName, "0_Bestiary", StringComparison.Ordinal); } [IteratorStateMachine(typeof(<GetBuiltInRegexRules>d__26))] private static IEnumerable<KeyValuePair<string, string>> GetBuiltInRegexRules() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <GetBuiltInRegexRules>d__26(-2); } private static string AlignPlaceholderTokens(string translatedText, string originalText) { if (string.IsNullOrEmpty(translatedText) || string.IsNullOrEmpty(originalText)) { return translatedText; } MatchCollection matchCollection = PlaceholderTokenRegex.Matches(translatedText); if (matchCollection.Count == 0) { return translatedText; } MatchCollection originalTokens = PlaceholderTokenRegex.Matches(originalText); if (originalTokens.Count == 0) { return translatedText; } int limit = Math.Min(matchCollection.Count, originalTokens.Count); int tokenIndex = 0; return PlaceholderTokenRegex.Replace(translatedText, delegate(Match match) { if (tokenIndex < limit) { string value = originalTokens[tokenIndex].Value; tokenIndex++; return value; } return match.Value; }); } private static string Escape(string value) { if (value == null) { return string.Empty; } return value.Replace("\\", "\\\\").Replace("\t", "\\t").Replace("\r", "\\r") .Replace("\n", "\\n"); } private static string Unescape(string value) { if (string.IsNullOrEmpty(value)) { return string.Empty; } StringBuilder stringBuilder = new StringBuilder(value.Length); for (int i = 0; i < value.Length; i++) { char c = value[i]; if (c == '\\' && i + 1 < value.Length) { i++; char c2 = value[i]; switch (c2) { case 'n': stringBuilder.Append('\n'); break; case 'r': stringBuilder.Append('\r'); break; case 't': stringBuilder.Append('\t'); break; case '\\': stringBuilder.Append('\\'); break; default: stringBuilder.Append(c2); break; } } else { stringBuilder.Append(c); } } return stringBuilder.ToString(); } } [HarmonyPatch(typeof(Terminal), "Awake")] internal static class PatchTerminalAwake { public static readonly Dictionary<string, string> SpecialNodes = new Dictionary<string, string>(StringComparer.Ordinal); [HarmonyPrefix] private static void Prefix(ref Terminal __instance) { if ((Object)(object)__instance == (Object)null || (Object)(object)__instance.terminalNodes == (Object)null || __instance.terminalNodes.specialNodes == null || SpecialNodes.Count > 0) { return; } foreach (TerminalNode specialNode in __instance.terminalNodes.specialNodes) { if (!((Object)(object)specialNode == (Object)null)) { string name = ((Object)specialNode).name; if (!string.IsNullOrEmpty(name) && !SpecialNodes.ContainsKey(name)) { SpecialNodes[name] = specialNode.displayText ?? string.Empty; } } } } } internal static class PatchSettings { public static bool IsPatchEnabled { get; set; } = true; public static bool DumpTerminalNodesOnStart { get; set; } = true; public static bool DebugMode { get; set; } = false; public static bool DebugModeFromConfig { get; set; } = false; public static bool UseExternalTranslationFile { get; set; } = true; public static string ExternalTranslationFileName { get; set; } = "LCTC.Terminal.Translations.txt"; public static bool ReloadExternalTranslationOnChange { get; set; } = true; public static bool AutoAppendMissingNodeRules { get; set; } = true; public static void ResolveDebugMode() { string path = Path.Combine(Paths.BepInExRootPath, "LCTC_DEBUG.flag"); bool flag = File.Exists(path); DebugMode = DebugModeFromConfig || flag; } } internal static class DebugTranslationReporter { private static readonly object SyncRoot = new object(); private static readonly HashSet<string> SeenNewNodes = new HashSet<string>(StringComparer.Ordinal); private static string DebugDir => Path.Combine(Paths.BepInExRootPath, "LCTC_Debug"); private static string NewNodesPath => Path.Combine(DebugDir, "new_nodes.log"); public static void ReportNode(string nodeName, string nodeText) { if (!PatchSettings.DebugMode || string.IsNullOrEmpty(nodeName) || (PatchSettings.UseExternalTranslationFile && ExternalTextTranslator.HasNodeRule(nodeName))) { return; } string text = Sanitize(nodeText); string item = nodeName + "|" + text; lock (SyncRoot) { if (SeenNewNodes.Add(item)) { EnsureDir(); File.AppendAllText(NewNodesPath, $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {nodeName}\t{text}{Environment.NewLine}", Encoding.UTF8); } } } private static string Sanitize(string text) { string text2 = text ?? string.Empty; text2 = text2.Replace('\r', ' ').Replace('\n', ' ').Trim(); if (text2.Length > 300) { text2 = text2.Substring(0, 300) + "..."; } return text2; } private static void EnsureDir() { if (!Directory.Exists(DebugDir)) { Directory.CreateDirectory(DebugDir); } } } [HarmonyPatch(typeof(Terminal), "Start")] internal static class TerminalNodeDumpPatch { private static bool _dumped; [HarmonyPostfix] private static void Postfix() { PatchSettings.ResolveDebugMode(); if (!PatchSettings.DumpTerminalNodesOnStart || _dumped) { return; } _dumped = true; List<TerminalNode> list = (from node in Resources.FindObjectsOfTypeAll<TerminalNode>() where (Object)(object)node != (Object)null orderby ((Object)node).name select node).ToList(); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine($"Total TerminalNodes: {list.Count}"); foreach (TerminalNode item in list) { string text = item.displayText ?? string.Empty; text = text.Replace('\r', ' ').Replace('\n', ' '); if (text.Length > 160) { text = text.Substring(0, 160) + "..."; } stringBuilder.AppendLine(((Object)item).name + "\t" + text); } string text2 = Path.Combine(Paths.BepInExRootPath, "TerminalNodes_dump.txt"); File.WriteAllText(text2, stringBuilder.ToString(), Encoding.UTF8); Debug.Log((object)("[LCTC] Terminal nodes dumped: " + text2)); if (PatchSettings.UseExternalTranslationFile && PatchSettings.AutoAppendMissingNodeRules) { int num = ExternalTextTranslator.AppendMissingNodeRules(list); if (num > 0) { Debug.Log((object)$"[LCTC] Appended {num} missing NODE rules to external translation file."); } } } } [HarmonyPatch(typeof(Terminal), "LoadNewNode")] internal class PatchLoadNewNode { [HarmonyPrefix] private static bool Prefix(ref TerminalNode node) { if (!PatchSettings.IsPatchEnabled || (Object)(object)node == (Object)null) { return true; } string name = ((Object)node).name; DebugTranslationReporter.ReportNode(name, node.displayText); if (PatchSettings.UseExternalTranslationFile) { if (!IsSensitiveDynamicNode(name, node.displayText) && ExternalTextTranslator.TryGetNodeRule(name, out var translatedNodeText)) { if (PatchTerminalAwake.SpecialNodes.TryGetValue(name, out var value)) { node.displayText = MergeBySourceLines(node.displayText ?? string.Empty, value, translatedNodeText); } else { node.displayText = translatedNodeText; } } else { node.displayText = ExternalTextTranslator.TranslateNodeTemplate(node.displayText, name); } } return true; } private static string MergeBySourceLines(string currentText, string sourceTemplateText, string translatedTemplateText) { string[] array = (sourceTemplateText ?? string.Empty).Replace("\r\n", "\n").Split('\n'); string[] array2 = (translatedTemplateText ?? string.Empty).Replace("\r\n", "\n").Split('\n'); int num = Math.Min(array.Length, array2.Length); string text = currentText ?? string.Empty; for (int i = 0; i < num; i++) { if (!string.IsNullOrEmpty(array[i]) && !string.IsNullOrEmpty(array2[i])) { text = text.Replace(array[i], array2[i]); } } return text; } private static bool IsSensitiveDynamicNode(string nodeName, string templateText) { if (string.Equals(nodeName, "0_Bestiary", StringComparison.Ordinal)) { return true; } if (string.IsNullOrEmpty(templateText)) { return false; } return templateText.Contains("[currentScannedEnemiesList]"); } } [HarmonyPatch(typeof(Terminal), "ParsePlayerSentence")] internal class TerminalParsePatch { [HarmonyPostfix] private static void Postfix(ref Terminal __instance, ref TerminalNode __result) { if (!((Object)(object)__instance == (Object)null) && !((Object)(object)__instance.screenText == (Object)null) && __instance.textAdded > 0 && __instance.screenText.text.Length >= __instance.textAdded) { string text = __instance.screenText.text.Substring(__instance.screenText.text.Length - __instance.textAdded); if ((Object)(object)__result != (Object)null && text.ToLower() == "translate") { __result.displayText = "[translate]"; } } } } [HarmonyPatch(typeof(Terminal), "TextPostProcess")] internal class TerminalPatch { private static readonly Dictionary<string, string> BestiaryNameMap = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) { { "Snareflea", "陷阱蜈蚣" }, { "Bracken", "花人布拉肯" }, { "Thumper", "重擊者" }, { "EyelessDog", "無眼犬" }, { "HoardingBug", "囤積蟲" }, { "Hygrodere", "史萊姆" }, { "ForestKeeper", "森林守護者" }, { "CoilHead", "彈簧頭" }, { "LassoMan", "套索人" }, { "EarthLeviathan", "大地利維坦" }, { "Jester", "小丑" }, { "Puffer", "孢子蜥蜴" }, { "BunkerSpider", "碉堡蜘蛛" }, { "Manticoil", "四翼鳥" }, { "CircuitBee", "電路蜜蜂" }, { "Locust", "遊蕩蝗蟲" }, { "BaboonHawk", "狒狒鷹" }, { "Nutcracker", "胡桃鉗" }, { "RadMech", "古代鳥(機器人)" }, { "Butler", "管家" }, { "MaskHornets", "假面大黃蜂" }, { "TulipSnake", "鬱金香蛇" }, { "VainShroud", "紅蔓蕨" }, { "BushWolf", "綁架狐" }, { "ClaySurgeon", "理髮師" }, { "Maneater", "食人者" }, { "Sapsucker", "巨啄木鳥" }, { "Gunkfish", "污泥魟魚" }, { "Feiopar", "伏影豹" }, { "CadaverBloom", "屍花" } }; [HarmonyPrefix] private static bool Prefix(Terminal __instance, ref string __result, ref string modifiedDisplayText, TerminalNode node) { List<string> list = new List<string>(7) { "快樂的星期日", "快樂的星期一", "快樂的星期二", "快樂的星期三", "快樂的星期四", "快樂的星期五", "快樂的星期六" }; int dayOfWeek = (int)DateTime.Now.DayOfWeek; if (dayOfWeek >= 0 && dayOfWeek < list.Count) { modifiedDisplayText = (modifiedDisplayText ?? string.Empty).Replace("[currentDayRussian]", list[dayOfWeek]); } string a = (((Object)(object)node == (Object)null) ? string.Empty : ((Object)node).name); if (string.Equals(a, "0_Bestiary", StringComparison.Ordinal)) { string text = modifiedDisplayText ?? string.Empty; text = text.Replace("[currentScannedEnemiesList]", BuildBestiaryList(__instance)); __result = (PatchSettings.UseExternalTranslationFile ? ExternalTextTranslator.TranslatePostProcess(text) : text); return false; } return true; } [HarmonyPostfix] private static void Postfix(ref string __result, TerminalNode node) { if (PatchSettings.IsPatchEnabled && PatchSettings.UseExternalTranslationFile) { __result = ExternalTextTranslator.TranslatePostProcess(__result); } } [HarmonyFinalizer] private static Exception Finalizer(Exception __exception, ref string __result, string modifiedDisplayText, TerminalNode node) { if (__exception == null) { return null; } if (__exception is ArgumentOutOfRangeException) { string text = modifiedDisplayText ?? __result ?? string.Empty; text = text.Replace("[currentScannedEnemiesList]", string.Empty).Replace("[currentUnlockedLogsList]", string.Empty).Replace("[buyableItemsList]", string.Empty) .Replace("[buyableVehiclesList]", string.Empty) .Replace("[storedUnlockablesList]", string.Empty) .Replace("[unlockablesSelectionList]", string.Empty); __result = text; string text2 = (((Object)(object)node == (Object)null) ? "<null>" : ((Object)node).name); Debug.LogWarning((object)("[LCTC] TextPostProcess fallback applied on node: " + text2 + ". Error: " + __exception.Message)); return null; } return __exception; } private static string BuildBestiaryList(Terminal terminal) { if ((Object)(object)terminal == (Object)null) { return string.Empty; } List<int> list = TryGetIntList(terminal, "scannedEnemyIDs", "scannedEnemies", "scannedBestiaryIDs"); if (list == null || list.Count == 0) { return string.Empty; } object obj = TryGetMemberValue(terminal, "enemyFiles", "creatureFiles", "bestiaryCreatureFiles", "allCreatureFiles"); if (!(obj is IList list2) || list2.Count == 0) { return string.Empty; } List<string> list3 = new List<string>(); foreach (int item in list) { if (item >= 0 && item < list2.Count) { object fileObj = list2[item]; string text = TryReadCreatureName(fileObj); if (!string.IsNullOrWhiteSpace(text)) { string text2 = TranslateBestiaryName(text.Trim()); list3.Add("* " + text2); } } } return (list3.Count == 0) ? string.Empty : (string.Join("\n", list3) + "\n"); } private static List<int> TryGetIntList(object target, params string[] names) { foreach (string text in names) { object obj = TryGetMemberValue(target, text); if (obj is IEnumerable<int> source) { return source.ToList(); } if (!(obj is IEnumerable enumerable)) { continue; } List<int> list = new List<int>(); foreach (object item2 in enumerable) { if (item2 is int item) { list.Add(item); } } if (list.Count > 0) { return list; } } return null; } private static string TryReadCreatureName(object fileObj) { if (fileObj == null) { return null; } Object val = (Object)((fileObj is Object) ? fileObj : null); if (val != null && !string.IsNullOrWhiteSpace(val.name)) { return val.name.Replace("File", string.Empty).Trim(); } return TryGetMemberValue(fileObj, "creatureName", "enemyName", "name", "displayName") as string; } private static string TranslateBestiaryName(string originalName) { if (string.IsNullOrWhiteSpace(originalName)) { return originalName; } string key = NormalizeBestiaryKey(originalName); if (BestiaryNameMap.TryGetValue(key, out var value) && !string.IsNullOrWhiteSpace(value)) { return originalName + " (" + value + ")"; } return originalName; } private static string NormalizeBestiaryKey(string name) { if (string.IsNullOrWhiteSpace(name)) { return string.Empty; } StringBuilder stringBuilder = new StringBuilder(name.Length); foreach (char c in name) { if (char.IsLetterOrDigit(c)) { stringBuilder.Append(c); } } return stringBuilder.ToString(); } private static object TryGetMemberValue(object target, params string[] names) { if (target == null) { return null; } Type type = target.GetType(); foreach (string name in names) { FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { return field.GetValue(target); } PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null) { return property.GetValue(target, null); } } return null; } } } namespace LCTC { [BepInPlugin("LCTCTerminal", "LCTC Terminal", "1.0.0")] public class Plugin : BaseUnityPlugin { public static Flags Flags { get; private set; } private void Awake() { //IL_001d: Unknown result type (might be due to invalid IL or missing references) ApplyDedicatedConfig(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"LCTC Terminal loaded."); new Harmony("LCTCTerminal.patch").PatchAll(); foreach (PluginInfo value in Chainloader.PluginInfos.Values) { if (value.Metadata.GUID == "NewTerminal") { ((BaseUnityPlugin)this).Logger.LogWarning((object)"Detected old terminal translation plugin (NewTerminal). It may conflict."); Flags = Flags.NewTerminalDetected; } } } private static void ApplyDedicatedConfig() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Expected O, but got Unknown string text = Path.Combine(Paths.ConfigPath, "LCTC.Terminal.Debug.cfg"); ConfigFile val = new ConfigFile(text, true); PatchSettings.IsPatchEnabled = val.Bind<bool>("General", "EnableTranslationPatch", true, "Enable terminal translation patch pipeline.").Value; PatchSettings.DumpTerminalNodesOnStart = val.Bind<bool>("Debug", "DumpTerminalNodesOnStart", true, "Dump all runtime terminal nodes to BepInExRoot/TerminalNodes_dump.txt on startup.").Value; PatchSettings.DebugModeFromConfig = val.Bind<bool>("Debug", "EnableDebugCapture", false, "Capture unknown nodes and untranslated text to BepInExRoot/LCTC_Debug.").Value; PatchSettings.UseExternalTranslationFile = val.Bind<bool>("Translation", "UseExternalTranslationFile", true, "Use external translation text file (NODE + REGEX rules).").Value; PatchSettings.ExternalTranslationFileName = val.Bind<string>("Translation", "ExternalTranslationFileName", "LCTC.Terminal.Translations.txt", "Translation rules file name under BepInEx/config.").Value; PatchSettings.ReloadExternalTranslationOnChange = val.Bind<bool>("Translation", "ReloadExternalTranslationOnChange", true, "Reload external translation file when it changes.").Value; PatchSettings.AutoAppendMissingNodeRules = val.Bind<bool>("Translation", "AutoAppendMissingNodeRules", true, "Append missing NODE rules from runtime terminal nodes into external translation file.").Value; PatchSettings.ResolveDebugMode(); ExternalTextTranslator.Initialize(); } } internal static class PluginConstants { public const string Guid = "LCTCTerminal"; public const string Name = "LCTC Terminal"; public const string Version = "1.0.0"; } [Flags] public enum Flags { None = 0, NewTerminalDetected = 1 } }