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 KZT Currency v1.2.1
kztCurrency.dll
Decompiled 3 months agousing System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; 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.RegularExpressions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("kossnikita")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.2.1.0")] [assembly: AssemblyInformationalVersion("1.2.1+f62ca6acd693a1a39864d73f984689ce94e252a9")] [assembly: AssemblyProduct("kztCurrency")] [assembly: AssemblyTitle("kztCurrency")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.2.1.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; } } } namespace kztCurrency { public static class CurrencyFormatter { private static class RegexCache { public static readonly Regex GroupingChars = new Regex("[ \\u00A0\\.,]", RegexOptions.Compiled); public static readonly Regex GroupingCharsExtended = new Regex("[ \\u00A0\\u202F,\\u2009]", RegexOptions.Compiled); public static readonly Regex NonDigits = new Regex("[^0-9-]", RegexOptions.Compiled); } private const string Nbsp = "\u00a0"; public static readonly string NBSP = "\u00a0"; private static readonly NumberFormatInfo NbspNumberFormat = new NumberFormatInfo { NumberGroupSeparator = "\u00a0", NumberDecimalSeparator = ".", NumberGroupSizes = new int[1] { 3 } }; private static readonly ConcurrentDictionary<string, string> LastLoggedOutputs = new ConcurrentDictionary<string, string>(); private static string ApplyTemplate(string template, string formattedValue) { if (string.IsNullOrEmpty(template)) { return formattedValue; } return template.Replace("{value}", formattedValue); } private static void LogFormatCall(string methodName, string input, string output) { try { string text = output ?? string.Empty; if (!LastLoggedOutputs.TryGetValue(methodName, out var value) || !(value == text)) { LastLoggedOutputs[methodName] = text; } } catch (Exception ex) { try { Plugin.Logger.LogWarning((object)("[Formatting] " + methodName + " logging failed: " + ex.Message)); } catch { } } } public static string FormatPlayerUI(string value) { Plugin.TemplateInfo templateInfo = Plugin.Template_PlayerUI ?? new Plugin.TemplateInfo("{value}K\u00a0₸", multiplyBy1000: false); if (templateInfo.MultiplyBy1000 && TryParseDigitsToLong(value, out var value2)) { string text = (RegexCache.GroupingChars.IsMatch(value ?? "") ? Math.Max(0L, value2) : (Math.Max(0L, value2) * 1000)).ToString("N0", NbspNumberFormat); return ApplyTemplate((text == "0") ? (Plugin.TemplateZero_PlayerUI ?? templateInfo.Template) : templateInfo.Template, text); } string text2 = FormatNumber(value); return ApplyTemplate((text2 == "0") ? (Plugin.TemplateZero_PlayerUI ?? templateInfo.Template) : templateInfo.Template, text2); } public static string FormatPlayerUICurrency(string value) { Plugin.TemplateInfo templateInfo = Plugin.Template_PlayerUI ?? new Plugin.TemplateInfo("{value}K\u00a0₸", multiplyBy1000: false); bool flag = !string.IsNullOrEmpty(value) && value.Contains('.'); bool flag2 = RegexCache.GroupingCharsExtended.IsMatch(value ?? ""); if (templateInfo.MultiplyBy1000 && (flag || flag2)) { if (long.TryParse(RegexCache.NonDigits.Replace(value ?? "", ""), out var result)) { string text = Math.Max(0L, result).ToString("N0", NbspNumberFormat); return ApplyTemplate((text == "0") ? (Plugin.TemplateZero_PlayerUI ?? templateInfo.Template) : templateInfo.Template, text); } string text2 = (value ?? string.Empty).Replace('.', '\u00a0'); return ApplyTemplate((text2 == "0") ? (Plugin.TemplateZero_PlayerUI ?? templateInfo.Template) : templateInfo.Template, text2); } string text3 = FormatNumber(value); return ApplyTemplate((text3 == "0") ? (Plugin.TemplateZero_PlayerUI ?? templateInfo.Template) : templateInfo.Template, text3); } public static string FormatShopCost(string value) { Plugin.TemplateInfo templateInfo = Plugin.Template_ItemCost ?? new Plugin.TemplateInfo("{value}K\u00a0KZT", multiplyBy1000: false); if (templateInfo.MultiplyBy1000 && TryParseDigitsToLong(value, out var value2)) { string text = (RegexCache.GroupingChars.IsMatch(value ?? "") ? Math.Max(0L, value2) : (Math.Max(0L, value2) * 1000)).ToString("N0", NbspNumberFormat); return ApplyTemplate((text == "0") ? (Plugin.TemplateZero_ItemCost ?? templateInfo.Template) : templateInfo.Template, text); } string text2 = FormatNumber(value); return ApplyTemplate((text2 == "0") ? (Plugin.TemplateZero_ItemCost ?? templateInfo.Template) : templateInfo.Template, text2); } public static string FormatItemCost(string value) { Plugin.TemplateInfo templateInfo = Plugin.Template_ItemCost ?? new Plugin.TemplateInfo("{value}K\u00a0KZT", multiplyBy1000: false); if (templateInfo.MultiplyBy1000 && TryParseDigitsToLong(value, out var value2)) { string text = (RegexCache.GroupingChars.IsMatch(value ?? "") ? Math.Max(0L, value2) : (Math.Max(0L, value2) * 1000)).ToString("N0", NbspNumberFormat); return ApplyTemplate((text == "0") ? (Plugin.TemplateZero_ItemCost ?? templateInfo.Template) : templateInfo.Template, text); } string text2 = FormatNumber(value); return ApplyTemplate((text2 == "0") ? (Plugin.TemplateZero_ItemCost ?? templateInfo.Template) : templateInfo.Template, text2); } public static string FormatItemCost(int thousands) { Plugin.TemplateInfo templateInfo = Plugin.Template_ItemCost ?? new Plugin.TemplateInfo("{value}K\u00a0₸", multiplyBy1000: false); if (templateInfo.MultiplyBy1000) { int num = thousands * 1000; return templateInfo.Template.Replace("{value}", num.ToString("N0", NbspNumberFormat)); } return ((thousands == 0) ? (Plugin.TemplateZero_ItemCost ?? templateInfo.Template) : templateInfo.Template).Replace("{value}", thousands.ToString()); } public static string FormatItemValue(string value) { Plugin.TemplateInfo templateInfo = Plugin.Template_ItemCost ?? new Plugin.TemplateInfo("{value}\u00a0тыс.\u00a0₸", multiplyBy1000: false); if (templateInfo.MultiplyBy1000 && TryParseDigitsToLong(value, out var value2)) { string text = (RegexCache.GroupingChars.IsMatch(value ?? "") ? Math.Max(0L, value2) : (Math.Max(0L, value2) * 1000)).ToString("N0", NbspNumberFormat); string text2 = ApplyTemplate((text == "0") ? (Plugin.TemplateZero_ItemPickup ?? templateInfo.Template) : templateInfo.Template, text); LogFormatCall("FormatItemValue(string)", value, text2); return text2; } string text3 = FormatNumber(value); string text4 = ApplyTemplate((text3 == "0") ? (Plugin.TemplateZero_ItemPickup ?? templateInfo.Template) : templateInfo.Template, text3); LogFormatCall("FormatItemValue(string)", value, text4); return text4; } public static string FormatItemValue(int units) { Plugin.TemplateInfo templateInfo = Plugin.Template_ItemCost ?? new Plugin.TemplateInfo("{value}\u00a0тыс.\u00a0₸", multiplyBy1000: false); if (units < 1000) { string text = units + "\u00a0₸"; LogFormatCall("FormatItemValue(int)", units.ToString(), text); return text; } if (templateInfo.MultiplyBy1000) { string text2 = templateInfo.Template.Replace("{value}", units.ToString("N0", NbspNumberFormat)); LogFormatCall("FormatItemValue(int)", units.ToString(), text2); return text2; } int num = units / 1000; string text3 = templateInfo.Template.Replace("{value}", num.ToString()); LogFormatCall("FormatItemValue(int)", units.ToString(), text3); return text3; } public static string FormatBlackbox(long units) { string text = Math.Max(0L, units).ToString("N0", NbspNumberFormat) + "\u00a0₸"; LogFormatCall("FormatBlackbox", units.ToString(), text); return text; } public static string FormatBlackboxDigits(long units) { return Math.Max(0L, units).ToString("N0", NbspNumberFormat); } public static string FormatShoppingListDigits(long units) { string text = Math.Max(0L, units).ToString("N0", NbspNumberFormat); LogFormatCall("FormatShoppingListDigits", units.ToString(), text); return text; } public static string FormatShoppingListUI(long units) { string text = Math.Max(0L, units).ToString("N0", NbspNumberFormat) + "\u00a0₸"; LogFormatCall("FormatShoppingListUI", units.ToString(), text); return text; } public static string FormatHaulUI(string value) { Plugin.TemplateInfo templateInfo = Plugin.Template_HaulUI ?? new Plugin.TemplateInfo("{value}\u00a0тыс.\u00a0₸", multiplyBy1000: false); if (templateInfo.MultiplyBy1000 && TryParseDigitsToLong(value, out var value2)) { string text = (Math.Max(0L, value2) * 1000).ToString("N0", NbspNumberFormat); return ApplyTemplate((text == "0") ? (Plugin.TemplateZero_HaulUI ?? templateInfo.Template) : templateInfo.Template, text); } string text2 = FormatNumber(value); return ApplyTemplate((text2 == "0") ? (Plugin.TemplateZero_HaulUI ?? templateInfo.Template) : templateInfo.Template, text2); } public static string FormatHaulUI(int units) { Plugin.TemplateInfo templateInfo = Plugin.Template_HaulUI ?? new Plugin.TemplateInfo("{value}\u00a0тыс.\u00a0₸", multiplyBy1000: false); string text = Math.Max(0, units).ToString("N0", NbspNumberFormat); return ApplyTemplate((text == "0") ? (Plugin.TemplateZero_HaulUI ?? templateInfo.Template) : templateInfo.Template, text); } public static string FormatHaul(string value) { Plugin.TemplateInfo templateInfo = Plugin.Template_Haul ?? new Plugin.TemplateInfo("{value}K\u00a0₸", multiplyBy1000: false); if (templateInfo.MultiplyBy1000 && TryParseDigitsToLong(value, out var value2)) { string text = (Math.Max(0L, value2) * 1000).ToString("N0", NbspNumberFormat); string text2 = ApplyTemplate((text == "0") ? (Plugin.TemplateZero_Haul ?? templateInfo.Template) : templateInfo.Template, text); LogFormatCall("FormatHaul(string)", value, text2); return text2; } string text3 = FormatNumber(value); string text4 = ApplyTemplate((text3 == "0") ? (Plugin.TemplateZero_Haul ?? templateInfo.Template) : templateInfo.Template, text3); LogFormatCall("FormatHaul(string)", value, text4); return text4; } public static string FormatHaul(int units) { Plugin.TemplateInfo templateInfo = Plugin.Template_Haul ?? new Plugin.TemplateInfo("{value}K\u00a0₸", multiplyBy1000: false); int num = Math.Max(0, units); string text2; if (templateInfo.MultiplyBy1000) { string text = num.ToString("N0", NbspNumberFormat); text2 = ApplyTemplate((text == "0") ? (Plugin.TemplateZero_Haul ?? templateInfo.Template) : templateInfo.Template, text); } else if (num < 1000) { string text3 = num.ToString(); text2 = ApplyTemplate((text3 == "0") ? (Plugin.TemplateZero_Haul ?? templateInfo.Template) : templateInfo.Template, text3); } else { double num2 = (double)num / 1000.0; string text4 = ((Math.Abs(num2 - Math.Truncate(num2)) < 0.001) ? ((int)Math.Truncate(num2)).ToString() : num2.ToString("0.#", CultureInfo.InvariantCulture)); text2 = ApplyTemplate((text4 == "0") ? (Plugin.TemplateZero_Haul ?? templateInfo.Template) : templateInfo.Template, text4); } LogFormatCall("FormatHaul(int)", units.ToString(), text2); return text2; } public static string FormatCart(string value) { Plugin.TemplateInfo templateInfo = Plugin.Template_Cart ?? new Plugin.TemplateInfo("{value}\u00a0тыс.\u00a0₸", multiplyBy1000: false); if (templateInfo.MultiplyBy1000 && TryParseDigitsToLong(value, out var value2)) { string text = (RegexCache.GroupingChars.IsMatch(value ?? "") ? Math.Max(0L, value2) : (Math.Max(0L, value2) * 1000)).ToString("N0", NbspNumberFormat); return ApplyTemplate((text == "0") ? (Plugin.TemplateZero_Cart ?? templateInfo.Template) : templateInfo.Template, text); } string text2 = FormatNumber(value); return ApplyTemplate((text2 == "0") ? (Plugin.TemplateZero_Cart ?? templateInfo.Template) : templateInfo.Template, text2); } public static string FormatCart(int units) { Plugin.TemplateInfo templateInfo = Plugin.Template_Cart ?? new Plugin.TemplateInfo("{value}\u00a0тыс.\u00a0₸", multiplyBy1000: false); int num = Math.Max(0, units); string text2; if (templateInfo.MultiplyBy1000) { string text = num.ToString("N0", NbspNumberFormat); text2 = ApplyTemplate((text == "0") ? (Plugin.TemplateZero_Cart ?? templateInfo.Template) : templateInfo.Template, text); } else if (num < 1000) { string formattedValue = num.ToString(); text2 = ApplyTemplate(Plugin.TemplateZero_Cart ?? "{value}\u00a0тг", formattedValue); } else { double num2 = (double)num / 1000.0; string text3 = ((Math.Abs(num2 - Math.Truncate(num2)) < 0.001) ? ((int)Math.Truncate(num2)).ToString() : num2.ToString("0.#", CultureInfo.InvariantCulture)); text2 = ApplyTemplate((text3 == "0") ? (Plugin.TemplateZero_Cart ?? templateInfo.Template) : templateInfo.Template, text3); } LogFormatCall("FormatCart(int)", units.ToString(), text2); return text2; } private static bool TryParseDigitsToLong(string raw, out long value) { value = 0L; if (string.IsNullOrWhiteSpace(raw)) { return false; } string text = RegexCache.NonDigits.Replace(raw, ""); if (string.IsNullOrEmpty(text)) { return false; } return long.TryParse(text, out value); } private static string FormatNumber(string raw) { if (string.IsNullOrWhiteSpace(raw)) { return raw ?? string.Empty; } string text = raw.Trim(); text = text.Replace("\u00a0", ""); text = text.Replace(" ", ""); if (long.TryParse(text, NumberStyles.Integer, NbspNumberFormat, out var result)) { return result.ToString("N0", NbspNumberFormat); } if (decimal.TryParse(text, NumberStyles.Number, NbspNumberFormat, out var result2)) { return result2.ToString("N2", NbspNumberFormat).TrimEnd('0').TrimEnd('.'); } return raw; } } [HarmonyPatch(typeof(HaulUI), "Update")] public class HaulUIPatch { private static string _lastOriginalText; private static string _lastProcessedText; [HarmonyPostfix] public static void UpdatePostfix(HaulUI __instance, TextMeshProUGUI ___Text) { if ((Object)(object)___Text == (Object)null) { return; } string text = ((TMP_Text)___Text).text; if (string.IsNullOrEmpty(text) || text == _lastProcessedText) { return; } try { int units = (((Object)(object)RoundDirector.instance != (Object)null) ? RoundDirector.instance.currentHaul : 0); int units2 = (((Object)(object)RoundDirector.instance != (Object)null) ? RoundDirector.instance.extractionHaulGoal : 0); string text2 = CurrencyFormatter.FormatHaulUI(units); string text3 = CurrencyFormatter.FormatHaulUI(units2); string text4 = "<size=30>" + text2 + "<color=#616161> <size=45>/</size> </color><u>" + text3 + "</u>"; _lastOriginalText = text; _lastProcessedText = text4; ((TMP_Text)___Text).text = text4; } catch { } } } [HarmonyPatch] public static class BlackboxInitWatcher { [HarmonyPostfix] [HarmonyPatch(typeof(GameObject), "AddComponent", new Type[] { typeof(Type) })] private static void AddComponent_Postfix(GameObject __instance, Type componentType, Component __result) { //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Expected O, but got Unknown try { if (componentType == null) { return; } string text = componentType.FullName ?? string.Empty; if (!((componentType.Name ?? string.Empty) == "Blackbox") && !text.EndsWith(".Blackbox") && !(text == "Blackbox")) { return; } ManualLogSource logger = Plugin.Logger; if (logger != null) { logger.LogInfo((object)"[BlackboxInitWatcher] Detected Blackbox component added — applying runtime patches."); } try { if (BlackboxPatches.TryPatchNow(new Harmony("kztCurrency.blackbox"))) { ManualLogSource logger2 = Plugin.Logger; if (logger2 != null) { logger2.LogInfo((object)"[BlackboxInitWatcher] Blackbox patches applied successfully on AddComponent"); } } else { ManualLogSource logger3 = Plugin.Logger; if (logger3 != null) { logger3.LogDebug((object)"[BlackboxInitWatcher] Blackbox patches not applied (may already be patched or types not found)"); } } } catch (Exception arg) { ManualLogSource logger4 = Plugin.Logger; if (logger4 != null) { logger4.LogWarning((object)$"[BlackboxInitWatcher] Failed to apply Blackbox patches: {arg}"); } } } catch (Exception arg2) { ManualLogSource logger5 = Plugin.Logger; if (logger5 != null) { logger5.LogWarning((object)$"[BlackboxInitWatcher] unexpected error: {arg2}"); } } } } public static class BlackboxPatches { private static readonly Regex DollarNumberRegex = new Regex("\\$(?:<[^>]+>)*\\s*([0-9][0-9\\.,\\u00A0\\s]*)", RegexOptions.Compiled); internal static string Preview(string s, int max = 120) { if (s == null) { return "<null>"; } if (s.Length > max) { return s.Substring(0, max) + "..."; } return s; } internal static string ReplaceDollarNumbers(string input) { if (string.IsNullOrEmpty(input)) { return input; } int replaced = 0; return DollarNumberRegex.Replace(input, delegate(Match m) { string value = m.Groups[1].Value; string text = Regex.Replace(value, "[^0-9]", ""); if (string.IsNullOrEmpty(text)) { return m.Value; } if (!long.TryParse(text, out var result)) { return m.Value; } string text2 = CurrencyFormatter.FormatBlackbox((int)Math.Min(result, 2147483647L)); string value2 = m.Groups[0].Value; int num = value2.IndexOf(value, StringComparison.Ordinal); if (num < 0) { replaced++; ManualLogSource logger = Plugin.Logger; if (logger != null) { logger.LogDebug((object)("[BlackboxPatches] Unexpected match structure, replacing whole '" + value2 + "' with '" + text2 + "'")); } return text2; } string text3 = value2.Substring(0, num); string text4 = string.Concat(str2: value2.Substring(num + value.Length), str0: text3.Replace("$", ""), str1: text2); replaced++; ManualLogSource logger2 = Plugin.Logger; if (logger2 != null) { logger2.LogDebug((object)("[BlackboxPatches] Replaced '" + value2 + "' -> '" + text4 + "' in preview: '" + Preview(input) + "'")); } return text4; }); } public static bool TryPatchNow(Harmony harmony) { int num = 0; try { Type[] array = new Type[3] { typeof(Blackbox_getPostRunStatString_Patch), typeof(ValuableEventTracker_getStatsByPlayerValueLost_Patch), typeof(ValuableEventTracker_getDetailedStatsByValueLost_Patch) }; foreach (Type type in array) { try { List<MethodInfo> list = harmony.CreateClassProcessor(type).Patch(); if (list != null && list.Count > 0) { num++; ManualLogSource logger = Plugin.Logger; if (logger != null) { logger.LogDebug((object)("[BlackboxPatches] Applied patch: " + type.Name)); } } } catch (Exception ex) { ManualLogSource logger2 = Plugin.Logger; if (logger2 != null) { logger2.LogDebug((object)("[BlackboxPatches] Could not apply " + type.Name + ": " + ex.Message)); } } } if (num > 0) { ManualLogSource logger3 = Plugin.Logger; if (logger3 != null) { logger3.LogInfo((object)$"[BlackboxPatches] Successfully applied {num} patch(es)"); } return true; } } catch (Exception arg) { ManualLogSource logger4 = Plugin.Logger; if (logger4 != null) { logger4.LogWarning((object)$"[BlackboxPatches] TryPatchNow failed: {arg}"); } } return false; } } [HarmonyPatch] internal class Blackbox_getPostRunStatString_Patch { private static bool Prepare() { bool flag = AccessTools.TypeByName("Blackbox") != null; ManualLogSource logger = Plugin.Logger; if (logger != null) { logger.LogDebug((object)$"[BlackboxPatches] Prepare Blackbox.getPostRunStatString: {flag}"); } return flag; } private static MethodBase TargetMethod() { return AccessTools.Method("Blackbox:getPostRunStatString", (Type[])null, (Type[])null); } private static void Postfix(ref string __result) { __result = BlackboxPatches.ReplaceDollarNumbers(__result); } } [HarmonyPatch] internal class ValuableEventTracker_getStatsByPlayerValueLost_Patch { private static bool Prepare() { bool flag = AccessTools.TypeByName("ValuableEventTracker") != null; ManualLogSource logger = Plugin.Logger; if (logger != null) { logger.LogDebug((object)$"[BlackboxPatches] Prepare ValuableEventTracker.getStatsByPlayerValueLost: {flag}"); } return flag; } private static MethodBase TargetMethod() { return AccessTools.Method("ValuableEventTracker:getStatsByPlayerValueLost", (Type[])null, (Type[])null); } private static void Postfix(ref string __result) { __result = BlackboxPatches.ReplaceDollarNumbers(__result); } } [HarmonyPatch] internal class ValuableEventTracker_getDetailedStatsByValueLost_Patch { private static bool Prepare() { bool flag = AccessTools.TypeByName("ValuableEventTracker") != null; ManualLogSource logger = Plugin.Logger; if (logger != null) { logger.LogDebug((object)$"[BlackboxPatches] Prepare ValuableEventTracker.getDetailedStatsByValueLost: {flag}"); } return flag; } private static MethodBase TargetMethod() { return AccessTools.Method("ValuableEventTracker:getDetailedStatsByValueLost", (Type[])null, (Type[])null); } private static void Postfix(ref Dictionary<string, string> __result) { if (__result == null) { return; } foreach (string item in new List<string>(__result.Keys)) { string text = BlackboxPatches.Preview(__result[item]); __result[item] = BlackboxPatches.ReplaceDollarNumbers(__result[item]); string text2 = BlackboxPatches.Preview(__result[item]); if (text != text2) { ManualLogSource logger = Plugin.Logger; if (logger != null) { logger.LogDebug((object)("[BlackboxPatches] Replaced in key '" + item + "': '" + text + "' -> '" + text2 + "'")); } } } } } internal static class CurrencyUIHelper { public static int ParseThousandsToBase(string valueInThousands) { if (int.TryParse(Regex.Replace(valueInThousands ?? "", "[^0-9]", ""), out var result)) { return result * 1000; } return 0; } } [HarmonyPatch(typeof(CurrencyUI), "Update")] public class CurrencyUIPatch { private static TMP_FontAsset _cachedTengeFont; [HarmonyPostfix] public static void UpdatePostfix(CurrencyUI __instance, TextMeshProUGUI ___Text, ref float ___showTimer, ref int ___currentHaulValue) { if (!(___showTimer > 0f) || !((Object)(object)___Text != (Object)null)) { return; } string text = ((TMP_Text)___Text).text; if (text.StartsWith("$") && text.EndsWith("K")) { int units = CurrencyUIHelper.ParseThousandsToBase(text.Substring(1, text.Length - 2)); if (_cachedTengeFont == null) { _cachedTengeFont = TengeSymbolSupport.GetTengeFallbackFont(); } if ((Object)(object)_cachedTengeFont != (Object)null && (Object)(object)((TMP_Text)___Text).font != (Object)(object)_cachedTengeFont) { TengeSymbolSupport.SetPrimaryFont((TMP_Text)(object)___Text); } string text2 = CurrencyFormatter.FormatHaul(units); ((TMP_Text)___Text).text = text2; } } } [HarmonyPatch(typeof(CurrencyUI), "FetchCurrency")] public class CurrencyUIFetchCurrencyPatch { [HarmonyPostfix] public static void FetchCurrencyPostfix(CurrencyUI __instance, TextMeshProUGUI ___Text) { if ((Object)(object)___Text != (Object)null) { string text = ((TMP_Text)___Text).text; if (text.StartsWith("$") && text.EndsWith("K")) { int units = CurrencyUIHelper.ParseThousandsToBase(text.Substring(1, text.Length - 2)); TengeSymbolSupport.EnsureFallbackFont((TMP_Text)(object)___Text); ((TMP_Text)___Text).text = CurrencyFormatter.FormatHaul(units); } } } } [HarmonyPatch(typeof(ExtractionPoint))] internal class ExtractionPointPatch { private static readonly Regex TagRemoval = new Regex("<.*?>", RegexOptions.Compiled); [HarmonyPatch("SetHaulText")] [HarmonyPostfix] private static void SetHaulTextPostfix(ExtractionPoint __instance, int ___haulCurrent, bool ___isShop) { if ((Object)(object)__instance.haulGoalScreen == (Object)null) { return; } string text = ((TMP_Text)__instance.haulGoalScreen).text; if (string.IsNullOrEmpty(text) || text.Contains("₸") || text.Contains("тг") || text.Contains("KZT")) { return; } string text2 = TagRemoval.Replace(text, ""); if (text2.Contains("$")) { if (text.Contains("TAX")) { string text3 = text.Replace("$", "₸"); ((TMP_Text)__instance.haulGoalScreen).text = text3; return; } int num = ((!___isShop) ? ___haulCurrent : (__instance.haulGoal - ___haulCurrent)); bool flag = text2.TrimStart().StartsWith("-"); string text4 = CurrencyFormatter.FormatHaul(Math.Abs(num)); string text5 = ((!(num < 0 || flag)) ? text4 : ("<color=#bd4300>-" + text4 + "</color>")); ((TMP_Text)__instance.haulGoalScreen).text = text5; } } [HarmonyPatch("StateSuccess")] [HarmonyPostfix] private static void StateSuccessPostfix(ExtractionPoint __instance, int ___haulCurrent, bool ___isShop) { if ((Object)(object)__instance.haulGoalScreen == (Object)null) { return; } string text = ((TMP_Text)__instance.haulGoalScreen).text; if (!string.IsNullOrEmpty(text) && text.Contains("$") && !text.Contains("₸") && !text.Contains("тг") && !text.Contains("KZT")) { if (text.Contains("TAX")) { string text2 = text.Replace("$", "₸"); ((TMP_Text)__instance.haulGoalScreen).text = text2; return; } int units = Math.Max(0, __instance.haulGoal - ___haulCurrent); TagRemoval.Replace(text, "").TrimStart().StartsWith("-"); string text3 = CurrencyFormatter.FormatHaul(units); ((TMP_Text)__instance.haulGoalScreen).text = text3; } } [HarmonyPatch("StateSurplus")] [HarmonyPostfix] private static void StateSurplusPostfix(ExtractionPoint __instance) { if ((Object)(object)__instance.haulGoalScreen == (Object)null) { return; } string text = ((TMP_Text)__instance.haulGoalScreen).text; if (string.IsNullOrEmpty(text) || !text.Contains("$") || text.Contains("₸") || text.Contains("тг") || text.Contains("KZT")) { return; } string text2 = TagRemoval.Replace(text, ""); if (text.Contains("TAX")) { string text3 = text.Replace("$", "₸"); ((TMP_Text)__instance.haulGoalScreen).text = text3; return; } bool flag = text2.TrimStart().StartsWith("+"); bool flag2 = text2.TrimStart().StartsWith("-"); if (!int.TryParse(text2.Replace("+", "").Replace("-", "").Replace("$", "") .Replace(".", "") .Replace(" ", "") .Replace(",", ""), out var result)) { string text4 = text.Replace("$", "₸"); ((TMP_Text)__instance.haulGoalScreen).text = text4; return; } if (flag2 && result > 0) { result = -result; } string text5 = CurrencyFormatter.FormatHaul(Math.Abs(result)); string text6 = ((result < 0 || flag2) ? ("<color=#bd4300>-" + text5 + "</color>") : ((!flag) ? text5 : ("+" + text5))); ((TMP_Text)__instance.haulGoalScreen).text = text6; } } [HarmonyPatch(typeof(PhysGrabCart), "SetHaulText")] internal class PhysGrabCartHaulTextPatch { [HarmonyPostfix] private static void SetHaulTextPostfix(PhysGrabCart __instance, int ___haulCurrent) { try { if (!((Object)(object)__instance == (Object)null) && !((Object)(object)__instance.displayText == (Object)null)) { TengeSymbolSupport.EnsureFallbackFont((TMP_Text)(object)__instance.displayText); string text = CurrencyFormatter.FormatCart(___haulCurrent); ((TMP_Text)__instance.displayText).text = text; } } catch (Exception arg) { Debug.LogError((object)$"[PhysGrabCart] Exception in SetHaulTextPostfix: {arg}"); } } } [HarmonyPatch(typeof(ShopCostUI), "Update")] public class ShopCostUIPatch { private static TMP_FontAsset _cachedTengeFont; [HarmonyPostfix] public static void UpdatePostfix(ShopCostUI __instance, TextMeshProUGUI ___Text) { if ((Object)(object)___Text != (Object)null && !string.IsNullOrEmpty(((TMP_Text)___Text).text)) { string text = ((TMP_Text)___Text).text; if (text.StartsWith("-$") && text.EndsWith("K")) { string value = text.Substring(2, text.Length - 3); ((TMP_Text)___Text).text = "-" + CurrencyFormatter.FormatShopCost(value); } else if (text.StartsWith("+$") && text.EndsWith("K")) { string value2 = text.Substring(2, text.Length - 3); ((TMP_Text)___Text).text = "+" + CurrencyFormatter.FormatShopCost(value2); } if (_cachedTengeFont == null) { _cachedTengeFont = TengeSymbolSupport.GetTengeFallbackFont(); } if ((Object)(object)_cachedTengeFont != (Object)null && (Object)(object)((TMP_Text)___Text).font != (Object)(object)_cachedTengeFont) { TengeSymbolSupport.SetPrimaryFont((TMP_Text)(object)___Text); } } } } [HarmonyPatch] public static class ShoppingListInitWatcher { [HarmonyPostfix] [HarmonyPatch(typeof(GameObject), "AddComponent", new Type[] { typeof(Type) })] private static void AddComponent_Postfix(GameObject __instance, Type componentType, Component __result) { //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Expected O, but got Unknown try { if (componentType == null) { return; } string text = componentType.FullName ?? string.Empty; if (!((componentType.Name ?? string.Empty) == "ShoppingListHUD") && !text.EndsWith(".ShoppingListHUD") && !(text == "ShoppingListHUD")) { return; } ManualLogSource logger = Plugin.Logger; if (logger != null) { logger.LogInfo((object)"[ShoppingListInitWatcher] Detected ShoppingListHUD component added — applying runtime patches."); } try { if (ShoppingListPatches.TryPatchNow(new Harmony("kztCurrency.shoppinglist"))) { ManualLogSource logger2 = Plugin.Logger; if (logger2 != null) { logger2.LogInfo((object)"[ShoppingListInitWatcher] ShoppingList patches applied successfully on AddComponent"); } } else { ManualLogSource logger3 = Plugin.Logger; if (logger3 != null) { logger3.LogDebug((object)"[ShoppingListInitWatcher] ShoppingList patches not applied (may already be patched or types not found)"); } } } catch (Exception arg) { ManualLogSource logger4 = Plugin.Logger; if (logger4 != null) { logger4.LogWarning((object)$"[ShoppingListInitWatcher] Failed to apply ShoppingList patches: {arg}"); } } } catch (Exception arg2) { ManualLogSource logger5 = Plugin.Logger; if (logger5 != null) { logger5.LogWarning((object)$"[ShoppingListInitWatcher] unexpected error: {arg2}"); } } } } public static class ShoppingListPatches { private static readonly Regex s_colorWrapped = new Regex("\\$<(?<tag>color=[^>]+)>(?<num>[0-9\\u00A0\\s\\.,]+)</color>", RegexOptions.Compiled); private static readonly Regex s_simpleDollar = new Regex("\\$([0-9\\u00A0\\s\\.,]+)", RegexOptions.Compiled); internal static FieldInfo s_statsInstanceField; internal static FieldInfo s_textField; internal static FieldInfo s_textNumbersField; internal static FieldInfo s_upgradesHeaderField; internal static readonly ConcurrentDictionary<Type, PropertyInfo> s_textPropCache = new ConcurrentDictionary<Type, PropertyInfo>(); internal static string Preview(string s, int max = 120) { if (s == null) { return "<null>"; } if (s.Length > max) { return s.Substring(0, max) + "..."; } return s; } internal static string ReplaceDollarNumbers(string input) { if (string.IsNullOrEmpty(input)) { return input; } int replaced = 0; if (!input.Contains("$")) { return input; } string text = s_colorWrapped.Replace(input, delegate(Match m) { string value = m.Groups["tag"].Value; if (!long.TryParse(Regex.Replace(m.Groups["num"].Value, "[^0-9]", ""), out var result2)) { return m.Value; } string text2 = CurrencyFormatter.FormatShoppingListDigits(result2); replaced++; return "<" + value + ">" + text2 + "</color>" + CurrencyFormatter.NBSP + "₸"; }); if (replaced == 0) { text = s_simpleDollar.Replace(text, delegate(Match m) { if (!long.TryParse(Regex.Replace(m.Groups[1].Value, "[^0-9]", ""), out var result)) { return m.Value; } replaced++; return CurrencyFormatter.FormatShoppingListUI(result); }); } if (replaced > 0) { ManualLogSource logger = Plugin.Logger; if (logger != null) { logger.LogDebug((object)$"[ShoppingListPatches] Replaced {replaced} dollar-formatted value(s)"); } } return text; } public static bool TryPatchNow(Harmony harmony) { try { List<MethodInfo> list = harmony.CreateClassProcessor(typeof(ShoppingListHUDPatches_SetUITexts_Patch)).Patch(); if (list != null && list.Count > 0) { ManualLogSource logger = Plugin.Logger; if (logger != null) { logger.LogInfo((object)"[ShoppingListPatches] Successfully applied patch"); } return true; } } catch (Exception ex) { ManualLogSource logger2 = Plugin.Logger; if (logger2 != null) { logger2.LogDebug((object)("[ShoppingListPatches] TryPatchNow failed: " + ex.Message)); } } return false; } internal static void UpdateUITexts() { try { try { if (s_statsInstanceField == null) { Type type = AccessTools.TypeByName("StatsUI"); if (type != null) { s_statsInstanceField = AccessTools.Field(type, "instance"); s_textField = AccessTools.Field(type, "Text"); s_textNumbersField = AccessTools.Field(type, "textNumbers"); s_upgradesHeaderField = AccessTools.Field(type, "upgradesHeader"); } } object obj = s_statsInstanceField?.GetValue(null); if (obj == null) { return; } object obj2 = s_textField?.GetValue(obj); object obj3 = s_textNumbersField?.GetValue(obj); object obj4 = s_upgradesHeaderField?.GetValue(obj); if (obj2 != null) { Type type2 = obj2.GetType(); PropertyInfo orAdd = s_textPropCache.GetOrAdd(type2, (Type tt) => tt.GetProperty("text")); if (orAdd != null) { string text = orAdd.GetValue(obj2) as string; if (!string.IsNullOrEmpty(text) && text.Contains("$")) { string text2 = ReplaceDollarNumbers(text); if (text2 != text) { orAdd.SetValue(obj2, text2); ManualLogSource logger = Plugin.Logger; if (logger != null) { logger.LogDebug((object)"[ShoppingListPatches] Updated TMP_Text.Text"); } } } } } if (obj3 != null) { Type type3 = obj3.GetType(); PropertyInfo orAdd2 = s_textPropCache.GetOrAdd(type3, (Type tt) => tt.GetProperty("text")); if (orAdd2 != null) { string text3 = orAdd2.GetValue(obj3) as string; if (!string.IsNullOrEmpty(text3) && text3.Contains("$")) { string text4 = ReplaceDollarNumbers(text3); if (text4 != text3) { orAdd2.SetValue(obj3, text4); ManualLogSource logger2 = Plugin.Logger; if (logger2 != null) { logger2.LogDebug((object)"[ShoppingListPatches] Updated TMP_Text.textNumbers"); } } } } } if (obj4 == null) { return; } Type type4 = obj4.GetType(); PropertyInfo orAdd3 = s_textPropCache.GetOrAdd(type4, (Type tt) => tt.GetProperty("text")); if (!(orAdd3 != null)) { return; } string text5 = orAdd3.GetValue(obj4) as string; if (string.IsNullOrEmpty(text5) || !text5.Contains("$")) { return; } string text6 = ReplaceDollarNumbers(text5); if (text6 != text5) { orAdd3.SetValue(obj4, text6); ManualLogSource logger3 = Plugin.Logger; if (logger3 != null) { logger3.LogDebug((object)"[ShoppingListPatches] Updated TMP_Text.upgradesHeader"); } } } catch (Exception arg) { ManualLogSource logger4 = Plugin.Logger; if (logger4 != null) { logger4.LogWarning((object)$"[ShoppingListPatches] Postfix_SetUITexts inner error: {arg}"); } } } catch (Exception arg2) { ManualLogSource logger5 = Plugin.Logger; if (logger5 != null) { logger5.LogWarning((object)$"[ShoppingListPatches] Postfix_SetUITexts error: {arg2}"); } } } } [HarmonyPatch] internal class ShoppingListHUDPatches_SetUITexts_Patch { private static bool Prepare() { bool flag = AccessTools.TypeByName("ShoppingListHUDPatches") != null; ManualLogSource logger = Plugin.Logger; if (logger != null) { logger.LogDebug((object)$"[ShoppingListPatches] Prepare ShoppingListHUDPatches.SetUITexts: {flag}"); } return flag; } private static MethodBase TargetMethod() { return AccessTools.Method("ShoppingListHUDPatches:SetUITexts", (Type[])null, (Type[])null); } private static void Postfix() { ShoppingListPatches.UpdateUITexts(); } } [HarmonyPatch(/*Could not decode attribute arguments.*/)] internal static class TMPTextAutoFontPatch { [HarmonyPrefix] private static void Prefix(TMP_Text __instance, ref string value) { if (!((Object)(object)__instance == (Object)null) && !string.IsNullOrEmpty(value) && ContainsCyrillicOrTenge(value)) { TMP_FontAsset tengeFallbackFont = TengeSymbolSupport.GetTengeFallbackFont(); if (!((Object)(object)tengeFallbackFont == (Object)null) && !((Object)(object)__instance.font == (Object)(object)tengeFallbackFont)) { TengeSymbolSupport.SetPrimaryFont(__instance); } } } private static bool ContainsCyrillicOrTenge(string s) { foreach (char c in s) { if ((c >= 'Ѐ' && c <= 'ӿ') || c == '₸') { return true; } } return false; } } [HarmonyPatch(typeof(WorldSpaceUIValueLost), "Start")] public static class WorldSpaceUIValueLostPatch { [HarmonyPostfix] public static void StartPostfix(WorldSpaceUIValueLost __instance, TextMeshProUGUI ___text, int ___value) { if ((Object)(object)___text == (Object)null) { return; } TengeSymbolSupport.SetPrimaryFont((TMP_Text)(object)___text); try { ((TMP_Text)___text).text = "-" + CurrencyFormatter.FormatItemValue(___value); } catch { ((TMP_Text)___text).text = "-$" + SemiFunc.DollarGetString(___value); } } } [HarmonyPatch(typeof(WorldSpaceUIValue), "Show")] public class WorldSpaceUIValuePatch { [HarmonyPostfix] public static void ShowPostfix(WorldSpaceUIValue __instance, PhysGrabObject _grabObject, int _value, bool _cost, TextMeshProUGUI ___text, PhysGrabObject ___currentPhysGrabObject) { if ((!((Object)(object)___currentPhysGrabObject != (Object)null) || !((Object)(object)___currentPhysGrabObject != (Object)(object)_grabObject)) && !((Object)(object)___text == (Object)null) && !string.IsNullOrEmpty(((TMP_Text)___text).text)) { _ = ((TMP_Text)___text).text; TengeSymbolSupport.SetPrimaryFont((TMP_Text)(object)___text); if (_cost) { ((TMP_Text)___text).text = "-" + CurrencyFormatter.FormatItemCost(_value); } else { ((TMP_Text)___text).text = CurrencyFormatter.FormatItemValue(_value); } } } } [BepInPlugin("kztCurrency", "kztCurrency", "1.2.1")] public class Plugin : BaseUnityPlugin { internal class TemplateInfo { public string Template { get; set; } public bool MultiplyBy1000 { get; set; } public TemplateInfo(string template, bool multiplyBy1000) { Template = template; MultiplyBy1000 = multiplyBy1000; } } internal static ManualLogSource Logger; private static readonly Regex ZeroSuffixPattern = new Regex("\\{value\\}(?:[Kkк]|[\\u00A0 ]?тыс\\.?)", RegexOptions.Compiled); private static readonly Dictionary<string, TemplateInfo> ExampleToTemplate_PlayerUI = new Dictionary<string, TemplateInfo> { { "100K тг", new TemplateInfo("{value}K\u00a0тг", multiplyBy1000: false) }, { "100K ₸", new TemplateInfo("{value}K\u00a0₸", multiplyBy1000: false) }, { "100K KZT", new TemplateInfo("{value}K\u00a0KZT", multiplyBy1000: false) }, { "100к тг", new TemplateInfo("{value}к\u00a0тг", multiplyBy1000: false) }, { "100к ₸", new TemplateInfo("{value}к\u00a0₸", multiplyBy1000: false) }, { "100к KZT", new TemplateInfo("{value}к\u00a0KZT", multiplyBy1000: false) }, { "100 тыс. тг", new TemplateInfo("{value}\u00a0тыс.\u00a0тг", multiplyBy1000: false) }, { "100 000 тг", new TemplateInfo("{value}\u00a0тг", multiplyBy1000: true) }, { "100 000 ₸", new TemplateInfo("{value}\u00a0₸", multiplyBy1000: true) }, { "100 000 KZT", new TemplateInfo("{value}\u00a0KZT", multiplyBy1000: true) } }; private static readonly Dictionary<string, TemplateInfo> ExampleToTemplate_ItemCost = new Dictionary<string, TemplateInfo> { { "100K тг", new TemplateInfo("{value}K\u00a0тг", multiplyBy1000: false) }, { "100K ₸", new TemplateInfo("{value}K\u00a0₸", multiplyBy1000: false) }, { "100K KZT", new TemplateInfo("{value}K\u00a0KZT", multiplyBy1000: false) }, { "100 тыс. тг", new TemplateInfo("{value}\u00a0тыс.\u00a0тг", multiplyBy1000: false) }, { "100 тыс. ₸", new TemplateInfo("{value}\u00a0тыс.\u00a0₸", multiplyBy1000: false) }, { "100 тыс. KZT", new TemplateInfo("{value}\u00a0тыс.\u00a0KZT", multiplyBy1000: false) }, { "100 000 тг", new TemplateInfo("{value}\u00a0тг", multiplyBy1000: true) }, { "100 000 ₸", new TemplateInfo("{value}\u00a0₸", multiplyBy1000: true) }, { "100 000 KZT", new TemplateInfo("{value}\u00a0KZT", multiplyBy1000: true) } }; private static readonly Dictionary<string, TemplateInfo> ExampleToTemplate_HaulUI = new Dictionary<string, TemplateInfo> { { "100K тг", new TemplateInfo("{value}K\u00a0<color=#558B2F>тг</color>", multiplyBy1000: false) }, { "100K ₸", new TemplateInfo("{value}K\u00a0<color=#558B2F>₸</color>", multiplyBy1000: false) }, { "100K KZT", new TemplateInfo("{value}K\u00a0<color=#558B2F>KZT</color>", multiplyBy1000: false) }, { "100к тг", new TemplateInfo("{value}к\u00a0<color=#558B2F>тг</color>", multiplyBy1000: false) }, { "100к ₸", new TemplateInfo("{value}к\u00a0<color=#558B2F>₸</color>", multiplyBy1000: false) }, { "100к KZT", new TemplateInfo("{value}к\u00a0<color=#558B2F>KZT</color>", multiplyBy1000: false) }, { "100 000 тг", new TemplateInfo("{value}\u00a0<color=#558B2F>тг</color>", multiplyBy1000: true) }, { "100 000 ₸", new TemplateInfo("{value}\u00a0<color=#558B2F>₸</color>", multiplyBy1000: true) }, { "100 000 KZT", new TemplateInfo("{value}\u00a0<color=#558B2F>KZT</color>", multiplyBy1000: true) } }; private static readonly Dictionary<string, TemplateInfo> ExampleToTemplate_Haul = new Dictionary<string, TemplateInfo> { { "100K тг", new TemplateInfo("{value}K\u00a0тг", multiplyBy1000: false) }, { "100K ₸", new TemplateInfo("{value}K\u00a0₸", multiplyBy1000: false) }, { "100K KZT", new TemplateInfo("{value}K\u00a0KZT", multiplyBy1000: false) }, { "100к тг", new TemplateInfo("{value}к\u00a0тг", multiplyBy1000: false) }, { "100к ₸", new TemplateInfo("{value}к\u00a0₸", multiplyBy1000: false) }, { "100 000 тг", new TemplateInfo("{value}\u00a0тг", multiplyBy1000: true) }, { "100 000 ₸", new TemplateInfo("{value}\u00a0₸", multiplyBy1000: true) }, { "100 000 KZT", new TemplateInfo("{value}\u00a0KZT", multiplyBy1000: true) } }; private static readonly Dictionary<string, TemplateInfo> ExampleToTemplate_Cart = new Dictionary<string, TemplateInfo> { { "100 тыс. тг", new TemplateInfo("{value}\u00a0тыс.\u00a0тг", multiplyBy1000: false) }, { "100 тыс. ₸", new TemplateInfo("{value}\u00a0тыс.\u00a0₸", multiplyBy1000: false) }, { "100 тыс. KZT", new TemplateInfo("{value}\u00a0тыс.\u00a0KZT", multiplyBy1000: false) }, { "100 тг", new TemplateInfo("{value}\u00a0тг", multiplyBy1000: false) }, { "100 000 тг", new TemplateInfo("{value}\u00a0тг", multiplyBy1000: true) }, { "100 000 ₸", new TemplateInfo("{value}\u00a0₸", multiplyBy1000: true) }, { "100 000 KZT", new TemplateInfo("{value}\u00a0KZT", multiplyBy1000: true) } }; internal static ConfigEntry<string> Format_PlayerUI; internal static ConfigEntry<string> Format_ItemCost; internal static ConfigEntry<string> Format_ItemPickup; internal static ConfigEntry<string> Format_ExtractionPointUI; internal static ConfigEntry<string> Format_ExtractionPoint; internal static ConfigEntry<string> Format_Cart; internal static TemplateInfo Template_PlayerUI; internal static TemplateInfo Template_ItemCost; internal static TemplateInfo Template_HaulUI; internal static TemplateInfo Template_Haul; internal static TemplateInfo Template_Cart; internal static string TemplateZero_PlayerUI; internal static string TemplateZero_ItemCost; internal static string TemplateZero_ItemPickup; internal static string TemplateZero_HaulUI; internal static string TemplateZero_Haul; internal static string TemplateZero_Cart; private static string MakeZeroFriendlyTemplate(string template) { if (string.IsNullOrEmpty(template)) { return template; } return ZeroSuffixPattern.Replace(template, "{value}"); } private static TemplateInfo ConvertExampleToTemplateInfo(string example, Dictionary<string, TemplateInfo> mapping, string mappingName = "Unknown") { if (mapping.TryGetValue(example, out var value)) { return value; } Logger.LogWarning((object)("[Formatting] No mapping found for example '" + example + "' in " + mappingName + " mapper. Using fallback with MultiplyBy1000=false.")); return new TemplateInfo(example, multiplyBy1000: false); } internal static TemplateInfo GetFormatTemplateInfo(ConfigEntry<string> configEntry, TemplateInfo defaultTemplateInfo, Dictionary<string, TemplateInfo> mapping, string mappingName = "Unknown") { if (configEntry == null || configEntry.Value == null) { return defaultTemplateInfo; } return ConvertExampleToTemplateInfo(configEntry.Value, mapping, mappingName); } internal static void RefreshTemplates(string reason) { try { string arg = Format_PlayerUI?.Value; string arg2 = Format_ItemCost?.Value; string arg3 = Format_ItemPickup?.Value; string arg4 = Format_ExtractionPointUI?.Value; string arg5 = Format_Cart?.Value; Template_PlayerUI = GetFormatTemplateInfo(Format_PlayerUI, new TemplateInfo("{value}K\u00a0₸", multiplyBy1000: false), ExampleToTemplate_PlayerUI, "PlayerUI"); Template_ItemCost = GetFormatTemplateInfo(Format_ItemCost, new TemplateInfo("{value}K\u00a0KZT", multiplyBy1000: false), ExampleToTemplate_ItemCost, "ItemCost"); Template_HaulUI = GetFormatTemplateInfo(Format_ExtractionPointUI, new TemplateInfo("{value}\u00a0тыс.\u00a0₸", multiplyBy1000: false), ExampleToTemplate_HaulUI, "HaulUI"); Template_Haul = GetFormatTemplateInfo(Format_ExtractionPoint, new TemplateInfo("{value}K\u00a0₸", multiplyBy1000: false), ExampleToTemplate_Haul, "Haul"); Template_Cart = GetFormatTemplateInfo(Format_Cart, new TemplateInfo("{value}\u00a0тыс.\u00a0₸", multiplyBy1000: false), ExampleToTemplate_Cart, "Cart"); TemplateZero_PlayerUI = MakeZeroFriendlyTemplate(Template_PlayerUI.Template); TemplateZero_ItemCost = MakeZeroFriendlyTemplate(Template_ItemCost.Template); TemplateZero_ItemPickup = MakeZeroFriendlyTemplate(Template_ItemCost.Template); TemplateZero_HaulUI = MakeZeroFriendlyTemplate(Template_HaulUI.Template); TemplateZero_Haul = MakeZeroFriendlyTemplate(Template_Haul.Template); TemplateZero_Cart = MakeZeroFriendlyTemplate(Template_Cart.Template); Logger.LogInfo((object)("[Formatting] Refreshed templates (" + reason + ")")); Logger.LogInfo((object)$" PlayerUI: raw='{arg}' => tpl='{Template_PlayerUI.Template}' x1000={Template_PlayerUI.MultiplyBy1000}"); Logger.LogInfo((object)$" ItemCost: raw='{arg2}' => tpl='{Template_ItemCost.Template}' x1000={Template_ItemCost.MultiplyBy1000}"); Logger.LogInfo((object)$" ItemPickup: raw='{arg3}' => tpl='{Template_ItemCost.Template}' x1000={Template_ItemCost.MultiplyBy1000}"); Logger.LogInfo((object)$" Haul: raw='{arg4}' => tpl='{Template_Haul.Template}' x1000={Template_Haul.MultiplyBy1000}"); Logger.LogInfo((object)$" HaulUI: raw='{arg4}' => tpl='{Template_HaulUI.Template}' x1000={Template_HaulUI.MultiplyBy1000}"); Logger.LogInfo((object)$" Cart: raw='{arg5}' => tpl='{Template_Cart.Template}' x1000={Template_Cart.MultiplyBy1000}"); } catch (Exception ex) { Logger.LogWarning((object)("[Formatting] Failed to log templates: " + ex.Message)); } } private void Awake() { //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Expected O, but got Unknown //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Expected O, but got Unknown //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Expected O, but got Unknown //IL_01ca: Unknown result type (might be due to invalid IL or missing references) //IL_01d4: Expected O, but got Unknown //IL_0243: Unknown result type (might be due to invalid IL or missing references) //IL_024d: Expected O, but got Unknown //IL_02ac: Unknown result type (might be due to invalid IL or missing references) //IL_02b6: Expected O, but got Unknown //IL_02f9: Unknown result type (might be due to invalid IL or missing references) Logger = ((BaseUnityPlugin)this).Logger; Format_PlayerUI = ((BaseUnityPlugin)this).Config.Bind<string>("Formatting", "PlayerUI", "100 000 ₸", new ConfigDescription("Player UI currency format.", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[9] { "100K тг", "100K ₸", "100K KZT", "100к тг", "100к ₸", "100 тыс. тг", "100 000 тг", "100 000 ₸", "100 000 KZT" }), Array.Empty<object>())); Format_ItemCost = ((BaseUnityPlugin)this).Config.Bind<string>("Formatting", "ItemCost", "100K ₸", new ConfigDescription("Shop/cart UI currency format.", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[6] { "100K тг", "100K ₸", "100K KZT", "100 000 тг", "100 000 ₸", "100 000 KZT" }), Array.Empty<object>())); Format_ItemPickup = ((BaseUnityPlugin)this).Config.Bind<string>("Formatting", "ItemPickup", "100 000 ₸", new ConfigDescription("Item pickup/value UI currency format. For values >= 1000: uses thousands format; for < 1000: shows units.", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[5] { "100 тыс. тг", "100 тыс. ₸", "100 тыс. KZT", "100 000 тг", "100 000 ₸" }), Array.Empty<object>())); Format_ExtractionPointUI = ((BaseUnityPlugin)this).Config.Bind<string>("Formatting", "HaulUI", "100 000 ₸", new ConfigDescription("Extraction point UI currency format.", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[9] { "100K тг", "100K ₸", "100K KZT", "100к тг", "100к ₸", "100к KZT", "100 000 тг", "100 000 ₸", "100 000 KZT" }), Array.Empty<object>())); Format_ExtractionPoint = ((BaseUnityPlugin)this).Config.Bind<string>("Formatting", "ExtractionPoint", "100 000 ₸", new ConfigDescription("Extraction point (plain) currency format used by extraction point screens.", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[8] { "100K тг", "100K ₸", "100K KZT", "100к тг", "100к ₸", "100 000 тг", "100 000 ₸", "100 000 KZT" }), Array.Empty<object>())); Format_Cart = ((BaseUnityPlugin)this).Config.Bind<string>("Formatting", "Cart", "100 тыс. тг", new ConfigDescription("Cart haul currency format.", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[6] { "100 тыс. тг", "100 тыс. ₸", "100 тыс. KZT", "100 000 тг", "100 000 ₸", "100 000 KZT" }), Array.Empty<object>())); TengeSymbolSupport.Initialize(); RefreshTemplates("startup"); ((BaseUnityPlugin)this).Config.SettingChanged += delegate(object _, SettingChangedEventArgs args) { RefreshTemplates("configChanged: " + args.ChangedSetting.Definition.Key); }; new Harmony("kztCurrency").PatchAll(); Logger.LogInfo((object)"Plugin loaded!"); } } public static class TengeSymbolSupport { private const string BUNDLE_FILE_NAME = "teko-cyrillic-tenge.bundle"; private static TMP_FontAsset tengeFallbackFont; private static bool initialized; public static void Initialize() { if (!initialized) { LoadFontAsset(); if ((Object)(object)tengeFallbackFont == (Object)null) { Plugin.Logger.LogError((object)"Failed to load Tenge font asset"); return; } AddFallbackToDefaultFont(); InjectFallbackIntoAllFonts(); initialized = true; Plugin.Logger.LogInfo((object)"Tenge symbol (₸) support initialized"); } } private static void LoadFontAsset() { Assembly executingAssembly = Assembly.GetExecutingAssembly(); string text = executingAssembly.GetManifestResourceNames().FirstOrDefault((string r) => r.EndsWith("teko-cyrillic-tenge.bundle", StringComparison.OrdinalIgnoreCase)); if (text == null) { throw new Exception("Embedded resource ending with 'teko-cyrillic-tenge.bundle' not found"); } using Stream stream = executingAssembly.GetManifestResourceStream(text); if (stream == null) { throw new Exception("Failed to open resource stream: " + text); } byte[] array = new byte[stream.Length]; stream.Read(array, 0, array.Length); AssetBundle obj = AssetBundle.LoadFromMemory(array); if ((Object)(object)obj == (Object)null) { throw new Exception("Failed to load AssetBundle from memory"); } TMP_FontAsset[] array2 = obj.LoadAllAssets<TMP_FontAsset>(); if (array2 == null || array2.Length == 0) { throw new Exception("No TMP_FontAsset found in bundle"); } tengeFallbackFont = array2[0]; EnsureFontMaterial(); } private static void EnsureFontMaterial() { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Expected O, but got Unknown if ((Object)(object)((TMP_Asset)tengeFallbackFont).material != (Object)null) { return; } Shader val = Shader.Find("TextMeshPro/Distance Field") ?? Shader.Find("TextMeshPro/Mobile/Distance Field"); if ((Object)(object)val == (Object)null) { Plugin.Logger.LogWarning((object)"TMP shader not found"); return; } Material val2 = new Material(val) { name = "TengeFont_Material" }; if ((Object)(object)tengeFallbackFont.atlasTexture != (Object)null) { val2.mainTexture = (Texture)(object)tengeFallbackFont.atlasTexture; } ((TMP_Asset)tengeFallbackFont).material = val2; } private static void AddFallbackToDefaultFont() { List<TMP_FontAsset> fallbackFontAssets = TMP_Settings.fallbackFontAssets; if (fallbackFontAssets != null && !fallbackFontAssets.Contains(tengeFallbackFont)) { fallbackFontAssets.Insert(0, tengeFallbackFont); } TMP_FontAsset defaultFontAsset = TMP_Settings.defaultFontAsset; if (!((Object)(object)defaultFontAsset == (Object)null)) { List<TMP_FontAsset> list2 = (defaultFontAsset.fallbackFontAssetTable = defaultFontAsset.fallbackFontAssetTable ?? new List<TMP_FontAsset>()); if (!list2.Contains(tengeFallbackFont)) { list2.Insert(0, tengeFallbackFont); } } } private static void InjectFallbackIntoAllFonts() { TMP_FontAsset[] array = Resources.FindObjectsOfTypeAll<TMP_FontAsset>(); foreach (TMP_FontAsset val in array) { if (!((Object)(object)val == (Object)null) && val != tengeFallbackFont) { List<TMP_FontAsset> list2 = (val.fallbackFontAssetTable = val.fallbackFontAssetTable ?? new List<TMP_FontAsset>()); if (!list2.Contains(tengeFallbackFont)) { list2.Insert(0, tengeFallbackFont); } } } } public static void EnsureFallbackFont(TMP_Text textComponent) { if ((Object)(object)textComponent == (Object)null || (Object)(object)tengeFallbackFont == (Object)null) { return; } TMP_FontAsset font = textComponent.font; if (!((Object)(object)font == (Object)null)) { List<TMP_FontAsset> list2 = (font.fallbackFontAssetTable = font.fallbackFontAssetTable ?? new List<TMP_FontAsset>()); if (!list2.Contains(tengeFallbackFont)) { list2.Add(tengeFallbackFont); } } } public static void SetPrimaryFont(TMP_Text textComponent) { if (!((Object)(object)textComponent == (Object)null) && !((Object)(object)tengeFallbackFont == (Object)null) && textComponent.font != tengeFallbackFont) { textComponent.font = tengeFallbackFont; if ((Object)(object)((TMP_Asset)tengeFallbackFont).material != (Object)null) { textComponent.fontSharedMaterial = ((TMP_Asset)tengeFallbackFont).material; textComponent.fontMaterial = ((TMP_Asset)tengeFallbackFont).material; } ((Graphic)textComponent).SetAllDirty(); } } public static TMP_FontAsset GetTengeFallbackFont() { return tengeFallbackFont; } } public static class MyPluginInfo { public const string PLUGIN_GUID = "kztCurrency"; public const string PLUGIN_NAME = "kztCurrency"; public const string PLUGIN_VERSION = "1.2.1"; } }