Decompiled source of Budgeting v1.1.4

BudgetingPlugin.dll

Decompiled a month ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using BudgetingPlugin.Config;
using BudgetingPlugin.Core;
using BudgetingPlugin.DistributionStats;
using BudgetingPlugin.Helpers;
using BudgetingPlugin.Network;
using BudgetingPlugin.UI;
using ExitGames.Client.Photon;
using HarmonyLib;
using MenuLib;
using MenuLib.MonoBehaviors;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using Photon.Realtime;
using REPOLib.Modules;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: IgnoresAccessChecksTo("")]
[assembly: AssemblyCompany("znore")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+2216aa20f366eefdca6ccf1de8c9e814ec121006")]
[assembly: AssemblyProduct("BudgetingPlugin")]
[assembly: AssemblyTitle("BudgetingPlugin")]
[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 BudgetingPlugin
{
	[BepInPlugin("znore.Budgeting", "Budgeting", "1.1.3")]
	public class BudgetingPlugin : BaseUnityPlugin
	{
		internal static BudgetingPlugin 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;
			PluginConfig.Initialize(((BaseUnityPlugin)this).Config);
			Patch();
			Logger.LogInfo((object)$"{((BaseUnityPlugin)this).Info.Metadata.GUID} v{((BaseUnityPlugin)this).Info.Metadata.Version} has loaded!");
		}

		private void Update()
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			if (!SemiFunc.RunIsShop())
			{
				BudgetWidget.Destroy();
			}
			else if (Input.GetKeyDown(PluginConfig.OpenMenuKey.Value))
			{
				BudgetSharePopup.TogglePopup();
			}
		}

		internal void Patch()
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Expected O, but got Unknown
			//IL_0026: 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();
			}
		}
	}
}
namespace BudgetingPlugin.UI
{
	internal static class BudgetSharePopup
	{
		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static Action<int> <>9__2_2;

			public static ScrollViewBuilderDelegate <>9__2_1;

			internal void <InitPopupPage>b__2_2(int i)
			{
				SetMoneyToDrop(i);
			}

			internal RectTransform <InitPopupPage>b__2_1(Transform scrollView)
			{
				//IL_0024: 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)
				REPOButton val = MenuAPI.CreateREPOButton("DROP THE BAG", (Action)ActionSubmit, scrollView, default(Vector2));
				return ((REPOElement)val).rectTransform;
			}
		}

		private static REPOPopupPage? _page;

		private static int _moneyToDrop;

