Decompiled source of FairShare v1.0.0

FairShare.dll

Decompiled 20 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using Microsoft.CodeAnalysis;
using On.RoR2;
using R2API;
using RoR2;
using UnityEngine;
using UnityEngine.Events;
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("FairShare")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+4f1aaccd70f98eafb26ffba818f3b21607794682")]
[assembly: AssemblyProduct("FairShare")]
[assembly: AssemblyTitle("FairShare")]
[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 FairShare
{
	[BepInPlugin("com.creeper2002.fairshare", "FairShare", "0.4.1")]
	[BepInProcess("Risk of Rain 2.exe")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string Guid = "com.creeper2002.fairshare";

		public const string Name = "FairShare";

		public const string Version = "0.4.1";

		internal static ConfigEntry<int> DeadZone;

		internal static ConfigEntry<float> PercentPerItem;

		internal static ConfigEntry<float> MinCostPercent;

		internal static ConfigEntry<float> MaxCostPercent;

		internal static ConfigEntry<bool> DebugLogging;

		private static int temporaryDisplayCostDepth;

		internal static ArtifactDef FairShareArtifact;

		private void Awake()
		{
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Expected O, but got Unknown
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Expected O, but got Unknown
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Expected O, but got Unknown
			DeadZone = ((BaseUnityPlugin)this).Config.Bind<int>("Scaling", "ItemsBeforeScaling", 5, "Inventory difference from the living-player average ignored before scaling begins.");
			PercentPerItem = ((BaseUnityPlugin)this).Config.Bind<float>("Scaling", "PercentPerItem", 5f, "Percent price change per item beyond the dead zone.");
			MinCostPercent = ((BaseUnityPlugin)this).Config.Bind<float>("Scaling", "MinimumCostPercent", 50f, "Lowest allowed personal money price as a percentage of normal cost.");
			MaxCostPercent = ((BaseUnityPlugin)this).Config.Bind<float>("Scaling", "MaximumCostPercent", 200f, "Highest allowed personal money price as a percentage of normal cost.");
			DebugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "LogPurchases", false, "Log FairShare purchase calculations for troubleshooting.");
			RegisterArtifact();
			PurchaseInteraction.GetContextString += new hook_GetContextString(PurchaseInteraction_GetContextString);
			PurchaseInteraction.GetInteractability += new hook_GetInteractability(PurchaseInteraction_GetInteractability);
			PurchaseInteraction.OnInteractionBegin += new hook_OnInteractionBegin(PurchaseInteraction_OnInteractionBegin);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"FairShare 0.4.1 loaded. Personal multiplayer money pricing is available through the Artifact of Fair Share.");
		}

		private void OnDestroy()
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected O, but got Unknown
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Expected O, but got Unknown
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Expected O, but got Unknown
			PurchaseInteraction.GetContextString -= new hook_GetContextString(PurchaseInteraction_GetContextString);
			PurchaseInteraction.GetInteractability -= new hook_GetInteractability(PurchaseInteraction_GetInteractability);
			PurchaseInteraction.OnInteractionBegin -= new hook_OnInteractionBegin(PurchaseInteraction_OnInteractionBegin);
		}

		private void RegisterArtifact()
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: 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)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			Texture2D val = LoadEmbeddedTexture("FairShare.artifact_fairshare.png");
			Texture2D val2 = CreateDeselectedTexture(val);
			Sprite smallIconSelectedSprite = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f), 100f);
			Sprite smallIconDeselectedSprite = Sprite.Create(val2, new Rect(0f, 0f, (float)((Texture)val2).width, (float)((Texture)val2).height), new Vector2(0.5f, 0.5f), 100f);
			FairShareArtifact = ScriptableObject.CreateInstance<ArtifactDef>();
			FairShareArtifact.cachedName = "ArtifactOfFairShare";
			FairShareArtifact.nameToken = "FAIRSHARE_ARTIFACT_NAME";
			FairShareArtifact.descriptionToken = "FAIRSHARE_ARTIFACT_DESCRIPTION";
			FairShareArtifact.smallIconSelectedSprite = smallIconSelectedSprite;
			FairShareArtifact.smallIconDeselectedSprite = smallIconDeselectedSprite;
			LanguageAPI.Add(FairShareArtifact.nameToken, "Artifact of Fair Share");
			LanguageAPI.Add(FairShareArtifact.descriptionToken, "Those with more must pay more. Money prices scale with your item count relative to the living team average.");
			ContentAddition.AddArtifactDef(FairShareArtifact);
		}

		private static Texture2D LoadEmbeddedTexture(string resourceName)
		{
			//IL_0060: 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_006c: Expected O, but got Unknown
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Expected O, but got Unknown
			using Stream stream = typeof(Plugin).Assembly.GetManifestResourceStream(resourceName);
			if (stream == null)
			{
				throw new InvalidOperationException("Embedded artifact icon '" + resourceName + "' was not found.");
			}
			byte[] array = new byte[stream.Length];
			int num;
			for (int i = 0; i < array.Length; i += num)
			{
				num = stream.Read(array, i, array.Length - i);
				if (num <= 0)
				{
					break;
				}
			}
			Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false);
			if (!ImageConversion.LoadImage(val, array))
			{
				throw new InvalidOperationException("Failed to load embedded Artifact of Fair Share icon.");
			}
			((Object)val).name = "ArtifactOfFairShareIcon";
			return val;
		}

		private static Texture2D CreateDeselectedTexture(Texture2D source)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Expected O, but got Unknown
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: 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_0033: 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_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)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			Texture2D val = new Texture2D(((Texture)source).width, ((Texture)source).height, (TextureFormat)4, false);
			Color[] pixels = source.GetPixels();
			for (int i = 0; i < pixels.Length; i++)
			{
				Color val2 = pixels[i];
				float num = (val2.r * 0.299f + val2.g * 0.587f + val2.b * 0.114f) * 0.55f;
				pixels[i] = new Color(num, num, num, val2.a);
			}
			val.SetPixels(pixels);
			val.Apply();
			((Object)val).name = "ArtifactOfFairShareIconDeselected";
			return val;
		}

		internal static bool IsFairShareEnabled()
		{
			if ((Object)(object)FairShareArtifact != (Object)null && (Object)(object)RunArtifactManager.instance != (Object)null)
			{
				return RunArtifactManager.instance.IsArtifactEnabled(FairShareArtifact);
			}
			return false;
		}

		private string PurchaseInteraction_GetContextString(orig_GetContextString orig, PurchaseInteraction self, Interactor activator)
		{
			if (!TryGetPersonalCost(self, activator, out var _, out var personalCost) || temporaryDisplayCostDepth > 0)
			{
				return orig.Invoke(self, activator);
			}
			int cost = self.cost;
			temporaryDisplayCostDepth++;
			try
			{
				self.cost = personalCost;
				return orig.Invoke(self, activator);
			}
			finally
			{
				self.cost = cost;
				temporaryDisplayCostDepth--;
			}
		}

		private Interactability PurchaseInteraction_GetInteractability(orig_GetInteractability orig, PurchaseInteraction self, Interactor activator)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//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_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Invalid comparison between Unknown and I4
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Invalid comparison between Unknown and I4
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			if (!TryGetPersonalCost(self, activator, out var master, out var personalCost) || temporaryDisplayCostDepth > 0)
			{
				return orig.Invoke(self, activator);
			}
			int cost = self.cost;
			uint money = master.money;
			temporaryDisplayCostDepth++;
			try
			{
				self.cost = personalCost;
				Interactability val = orig.Invoke(self, activator);
				if ((int)val != 2 && money < (uint)personalCost)
				{
					master.money = (uint)personalCost;
					if ((int)orig.Invoke(self, activator) == 2)
					{
						return (Interactability)2;
					}
				}
				return val;
			}
			finally
			{
				master.money = money;
				self.cost = cost;
				temporaryDisplayCostDepth--;
			}
		}

		private void PurchaseInteraction_OnInteractionBegin(orig_OnInteractionBegin orig, PurchaseInteraction self, Interactor activator)
		{
			if (!NetworkServer.active || !TryGetPersonalCost(self, activator, out var master, out var personalCost))
			{
				orig.Invoke(self, activator);
				return;
			}
			int num = Math.Max(0, self.cost);
			uint money = master.money;
			if (DebugLogging.Value)
			{
				int rawItemCount = GetRawItemCount(master);
				float averageItemCount = GetAverageItemCount();
				float personalMultiplier = GetPersonalMultiplier(master);
				((BaseUnityPlugin)this).Logger.LogInfo((object)($"Purchase attempt: base=${num}, personal=${personalCost}, money=${money}, " + $"items={rawItemCount}, living average={averageItemCount:0.##}, multiplier={personalMultiplier:0.###}x"));
			}
			if (money < (uint)personalCost)
			{
				if (DebugLogging.Value)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)"Purchase rejected by FairShare: player cannot afford their personal price.");
				}
				return;
			}
			if (personalCost == num)
			{
				orig.Invoke(self, activator);
				return;
			}
			long val = money + num - personalCost;
			uint money2 = (uint)Math.Max(0L, Math.Min(4294967295L, val));
			bool purchaseCompleted = false;
			UnityAction<Interactor> val2 = delegate
			{
				purchaseCompleted = true;
			};
			((UnityEvent<Interactor>)(object)self.onPurchase).AddListener(val2);
			master.money = money2;
			try
			{
				orig.Invoke(self, activator);
			}
			finally
			{
				((UnityEvent<Interactor>)(object)self.onPurchase).RemoveListener(val2);
				if (!purchaseCompleted)
				{
					master.money = money;
				}
			}
			if (DebugLogging.Value)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)(purchaseCompleted ? $"FairShare purchase completed. Remaining money=${master.money}." : "Vanilla did not complete the purchase; temporary money adjustment was restored."));
			}
		}

		internal static bool TryGetPersonalCost(PurchaseInteraction interaction, Interactor activator, out CharacterMaster master, out int personalCost)
		{
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Invalid comparison between Unknown and I4
			master = GetMaster(activator);
			personalCost = (((Object)(object)interaction != (Object)null) ? interaction.cost : 0);
			if (!IsFairShareEnabled())
			{
				return false;
			}
			if ((Object)(object)interaction == (Object)null || (Object)(object)master == (Object)null)
			{
				return false;
			}
			if ((int)interaction.costType != 1)
			{
				return false;
			}
			if (GetEligibleMasters().Count <= 1)
			{
				return false;
			}
			personalCost = GetPersonalMoneyCost(interaction.cost, master);
			return true;
		}

		internal static CharacterMaster GetMaster(Interactor activator)
		{
			if ((Object)(object)activator == (Object)null)
			{
				return null;
			}
			CharacterBody component = ((Component)activator).GetComponent<CharacterBody>();
			if (!((Object)(object)component != (Object)null))
			{
				return null;
			}
			return component.master;
		}

		internal unsafe static int GetRawItemCount(CharacterMaster master)
		{
			//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_002a: 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_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Invalid comparison between Unknown and I4
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)master == (Object)null || (Object)(object)master.inventory == (Object)null)
			{
				return 0;
			}
			int num = 0;
			AllItemsEnumerator enumerator = ItemCatalog.allItems.GetEnumerator();
			try
			{
				while (((AllItemsEnumerator)(ref enumerator)).MoveNext())
				{
					ItemIndex current = ((AllItemsEnumerator)(ref enumerator)).Current;
					ItemDef itemDef = ItemCatalog.GetItemDef(current);
					if (!((Object)(object)itemDef == (Object)null) && !itemDef.hidden && (int)itemDef.tier != 5)
					{
						num += Math.Max(0, master.inventory.GetItemCountPermanent(current));
					}
				}
				return num;
			}
			finally
			{
				((IDisposable)(*(AllItemsEnumerator*)(&enumerator))/*cast due to .constrained prefix*/).Dispose();
			}
		}

		internal static List<CharacterMaster> GetEligibleMasters()
		{
			return CharacterMaster.readOnlyInstancesList.Where((CharacterMaster m) => (Object)(object)m != (Object)null && (Object)(object)m.playerCharacterMasterController != (Object)null && (Object)(object)m.inventory != (Object)null && (Object)(object)m.GetBody() != (Object)null).ToList();
		}

		internal static float GetAverageItemCount()
		{
			List<CharacterMaster> eligibleMasters = GetEligibleMasters();
			if (eligibleMasters.Count == 0)
			{
				return 0f;
			}
			return (float)((IEnumerable<CharacterMaster>)eligibleMasters).Average((Func<CharacterMaster, int>)GetRawItemCount);
		}

		internal static float GetPersonalMultiplier(CharacterMaster master)
		{
			if ((Object)(object)master == (Object)null)
			{
				return 1f;
			}
			List<CharacterMaster> eligibleMasters = GetEligibleMasters();
			if (eligibleMasters.Count <= 1)
			{
				return 1f;
			}
			float num = (float)((IEnumerable<CharacterMaster>)eligibleMasters).Average((Func<CharacterMaster, int>)GetRawItemCount);
			float num2 = (float)GetRawItemCount(master) - num;
			int num3 = Math.Max(0, DeadZone.Value);
			float num4 = 0f;
			if (num2 > (float)num3)
			{
				num4 = num2 - (float)num3;
			}
			else if (num2 < (float)(-num3))
			{
				num4 = num2 + (float)num3;
			}
			float val = 1f + num4 * (PercentPerItem.Value / 100f);
			float val2 = Math.Max(0f, MinCostPercent.Value / 100f);
			float val3 = Math.Max(val2, MaxCostPercent.Value / 100f);
			return Math.Max(val2, Math.Min(val3, val));
		}

		internal static int GetPersonalMoneyCost(int baseCost, CharacterMaster buyer)
		{
			if (baseCost <= 0)
			{
				return Math.Max(0, baseCost);
			}
			double value = (double)baseCost * (double)GetPersonalMultiplier(buyer);
			return Math.Max(1, (int)Math.Round(value, MidpointRounding.AwayFromZero));
		}
	}
}