Decompiled source of PartialCommand v0.1.0

PartialCommand.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using R2API;
using RoR2;
using UnityEngine;
using UnityEngine.Networking;

[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("PartialCommand")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: AssemblyInformationalVersion("0.1.0")]
[assembly: AssemblyProduct("PartialCommand")]
[assembly: AssemblyTitle("PartialCommand")]
[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 PartialCommand
{
	[HarmonyPatch]
	internal static class CommandChancePatch
	{
		private static readonly List<PartialArtifact> Enabled = new List<PartialArtifact>(3);

		private static Xoroshiro128Plus rng;

		private static string lastDescription;

		internal static void Init()
		{
			Run.onRunStartGlobal += OnRunStart;
		}

		internal static void Dispose()
		{
			Run.onRunStartGlobal -= OnRunStart;
			rng = null;
			lastDescription = null;
			Enabled.Clear();
		}

		private static void OnRunStart(Run run)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Expected O, but got Unknown
			rng = new Xoroshiro128Plus(run.seed ^ 0x9E3779B97F4A7C15uL);
			lastDescription = null;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(PickupDropletController), "CreatePickupDroplet", new Type[]
		{
			typeof(CreatePickupInfo),
			typeof(Vector3),
			typeof(Vector3)
		})]
		private static void MaybeMakeChoosable(ref CreatePickupInfo pickupInfo)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: 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_0022: Unknown result type (might be due to invalid IL or missing references)
			if (!NetworkServer.active)
			{
				return;
			}
			PickupArtifactFlag artifactFlag = pickupInfo.artifactFlag;
			if (((Enum)artifactFlag).HasFlag((Enum)(object)(PickupArtifactFlag)1) || ((Enum)artifactFlag).HasFlag((Enum)(object)(PickupArtifactFlag)2))
			{
				return;
			}
			RunArtifactManager instance = RunArtifactManager.instance;
			if (!Object.op_Implicit((Object)(object)instance))
			{
				return;
			}
			if (instance.IsArtifactEnabled(Artifacts.commandArtifactDef))
			{
				Describe("Artifact of Command is enabled, so every drop is already choosable. PartialCommand is standing down for this run.");
				return;
			}
			float num = ChanceForCurrentRun(instance);
			if (Enabled.Count != 0)
			{
				Describe(Summarise(num));
				if (!(num <= 0f) && (!(num < 100f) || !(NextPercent() >= num)))
				{
					ref PickupArtifactFlag artifactFlag2 = ref pickupInfo.artifactFlag;
					artifactFlag2 = (PickupArtifactFlag)((uint)artifactFlag2 | 1u);
				}
			}
		}

		private static float ChanceForCurrentRun(RunArtifactManager artifacts)
		{
			Enabled.Clear();
			float num = 0f;
			float num2 = 1f;
			foreach (PartialArtifact item in PartialArtifacts.All)
			{
				if (!((Object)(object)item.Def == (Object)null) && artifacts.IsArtifactEnabled(item.Def))
				{
					float num3 = ModConfig.Percent(item.Chance);
					Enabled.Add(item);
					if (num3 > num)
					{
						num = num3;
					}
					num2 *= 1f - num3 / 100f;
				}
			}
			if (Enabled.Count == 0)
			{
				return 0f;
			}
			return Mathf.Clamp((ModConfig.MultipleArtifacts.Value == ModConfig.StackMode.Independent) ? ((1f - num2) * 100f) : num, 0f, ModConfig.Percent(ModConfig.MaximumChance));
		}

		private static string Summarise(float chance)
		{
			List<string> list = new List<string>(Enabled.Count);
			foreach (PartialArtifact item in Enabled)
			{
				list.Add(item.DisplayName + " " + Format(ModConfig.Percent(item.Chance)) + "%");
			}
			string text = string.Join(" + ", list.ToArray());
			if (Enabled.Count > 1)
			{
				text = text + " [" + ModConfig.MultipleArtifacts.Value.ToString() + "]";
			}
			text = text + " -> " + Format(chance) + "% of drops choosable this run.";
			float num = ModConfig.Percent(ModConfig.MaximumChance);
			if (num < 100f)
			{
				text = text + " (MaximumChance ceiling: " + Format(num) + "%)";
			}
			if (chance <= 0f)
			{
				text += " That is zero, so nothing will be choosable - check the Chances values and MaximumChance in the config.";
			}
			return text;
		}

		private static string Format(float value)
		{
			return value.ToString("0.##", CultureInfo.InvariantCulture);
		}

		private static void Describe(string message)
		{
			if (!(message == lastDescription))
			{
				lastDescription = message;
				Plugin.Log.LogInfo((object)message);
			}
		}

		private static float NextPercent()
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			if (rng == null)
			{
				rng = new Xoroshiro128Plus((ulong)DateTime.UtcNow.Ticks);
			}
			return rng.nextNormalizedFloat * 100f;
		}
	}
	internal static class IconFactory
	{
		private const int Size = 128;

		private const int Supersample = 3;

		internal static string IconDirectory;

		internal static Sprite Build(string key, float fraction, bool selected)
		{
			Sprite val = TryLoadFromDisk(key, selected);
			if (!((Object)(object)val != (Object)null))
			{
				return Generate(key, fraction, selected);
			}
			return val;
		}

		private static Sprite TryLoadFromDisk(string key, bool selected)
		{
			if (string.IsNullOrEmpty(IconDirectory))
			{
				return null;
			}
			string text = Path.Combine(IconDirectory, key.ToLowerInvariant() + (selected ? "_on.png" : "_off.png"));
			if (!File.Exists(text))
			{
				return null;
			}
			Texture2D val = NewTexture(key + (selected ? "_on" : "_off"));
			if (!ImageConversion.LoadImage(val, File.ReadAllBytes(text), false))
			{
				Object.Destroy((Object)(object)val);
				Plugin.Log.LogWarning((object)("Could not decode icon at " + text + "; falling back to the generated one."));
				return null;
			}
			return MakeSprite(val);
		}

		private static Sprite Generate(string key, float fraction, bool selected)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: 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_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_0204: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0209: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_018b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0197: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0186: Unknown result type (might be due to invalid IL or missing references)
			//IL_0183: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			Color val = (selected ? new Color(1f, 0.8f, 0.38f) : new Color(0.4f, 0.41f, 0.45f));
			Color val2 = (selected ? new Color(1f, 0.56f, 0.2f) : new Color(0.29f, 0.3f, 0.33f));
			Color val3 = (selected ? new Color(0.26f, 0.2f, 0.13f) : new Color(0.16f, 0.16f, 0.18f));
			float num = Mathf.Clamp01(fraction) * MathF.PI * 2f;
			float num2 = 1f / 9f;
			Color[] array = (Color[])(object)new Color[16384];
			for (int i = 0; i < 128; i++)
			{
				for (int j = 0; j < 128; j++)
				{
					float num3 = 0f;
					float num4 = 0f;
					float num5 = 0f;
					float num6 = 0f;
					for (int k = 0; k < 3; k++)
					{
						for (int l = 0; l < 3; l++)
						{
							float num7 = (float)j + ((float)l + 0.5f) / 3f - 0.5f - 63.5f;
							float num8 = (float)i + ((float)k + 0.5f) / 3f - 0.5f - 63.5f;
							float num9 = Mathf.Sqrt(num7 * num7 + num8 * num8);
							Color val4;
							if (num9 >= 50.56f && num9 <= 60.16f)
							{
								val4 = val;
							}
							else
							{
								if (!(num9 <= 42.24f))
								{
									continue;
								}
								float num10 = Mathf.Atan2(num7, num8);
								if (num10 < 0f)
								{
									num10 += MathF.PI * 2f;
								}
								val4 = ((num10 < num) ? val2 : val3);
							}
							num3 += val4.r;
							num4 += val4.g;
							num5 += val4.b;
							num6 += 1f;
						}
					}
					array[i * 128 + j] = (Color)((num6 > 0f) ? new Color(num3 / num6, num4 / num6, num5 / num6, num6 * num2) : Color.clear);
				}
			}
			Texture2D obj = NewTexture(key + (selected ? "_on" : "_off"));
			obj.SetPixels(array);
			obj.Apply(false, false);
			return MakeSprite(obj);
		}

		private static Texture2D NewTexture(string name)
		{
			//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)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Expected O, but got Unknown
			return new Texture2D(128, 128, (TextureFormat)4, false)
			{
				name = "PartialCommand_" + name,
				wrapMode = (TextureWrapMode)1,
				filterMode = (FilterMode)1,
				hideFlags = (HideFlags)32
			};
		}

		private static Sprite MakeSprite(Texture2D texture)
		{
			//IL_0019: 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)
			Sprite obj = Sprite.Create(texture, new Rect(0f, 0f, (float)((Texture)texture).width, (float)((Texture)texture).height), new Vector2(0.5f, 0.5f), 100f);
			((Object)obj).name = ((Object)texture).name;
			((Object)obj).hideFlags = (HideFlags)32;
			return obj;
		}
	}
	internal static class ModConfig
	{
		internal enum StackMode
		{
			Highest,
			Independent
		}

		internal static ConfigEntry<float> DirectChance;

		internal static ConfigEntry<float> SuggestChance;

		internal static ConfigEntry<float> AskChance;

		internal static ConfigEntry<StackMode> MultipleArtifacts;

		internal static ConfigEntry<float> MaximumChance;

		internal static void Init(ConfigFile file)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Expected O, but got Unknown
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Expected O, but got Unknown
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Expected O, but got Unknown
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Expected O, but got Unknown
			DirectChance = file.Bind<float>("Chances", "Direct", 75f, new ConfigDescription("Percent of dropped items that become choosable under Artifact of Direct.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
			SuggestChance = file.Bind<float>("Chances", "Suggest", 50f, new ConfigDescription("Percent of dropped items that become choosable under Artifact of Suggest.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
			AskChance = file.Bind<float>("Chances", "Ask", 25f, new ConfigDescription("Percent of dropped items that become choosable under Artifact of Ask.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
			MultipleArtifacts = file.Bind<StackMode>("Behaviour", "MultipleArtifacts", StackMode.Highest, "What happens when several of these artifacts are enabled at once. Highest: only the biggest chance applies. Independent: each one rolls on its own, so 75 + 50 lands at 87.5%.");
			MaximumChance = file.Bind<float>("Behaviour", "MaximumChance", 100f, new ConfigDescription("Hard ceiling on the combined chance, applied after the artifacts and the stacking mode are worked out. A safety valve for odd combinations; 100 disables it.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
		}

		internal static float Percent(ConfigEntry<float> entry)
		{
			float value = entry.Value;
			if (float.IsNaN(value))
			{
				return 0f;
			}
			return Mathf.Clamp(value, 0f, 100f);
		}
	}
	internal sealed class PartialArtifact
	{
		internal string Key { get; }

		internal string DisplayName { get; }

		internal ConfigEntry<float> Chance { get; }

		internal ArtifactDef Def { get; set; }

		internal PartialArtifact(string key, string displayName, ConfigEntry<float> chance)
		{
			Key = key;
			DisplayName = displayName;
			Chance = chance;
		}
	}
	internal static class PartialArtifacts
	{
		internal static readonly List<PartialArtifact> All = new List<PartialArtifact>();

		internal static void Register()
		{
			All.Clear();
			Add(new PartialArtifact("Direct", "Artifact of Direct", ModConfig.DirectChance));
			Add(new PartialArtifact("Suggest", "Artifact of Suggest", ModConfig.SuggestChance));
			Add(new PartialArtifact("Ask", "Artifact of Ask", ModConfig.AskChance));
		}

		private static void Add(PartialArtifact artifact)
		{
			string text = "PARTIALCOMMAND_" + artifact.Key.ToUpperInvariant();
			string text2 = text + "_NAME";
			string text3 = text + "_DESCRIPTION";
			float num = ModConfig.Percent(artifact.Chance);
			LanguageAPI.Add(text2, artifact.DisplayName);
			LanguageAPI.Add(text3, "<style=cIsUtility>" + num.ToString("0.##", CultureInfo.InvariantCulture) + "%</style> of dropped items let you pick what they become. The rest drop as normal.");
			ArtifactDef val = ScriptableObject.CreateInstance<ArtifactDef>();
			val.cachedName = "PARTIALCOMMAND_" + artifact.Key;
			val.nameToken = text2;
			val.descriptionToken = text3;
			val.unlockableDef = null;
			val.requiredExpansion = null;
			val.smallIconSelectedSprite = IconFactory.Build(artifact.Key, num / 100f, selected: true);
			val.smallIconDeselectedSprite = IconFactory.Build(artifact.Key, num / 100f, selected: false);
			if (!ContentAddition.AddArtifactDef(val))
			{
				Plugin.Log.LogError((object)("R2API rejected " + artifact.DisplayName + "; it will not appear in the lobby."));
				return;
			}
			artifact.Def = val;
			All.Add(artifact);
			Plugin.Log.LogInfo((object)("Registered " + artifact.DisplayName + " at " + num + "%."));
		}
	}
	[BepInPlugin("com.zackman.partialcommand", "PartialCommand", "0.1.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInProcess("Risk of Rain 2.exe")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string Guid = "com.zackman.partialcommand";

		public const string Name = "PartialCommand";

		public const string Version = "0.1.0";

		private Harmony harmony;

		internal static ManualLogSource Log { get; private set; }

		private void Awake()
		{
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			ModConfig.Init(((BaseUnityPlugin)this).Config);
			IconFactory.IconDirectory = Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location) ?? string.Empty, "icons");
			PartialArtifacts.Register();
			CommandChancePatch.Init();
			harmony = new Harmony("com.zackman.partialcommand");
			harmony.PatchAll(typeof(CommandChancePatch));
		}

		private void OnDestroy()
		{
			Harmony obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
			CommandChancePatch.Dispose();
		}
	}
}