		internal static void InitPopupPage()
		{
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Expected O, but got Unknown
			//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_00aa: Expected O, but got Unknown
			_moneyToDrop = 0;
			int actorNumber = PhotonNetwork.LocalPlayer.ActorNumber;
			int playerMax = BudgetingStats.Instance.getShare(actorNumber) / 1000;
			if (playerMax <= 0)
			{
				PlayerBudgetLines.SaySomethingStupid(PlayerBudgetLines.GetRandomLine(PlayerBudgetLines.LineTypes.NotEnoughMoneyToGive));
				return;
			}
			_page = MenuAPI.CreateREPOPopupPage("How much money to share?", (PresetSide)0, false, true, 1.5f);
			_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)
				REPOSlider val2 = MenuAPI.CreateREPOSlider(string.Empty, string.Empty, (Action<int>)delegate(int i)
				{
					SetMoneyToDrop(i);
				}, scrollView, default(Vector2), 0, playerMax, 0, "$", "K", (BarBehavior)0);
				return ((REPOElement)val2).rectTransform;
			}, 0f, 0f);
			REPOPopupPage? page = _page;
			object obj = <>c.<>9__2_1;
			if (obj == null)
			{
				ScrollViewBuilderDelegate val = delegate(Transform scrollView)
				{
					//IL_0024: 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)
					REPOButton val2 = MenuAPI.CreateREPOButton("DROP THE BAG", (Action)ActionSubmit, scrollView, default(Vector2));
					return ((REPOElement)val2).rectTransform;
				};
				<>c.<>9__2_1 = val;
				obj = (object)val;
			}
			page.AddElementToScrollView((ScrollViewBuilderDelegate)obj, 0f, 0f);
		}

		private static void SetMoneyToDrop(int inMoney)
		{
			_moneyToDrop = inMoney;
		}

		private static void ActionSubmit()
		{
			if (_moneyToDrop <= 0)
			{
				Close();
				return;
			}
			BudgetingNetwork.Instance?.SpawnMoneyBag(_moneyToDrop * 1000);
			Close();
		}

		internal static void TogglePopup()
		{
			if (IsOpen())
			{
				Close();
			}
			else
			{
				Open();
			}
		}

		private static void Open()
		{
			InitPopupPage();
			_page.OpenPage(true);
		}

		private static void Close()
		{
			_page.ClosePage(true);
			_page = null;
		}

		private static bool IsOpen()
		{
			return _page != null;
		}
	}
	internal static class BudgetWidget
	{
		private const string TextTemplate = "Your wallet: ${0}K";

		private static readonly Vector2 PositionOffset = new Vector2(PluginConfig.WalletOffsetX.Value, PluginConfig.WalletOffsetY.Value);

		private static TextMeshProUGUI? _widgetText;

		internal static void Initialize()
		{
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: 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_013a: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_widgetText != (Object)null)
			{
				return;
			}
			CurrencyUI instance = CurrencyUI.instance;
			if ((Object)(object)instance == (Object)null)
			{
				BudgetingPlugin.Logger.LogWarning((object)"BudgetWidget.Initialize: CurrencyUI.instance is null; widget not created.");
				return;
			}
			TextMeshProUGUI text = instance.Text;
			BudgetingPlugin.Logger.LogDebug((object)$"CurrencyUI font size: {((TMP_Text)text).fontSize}");
			if ((Object)(object)text == (Object)null)
			{
				BudgetingPlugin.Logger.LogWarning((object)"BudgetWidget.Initialize: CurrencyUI.Text is null; widget not created.");
				return;
			}
			GameObject val = Object.Instantiate<GameObject>(((Component)text).gameObject, ((TMP_Text)text).transform.parent);
			((Object)val).name = "BudgetWidget";
			RectTransform component = ((Component)text).GetComponent<RectTransform>();
			RectTransform component2 = val.GetComponent<RectTransform>();
			component2.anchoredPosition = component.anchoredPosition + PositionOffset;
			MonoBehaviour[] components = val.GetComponents<MonoBehaviour>();
			foreach (MonoBehaviour val2 in components)
			{
				if (!(val2 is TextMeshProUGUI))
				{
					((Behaviour)val2).enabled = false;
				}
			}
			_widgetText = val.GetComponent<TextMeshProUGUI>();
			((TMP_Text)_widgetText).fontSize = ((TMP_Text)text).fontSize * 0.6f;
			((Graphic)_widgetText).color = Color.yellow;
			SetValue(0);
		}

		internal static void Destroy()
		{
			if (!((Object)(object)_widgetText == (Object)null))
			{
				Object.Destroy((Object)(object)((Component)_widgetText).gameObject);
				_widgetText = null;
			}
		}

		internal static void SetValue(int amount)
		{
			amount /= 1000;
			if ((Object)(object)_widgetText == (Object)null)
			{
				BudgetingPlugin.Logger.LogWarning((object)"BudgetWidget.SetValue called before the widget was initialized.");
			}
			else
			{
				((TMP_Text)_widgetText).text = $"Your wallet: ${amount}K";
			}
		}
	}
}
namespace BudgetingPlugin.Patches
{
	[HarmonyPatch(typeof(ShopManager))]
	public static class BudgetingPatch
	{
		[HarmonyPostfix]
		[HarmonyPatch("ShopInitialize")]
		private static void ShopInitialize_Postfix(ShopManager __instance)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Expected O, but got Unknown
			if ((Object)(object)BudgetingNetwork.Instance == (Object)null)
			{
				GameObject val = new GameObject("BudgetingNetwork");
				Object.DontDestroyOnLoad((Object)(object)val);
				val.AddComponent<BudgetingNetwork>();
			}
			if (!SemiFunc.IsMasterClientOrSingleplayer())
			{
				return;
			}
			int num = __instance.totalCurrency * 1000;
			if (num == 0)
			{
				return;
			}
			List<PlayerAvatar> list = SemiFunc.PlayerGetList();
			BudgetingStats.Instance.setPlayers(list);
			double[] weights = GetWeights();
			Dictionary<int, int> shares = BudgetingStats.Instance.getShares();
			int num2 = 0;
			foreach (int value in shares.Values)
			{
				num2 += value;
			}
			int total = num - num2;
			int[][] playerStandings = BudgetingStats.Instance.GetPlayerStandings();
			int[] array = playerStandings.SelectMany((int[] tier) => tier).ToArray();
			bool flag = array.Length == list.Count;
			double[] weights2 = (flag ? BudgetWeights.AveragePerTieGroup(weights, playerStandings.Select((int[] tier) => tier.Length).ToArray()) : weights);
			int[] rawShares = BudgetDistribution.SelectShares(total, weights2, list.Count, flag);
			Dictionary<int, int> shares2 = new Dictionary<int, int>(list.Count);
			AddTotals(list, rawShares, array, shares2);
			BudgetingStats.Instance.setShares(shares2);
			BudgetingNetwork.Instance?.SendShares(shares2);
		}

		private static void AddTotals(List<PlayerAvatar> players, int[] rawShares, int[] rankedActors, Dictionary<int, int> shares)
		{
			bool flag = rankedActors.Length == players.Count;
			if (rankedActors.Length != 0 && !flag)
			{
				BudgetingPlugin.Logger.LogError((object)$"[BudgetingPlugin] Standings count ({rankedActors.Length}) != player count ({players.Count}); falling back to positional assignment. Likely an indexing issue between Weighting and 'Standing' logic.");
			}
			for (int i = 0; i < players.Count; i++)
			{
				int num = -1;
				if (!flag)
				{
					PhotonView photonView = players[i].photonView;
					int? obj;
					if (photonView == null)
					{
						obj = null;
					}
					else
					{
						Player owner = photonView.Owner;
						obj = ((owner != null) ? new int?(owner.ActorNumber) : ((int?)null));
					}
					num = obj ?? i;
				}
				else
				{
					num = rankedActors[i];
				}
				int share = BudgetingStats.Instance.getShare(num);
				int value = share + rawShares[i];
				shares[num] = value;
				BudgetingPlugin.Logger.LogDebug((object)$"===== Player [{num}] | Existing: {share} | Added On: {rawShares[i]} | TotalShare : ({shares[num]}) =====");
			}
		}

