Decompiled source of ForgeOdds v0.1.0

plugins/ForgeOdds.dll

Decompiled 14 hours ago
using System;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using ForgeOdds.Core;
using HarmonyLib;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")]
[assembly: AssemblyCompany("ForgeOdds")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: AssemblyInformationalVersion("0.1.0+b58fe41431997a909b4c1db87a6bff19307b4f9b")]
[assembly: AssemblyProduct("ForgeOdds")]
[assembly: AssemblyTitle("ForgeOdds")]
[assembly: AssemblyVersion("0.1.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 ForgeOdds
{
	[BepInPlugin("sansara.ForgeOdds", "Forge Odds", "0.1.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string Guid = "sansara.ForgeOdds";

		public const string Version = "0.1.0";

		internal static ManualLogSource Log;

		private void Awake()
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			PluginConfig.Bind(((BaseUnityPlugin)this).Config);
			new Harmony("sansara.ForgeOdds").PatchAll(typeof(Plugin).Assembly);
			Log.LogInfo((object)("Forge Odds 0.1.0 loaded: " + PluginConfig.Describe()));
		}
	}
	internal static class PluginConfig
	{
		private static ConfigEntry<bool> _enabled;

		private static ConfigEntry<float> _basePercent;

		private static ConfigEntry<int> _flatUpToLevel;

		private static ConfigEntry<float> _dropPerLevelPercent;

		private static ConfigEntry<float> _minPercent;

		public static OddsCurve Curve { get; private set; }

		public static bool Enabled => _enabled.Value;

		public static void Bind(ConfigFile cfg)
		{
			_enabled = cfg.Bind<bool>("Odds (server)", "Enabled", true, Description("Шанс улучшения в Кузнице потенциала по кривой ниже. false — ванильный шанс идола (65 %)."));
			_basePercent = cfg.Bind<float>("Odds (server)", "BaseChancePercent", 90f, Description("Шанс успеха до уровня FlatUpToLevel включительно (уровень — качество предмета после улучшения), %.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f)));
			_flatUpToLevel = cfg.Bind<int>("Odds (server)", "FlatUpToLevel", 10, Description("До какого уровня (включительно) действует BaseChancePercent.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 1000)));
			_dropPerLevelPercent = cfg.Bind<float>("Odds (server)", "DropPerLevelPercent", 10f, Description("На сколько процентных пунктов шанс падает за каждый уровень выше FlatUpToLevel.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f)));
			_minPercent = cfg.Bind<float>("Odds (server)", "MinChancePercent", 20f, Description("Нижний предел шанса, %. Не выше BaseChancePercent.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f)));
			Rebuild(log: false);
			_basePercent.SettingChanged += delegate
			{
				Rebuild(log: true);
			};
			_flatUpToLevel.SettingChanged += delegate
			{
				Rebuild(log: true);
			};
			_dropPerLevelPercent.SettingChanged += delegate
			{
				Rebuild(log: true);
			};
			_minPercent.SettingChanged += delegate
			{
				Rebuild(log: true);
			};
			_enabled.SettingChanged += delegate
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)("Forge Odds: " + Describe()));
				}
			};
		}

		public static string Describe()
		{
			if (!_enabled.Value)
			{
				return "выключено, ванильный шанс";
			}
			return $"до {Curve.FlatUpToLevel} ур. {OddsCurve.FormatPercent(Curve.BasePercent)}, дальше −{Curve.DropPerLevelPercent} за уровень, " + "не ниже " + OddsCurve.FormatPercent(Curve.MinPercent);
		}

		private static void Rebuild(bool log)
		{
			Curve = new OddsCurve(_basePercent.Value, _flatUpToLevel.Value, _dropPerLevelPercent.Value, _minPercent.Value);
			if (log)
			{
				ManualLogSource log2 = Plugin.Log;
				if (log2 != null)
				{
					log2.LogInfo((object)("Forge Odds: " + Describe()));
				}
			}
		}

		private static ConfigDescription Description(string text, AcceptableValueBase range = null)
		{
			//IL_000a: 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_0017: Expected O, but got Unknown
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Expected O, but got Unknown
			return new ConfigDescription(text, range, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true
			} });
		}
	}
}
namespace ForgeOdds.Core
{
	public sealed class OddsCurve
	{
		public float BasePercent { get; }

		public int FlatUpToLevel { get; }

		public float DropPerLevelPercent { get; }

		public float MinPercent { get; }

		public OddsCurve(float basePercent, int flatUpToLevel, float dropPerLevelPercent, float minPercent)
		{
			BasePercent = Clamp(basePercent, 0f, 100f);
			FlatUpToLevel = Math.Max(0, flatUpToLevel);
			DropPerLevelPercent = Clamp(dropPerLevelPercent, 0f, 100f);
			MinPercent = Clamp(minPercent, 0f, BasePercent);
		}

		public float ChancePercent(int targetLevel)
		{
			int num = Math.Max(0, targetLevel - FlatUpToLevel);
			return Math.Max(MinPercent, BasePercent - DropPerLevelPercent * (float)num);
		}

		public float Chance(int targetLevel)
		{
			return ChancePercent(targetLevel) / 100f;
		}

		public static string FormatPercent(float percent)
		{
			float num = (float)Math.Round(percent, 1);
			return ((num == (float)Math.Round(num)) ? ((int)Math.Round(num)).ToString(CultureInfo.InvariantCulture) : num.ToString("0.0", CultureInfo.InvariantCulture).Replace('.', ',')) + " %";
		}

		private static float Clamp(float value, float min, float max)
		{
			if (float.IsNaN(value))
			{
				return min;
			}
			if (!(value < min))
			{
				if (!(value > max))
				{
					return value;
				}
				return max;
			}
			return min;
		}
	}
}
namespace ForgeOdds.Patches
{
	[HarmonyPatch(typeof(InventoryGui), "UpdateRecipe")]
	internal static class ChanceDisplayPatch
	{
		private static Func<InventoryGui, Recipe> _selectedRecipe;

		private static Func<InventoryGui, ItemData> _selectedItem;

		private static bool Prepare()
		{
			try
			{
				_selectedRecipe = Getter<Recipe>("Recipe");
				_selectedItem = Getter<ItemData>("ItemData");
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Forge Odds: шанс в окне кузницы не показывается: " + ex.Message));
				return false;
			}
			if (_selectedRecipe != null && _selectedItem != null)
			{
				return true;
			}
			Plugin.Log.LogWarning((object)"Forge Odds: InventoryGui.m_selectedRecipe не найден, шанс в окне кузницы не показывается.");
			return false;
		}

		private static void Postfix(InventoryGui __instance)
		{
			if (PluginConfig.Enabled && !((Object)(object)__instance.m_recipeDecription == (Object)null) && ForgeStation.IsActive())
			{
				ItemData val = _selectedItem(__instance);
				SharedData val2 = ((val != null) ? ForgeStation.UpgraderResource(_selectedRecipe(__instance)) : null);
				if (val2 != null)
				{
					float percent = PluginConfig.Curve.ChancePercent(val.m_quality + 1);
					string text = ((val2.m_breakChance >= 1f) ? "предмет ломается" : "предмет может сломаться или потерять уровень");
					TMP_Text recipeDecription = __instance.m_recipeDecription;
					recipeDecription.text = recipeDecription.text + "\n\n<color=orange>Шанс успеха: " + OddsCurve.FormatPercent(percent) + ". При неудаче " + text + ".</color>";
				}
			}
		}

		private static Func<InventoryGui, T> Getter<T>(string property)
		{
			FieldInfo fieldInfo = AccessTools.Field(typeof(InventoryGui), "m_selectedRecipe");
			if (fieldInfo == null || !fieldInfo.FieldType.IsValueType)
			{
				return null;
			}
			MethodInfo methodInfo = fieldInfo.FieldType.GetProperty(property, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetGetMethod(nonPublic: true);
			if (methodInfo == null || methodInfo.ReturnType != typeof(T))
			{
				return null;
			}
			DynamicMethod dynamicMethod = new DynamicMethod("ForgeOdds_SelectedRecipe_" + property, typeof(T), new Type[1] { typeof(InventoryGui) }, typeof(InventoryGui), skipVisibility: true);
			ILGenerator iLGenerator = dynamicMethod.GetILGenerator();
			iLGenerator.Emit(OpCodes.Ldarg_0);
			iLGenerator.Emit(OpCodes.Ldflda, fieldInfo);
			iLGenerator.Emit(OpCodes.Call, methodInfo);
			iLGenerator.Emit(OpCodes.Ret);
			return (Func<InventoryGui, T>)dynamicMethod.CreateDelegate(typeof(Func<InventoryGui, T>));
		}
	}
	internal static class ForgeStation
	{
		public static bool IsActive()
		{
			CraftingStation val = (((Object)(object)Player.m_localPlayer != (Object)null) ? Player.m_localPlayer.GetCurrentCraftingStation() : null);
			if ((Object)(object)val != (Object)null)
			{
				return val.m_upgrader;
			}
			return false;
		}

		public static SharedData UpgraderResource(Recipe recipe)
		{
			if ((Object)(object)recipe == (Object)null || recipe.m_resources == null)
			{
				return null;
			}
			Requirement[] resources = recipe.m_resources;
			foreach (Requirement val in resources)
			{
				if (val != null && val.m_upgraderResource && (Object)(object)val.m_resItem != (Object)null)
				{
					return val.m_resItem.m_itemData?.m_shared;
				}
			}
			return null;
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "DoCrafting")]
	internal static class UpgraderRollPatch
	{
		internal sealed class Saved
		{
			public SharedData Shared;

			public float Chance;
		}

		private static void Prefix(Recipe ___m_craftRecipe, ItemData ___m_craftUpgradeItem, out Saved __state)
		{
			__state = null;
			if (PluginConfig.Enabled && ___m_craftUpgradeItem != null && ForgeStation.IsActive())
			{
				SharedData val = ForgeStation.UpgraderResource(___m_craftRecipe);
				if (val != null)
				{
					int num = ___m_craftUpgradeItem.m_quality + 1;
					__state = new Saved
					{
						Shared = val,
						Chance = val.m_upgradeChance
					};
					val.m_upgradeChance = PluginConfig.Curve.Chance(num);
					Plugin.Log.LogDebug((object)$"Forge Odds: {___m_craftUpgradeItem.m_shared.m_name} → {num}, шанс {val.m_upgradeChance:0.###}");
				}
			}
		}

		private static void Finalizer(Saved __state)
		{
			if (__state != null)
			{
				__state.Shared.m_upgradeChance = __state.Chance;
			}
		}
	}
}