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 AF TaxTerminal v1.0.0
AF_TaxTerminal.dll
Decompiled 3 hours agousing System; using System.Collections; 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; using AF_TaxTerminal.Delivery; using AF_TaxTerminal.Terminal; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using ExitGames.Client.Photon; using HarmonyLib; using MenuLib; using MenuLib.MonoBehaviors; using Microsoft.CodeAnalysis; using Photon.Pun; using Photon.Realtime; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.Localization; using UnityEngine.Localization.Settings; using UnityEngine.Rendering; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: AssemblyCompany("Kuraputi")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("AF_TaxTerminal")] [assembly: AssemblyTitle("AF_TaxTerminal")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [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 AF_TaxTerminal { [BepInPlugin("Kuraputi.AF_TaxTerminal", "AF_TaxTerminal", "1.0.0")] public class AF_TaxTerminal : BaseUnityPlugin { internal static ConfigEntry<int> FeeRate; internal static ConfigEntry<int> MinFee; internal static ConfigEntry<int> MaxOrders; internal static AF_TaxTerminal Instance { get; private set; } internal static ManualLogSource Logger => Instance._logger; private ManualLogSource _logger => ((BaseUnityPlugin)this).Logger; internal Harmony? Harmony { get; set; } private void Awake() { Instance = this; ((Component)this).gameObject.transform.parent = null; ((Object)((Component)this).gameObject).hideFlags = (HideFlags)61; BindConfig(); Patch(); ((Component)this).gameObject.AddComponent<DeliveryNetwork>(); Logger.LogInfo((object)$"{((BaseUnityPlugin)this).Info.Metadata.GUID} v{((BaseUnityPlugin)this).Info.Metadata.Version} has loaded!"); } private void BindConfig() { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Expected O, but got Unknown //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Expected O, but got Unknown FeeRate = ((BaseUnityPlugin)this).Config.Bind<int>("Delivery", "ShippingCostRate", 35, new ConfigDescription("商品の平均価格に対する送料の割合(%)", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>())); MinFee = ((BaseUnityPlugin)this).Config.Bind<int>("Delivery", "MinShippingCost", 2, new ConfigDescription("送料の最低額($K)", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 50), Array.Empty<object>())); MaxOrders = ((BaseUnityPlugin)this).Config.Bind<int>("Delivery", "MaxOrders", 3, new ConfigDescription("次のショップまでに注文できる最大数", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 10), Array.Empty<object>())); } internal void Patch() { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Expected O, but got Unknown //IL_0025: Expected O, but got Unknown if (Harmony == null) { Harmony val = new Harmony(((BaseUnityPlugin)this).Info.Metadata.GUID); Harmony val2 = val; Harmony = val; } Harmony.PatchAll(); } internal void Unpatch() { Harmony? harmony = Harmony; if (harmony != null) { harmony.UnpatchSelf(); } } private void Update() { } } internal static class Texts { private static readonly Dictionary<string, (string en, string ja)> table = new Dictionary<string, (string, string)> { ["Open"] = ("[interact] Open terminal", "[interact]キーで端末を開く"), ["ConfirmTitle"] = ("Confirm Order", "注文の確認"), ["ConfirmBody"] = ("{0}\nOrder for ${1}K?\nMoney ${2}K > ${3}K", "{0}\n${1}K で注文しますか?\n所持金 ${2}K > ${3}K"), ["Yes"] = ("Yes", "はい"), ["No"] = ("No", "いいえ"), ["OrderLimit"] = ("You can order up to {0} items", "注文は{0}個までです"), ["PurchaseLimit"] = ("Purchase limit reached", "購入上限に達しています"), ["NoMoney"] = ("Not enough money (${0}K)", "所持金が足りません(${0}K)"), ["Ordered"] = ("Ordered {0} (${1}K)", "{0}を注文しました(${1}K)"), ["Refunded"] = ("No space for {0} (Refunded ${1}K)", "{0}は置き場所が無く届きませんでした(${1}K を返金)"), ["OrderedMark"] = ("(ordered)", "(注文済み)"), ["OrdersTitle"] = ("ORDERS {0}/{1}", "注文中 {0}/{1}"), ["NoOrders"] = ("No orders", "注文はありません"), ["Cancel"] = ("Cancel", "取消"), ["CancelTitle"] = ("Cancel Order", "注文の取消"), ["CancelBody"] = ("{0}\nRefund ${1}K", "{0}\n${1}K を返金します") }; private static bool IsJapanese() { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) Locale selectedLocale = LocalizationSettings.SelectedLocale; object obj; if (selectedLocale == null) { obj = null; } else { LocaleIdentifier identifier = selectedLocale.Identifier; obj = ((LocaleIdentifier)(ref identifier)).Code; } return ((string)obj)?.StartsWith("ja") ?? false; } internal static string Get(string key, params object[] args) { if (!table.TryGetValue(key, out (string, string) value)) { return key; } string text; if (!IsJapanese()) { (text, _) = value; } else { text = value.Item2; } string text2 = text; if (args.Length == 0) { return text2; } return string.Format(text2, args); } } } namespace AF_TaxTerminal.Terminal { internal static class ModelLoader { internal static Texture2D? LoadTexture(string name) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown byte[] array = ReadResource(name); if (array == null) { return null; } Texture2D val = new Texture2D(2, 2) { name = name }; ImageConversion.LoadImage(val, array); ((Object)val).hideFlags = (HideFlags)32; return val; } internal static Mesh? LoadMesh(string name, List<string> materialNames) { //IL_0251: Unknown result type (might be due to invalid IL or missing references) //IL_0256: Unknown result type (might be due to invalid IL or missing references) //IL_025d: Unknown result type (might be due to invalid IL or missing references) //IL_0267: Expected O, but got Unknown //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_0170: Unknown result type (might be due to invalid IL or missing references) byte[] array = ReadResource(name); if (array == null) { return null; } List<Vector3> srcPos = new List<Vector3>(); List<Vector2> srcUv = new List<Vector2>(); List<Vector3> srcNrm = new List<Vector3>(); List<Vector3> pos = new List<Vector3>(); List<Vector2> uv = new List<Vector2>(); List<Vector3> nrm = new List<Vector3>(); Dictionary<string, int> cache = new Dictionary<string, int>(); List<List<int>> list = new List<List<int>>(); List<int> list2 = null; using (StringReader stringReader = new StringReader(Encoding.UTF8.GetString(array))) { string text; while ((text = stringReader.ReadLine()) != null) { string[] array2 = text.Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries); if (array2.Length == 0) { continue; } switch (array2[0]) { case "v": srcPos.Add(new Vector3(0f - F(array2[1]), F(array2[2]), F(array2[3]))); break; case "vn": srcNrm.Add(new Vector3(0f - F(array2[1]), F(array2[2]), F(array2[3]))); break; case "vt": srcUv.Add(new Vector2(F(array2[1]), F(array2[2]))); break; case "usemtl": { int num = materialNames.IndexOf(array2[1]); if (num < 0) { materialNames.Add(array2[1]); list.Add(new List<int>()); num = list.Count - 1; } list2 = list[num]; break; } case "f": { if (list2 == null) { materialNames.Add("Default"); list2 = new List<int>(); list.Add(list2); } int[] array3 = new int[array2.Length - 1]; for (int i = 1; i < array2.Length; i++) { array3[i - 1] = GetVertex(array2[i]); } for (int j = 1; j < array3.Length - 1; j++) { list2.Add(array3[0]); list2.Add(array3[j + 1]); list2.Add(array3[j]); } break; } } } Mesh val = new Mesh { name = name, hideFlags = (HideFlags)32 }; if (pos.Count > 65535) { val.indexFormat = (IndexFormat)1; } val.SetVertices(pos); val.SetUVs(0, uv); val.SetNormals(nrm); val.subMeshCount = list.Count; for (int k = 0; k < list.Count; k++) { val.SetTriangles(list[k], k); } val.RecalculateBounds(); AF_TaxTerminal.Logger.LogInfo((object)$"[Model] Loaded {name} (vertices: {pos.Count}, submeshes: {list.Count})"); return val; } int GetVertex(string key) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) if (cache.TryGetValue(key, out var value)) { return value; } string[] array4 = key.Split('/'); pos.Add(srcPos[int.Parse(array4[0]) - 1]); uv.Add((array4.Length > 1 && array4[1].Length > 0) ? srcUv[int.Parse(array4[1]) - 1] : Vector2.zero); nrm.Add((array4.Length > 2 && array4[2].Length > 0) ? srcNrm[int.Parse(array4[2]) - 1] : Vector3.up); cache[key] = pos.Count - 1; return pos.Count - 1; } } private static byte[]? ReadResource(string name) { using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(name); if (stream == null) { AF_TaxTerminal.Logger.LogError((object)("[Model] Resource not found: " + name)); return null; } using MemoryStream memoryStream = new MemoryStream(); stream.CopyTo(memoryStream); return memoryStream.ToArray(); } private static float F(string s) { return float.Parse(s, CultureInfo.InvariantCulture); } } internal class TerminalFollower : MonoBehaviour { private const float MaxDistance = 2f; private const float MaxAngle = 15f; private TerminalModel? model; private Collider? box; private void Start() { model = ((Component)this).GetComponent<TerminalModel>(); box = ((Component)this).GetComponent<Collider>(); } private void Update() { bool flag = IsLookedAt(); if (TerminalMenu.IsOpen) { flag = false; } if ((Object)(object)model != (Object)null) { model.SetHighlight(flag); } if (flag) { SemiFunc.UIItemInfoText((ItemAttributes)null, SemiFunc.InputDisplayReplaceTags(Texts.Get("Open"), "<u><b>", "</b></u>")); if (SemiFunc.InputDown((InputKey)2)) { AF_TaxTerminal.Logger.LogInfo((object)"[Terminal] Opened"); TerminalMenu.Open(); } } } private void LateUpdate() { TerminalObject.UpdatePosition(); } private bool IsLookedAt() { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) Camera main = Camera.main; if ((Object)(object)main == (Object)null) { return false; } Vector3 val = Center() - ((Component)main).transform.position; if (((Vector3)(ref val)).magnitude > 2f) { return false; } return Vector3.Angle(((Component)main).transform.forward, val) < 15f; } private Vector3 Center() { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)box != (Object)null)) { return ((Component)this).transform.position; } Bounds bounds = box.bounds; return ((Bounds)(ref bounds)).center; } } internal static class TerminalMenu { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static ShouldCloseMenuDelegate <>9__6_1; public static ScrollViewBuilderDelegate <>9__12_0; internal bool <Open>b__6_1() { return false; } internal RectTransform <RefreshOrders>b__12_0(Transform scrollView) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) return ((REPOElement)MenuAPI.CreateREPOLabel(Texts.Get("NoOrders"), scrollView, default(Vector2))).rectTransform; } } private static MenuButtonPopUp? popup; private static REPOPopupPage? listPage; private static REPOPopupPage? orderPage; private static readonly Dictionary<Item, REPOButton> itemButtons = new Dictionary<Item, REPOButton>(); internal static bool IsOpen { get { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Invalid comparison between Unknown and I4 if ((Object)(object)listPage != (Object)null) { return (int)listPage.menuPage.currentPageState != 2; } return false; } } internal static void Open() { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Expected O, but got Unknown //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Expected O, but got Unknown //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Expected O, but got Unknown REPOPopupPage page = MenuAPI.CreateREPOPopupPage(ListHeader(), (PresetSide)0, false, true, 1.5f); listPage = page; page.onEscapePressed = (ShouldCloseMenuDelegate)delegate { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Invalid comparison between Unknown and I4 if ((int)page.menuPage.currentPageState == 1) { page.ClosePage(true); } return false; }; itemButtons.Clear(); foreach (Item item in DeliveryManager.GetItems()) { page.AddElementToScrollView((ScrollViewBuilderDelegate)delegate(Transform scrollView) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) REPOButton val2 = MenuAPI.CreateREPOButton(ItemLabel(item), (Action)delegate { OnSelect(item); }, scrollView, default(Vector2)); itemButtons[item] = val2; return ((REPOElement)val2).rectTransform; }, 0f, 0f); } page.OpenPage(false); ((MonoBehaviour)page).StartCoroutine(SlideIn(page)); orderPage = MenuAPI.CreateREPOPopupPage(OrderHeader(), (PresetSide)1, false, false, 1.5f); REPOPopupPage? obj = orderPage; object obj2 = <>c.<>9__6_1; if (obj2 == null) { ShouldCloseMenuDelegate val = () => false; <>c.<>9__6_1 = val; obj2 = (object)val; } obj.onEscapePressed = (ShouldCloseMenuDelegate)obj2; RefreshOrders(); orderPage.OpenPage(true); ((MonoBehaviour)orderPage).StartCoroutine(SlideIn(orderPage)); } private static void OnSelect(Item item) { if (DeliveryManager.CanOrder(item, out string _)) { int fee = DeliveryManager.GetFee(item); int num = SemiFunc.StatGetRunCurrency(); string content = Texts.Get("ConfirmBody", DeliveryManager.GetName(item), fee, num, num - fee); OpenConfirm(Texts.Get("ConfirmTitle"), content, delegate { OnConfirm(item); }); } } private static void OnConfirm(Item item) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) if (!SemiFunc.IsMasterClientOrSingleplayer()) { DeliveryNetwork.RequestOrder(item); return; } string message; bool flag = DeliveryManager.TryOrder(item, out message); SemiFunc.UIFocusText(message, Color.white, flag ? Color.green : Color.red, 3f); } private static void OnCancelSelect(DeliveryManager.Order order) { string content = Texts.Get("CancelBody", DeliveryManager.GetName(order.item), order.fee); OpenConfirm(Texts.Get("CancelTitle"), content, delegate { OnCancelConfirm(order); }); } private static void OnCancelConfirm(DeliveryManager.Order order) { if (!SemiFunc.IsMasterClientOrSingleplayer()) { DeliveryNetwork.RequestCancel(order); } else { DeliveryManager.Cancel(order); } } internal static void Refresh() { if ((Object)(object)listPage != (Object)null) { ((TMP_Text)listPage.headerTMP).text = ListHeader(); foreach (KeyValuePair<Item, REPOButton> itemButton in itemButtons) { if ((Object)(object)itemButton.Value != (Object)null) { ((TMP_Text)itemButton.Value.labelTMP).text = ItemLabel(itemButton.Key); } } } RefreshOrders(); } private static void RefreshOrders() { //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Expected O, but got Unknown //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Expected O, but got Unknown if ((Object)(object)orderPage == (Object)null) { return; } ((TMP_Text)orderPage.headerTMP).text = OrderHeader(); REPOScrollViewElement[] componentsInChildren = ((Component)orderPage.scrollView).GetComponentsInChildren<REPOScrollViewElement>(true); foreach (REPOScrollViewElement val in componentsInChildren) { val.visibility = false; Object.Destroy((Object)(object)((Component)val).gameObject); } if (DeliveryManager.Orders.Count == 0) { REPOPopupPage? obj = orderPage; object obj2 = <>c.<>9__12_0; if (obj2 == null) { ScrollViewBuilderDelegate val2 = (Transform scrollView) => ((REPOElement)MenuAPI.CreateREPOLabel(Texts.Get("NoOrders"), scrollView, default(Vector2))).rectTransform; <>c.<>9__12_0 = val2; obj2 = (object)val2; } obj.AddElementToScrollView((ScrollViewBuilderDelegate)obj2, 0f, 0f); } foreach (DeliveryManager.Order order in DeliveryManager.Orders) { orderPage.AddElementToScrollView((ScrollViewBuilderDelegate)delegate(Transform scrollView) { //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) string text = string.Format("{0} ${1}K <color=#ff6040>[{2}]</color>", DeliveryManager.GetName(order.item), order.fee, Texts.Get("Cancel")); REPOButton val3 = MenuAPI.CreateREPOButton(text, (Action)delegate { OnCancelSelect(order); }, scrollView, default(Vector2)); return ((REPOElement)val3).rectTransform; }, 0f, 0f); } orderPage.scrollView.UpdateElements(); } private static string ItemLabel(Item item) { string text = $"{DeliveryManager.GetName(item)} ${DeliveryManager.GetFee(item)}K"; if (!DeliveryManager.IsOrderedHere(item)) { return text; } return "<color=#808080>" + text + " " + Texts.Get("OrderedMark") + "</color>"; } private static string ListHeader() { return $"DELIVERY ${SemiFunc.StatGetRunCurrency()}K"; } private static string OrderHeader() { return Texts.Get("OrdersTitle", DeliveryManager.Orders.Count, DeliveryNetwork.MaxOrders); } private static void OpenConfirm(string header, string content, Action onYes) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Expected O, but got Unknown //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Expected O, but got Unknown //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Expected O, but got Unknown //IL_0073: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)popup == (Object)null) { popup = ((Component)MenuManager.instance).gameObject.AddComponent<MenuButtonPopUp>(); } popup.option1Event = new UnityEvent(); popup.option2Event = new UnityEvent(); popup.option1Event.AddListener((UnityAction)delegate { onYes(); }); MenuManager.instance.PagePopUpTwoOptions(popup, header, (LocalizedAsset)null, Color.yellow, content, (LocalizedAsset)null, Texts.Get("Yes"), (LocalizedAsset)null, Texts.Get("No"), (LocalizedAsset)null, true); } private static IEnumerator SlideIn(REPOPopupPage page) { CanvasGroup group = ((Component)page.menuPage).GetComponent<CanvasGroup>(); if ((Object)(object)group != (Object)null) { group.alpha = 0f; } yield return null; RectTransform val = (RectTransform)((Component)page.menuPage).transform; Vector2 originalPosition = page.menuPage.originalPosition; Vector2 up = Vector2.up; Rect rect = val.rect; float height = ((Rect)(ref rect)).height; rect = page.rectTransform.rect; ((Transform)val).localPosition = Vector2.op_Implicit(originalPosition + up * (height + ((Rect)(ref rect)).height)); if ((Object)(object)group != (Object)null) { group.alpha = 1f; } } } internal class TerminalModel : MonoBehaviour { private static readonly Color Amber = new Color(1f, 0.55f, 0.1f); private static Mesh? mesh; private static readonly List<string> matNames = new List<string>(); private static Texture2D? bodyTex; private static Texture2D? screenOn; private static Texture2D? screenOff; private Material? screenMat; private Material? lampMat; private Light? glowLight; private bool highlight; internal static GameObject Create() { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Expected O, but got Unknown //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) LoadAssets(); GameObject val = new GameObject("AF_TaxTerminal"); TerminalModel terminalModel = val.AddComponent<TerminalModel>(); if ((Object)(object)mesh == (Object)null) { AF_TaxTerminal.Logger.LogError((object)"[Model] Terminal model is missing"); return val; } val.AddComponent<MeshFilter>().sharedMesh = mesh; Material[] array = (Material[])(object)new Material[matNames.Count]; for (int i = 0; i < array.Length; i++) { array[i] = terminalModel.CreateMaterial(matNames[i]); } ((Renderer)val.AddComponent<MeshRenderer>()).sharedMaterials = array; BoxCollider val2 = val.AddComponent<BoxCollider>(); Bounds bounds = mesh.bounds; val2.center = ((Bounds)(ref bounds)).center; bounds = mesh.bounds; val2.size = ((Bounds)(ref bounds)).size; GameObject val3 = new GameObject("Glow"); val3.transform.SetParent(val.transform, false); val3.transform.localPosition = new Vector3(0f, 0.2f, 0.3f); Light val4 = val3.AddComponent<Light>(); val4.type = (LightType)2; val4.color = Amber; val4.range = 1f; val4.intensity = 0.4f; terminalModel.glowLight = val4; return val; } private void Update() { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) bool flag = Time.time % 1f < 0.5f; if ((Object)(object)lampMat != (Object)null) { lampMat.SetColor("_EmissionColor", flag ? (Color.red * 2f) : Color.black); } if ((Object)(object)screenMat != (Object)null) { Texture2D val = (flag ? screenOn : screenOff); screenMat.mainTexture = (Texture)(object)val; screenMat.SetTexture("_EmissionMap", (Texture)(object)val); screenMat.SetColor("_EmissionColor", Color.white * (highlight ? 1.2f : 0.7f)); } if ((Object)(object)glowLight != (Object)null) { glowLight.range = 1f * ((Component)this).transform.lossyScale.x; } } internal void SetHighlight(bool looked) { highlight = looked; } private Material CreateMaterial(string name) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) Material val = new Material(Shader.Find("Standard")); switch (name) { case "Body": val.mainTexture = (Texture)(object)bodyTex; val.SetFloat("_Glossiness", 0.3f); break; case "Screen": val.mainTexture = (Texture)(object)screenOn; val.SetFloat("_Glossiness", 0.85f); val.EnableKeyword("_EMISSION"); val.SetTexture("_EmissionMap", (Texture)(object)screenOn); val.SetColor("_EmissionColor", Color.white * 0.9f); screenMat = val; break; case "Lamp": val.color = new Color(0.6f, 0.05f, 0.03f); val.EnableKeyword("_EMISSION"); val.SetColor("_EmissionColor", Color.red * 2f); lampMat = val; break; } return val; } private static void LoadAssets() { if (!((Object)(object)mesh != (Object)null)) { bodyTex = ModelLoader.LoadTexture("body_albedo.png"); screenOn = ModelLoader.LoadTexture("screen_on.png"); screenOff = ModelLoader.LoadTexture("screen_off.png"); if ((Object)(object)screenOn != (Object)null) { ((Texture)screenOn).wrapMode = (TextureWrapMode)1; } if ((Object)(object)screenOff != (Object)null) { ((Texture)screenOff).wrapMode = (TextureWrapMode)1; } matNames.Clear(); mesh = ModelLoader.LoadMesh("terminal.obj", matNames); } } } internal static class TerminalObject { private static GameObject? terminal; private static Transform? anchor; private static readonly Vector3 Offset = new Vector3(-5f, -3.2f, -2.1f); private const float RotY = 0f; private const float Scale = 3f; internal static void Spawn(Transform screen) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)terminal != (Object)null) { Object.Destroy((Object)(object)terminal); } anchor = screen; terminal = TerminalModel.Create(); terminal.AddComponent<TerminalFollower>(); UpdatePosition(); AF_TaxTerminal.Logger.LogInfo((object)$"[Terminal] Spawned at {terminal.transform.position} (screen: {screen.position}, rot: {screen.eulerAngles})"); } internal static void UpdatePosition() { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)terminal == (Object)null) { return; } if ((Object)(object)anchor == (Object)null) { Object.Destroy((Object)(object)terminal); terminal = null; return; } Vector3 val = Vector3.ProjectOnPlane(anchor.forward, Vector3.up); if (((Vector3)(ref val)).sqrMagnitude < 0.001f) { val = Vector3.ProjectOnPlane(anchor.up, Vector3.up); } Quaternion val2 = Quaternion.LookRotation(val, Vector3.up); Vector3 val3 = Vector3.up * 0.6f; terminal.transform.position = anchor.position + val2 * Offset + val3; terminal.transform.rotation = val2 * Quaternion.Euler(0f, 0f, 0f); terminal.transform.localScale = Vector3.one * 3f; } } [HarmonyPatch(typeof(TruckScreenText))] internal static class TerminalSpawnPatch { [HarmonyPostfix] [HarmonyPatch("Start")] private static void Start_Postfix(TruckScreenText __instance) { if (SemiFunc.RunIsLevel() || SemiFunc.RunIsShop() || SemiFunc.RunIsLobby()) { TerminalObject.Spawn(((Component)__instance).transform); } } } } namespace AF_TaxTerminal.Delivery { internal static class DeliveryManager { internal class Order { internal Item item; internal int fee; internal string area = ""; } internal static readonly List<Order> Orders = new List<Order>(); internal static string AreaKey() { RunManager instance = RunManager.instance; Level levelCurrent = instance.levelCurrent; return $"{((levelCurrent != null) ? ((Object)levelCurrent).name : null)}#{instance.levelsCompleted}"; } internal static bool IsOrderedHere(Item item) { string area = AreaKey(); return Orders.Any((Order o) => (Object)(object)o.item == (Object)(object)item && o.area == area); } internal static bool TryOrder(Item item, out string message) { if (!SemiFunc.IsMasterClientOrSingleplayer()) { message = ""; return false; } if (!CanOrder(item, out message)) { return false; } int fee = GetFee(item); int num = SemiFunc.StatGetRunCurrency(); SemiFunc.StatSetRunCurrency(num - fee); Orders.Add(new Order { item = item, fee = fee, area = AreaKey() }); AF_TaxTerminal.Logger.LogInfo((object)$"[Delivery] Ordered {item.itemName} (fee ${fee}K, money ${num}K -> ${num - fee}K)"); Changed(); message = Texts.Get("Ordered", GetName(item), fee); return true; } internal static bool CanOrder(Item item, out string message) { if (IsOrderedHere(item)) { message = ""; return false; } if (Orders.Count >= DeliveryNetwork.MaxOrders) { message = Texts.Get("OrderLimit", DeliveryNetwork.MaxOrders); return false; } int num = Orders.Count((Order o) => (Object)(object)o.item == (Object)(object)item); if (item.maxPurchase && StatsManager.instance.GetItemsUpgradesPurchasedTotal(((Object)item).name) + num >= item.maxPurchaseAmount) { message = Texts.Get("PurchaseLimit"); return false; } int fee = GetFee(item); if (SemiFunc.StatGetRunCurrency() < fee) { message = Texts.Get("NoMoney", fee); return false; } message = ""; return true; } internal static bool Cancel(Order order) { if (!SemiFunc.IsMasterClientOrSingleplayer()) { return false; } if (!Orders.Remove(order)) { return false; } int num = SemiFunc.StatGetRunCurrency(); SemiFunc.StatSetRunCurrency(num + order.fee); AF_TaxTerminal.Logger.LogInfo((object)$"[Delivery] Canceled {order.item.itemName} (refund ${order.fee}K, money ${num}K -> ${num + order.fee}K)"); Changed(); return true; } internal static List<Item> GetItems() { return StatsManager.instance.itemDictionary.Values.OrderBy(GetFee).ThenBy(GetName).ToList(); } internal static string GetName(Item item) { LocalizedAsset itemNameLocalized = item.itemNameLocalized; return ((itemNameLocalized != null) ? itemNameLocalized.GetLocalizedString((Locale)null) : null) ?? item.itemName; } internal static Item? FindItem(string itemKey) { Dictionary<string, Item> itemDictionary = StatsManager.instance.itemDictionary; if (itemDictionary.TryGetValue(itemKey, out var value)) { return value; } return ((IEnumerable<Item>)itemDictionary.Values).FirstOrDefault((Func<Item, bool>)((Item i) => ((Object)i).name == itemKey)); } internal static Order? FindOrder(string itemKey, string area) { return Orders.FirstOrDefault((Order o) => ((Object)o.item).name == itemKey && o.area == area); } internal static string ToText(Order order) { return $"{((Object)order.item).name}|{order.fee}|{order.area}"; } internal static Order? FromText(string line) { string[] array = line.Split(new char[1] { '|' }, 3); if (array.Length < 3 || !int.TryParse(array[1], out var result)) { return null; } Item val = FindItem(array[0]); if ((Object)(object)val == (Object)null) { return null; } return new Order { item = val, fee = result, area = array[2] }; } internal static void Changed() { DeliveryNetwork.PushOrders(); TerminalMenu.Refresh(); } internal static int GetFee(Item item) { int num = Mathf.CeilToInt(GetPrice(item) * (float)DeliveryNetwork.FeeRate / 100f); return Mathf.Max(DeliveryNetwork.MinFee, num); } internal static float GetPrice(Item item) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Invalid comparison between Unknown and I4 //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Invalid comparison between Unknown and I4 //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Invalid comparison between Unknown and I4 ShopManager instance = ShopManager.instance; float num = (item.value.valueMin + item.value.valueMax) / 2f / 1000f * instance.itemValueMultiplier; if ((int)item.itemType == 3) { num = instance.UpgradeValueGet(num, item); } else if ((int)item.itemType == 8) { num = instance.HealthPackValueGet(num); } else if ((int)item.itemType == 5) { num = instance.CrystalValueGet(num); } return Mathf.Max(1f, Mathf.Ceil(num)); } } internal class DeliveryNetwork : MonoBehaviourPunCallbacks, IOnEventCallback { private const byte RequestCode = 173; private const byte ResultCode = 174; private const byte ActOrder = 0; private const byte ActCancel = 1; private const string OrdersKey = "AFTaxTerminal.Orders"; private const string ConfigKey = "AFTaxTerminal.Config"; private static int[]? hostConfig; internal static int FeeRate => HostValue(0, AF_TaxTerminal.FeeRate.Value); internal static int MinFee => HostValue(1, AF_TaxTerminal.MinFee.Value); internal static int MaxOrders => HostValue(2, AF_TaxTerminal.MaxOrders.Value); private static bool InRoom { get { if (SemiFunc.IsMultiplayer()) { return PhotonNetwork.InRoom; } return false; } } private void Awake() { AF_TaxTerminal.FeeRate.SettingChanged += OnConfigChanged; AF_TaxTerminal.MinFee.SettingChanged += OnConfigChanged; AF_TaxTerminal.MaxOrders.SettingChanged += OnConfigChanged; } private static void OnConfigChanged(object? sender, EventArgs e) { PushConfig(); TerminalMenu.Refresh(); } private static int HostValue(int index, int mine) { if (!InRoom || PhotonNetwork.IsMasterClient || hostConfig == null || hostConfig.Length <= index) { return mine; } return hostConfig[index]; } internal static void RequestOrder(Item item) { SendRequest(0, ((Object)item).name, DeliveryManager.AreaKey()); } internal static void RequestCancel(DeliveryManager.Order order) { SendRequest(1, ((Object)order.item).name, order.area); } private static void SendRequest(byte act, string itemKey, string area) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Expected O, but got Unknown //IL_0032: Unknown result type (might be due to invalid IL or missing references) if (InRoom) { RaiseEventOptions val = new RaiseEventOptions { Receivers = (ReceiverGroup)2 }; PhotonNetwork.RaiseEvent((byte)173, (object)new object[3] { act, itemKey, area }, val, SendOptions.SendReliable); } } internal static void PushOrders() { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Expected O, but got Unknown //IL_0057: Expected O, but got Unknown if (InRoom && PhotonNetwork.IsMasterClient) { string[] value = DeliveryManager.Orders.Select(DeliveryManager.ToText).ToArray(); Room currentRoom = PhotonNetwork.CurrentRoom; Hashtable val = new Hashtable(); ((Dictionary<object, object>)val).Add((object)"AFTaxTerminal.Orders", (object)value); currentRoom.SetCustomProperties(val, (Hashtable)null, (WebFlags)null); } } internal static void PushConfig() { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Expected O, but got Unknown //IL_005a: Expected O, but got Unknown if (InRoom && PhotonNetwork.IsMasterClient) { int[] value = new int[3] { AF_TaxTerminal.FeeRate.Value, AF_TaxTerminal.MinFee.Value, AF_TaxTerminal.MaxOrders.Value }; Room currentRoom = PhotonNetwork.CurrentRoom; Hashtable val = new Hashtable(); ((Dictionary<object, object>)val).Add((object)"AFTaxTerminal.Config", (object)value); currentRoom.SetCustomProperties(val, (Hashtable)null, (WebFlags)null); } } private static void PullConfig() { hostConfig = null; if (InRoom && !PhotonNetwork.IsMasterClient) { if (((Dictionary<object, object>)(object)((RoomInfo)PhotonNetwork.CurrentRoom).CustomProperties).TryGetValue((object)"AFTaxTerminal.Config", out object value) && value is int[] array) { hostConfig = array; } TerminalMenu.Refresh(); } } private static void PullOrders() { if (!InRoom || PhotonNetwork.IsMasterClient) { return; } DeliveryManager.Orders.Clear(); if (((Dictionary<object, object>)(object)((RoomInfo)PhotonNetwork.CurrentRoom).CustomProperties).TryGetValue((object)"AFTaxTerminal.Orders", out object value) && value is string[] array) { string[] array2 = array; foreach (string line in array2) { DeliveryManager.Order order = DeliveryManager.FromText(line); if (order != null) { DeliveryManager.Orders.Add(order); } } } TerminalMenu.Refresh(); } public void OnEvent(EventData e) { if (e.Code == 173 && PhotonNetwork.IsMasterClient) { OnRequest(e); } else if (e.Code == 174) { OnResult(e); } } private static void OnRequest(EventData e) { //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Expected O, but got Unknown //IL_00bf: Unknown result type (might be due to invalid IL or missing references) if (!(e.CustomData is object[] array) || array.Length < 3) { return; } byte b = (byte)array[0]; string text = (string)array[1]; string area = (string)array[2]; switch (b) { case 0: { Item val = DeliveryManager.FindItem(text); if (!((Object)(object)val == (Object)null)) { string message; bool flag = DeliveryManager.TryOrder(val, out message); int num = (flag ? DeliveryManager.Orders[DeliveryManager.Orders.Count - 1].fee : 0); RaiseEventOptions val2 = new RaiseEventOptions(); val2.TargetActors = new int[1] { e.Sender }; RaiseEventOptions val3 = val2; PhotonNetwork.RaiseEvent((byte)174, (object)new object[3] { flag, text, num }, val3, SendOptions.SendReliable); } break; } case 1: { DeliveryManager.Order order = DeliveryManager.FindOrder(text, area); if (order != null) { DeliveryManager.Cancel(order); } break; } } } private static void OnResult(EventData e) { //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) if (e.CustomData is object[] array && array.Length >= 3 && (bool)array[0]) { Item val = DeliveryManager.FindItem((string)array[1]); if (!((Object)(object)val == (Object)null)) { SemiFunc.UIFocusText(Texts.Get("Ordered", DeliveryManager.GetName(val), (int)array[2]), Color.white, Color.green, 3f); } } } public override void OnRoomPropertiesUpdate(Hashtable changed) { if (((Dictionary<object, object>)(object)changed).ContainsKey((object)"AFTaxTerminal.Config")) { PullConfig(); } if (((Dictionary<object, object>)(object)changed).ContainsKey((object)"AFTaxTerminal.Orders")) { PullOrders(); } } public override void OnJoinedRoom() { PushConfig(); PushOrders(); PullConfig(); PullOrders(); } } [HarmonyPatch(typeof(StatsManager), "ResetAllStats")] internal static class DeliveryResetPatch { private static void Postfix() { if (DeliveryManager.Orders.Count != 0) { AF_TaxTerminal.Logger.LogInfo((object)$"[Delivery] Orders cleared ({DeliveryManager.Orders.Count})"); DeliveryManager.Orders.Clear(); DeliveryManager.Changed(); } } } [HarmonyPatch(typeof(StatsManager))] internal static class DeliverySavePatch { private const string SaveName = "AF_TaxTerminal.txt"; [HarmonyPostfix] [HarmonyPatch("SaveGame")] private static void SaveGame_Postfix(string fileName) { if (string.IsNullOrEmpty(fileName) || !SemiFunc.IsMasterClientOrSingleplayer()) { return; } try { string path = SavePath(fileName); if (DeliveryManager.Orders.Count == 0) { if (File.Exists(path)) { File.Delete(path); } } else { File.WriteAllLines(path, DeliveryManager.Orders.Select(DeliveryManager.ToText)); AF_TaxTerminal.Logger.LogInfo((object)$"[Delivery] Saved {DeliveryManager.Orders.Count} orders"); } } catch (Exception ex) { AF_TaxTerminal.Logger.LogError((object)("[Delivery] Save failed: " + ex.Message)); } } [HarmonyPostfix] [HarmonyPatch("LoadGame")] private static void LoadGame_Postfix(string fileName) { if (string.IsNullOrEmpty(fileName) || !SemiFunc.IsMasterClientOrSingleplayer()) { return; } DeliveryManager.Orders.Clear(); try { string path = SavePath(fileName); if (File.Exists(path)) { string[] array = File.ReadAllLines(path); foreach (string line in array) { DeliveryManager.Order order = DeliveryManager.FromText(line); if (order != null) { DeliveryManager.Orders.Add(order); } } AF_TaxTerminal.Logger.LogInfo((object)$"[Delivery] Loaded {DeliveryManager.Orders.Count} orders"); } } catch (Exception ex) { AF_TaxTerminal.Logger.LogError((object)("[Delivery] Load failed: " + ex.Message)); } DeliveryManager.Changed(); } private static string SavePath(string fileName) { return Path.Combine(Application.persistentDataPath, "saves", fileName, "AF_TaxTerminal.txt"); } } [HarmonyPatch(typeof(PunManager), "ShopPopulateItemVolumes", new Type[] { typeof(List<ItemSpawn>) })] internal static class DeliveryShopPatch { private static void Prefix(PunManager __instance, List<ItemSpawn> _spawns) { if (SemiFunc.IsNotMasterClient() || DeliveryManager.Orders.Count == 0) { return; } ShopManager instance = ShopManager.instance; foreach (DeliveryManager.Order order in DeliveryManager.Orders) { Item item = order.item; ItemVolume val = instance.itemVolumes.Find((ItemVolume v) => (Object)(object)v != (Object)null && v.itemVolume == item.itemVolume); if ((Object)(object)val == (Object)null) { Refund(instance, order); continue; } int num = 0; __instance.SpawnShopItem(val, new List<Item> { item }, ref num, false, _spawns); instance.itemVolumes.Remove(val); Object.Destroy((Object)(object)((Component)val).gameObject); AF_TaxTerminal.Logger.LogInfo((object)("[Delivery] Delivered " + item.itemName)); } DeliveryManager.Orders.Clear(); DeliveryManager.Changed(); } private static void Refund(ShopManager shop, DeliveryManager.Order order) { //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) SemiFunc.StatSetRunCurrency(SemiFunc.StatGetRunCurrency() + order.fee); shop.totalCurrency += order.fee; AF_TaxTerminal.Logger.LogWarning((object)$"[Delivery] No space for {order.item.itemName} (refunded ${order.fee}K)"); SemiFunc.UIFocusText(Texts.Get("Refunded", DeliveryManager.GetName(order.item), order.fee), Color.white, Color.yellow, 3f); } } }