		private static double[] GetWeights()
		{
			List<PlayerAvatar> players = BudgetingStats.Instance.getPlayers();
			return BudgetWeights.GetWeights(players.Count, PluginConfig.DesiredDist.Value);
		}
	}
	[HarmonyPatch(typeof(ItemAttributes))]
	public static class DiscountPatch
	{
		[HarmonyPostfix]
		[HarmonyPatch("GetValue")]
		private static void GetValue_Postfix(ItemAttributes __instance)
		{
			if (!__instance.shopItem || SemiFunc.IsNotMasterClient() || __instance.value < 10)
			{
				return;
			}
			int value = PluginConfig.ShopDiscountPercent.Value;
			if (value > 0)
			{
				int num = __instance.value * 1000;
				int num2 = num * (100 - value) / 100 / 1000;
				BudgetingPlugin.Logger.LogDebug((object)$"[BudgetingPlugin] Discounted {((Object)__instance).name} by {value}% for a resulting value of ${num2 * 1000} (from {num})");
				__instance.value = num2;
				if (GameManager.Multiplayer() && __instance.photonView.ViewID != 0)
				{
					__instance.photonView.RPC("GetValueRPC", (RpcTarget)1, new object[1] { __instance.value });
				}
			}
		}
	}
	[HarmonyPatch(typeof(ExtractionPoint))]
	public static class ExtractionPointPatch
	{
		private static int _initialPresserActorNumber = -1;

		private static bool _purchaseMade;

		private static int _approvedCartTotal;

		[HarmonyPrefix]
		[HarmonyPatch("OnShopClick")]
		private static bool OnShopClick_Prefix(ExtractionPoint __instance)
		{
			_purchaseMade = false;
			if (_initialPresserActorNumber < 0)
			{
				_initialPresserActorNumber = BudgetingNetwork.LastShopButtonPresserActorNumber;
			}
			int share = BudgetingStats.Instance.getShare(_initialPresserActorNumber);
			int haulCurrent = __instance.haulCurrent;
			if (share >= haulCurrent && haulCurrent > 0)
			{
				_purchaseMade = true;
				return true;
			}
			if (haulCurrent == 0)
			{
				return true;
			}
			BudgetingNetwork.TriggerPlayerTalk(_initialPresserActorNumber);
			return false;
		}

		[HarmonyPrefix]
		[HarmonyPatch("DestroyAllPhysObjectsInShoppingList")]
		private static bool DestroyAllPhysObjectsInShoppingList_Prefix(ExtractionPoint __instance)
		{
			if (!_purchaseMade || !__instance.isShop)
			{
				return true;
			}
			int num = SemiFunc.ShopGetTotalCost() * 1000;
			if (BudgetingStats.Instance.getShare(_initialPresserActorNumber) < num)
			{
				_purchaseMade = false;
				ShopManager.instance.isThief = true;
				__instance.isThief = true;
				return false;
			}
			_approvedCartTotal = num;
			return true;
		}

		[HarmonyPostfix]
		[HarmonyPatch("DestroyAllPhysObjectsInShoppingList")]
		private static void DestroyAllPhysObjectsInShoppingList_Postfix(ExtractionPoint __instance)
		{
			if (_purchaseMade && __instance.isShop)
			{
				int amount = BudgetingStats.Instance.deductAmountFromPlayerShare(_initialPresserActorNumber, _approvedCartTotal);
				_purchaseMade = false;
				BudgetingNetwork.LastShopButtonPresserActorNumber = -1;
				BudgetingNetwork.TriggerMoneyDeductedEvent(_initialPresserActorNumber, amount);
				_initialPresserActorNumber = -1;
			}
		}
	}
	[HarmonyPatch(typeof(MenuManager))]
	public class MenuManagerPatch
	{
		[HarmonyPostfix]
		[HarmonyPatch("PageOpen")]
		public static void PageOpen(MenuManager __instance)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Invalid comparison between Unknown and I4
			if ((int)__instance.currentMenuPageIndex == 0)
			{
				BudgetingStats.Instance.Nuke();
			}
		}
	}
	[HarmonyPatch(typeof(PhysGrabber))]
	public class PhysGrabberPatch
	{
		private const string ShopButtonName = "Shop Button";

		[HarmonyPostfix]
		[HarmonyPatch(typeof(PhysGrabber), "StartGrabbingPhysObject")]
		private static void StartGrabbingPhysObject_Postfix(PhysGrabber __instance)
		{
			if (SemiFunc.RunIsShop())
			{
				NotifyNetworkWhenShopButtonPressed(__instance.grabbedStaticGrabObject);
				NotifyNetworkUponPhysGrabPickup(__instance.grabbedPhysGrabObject);
			}
		}

		private static void NotifyNetworkUponPhysGrabPickup(PhysGrabObject inPhysGrabObject)
		{
			if (!((Object)(object)inPhysGrabObject == (Object)null))
			{
				int viewID = ((Component)inPhysGrabObject).GetComponent<PhotonView>().ViewID;
				BudgetingNetwork.NotifyBagPickedUp(viewID);
			}
		}

		private static void NotifyNetworkWhenShopButtonPressed(StaticGrabObject inStaticGrabObject)
		{
			if (!((Object)(object)inStaticGrabObject == (Object)null) && ((Object)inStaticGrabObject).name.StartsWith("Shop Button"))
			{
				BudgetingNetwork.NotifyShopButtonPressed();
			}
		}
	}
	[HarmonyPatch(typeof(UpgradeStand))]
	public class UpgradeStandPatch
	{
		private static bool _ranOnce;

		[HarmonyPrefix]
		[HarmonyPatch(typeof(UpgradeStand), "StatePressSucceed")]
		private static bool StatePressSucceed_Prefix(UpgradeStand __instance)
		{
			if (_ranOnce)
			{
				return true;
			}
			int actorNumber = PhotonNetwork.LocalPlayer.ActorNumber;
			int share = BudgetingStats.Instance.getShare(actorNumber);
			int num = __instance.RerollCost * 1000;
			if (!__instance.stateStart)
			{
				__instance.StateSet((State)2);
				return false;
			}
			if (SemiFunc.IsMasterClientOrSingleplayer() && share >= num)
			{
				_ranOnce = true;
				int amount = BudgetingStats.Instance.deductAmountFromPlayerShare(actorNumber, num);
				BudgetingNetwork.TriggerMoneyDeductedEvent(actorNumber, amount);
				return true;
			}
			return false;
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(UpgradeStand), "StateRerollOpenHatch")]
		private static void StateRerollOpenHatch_Postfix(UpgradeStand __instance)
		{
		}
	}
}
namespace BudgetingPlugin.Network
{
	public class BudgetingNetwork : MonoBehaviour, IInRoomCallbacks
	{
		private const string SharesKey = "BudgetShares";

