using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [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("UltimateLethal.StorePriceModifier")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("UltimateLethal.StorePriceModifier")] [assembly: AssemblyTitle("UltimateLethal.StorePriceModifier")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [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 UltimateLethal.StorePriceModifier { [BepInPlugin("com.ultimatelethal.storepricemodifier", "Ultimate Lethal Store Price Modifier", "0.1.0")] public sealed class Plugin : BaseUnityPlugin { private sealed class PriceRule { internal string DisplayName; internal int Price; internal bool Matched; } public const string Guid = "com.ultimatelethal.storepricemodifier"; public const string Name = "Ultimate Lethal Store Price Modifier"; public const string Version = "0.1.0"; private const string DefaultOverrides = "Belt Bag=800;Company Hauler=250;Assault Rifle=450;Bolt-Action Rifle=500;Flame Thrower=400;Heavy Shotgun=375;Pistol=300;Revolver=275"; internal static Plugin Instance; internal static ConfigEntry PriceOverrides; internal static ConfigEntry LogSuccessfulOverrides; private Harmony _harmony; private float _nextApplyAt; private string _lastRawOverrides; private readonly Dictionary _rules = new Dictionary(StringComparer.OrdinalIgnoreCase); private readonly HashSet _loggedMatches = new HashSet(StringComparer.OrdinalIgnoreCase); private readonly HashSet _loggedMissing = new HashSet(StringComparer.OrdinalIgnoreCase); private float _startupTime; private void Awake() { //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Expected O, but got Unknown Instance = this; PriceOverrides = ((BaseUnityPlugin)this).Config.Bind("Store Prices", "Overrides", "Belt Bag=800;Company Hauler=250;Assault Rifle=450;Bolt-Action Rifle=500;Flame Thrower=400;Heavy Shotgun=375;Pistol=300;Revolver=275", "Semicolon-separated Item Name=Price rules. Names ignore spaces, hyphens and punctuation. Use -1 to leave a listed item unchanged. This is the authoritative Ultimate Lethal store balance list."); LogSuccessfulOverrides = ((BaseUnityPlugin)this).Config.Bind("Diagnostics", "Log Successful Overrides", true, "Log each configured store item the first time Ultimate Lethal successfully applies its price."); ParseRules(force: true); _startupTime = Time.unscaledTime; _harmony = new Harmony("com.ultimatelethal.storepricemodifier"); _harmony.PatchAll(); ((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("{0} {1} loaded with {2} configured price override(s).", "Ultimate Lethal Store Price Modifier", "0.1.0", _rules.Count)); RequestImmediateApply(); } private void OnDestroy() { try { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } catch { } if (Instance == this) { Instance = null; } } private void Update() { ParseRules(force: false); if (!(Time.unscaledTime < _nextApplyAt)) { float num = Time.unscaledTime - _startupTime; _nextApplyAt = Time.unscaledTime + ((num < 20f) ? 0.5f : 3f); ApplyAll(); if (num > 12f) { LogMissingRulesOnce(); } } } internal void RequestImmediateApply() { _nextApplyAt = 0f; } private void ParseRules(bool force) { string text = PriceOverrides?.Value ?? string.Empty; if (!force && string.Equals(text, _lastRawOverrides, StringComparison.Ordinal)) { return; } _lastRawOverrides = text; _rules.Clear(); _loggedMatches.Clear(); _loggedMissing.Clear(); string[] array = text.Split(new char[3] { ';', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); foreach (string text2 in array) { int num = text2.IndexOf('='); if (num <= 0 || num >= text2.Length - 1) { continue; } string text3 = text2.Substring(0, num).Trim(); string s = text2.Substring(num + 1).Trim(); if (!string.IsNullOrWhiteSpace(text3) && int.TryParse(s, out var result) && result >= -1 && result >= 0) { string text4 = Normalize(text3); if (text4.Length != 0) { _rules[text4] = new PriceRule { DisplayName = text3, Price = result }; } } } ((BaseUnityPlugin)this).Logger.LogInfo((object)$"Parsed {_rules.Count} Ultimate Lethal store price override(s)."); RequestImmediateApply(); } internal void ApplyAll() { if (_rules.Count != 0) { ApplyBetterArmoryConfigAdapter(); ApplyItemPrices(); ApplyVehiclePrices(); } } private void ApplyItemPrices() { Item[] array; try { array = Resources.FindObjectsOfTypeAll(); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Item price scan failed: " + ex.GetBaseException().Message)); return; } foreach (Item val in array) { if ((Object)(object)val == (Object)null) { continue; } string text = (string.IsNullOrWhiteSpace(val.itemName) ? ((Object)val).name : val.itemName); if (!TryGetRule(text, out var rule)) { continue; } try { if (val.creditsWorth != rule.Price) { val.creditsWorth = rule.Price; } MarkMatched(rule, "Item '" + text + "'"); } catch (Exception ex2) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to price '" + text + "': " + ex2.GetBaseException().Message)); } } } private void ApplyBetterArmoryConfigAdapter() { //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Expected O, but got Unknown if (!Chainloader.PluginInfos.TryGetValue("com.y4ngz.betterarmory", out var value) || (Object)(object)((value != null) ? value.Instance : null) == (Object)null) { return; } ConfigFile config = value.Instance.Config; if (config == null) { return; } string[] array = new string[6] { "Assault Rifle", "Bolt-Action Rifle", "Flame Thrower", "Heavy Shotgun", "Pistol", "Revolver" }; ConfigEntry val2 = default(ConfigEntry); foreach (string text in array) { if (!TryGetRule(text, out var rule)) { continue; } try { ConfigDefinition val = new ConfigDefinition("Weapon: " + text, "Shop Price"); if (config.TryGetEntry(val, ref val2) && val2 != null) { if (val2.Value != rule.Price) { val2.Value = rule.Price; } MarkMatched(rule, "BetterArmory '" + text + "'"); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("BetterArmory adapter failed for '" + text + "': " + ex.GetBaseException().Message)); } } } private void ApplyVehiclePrices() { if (!TryGetRule("Company Hauler", out var rule)) { return; } Terminal val = Object.FindObjectOfType(); if ((Object)(object)val == (Object)null) { return; } BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; int num = 0; FieldInfo[] fields = ((object)val).GetType().GetFields(bindingAttr); foreach (FieldInfo fieldInfo in fields) { if (fieldInfo.Name.IndexOf("vehicle", StringComparison.OrdinalIgnoreCase) >= 0) { try { num += ApplyVehicleEnumerable(fieldInfo.GetValue(val) as IEnumerable, rule); } catch { } } } PropertyInfo[] properties = ((object)val).GetType().GetProperties(bindingAttr); foreach (PropertyInfo propertyInfo in properties) { if (propertyInfo.CanRead && propertyInfo.Name.IndexOf("vehicle", StringComparison.OrdinalIgnoreCase) >= 0) { try { num += ApplyVehicleEnumerable(propertyInfo.GetValue(val, null) as IEnumerable, rule); } catch { } } } if (num > 0) { MarkMatched(rule, "Company Hauler vehicle registration"); } } private int ApplyVehicleEnumerable(IEnumerable entries, PriceRule rule) { if (entries == null) { return 0; } int num = 0; foreach (object entry in entries) { if (entry != null && ObjectContainsName(entry, "hauler") && TrySetIntPriceMember(entry, rule.Price)) { num++; } } return num; } private static bool ObjectContainsName(object entry, string token) { Type type = entry.GetType(); BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; string[] array = new string[5] { "vehicleDisplayName", "vehicleName", "displayName", "name", "itemName" }; foreach (string name in array) { try { if (type.GetField(name, bindingAttr)?.GetValue(entry) is string text && text.IndexOf(token, StringComparison.OrdinalIgnoreCase) >= 0) { return true; } PropertyInfo property = type.GetProperty(name, bindingAttr); if (property != null && property.CanRead && property.GetValue(entry, null) is string text2 && text2.IndexOf(token, StringComparison.OrdinalIgnoreCase) >= 0) { return true; } } catch { } } Object val = (Object)((entry is Object) ? entry : null); if (val != null && !string.IsNullOrEmpty(val.name)) { return val.name.IndexOf(token, StringComparison.OrdinalIgnoreCase) >= 0; } return false; } private static bool TrySetIntPriceMember(object entry, int price) { Type type = entry.GetType(); BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; string[] array = new string[5] { "creditsWorth", "creditsCost", "creditCost", "price", "cost" }; foreach (string name in array) { try { FieldInfo field = type.GetField(name, bindingAttr); if (field != null && field.FieldType == typeof(int)) { field.SetValue(entry, price); return true; } PropertyInfo property = type.GetProperty(name, bindingAttr); if (property != null && property.CanWrite && property.PropertyType == typeof(int)) { property.SetValue(entry, price, null); return true; } } catch { } } return false; } private bool TryGetRule(string name, out PriceRule rule) { rule = null; string text = Normalize(name); if (text.Length > 0) { return _rules.TryGetValue(text, out rule); } return false; } private void MarkMatched(PriceRule rule, string source) { if (rule != null) { rule.Matched = true; ConfigEntry logSuccessfulOverrides = LogSuccessfulOverrides; if (logSuccessfulOverrides != null && logSuccessfulOverrides.Value && _loggedMatches.Add(rule.DisplayName)) { ((BaseUnityPlugin)this).Logger.LogInfo((object)$"STORE PRICE: {rule.DisplayName} = {rule.Price} credits ({source})."); } } } private void LogMissingRulesOnce() { foreach (PriceRule value in _rules.Values) { if (!value.Matched && _loggedMissing.Add(value.DisplayName)) { ((BaseUnityPlugin)this).Logger.LogWarning((object)($"STORE PRICE target not found yet: '{value.DisplayName}' ({value.Price} credits). " + "Check the runtime item name if this remains missing after store registration.")); } } } private static string Normalize(string value) { if (string.IsNullOrWhiteSpace(value)) { return string.Empty; } char[] array = new char[value.Length]; int length = 0; foreach (char c in value) { if (char.IsLetterOrDigit(c)) { array[length++] = char.ToLowerInvariant(c); } } return new string(array, 0, length); } } [HarmonyPatch] internal static class TerminalStoreRefreshHooks { private static IEnumerable TargetMethods() { string[] array = new string[5] { "Start", "TextPostProcess", "LoadNewNode", "LoadNewNodeIfAffordable", "ParsePlayerSentence" }; HashSet seen = new HashSet(); string[] array2 = array; foreach (string name in array2) { MethodInfo[] methods = typeof(Terminal).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); MethodInfo[] array3 = methods; foreach (MethodInfo methodInfo in array3) { if (methodInfo.Name == name && seen.Add(methodInfo)) { yield return methodInfo; } } } } [HarmonyPostfix] [HarmonyPriority(0)] private static void Postfix() { Plugin.Instance?.ApplyAll(); Plugin.Instance?.RequestImmediateApply(); } } }