		internal static readonly Dictionary<int, int> BudgetBags = new Dictionary<int, int>();

		internal static int LastShopButtonPresserActorNumber = -1;

		private static readonly Durability InvincibleDurability = CreateInvincibleDurability();

		private NetworkedEvent _spawnBagEvent = null;

		private NetworkedEvent _pickupBagEvent = null;

		private NetworkedEvent _makePlayerTalk = null;

		private NetworkedEvent _shopButtonPressedEvent = null;

		private NetworkedEvent _moneyDeductedEvent = null;

		public static BudgetingNetwork? Instance { get; private set; }

		private void Awake()
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Expected O, but got Unknown
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Expected O, but got Unknown
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Expected O, but got Unknown
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Expected O, but got Unknown
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Expected O, but got Unknown
			Instance = this;
			PhotonNetwork.AddCallbackTarget((object)this);
			_spawnBagEvent = new NetworkedEvent("BudgetBag_Spawn", (Action<EventData>)OnSpawnBagEvent);
			_pickupBagEvent = new NetworkedEvent("BudgetBag_Pickup", (Action<EventData>)OnPickupBagEvent);
			_makePlayerTalk = new NetworkedEvent("MakePlayerTalk", (Action<EventData>)OnMakePlayerTalk);
			_shopButtonPressedEvent = new NetworkedEvent("ShopButtonPressed", (Action<EventData>)OnShopButtonPressed);
			_moneyDeductedEvent = new NetworkedEvent("MoneyDeductedFromPlayers", (Action<EventData>)OnMoneyDeducted);
		}

		private void OnDestroy()
		{
			PhotonNetwork.RemoveCallbackTarget((object)this);
			BudgetWidget.Destroy();
			Instance = null;
		}

		public unsafe void OnRoomPropertiesUpdate(Hashtable changedProps)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Expected O, but got Unknown
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			if (!((Dictionary<object, object>)(object)changedProps).ContainsKey((object)"BudgetShares"))
			{
				return;
			}
			Hashtable val = (Hashtable)changedProps[(object)"BudgetShares"];
			Dictionary<int, int> dictionary = new Dictionary<int, int>();
			DictionaryEntryEnumerator enumerator = val.GetEnumerator();
			try
			{
				while (((DictionaryEntryEnumerator)(ref enumerator)).MoveNext())
				{
					var (obj3, obj4) = (DictionaryEntry)(ref ((DictionaryEntryEnumerator)(ref enumerator)).Current);
					dictionary[(int)obj3] = (int)obj4;
				}
			}
			finally
			{
				((IDisposable)(*(DictionaryEntryEnumerator*)(&enumerator))/*cast due to .constrained prefix*/).Dispose();
			}
			BudgetingStats.Instance.setShares(dictionary);
			int actorNumber = PhotonNetwork.LocalPlayer.ActorNumber;
			if (!((Dictionary<object, object>)(object)val).ContainsKey((object)actorNumber))
			{
				BudgetingPlugin.Logger.LogWarning((object)$"No share found for player with ActorNumber {actorNumber}");
				return;
			}
			BudgetWidget.Initialize();
			BudgetWidget.SetValue((int)val[(object)actorNumber]);
		}

		public void OnPlayerEnteredRoom(Player newPlayer)
		{
		}

		public void OnPlayerLeftRoom(Player otherPlayer)
		{
		}

		public void OnPlayerPropertiesUpdate(Player targetPlayer, Hashtable changedProps)
		{
		}

		public void OnMasterClientSwitched(Player newMasterClient)
		{
		}

		private void OnMakePlayerTalk(EventData photonEvent)
		{
			object[] array = (object[])photonEvent.CustomData;
			if (PhotonNetwork.LocalPlayer.ActorNumber == (int)array[0])
			{
				string randomLine = PlayerBudgetLines.GetRandomLine((PlayerBudgetLines.LineTypes)array[1]);
				PlayerBudgetLines.SaySomethingStupid(randomLine);
			}
		}

		private void OnMoneyDeducted(EventData photonEvent)
		{
			object[] array = (object[])photonEvent.CustomData;
			if (PhotonNetwork.LocalPlayer.ActorNumber == (int)array[0])
			{
				BudgetWidget.SetValue((int)array[1]);
			}
		}

		private void OnShopButtonPressed(EventData photonEvent)
		{
			if (PhotonNetwork.IsMasterClient)
			{
				LastShopButtonPresserActorNumber = photonEvent.Sender;
			}
		}

		internal static void NotifyShopButtonPressed()
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			if (PhotonNetwork.IsMasterClient)
			{
				LastShopButtonPresserActorNumber = PhotonNetwork.LocalPlayer.ActorNumber;
				return;
			}
			BudgetingNetwork? instance = Instance;
			if (instance != null)
			{
				instance._shopButtonPressedEvent.RaiseEvent((object)PhotonNetwork.LocalPlayer.ActorNumber, NetworkingEvents.RaiseMasterClient, SendOptions.SendReliable);
			}
		}

		internal static void TriggerPlayerTalk(int actorNumber, PlayerBudgetLines.LineTypes lineType = PlayerBudgetLines.LineTypes.NotEnoughMoney)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			BudgetingNetwork? instance = Instance;
			if (instance != null)
			{
				instance._makePlayerTalk.RaiseEvent((object)new object[2] { actorNumber, lineType }, NetworkingEvents.RaiseAll, SendOptions.SendReliable);
			}
		}

		internal static void TriggerMoneyDeductedEvent(int actorNumber, int amount)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			BudgetingNetwork? instance = Instance;
			if (instance != null)
			{
				instance._moneyDeductedEvent.RaiseEvent((object)new object[2] { actorNumber, amount }, NetworkingEvents.RaiseAll, SendOptions.SendReliable);
			}
		}

		private void OnSpawnBagEvent(EventData photonEvent)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			if (PhotonNetwork.IsMasterClient)
			{
				object[] array = (object[])photonEvent.CustomData;
				ProcessSpawnRequest(photonEvent.Sender, (int)array[0], (Vector3)array[1]);
			}
		}

		private void OnPickupBagEvent(EventData photonEvent)
		{
			if (PhotonNetwork.IsMasterClient)
			{
				ProcessBagPickup((int)photonEvent.CustomData, photonEvent.Sender);
			}
		}

		public void SpawnMoneyBag(int amount)
		{
			//IL_005e: 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_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: 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_00bb: Unknown result type (might be due to invalid IL or missing references)
			if (!SemiFunc.RunIsShop())
			{
				return;
			}
			int actorNumber = PhotonNetwork.LocalPlayer.ActorNumber;
			int share = BudgetingStats.Instance.getShare(actorNumber);
			if (amount > 0)
			{
				if (amount > share)
				{
					TriggerPlayerTalk(actorNumber, PlayerBudgetLines.LineTypes.NotEnoughMoneyToGive);
					return;
				}
				Transform transform = ((Component)PlayerAvatar.instance).transform;
				Vector3 val = transform.position + transform.forward * PluginConfig.BagSpawnForwardOffset.Value + transform.up * PluginConfig.BagSpawnHeightOffset.Value;
				_spawnBagEvent.RaiseEvent((object)new object[2] { amount, val }, NetworkingEvents.RaiseMasterClient, SendOptions.SendReliable);
			}
		}

		private void ProcessSpawnRequest(int requesterActor, int amount, Vector3 spawnPos)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			int share = BudgetingStats.Instance.getShare(requesterActor);
			if (share >= amount)
			{
				SpawnBag(requesterActor, amount, spawnPos);
			}
		}

		private void SpawnBag(int ownerActor, int amount, Vector3 spawnPos)
		{
			//IL_001c: 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)
			GameObject surplusValuableSmall = AssetManager.instance.surplusValuableSmall;
			GameObject val = PhotonNetwork.InstantiateRoomObject("Valuables/" + ((Object)surplusValuableSmall).name, spawnPos, Quaternion.identity, (byte)0, (object[])null);
			val.GetComponent<ValuableObject>().dollarValueOverride = amount;
			val.GetComponent<ValuableObject>().durabilityPreset = InvincibleDurability;
			int viewID = val.GetComponent<PhotonView>().ViewID;
			BudgetBags[viewID] = amount;
			BudgetingStats.Instance.deductAmountFromPlayerShare(ownerActor, amount);
			SendShares(BudgetingStats.Instance.getShares());
		}

		private void ProcessBagPickup(int bagViewId, int pickerActor)
		{
			if (BudgetBags.TryGetValue(bagViewId, out var value))
			{
				BudgetBags.Remove(bagViewId);
				BudgetingStats.Instance.addAmountToPlayerShare(pickerActor, value);
				SendShares(BudgetingStats.Instance.getShares());
				PhotonView val = PhotonView.Find(bagViewId);
				if ((Object)(object)val != (Object)null)
				{
					PhotonNetwork.Destroy(((Component)val).gameObject);
				}
			}
		}

		internal static void NotifyBagPickedUp(int bagViewId)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			if (SemiFunc.RunIsShop())
			{
				BudgetingNetwork? instance = Instance;
				if (instance != null)
				{
					instance._pickupBagEvent.RaiseEvent((object)bagViewId, NetworkingEvents.RaiseMasterClient, SendOptions.SendReliable);
				}
			}
		}

		public void SendShares(Dictionary<int, int> shares)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Expected O, but got Unknown
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Expected O, but got Unknown
			//IL_0087: Expected O, but got Unknown
			if (!PhotonNetwork.IsMasterClient)
			{
				return;
			}
			Hashtable val = new Hashtable();
			foreach (var (num3, num4) in shares)
			{
				((Dictionary<object, object>)(object)val).Add((object)num3, (object)num4);
			}
			Room currentRoom = PhotonNetwork.CurrentRoom;
			Hashtable val2 = new Hashtable();
			((Dictionary<object, object>)val2).Add((object)"BudgetShares", (object)val);
			currentRoom.SetCustomProperties(val2, (Hashtable)null, (WebFlags)null);
		}

		private static Durability CreateInvincibleDurability()
		{
			Durability val = ScriptableObject.CreateInstance<Durability>();
			val.durability = 100f;
			val.fragility = 0f;
			return val;
		}
	}
}
namespace BudgetingPlugin.Helpers
{
	internal static class StandingsHelper
	{
		internal static int[][] GroupByValue(Dictionary<int, int> valueByActor, bool ascending)
		{
			IEnumerable<IGrouping<int, KeyValuePair<int, int>>> source = from pair in valueByActor
				group pair by pair.Value;
			source = (ascending ? source.OrderBy((IGrouping<int, KeyValuePair<int, int>> g) => g.Key) : source.OrderByDescending((IGrouping<int, KeyValuePair<int, int>> g) => g.Key));
			return source.Select((IGrouping<int, KeyValuePair<int, int>> g) => g.Select((KeyValuePair<int, int> pair) => pair.Key).ToArray()).ToArray();
		}
	}
}
namespace BudgetingPlugin.DistributionStats
{
	public class ByUpgrade : IStandings
	{
		public int[][] GetStandings(List<PlayerAvatar> playerList)
		{
			if ((Object)(object)StatsManager.instance == (Object)null)
			{
				BudgetingPlugin.Logger.LogWarning((object)"[ByUpgrade] StatsManager.instance is null; cannot compute upgrade standings.");
				return Array.Empty<int[]>();
			}
			Dictionary<int, int> dictionary = new Dictionary<int, int>();
			foreach (PlayerAvatar player in playerList)
			{
				if ((Object)(object)player == (Object)null || (Object)(object)player.photonView == (Object)null)
				{
					BudgetingPlugin.Logger.LogWarning((object)"[ByUpgrade] Skipping player with null photonView.");
					continue;
				}
				int num = 0;
				Dictionary<string, int> dictionary2 = StatsManager.instance.FetchPlayerUpgrades(player.steamID);
				if (dictionary2 != null)
				{
					foreach (KeyValuePair<string, int> item in dictionary2)
					{
						num += item.Value;
					}
				}
				dictionary[player.photonView.OwnerActorNr] = num;
			}
			return StandingsHelper.GroupByValue(dictionary, ascending: true);
		}
	}
	internal interface IStandings
	{
		int[][] GetStandings(List<PlayerAvatar> playerList);
	}
}
namespace BudgetingPlugin.Core
{
	public static class BudgetDistribution
	{
		public static int[] Distribute(int total, double[] weights)
		{
			if (weights == null || weights.Length == 0)
			{
				return Array.Empty<int>();
			}
			if (total < 0)
			{
				total = 0;
			}
			int num = weights.Length;
			double num2 = 0.0;
			for (int i = 0; i < num; i++)
			{
				num2 += Math.Max(0.0, weights[i]);
			}
			if (num2 <= 0.0)
			{
				return DistributeEven(total, num);
			}
			int num3 = total / 1000;
			int[] array = new int[num];
			double[] array2 = new double[num];
			int num4 = 0;
			for (int j = 0; j < num; j++)
			{
				double num5 = (double)num3 * (Math.Max(0.0, weights[j]) / num2);
				double num6 = Math.Floor(num5);
				array[j] = (int)num6 * 1000;
				array2[j] = num5 - num6;
				num4 += (int)num6;
			}
			for (int num7 = num3 - num4; num7 > 0; num7--)
			{
				int num8 = IndexOfMaxFraction(array2);
				array[num8] += 1000;
				array2[num8] = -1.0;
			}
			return array;
		}

		private static int IndexOfMaxFraction(double[] fractions)
		{
			int num = 0;
			for (int i = 1; i < fractions.Length; i++)
			{
				if (fractions[i] > fractions[num])
				{
					num = i;
				}
			}
			return num;
		}

		public static int[] DistributeEven(int total, int playerCount)
		{
			double[] array = new double[playerCount];
			for (int i = 0; i < playerCount; i++)
			{
				array[i] = 1.0;
			}
			return Distribute(total, array);
		}

		public static int[] SelectShares(int total, double[] weights, int playerCount, bool standingsValid)
		{
			if (standingsValid)
			{
				return Distribute(total, weights);
			}
			return DistributeEven(total, playerCount);
		}
	}
	public class BudgetingStats
	{
		private List<PlayerAvatar> _players = new List<PlayerAvatar>();

		private Dictionary<int, int> _shares = new Dictionary<int, int>();

		public static BudgetingStats Instance { get; } = new BudgetingStats();

		public int playerCount => _players.Count;

		public void Nuke()
		{
			BudgetingPlugin.Logger.LogError((object)"[Budgeting] NUKING");
			if (_shares.Count != 0)
			{
				_shares.Clear();
			}
			if (_players.Count != 0)
			{
				_players = new List<PlayerAvatar>();
			}
		}

		public Dictionary<int, int> getShares()
		{
			return _shares;
		}

		public int deductAmountFromPlayerShare(int actorNumber, int amountLost)
		{
			BudgetingPlugin.Logger.LogDebug((object)$"==== deductAmountFromPlayerShare; Actor = {actorNumber}; amountLost = {amountLost};");
			if (!_shares.ContainsKey(actorNumber))
			{
				BudgetingPlugin.Logger.LogWarning((object)$"deductAmountFromPlayerShare: actor {actorNumber} not in shares, skipping deduction.");
				return 0;
			}
			int share = getShare(actorNumber);
			int num = Math.Max(0, share - amountLost);
			_shares[actorNumber] = num;
			return num;
		}

		public int addAmountToPlayerShare(int actorNumber, int amount)
		{
			if (!_shares.ContainsKey(actorNumber))
			{
				BudgetingPlugin.Logger.LogWarning((object)$"addAmountToPlayerShare: actor {actorNumber} not in shares, skipping.");
				return 0;
			}
			int num = _shares[actorNumber] + amount;
			_shares[actorNumber] = num;
			return num;
		}

		public int getShare(int actorNumber)
		{
			if (!_shares.TryGetValue(actorNumber, out var value))
			{
				BudgetingPlugin.Logger.LogWarning((object)$"getShare: no share found for actor {actorNumber}, defaulting to 0.");
				return 0;
			}
			return value;
		}

		public void setShares(Dictionary<int, int> shares)
		{
			_shares = shares;
		}

		public List<PlayerAvatar> getPlayers()
		{
			return _players;
		}

		public PlayerAvatar getPlayer(int index)
		{
			return _players[index];
		}

		public void setPlayers(List<PlayerAvatar> players)
		{
			_players = players;
		}

		public int[][] GetPlayerStandings()
		{
			string value = PluginConfig.DesiredDist.Value;
			string text = value;
			if (text == "Upgrade Count")
			{
				IStandings standings = new ByUpgrade();
				return standings.GetStandings(_players);
			}
			return GetDefaultStandingsTiers();
		}

		private int[][] GetDefaultStandingsTiers()
		{
			int[][] array = new int[_players.Count][];
			for (int i = 0; i < _players.Count; i++)
			{
				array[i] = new int[1] { _players[i].photonView.OwnerActorNr };
			}
			return array;
		}
	}
	public static class BudgetWeights
	{
		public static double[] GetWeights(int playerCount, string method)
		{
			double[] array = new double[playerCount];
			if (!(method == "Upgrade Count"))
			{
				if (method == "Manual")
				{
					GetWeights_Manual(array);
				}
				else
				{
					GetWeights_Even(array);
				}
			}
			else
			{
				GetWeights_Weighted(array);
			}
			return array;
		}

		public static double[] AveragePerTieGroup(double[] baseWeights, int[] tierSizes)
		{
			double[] array = new double[baseWeights.Length];
			int num = 0;
			int num2 = 0;
			foreach (int num3 in tierSizes)
			{
				double num4 = 0.0;
				for (int j = 0; j < num3; j++)
				{
					num4 += baseWeights[num + j];
				}
				double num5 = num4 / (double)num3;
				for (int k = 0; k < num3; k++)
				{
					array[num2++] = num5;
				}
				num += num3;
			}
			return array;
		}

		private static void GetWeights_Even(double[] weights)
		{
			Array.Fill(weights, 1.0);
		}

		private static void GetWeights_Manual(double[] weights)
		{
			weights[0] = 1.0;
			for (int i = 1; i < weights.Length; i++)
			{
				weights[i] = 0.0;
			}
		}

		private static void GetWeights_Weighted(double[] weights)
		{
			for (int i = 0; i < weights.Length; i++)
			{
				weights[i] = GetSlotWeight(i);
			}
		}

		private static double GetSlotWeight(int playerSlot)
		{
			return 1.0 / ((double)playerSlot + 0.75);
		}
	}
	public static class PlayerBudgetLines
	{
		public enum LineTypes
		{
			NotEnoughMoney,
			NotEnoughMoneyToGive
		}

		private static readonly Random RandomNumGenerator = new Random();

		private static readonly string[] NotEnoughMoneyLines = new string[12]
		{
			"Nope. I'm outta cash", "No moners!", "Nope, gotta pay for rent this month", "I DON'T HAVE ANY MONEEEEEEEEEEEEEEEEEEEEEY", "NOOOOOOOOOO MONNNNEEEEEEEEY", "Holla holla get $", "Job application incoming", "WHAT DO I DO, MY CREDIT CARD DECLINED", "DID I LOSE MY WALLET", "Mom would be so disappointed",
			"Yeah in retrospect all those crypto investments weren't the right move", "'Put it all on black', they said"
		};

		private static readonly string[] NotEnoughMoneyToGiveLines = new string[2] { "I don't have this much money to give out", "I should probably keep this for myself" };

		public static string GetRandomLine(LineTypes type = LineTypes.NotEnoughMoney)
		{
			string[] array = Array.Empty<string>();
			array = ((type == LineTypes.NotEnoughMoney || type != LineTypes.NotEnoughMoneyToGive) ? NotEnoughMoneyLines : NotEnoughMoneyToGiveLines);
			if (array.Length != 0)
			{
				int num = RandomNumGenerator.Next(0, array.Length);
				return array[num];
			}
			return string.Empty;
		}

		public static void SaySomethingStupid(string message)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			ChatManager.instance.PossessChatScheduleStart(10);
			ChatManager.instance.PossessChat((PossessChatID)0, message, 3f, new Color(0f, 1f, 0f), 0f, false, 0, (UnityEvent)null);
			ChatManager.instance.PossessChatScheduleEnd();
		}
	}
}
namespace BudgetingPlugin.Config
{
	public static class DistributionMethods
	{
		public const string Even = "Even";

		public const string Manual = "Manual";

		public const string UpgradeCount = "Upgrade Count";
	}
	internal static class PluginConfig
	{
		internal static ConfigEntry<KeyCode> OpenMenuKey { get; private set; }

		internal static ConfigEntry<float> BagSpawnForwardOffset { get; private set; }

		internal static ConfigEntry<float> BagSpawnHeightOffset { get; private set; }

		internal static ConfigEntry<int> ShopDiscountPercent { get; private set; }

		internal static ConfigEntry<string> DesiredDist { get; private set; }

		internal static ConfigEntry<float> WalletOffsetX { get; private set; }

		internal static ConfigEntry<float> WalletOffsetY { get; private set; }

		internal static void Initialize(ConfigFile config)
		{
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: 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
			OpenMenuKey = config.Bind<KeyCode>("Controls", "ShareMoneyKey", (KeyCode)52, "What key should be pressed to open the 'Share Money' pop up");
			BagSpawnForwardOffset = config.Bind<float>("BagSpawn", "ForwardOffset", 1.5f, "How far in front of the player the money bag spawns (in units).");
			BagSpawnHeightOffset = config.Bind<float>("BagSpawn", "HeightOffset", 1.5f, "How high above the player the money bag spawns (in units).");
			ShopDiscountPercent = config.Bind<int>("Game", "ShopDiscountPercent", 10, new ConfigDescription("Discount applied to all items above $10 in the shop. Caps at 30%", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 30), Array.Empty<object>()));
			DesiredDist = config.Bind<string>("Game", "DistributionMethod", "Even", new ConfigDescription("Even: Money is distributed evenly to all playersManual: Host gets all money, then distributes it with money sharing", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[3] { "Even", "Manual", "Upgrade Count" }), Array.Empty<object>()));
			WalletOffsetX = config.Bind<float>("UI", "WalletOffset_X", 0f, "Where the 'Wallet' text should be displayed during the shop phase (horizontal location). This is relative to the existing money count text.");
			WalletOffsetY = config.Bind<float>("UI", "WalletOffset_Y", -50f, "Where the 'Wallet' text should be displayed during the shop phase (vertical location). This is relative to the existing money count text.");
		}
	}
}