Decompiled source of The Mass Binding v1.0.0

TheMassBinding.dll

Decompiled 5 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
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 UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("TheMassBinding")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("TheMassBinding")]
[assembly: AssemblyTitle("TheMassBinding")]
[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 TheMassMod
{
	[BepInPlugin("com.vilcan.themassbinding", "The Mass Binding", "1.0")]
	public class TheMassPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "com.vilcan.themassbinding";

		public const string PluginName = "The Mass Binding";

		public const string PluginVersion = "1.0";

		public static ConfigEntry<float> HungerDecayMultiplier;

		public static ConfigEntry<float> NerfedFoodFraction;

		public static ConfigEntry<float> DenizenEatRestoreMultiplier;

		public static ConfigEntry<float> DenizenMeatEatMultiplier;

		public static ConfigEntry<int> StartingThreshold;

		public static ConfigEntry<int> ThresholdStep;

		public static ConfigEntry<int> StartingMeatCount;

		public static ConfigEntry<bool> SyncForeignHungerModules;

		public static ConfigEntry<KeyCode> EatKey;

		public static ConfigEntry<KeyCode> FoodMakerKey;

		public static ConfigEntry<float> EatRange;

		public static ConfigEntry<bool> VerboseLogging;

		internal static ManualLogSource Logger;

		private Harmony _harmony;

		private void Awake()
		{
			//IL_01a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b1: Expected O, but got Unknown
			Logger = ((BaseUnityPlugin)this).Logger;
			HungerDecayMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Hunger", "HungerDecayMultiplier", 1f, "Multiplier on the hunger decay rate. 1.0 = same pace as vanilla Survival Mode.");
			NerfedFoodFraction = ((BaseUnityPlugin)this).Config.Bind<float>("Hunger", "NerfedFoodFraction", 0.1f, "Fraction of normal food's hunger restore that still applies while The Mass is active.");
			DenizenEatRestoreMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Hunger", "DenizenEatRestoreMultiplier", 1f, "Hunger restored by eating a denizen directly, as a multiple of a normal meal.");
			DenizenMeatEatMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Hunger", "DenizenMeatEatMultiplier", 1f, "Hunger restored by eating Denizen Meat, as a multiple of a normal meal.");
			StartingThreshold = ((BaseUnityPlugin)this).Config.Bind<int>("Perks", "StartingThreshold", 5, "Denizens eaten needed for the first random perk.");
			ThresholdStep = ((BaseUnityPlugin)this).Config.Bind<int>("Perks", "ThresholdStep", 5, "How much the required count increases after each perk reward.");
			StartingMeatCount = ((BaseUnityPlugin)this).Config.Bind<int>("Hunger", "StartingMeatCount", 3, "Denizen Meat granted on taking the binding, in case you can't find a denizen right away.");
			SyncForeignHungerModules = ((BaseUnityPlugin)this).Config.Bind<bool>("Perks", "SyncForeignHungerModules", true, "When a granted perk brings its own hunger module (e.g. Conditioned Polyphagia), tune that module's stats to match The Mass's own and feed it from the denizen/Denizen Meat mechanic, instead of leaving it as a separate, unrelated hunger system. The perk keeps its own module and UI — only its numbers and restores are kept in sync. Disable to leave foreign hunger modules completely untouched.");
			EatKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Controls", "EatKey", (KeyCode)103, "Hold both grab buttons on a denizen and press this to eat it.");
			FoodMakerKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Controls", "FoodMakerKey", (KeyCode)120, "Hold both grab buttons on a denizen and press this to turn it into Denizen Meat instead.");
			EatRange = ((BaseUnityPlugin)this).Config.Bind<float>("Controls", "EatRange", 3f, "Max distance to a denizen for the eat prompt to register.");
			VerboseLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "VerboseLogging", false, "Per-frame eat-detection logging. Only useful for troubleshooting.");
			_harmony = new Harmony("com.vilcan.themassbinding");
			_harmony.PatchAll();
			Logger.LogInfo((object)"The Mass Binding loaded.");
		}
	}
	[HarmonyPatch(typeof(UI_TrinketPicker), "PopulateTrinkets")]
	public static class UI_TrinketPicker_PopulateTrinkets_Patch
	{
		[HarmonyPrefix]
		public static void Prefix(List<Trinket> trinketsToPopulate, Transform root, bool allowInIronKnuckle)
		{
			if (trinketsToPopulate != null && trinketsToPopulate.Count != 0 && trinketsToPopulate.Any((Trinket t) => (Object)(object)t != (Object)null && t.isBinding))
			{
				TheMassBinding.EnsureRegistered(trinketsToPopulate);
			}
		}
	}
	[HarmonyPatch(typeof(HandItem_Food), "Eat")]
	public static class HandItem_Food_Eat_Patch
	{
		[HarmonyPostfix]
		public static void Postfix(HandItem_Food __instance)
		{
			if (((HandItem)(__instance?)).item != null && !(((HandItem)__instance).item.itemName != "Denizen Meat"))
			{
				PerkModule_TheMassEater.ActiveInstance?.OnDenizenMeatEaten();
			}
		}
	}
	[HarmonyPatch(typeof(HandItem_Buff), "StartBuff")]
	public static class HandItem_Buff_StartBuff_Patch
	{
		[HarmonyPostfix]
		public static void Postfix(HandItem_Buff __instance)
		{
			if (((HandItem)(__instance?)).item != null && !(((HandItem)__instance).item.itemName != "Denizen Meat"))
			{
				PerkModule_TheMassEater.ActiveInstance?.OnDenizenMeatEaten();
			}
		}
	}
	[HarmonyPatch(typeof(ENT_Player), "AddPerk")]
	public static class ENT_Player_AddPerk_Patch
	{
		[HarmonyPostfix]
		public static void Postfix(ENT_Player __instance, Perk perk, int stackAmount, bool firstTime, Perk __result)
		{
			if ((Object)(object)__instance == (Object)null || __result?.modules == null || __result.id == "Binding_TheMass" || !__instance.HasPerk("Binding_TheMass") || !TheMassPlugin.SyncForeignHungerModules.Value)
			{
				return;
			}
			PerkModule_TheMassEater activeInstance = PerkModule_TheMassEater.ActiveInstance;
			if (activeInstance == null)
			{
				return;
			}
			foreach (PerkModule_HungerMeter item in __result.modules.OfType<PerkModule_HungerMeter>())
			{
				activeInstance.SyncForeignHungerModule(item);
			}
		}
	}
	public static class TheMassBinding
	{
		public const string BindingId = "Binding_TheMass";

		private const string TrinketTitle = "The Mass";

		public const string DenizenMeatItemName = "Denizen Meat";

		private const string FunctionalItemPrefabName = "item_food_meat";

		private const string CosmeticItemPrefabName = "item_food_meat";

		private static readonly Color MeatTintOverride = new Color(0.35f, 1f, 0.35f, -1f);

		private static readonly Dictionary<string, GameObject> _prefabCache = new Dictionary<string, GameObject>();

		private const string GrubSacrificeStat = "grubs-used";

		private const string GrubSacrificeFlag = "session_sacrificed_grub";

		public static void EnsureRegistered(List<Trinket> bindings)
		{
			if (bindings == null || bindings.Any((Trinket t) => (Object)(object)t != (Object)null && t.title == "The Mass"))
			{
				return;
			}
			Trinket hungerTemplate = ((IEnumerable<Trinket>)bindings).FirstOrDefault((Func<Trinket, bool>)((Trinket t) => t?.perksToGrant != null && t.perksToGrant.OfType<Perk>().Any((Perk p) => p.modules != null && p.modules.OfType<PerkModule_HungerMeter>().Any())));
			if ((Object)(object)hungerTemplate == (Object)null)
			{
				TheMassPlugin.Logger.LogError((object)"[TheMassBinding] No hunger-based binding found to use as a template.");
				return;
			}
			Trinket val = ((IEnumerable<Trinket>)bindings).FirstOrDefault((Func<Trinket, bool>)((Trinket t) => (Object)(object)t != (Object)null && !string.IsNullOrEmpty(t.title) && t.title.IndexOf("Hunted", StringComparison.OrdinalIgnoreCase) >= 0)) ?? ((IEnumerable<Trinket>)bindings).FirstOrDefault((Func<Trinket, bool>)((Trinket t) => (Object)(object)t != (Object)null && (Object)(object)t != (Object)(object)hungerTemplate)) ?? hungerTemplate;
			PerkModule_HungerMeter templateHunger = hungerTemplate.perksToGrant.First((Perk p) => p.modules.OfType<PerkModule_HungerMeter>().Any()).modules.OfType<PerkModule_HungerMeter>().First();
			int activateOrder = ((bindings.Count != 0) ? bindings.Max((Trinket t) => t?.activateOrder ?? 0) : 0) + 1;
			Perk perk = BuildPerk(templateHunger, val);
			Trinket val2 = BuildTrinket(val, perk, activateOrder);
			bindings.Add(val2);
			RegisterInAssetDatabases(perk, val2);
		}

		private static void RegisterInAssetDatabases(Perk perk, Trinket trinket)
		{
			if (CL_AssetManager.instance == null || !(AccessTools.Field(typeof(CL_AssetManager), "databases").GetValue(CL_AssetManager.instance) is List<WKDatabaseHolder> list))
			{
				return;
			}
			foreach (WKDatabaseHolder item in list)
			{
				if (!((Object)(object)item?.database == (Object)null))
				{
					if (item.database.trinketAssets != null && !item.database.trinketAssets.Any((Trinket t) => (Object)(object)t != (Object)null && ((Object)t).name == ((Object)trinket).name))
					{
						item.database.trinketAssets.Add(trinket);
					}
					if (item.database.perkAssets != null && !item.database.perkAssets.Any((Perk p) => (Object)(object)p != (Object)null && ((Object)p).name == ((Object)perk).name))
					{
						item.database.perkAssets.Add(perk);
					}
				}
			}
		}

		private static Color ApplyMeatTint(Color original)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			float num = ((MeatTintOverride.a >= 0f) ? MeatTintOverride.a : original.a);
			return new Color(MeatTintOverride.r, MeatTintOverride.g, MeatTintOverride.b, num);
		}

		private static GameObject FindItemPrefabTemplate(string prefabName)
		{
			if (_prefabCache.TryGetValue(prefabName, out var value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			GameObject val = CL_AssetManager.GetFullCombinedAssetDatabase()?.itemPrefabs?.FirstOrDefault((Func<GameObject, bool>)((GameObject p) => (Object)(object)p != (Object)null && ((Object)p).name.Equals(prefabName, StringComparison.OrdinalIgnoreCase)));
			if ((Object)(object)val == (Object)null)
			{
				TheMassPlugin.Logger.LogWarning((object)("[TheMassBinding] '" + prefabName + "' not found in item prefab database."));
				return null;
			}
			_prefabCache[prefabName] = val;
			return val;
		}

		public static void SpawnDenizenMeat(Vector3 position, ENT_Player player)
		{
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			GameObject obj = FindItemPrefabTemplate("item_food_meat");
			Item_Object val = ((obj != null) ? obj.GetComponent<Item_Object>() : null);
			if (val?.itemData == null)
			{
				TheMassPlugin.Logger.LogError((object)"[TheMassBinding] Can't create Denizen Meat — template item not found.");
				return;
			}
			Item clone = val.itemData.GetClone((Item)null, false);
			if (clone == null)
			{
				return;
			}
			clone.itemName = "Denizen Meat";
			ApplyMeatCosmetics(clone);
			if (!((Object)(object)player == (Object)null))
			{
				object? value = AccessTools.Field(typeof(ENT_Player), "inventory").GetValue(player);
				Inventory val2 = (Inventory)((value is Inventory) ? value : null);
				if ((Object)(object)val2 == (Object)null)
				{
					TheMassPlugin.Logger.LogError((object)"[TheMassBinding] Couldn't reach the player's inventory.");
				}
				else
				{
					val2.AddItemToInventoryScreen(position, clone, true, false, false);
				}
			}
		}

		public static void ApplyGrubSacrificeEquivalent()
		{
			try
			{
				Type type = AccessTools.TypeByName("StatManager");
				object obj = AccessTools.Field(type, "sessionStats")?.GetValue(null);
				Type type2 = AccessTools.Inner(type, "GameStats");
				if (obj != null && type2 != null)
				{
					AccessTools.Method(type2, "UpdateStatistic", new Type[6]
					{
						typeof(string),
						typeof(object),
						typeof(bool),
						typeof(bool),
						typeof(bool),
						typeof(bool)
					}, (Type[])null)?.Invoke(obj, new object[6] { "grubs-used", 1, false, true, false, true });
				}
				AccessTools.Method(AccessTools.TypeByName("CL_GameManager"), "SetGameFlag", new Type[5]
				{
					typeof(string),
					typeof(bool),
					typeof(string),
					typeof(bool),
					typeof(bool)
				}, (Type[])null)?.Invoke(null, new object[5] { "session_sacrificed_grub", true, "", false, false });
			}
			catch (Exception arg)
			{
				TheMassPlugin.Logger.LogError((object)$"[TheMassBinding] ApplyGrubSacrificeEquivalent failed: {arg}");
			}
		}

		private static void ApplyMeatCosmetics(Item clone)
		{
			//IL_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_016f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0236: Unknown result type (might be due to invalid IL or missing references)
			//IL_023b: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = FindItemPrefabTemplate("item_food_meat");
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			Item val2 = val.GetComponent<Item_Object>()?.itemData;
			if (val2 != null)
			{
				if ((Object)(object)val2.normalSprite != (Object)null)
				{
					clone.normalSprite = val2.normalSprite;
				}
				List<AudioClip> pickupSounds = val2.pickupSounds;
				if (pickupSounds != null && pickupSounds.Count > 0)
				{
					clone.pickupSounds = new List<AudioClip>(val2.pickupSounds);
				}
			}
			Transform obj = FindChildByName(val.transform, "Item_Food_Fruit");
			Renderer val3 = ((obj != null) ? ((Component)obj).GetComponent<Renderer>() : null);
			MeshFilter val4 = ((obj != null) ? ((Component)obj).GetComponent<MeshFilter>() : null);
			SpriteRenderer val5 = null;
			HandItem_Buff val6 = null;
			if ((Object)(object)val2?.handItemAsset != (Object)null)
			{
				HandItem handItemAsset = val2.handItemAsset;
				val6 = (HandItem_Buff)(object)((handItemAsset is HandItem_Buff) ? handItemAsset : null);
				Transform obj2 = FindChildByName(((Component)val2.handItemAsset).transform, "ItemHands Item");
				val5 = ((obj2 != null) ? ((Component)obj2).GetComponent<SpriteRenderer>() : null);
			}
			if ((Object)(object)clone.itemAsset != (Object)null && ((Object)(object)val3 != (Object)null || (Object)(object)val4 != (Object)null))
			{
				GameObject obj3 = Object.Instantiate<GameObject>(((Component)clone.itemAsset).gameObject);
				((Object)obj3).name = "DenizenMeat_ItemAsset(Clone)";
				obj3.SetActive(false);
				Object.DontDestroyOnLoad((Object)(object)obj3);
				Renderer componentInChildren = obj3.GetComponentInChildren<Renderer>(true);
				MeshFilter componentInChildren2 = obj3.GetComponentInChildren<MeshFilter>(true);
				if ((Object)(object)componentInChildren != (Object)null && (Object)(object)val3 != (Object)null)
				{
					componentInChildren.sharedMaterials = val3.sharedMaterials;
					Material material = componentInChildren.material;
					material.color = ApplyMeatTint(material.color);
				}
				if ((Object)(object)componentInChildren2 != (Object)null && (Object)(object)val4 != (Object)null)
				{
					componentInChildren2.sharedMesh = val4.sharedMesh;
				}
				Item_Object componentInChildren3 = obj3.GetComponentInChildren<Item_Object>(true);
				if ((Object)(object)componentInChildren3 != (Object)null)
				{
					clone.itemAsset = componentInChildren3;
				}
			}
			if (!((Object)(object)clone.handItemAsset != (Object)null))
			{
				return;
			}
			GameObject val7 = Object.Instantiate<GameObject>(((Component)clone.handItemAsset).gameObject);
			((Object)val7).name = "DenizenMeat_HandItemAsset(Clone)";
			val7.SetActive(false);
			Object.DontDestroyOnLoad((Object)(object)val7);
			if ((Object)(object)val5 != (Object)null)
			{
				Transform obj4 = FindChildByName(val7.transform, "ItemHands Item");
				SpriteRenderer val8 = ((obj4 != null) ? ((Component)obj4).GetComponent<SpriteRenderer>() : null);
				if ((Object)(object)val8 != (Object)null)
				{
					val8.sprite = val5.sprite;
					val8.color = ApplyMeatTint(val5.color);
				}
			}
			HandItem_Buff component = val7.GetComponent<HandItem_Buff>();
			if ((Object)(object)component != (Object)null)
			{
				if ((Object)(object)val6 != (Object)null)
				{
					component.audioClip = val6.audioClip;
					component.audioVolume = val6.audioVolume;
				}
				component.buff = null;
				component.useSecondaryBuffs = false;
				component.secondaryBuffs = null;
			}
			HandItem componentInChildren4 = val7.GetComponentInChildren<HandItem>(true);
			if ((Object)(object)componentInChildren4 != (Object)null)
			{
				clone.handItemAsset = componentInChildren4;
			}
		}

		private static Transform FindChildByName(Transform root, string name)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Expected O, but got Unknown
			if ((Object)(object)root == (Object)null)
			{
				return null;
			}
			if (((Object)root).name == name)
			{
				return root;
			}
			foreach (Transform item in root)
			{
				Transform val = FindChildByName(item, name);
				if ((Object)(object)val != (Object)null)
				{
					return val;
				}
			}
			return null;
		}

		private static Perk BuildPerk(PerkModule_HungerMeter templateHunger, Trinket cosmeticsTemplate)
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: 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_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_015a: Unknown result type (might be due to invalid IL or missing references)
			//IL_015f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0164: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_016b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0170: Unknown result type (might be due to invalid IL or missing references)
			//IL_017d: Expected O, but got Unknown
			Perk val = ScriptableObject.CreateInstance<Perk>();
			((Object)val).name = "The Mass";
			val.id = "Binding_TheMass";
			val.title = "The Mass";
			val.flavorText = "It bonds flesh. So do you, now.";
			val.description = $"Food barely sustains you. Grab & eat denizens ({TheMassPlugin.EatKey.Value}) instead.";
			val.perkType = (PerkType)9;
			val.spawnPool = (PerkPool)2;
			val.canStack = false;
			Perk val2 = cosmeticsTemplate?.perksToGrant?.FirstOrDefault();
			if ((Object)(object)val2 != (Object)null)
			{
				val.icon = val2.icon;
				val.iconMat = val2.iconMat;
				val.perkCard = val2.perkCard;
				val.perkFrame = val2.perkFrame;
			}
			float eatRecovery = templateHunger.eatRecovery;
			PerkModule_HungerMeter item = new PerkModule_HungerMeter
			{
				hungerMax = templateHunger.hungerMax,
				hungerMeter = templateHunger.hungerMax,
				hungerDecayRate = templateHunger.hungerDecayRate * TheMassPlugin.HungerDecayMultiplier.Value,
				eatRecovery = eatRecovery * TheMassPlugin.NerfedFoodFraction.Value,
				consumeBuffIDs = templateHunger.consumeBuffIDs,
				hungerMeterAsset = templateHunger.hungerMeterAsset,
				buff = CloneBuffContainer(templateHunger.buff),
				debuff = CloneBuffContainer(templateHunger.debuff),
				buffCurve = templateHunger.buffCurve,
				debuffCurve = templateHunger.debuffCurve,
				fullColor = templateHunger.fullColor,
				emptyColor = templateHunger.emptyColor,
				hungerTickAudio = templateHunger.hungerTickAudio
			};
			val.modules = new List<PerkModule>
			{
				(PerkModule)(object)item,
				(PerkModule)(object)new PerkModule_TheMassEater
				{
					NormalEatRecoveryAmount = eatRecovery
				}
			};
			return val;
		}

		private static BuffContainer CloneBuffContainer(BuffContainer source)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: 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_0085: Expected O, but got Unknown
			if (source == null)
			{
				return null;
			}
			return new BuffContainer
			{
				id = source.id + "_TheMassBinding",
				desc = source.desc,
				buffs = ((source.buffs != null) ? new List<Buff>(source.buffs) : null),
				loseRate = source.loseRate,
				loseRateEffectedByPerks = source.loseRateEffectedByPerks,
				buffTime = source.buffTime,
				loseOverTime = source.loseOverTime,
				multiplier = source.multiplier
			};
		}

		private static Trinket BuildTrinket(Trinket template, Perk perk, int activateOrder)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			Trinket obj = ScriptableObject.CreateInstance<Trinket>();
			((Object)obj).name = "The Mass";
			obj.title = "The Mass";
			obj.description = $"Food barely sustains you. Grab & eat denizens ({TheMassPlugin.EatKey.Value}) instead.";
			obj.flavorText = "It bonds flesh. So do you, now.";
			obj.isBinding = true;
			obj.icon = template.icon;
			obj.lockIcon = template.lockIcon;
			obj.cost = template.cost;
			obj.itemsToGrant = new List<Item_Object>();
			obj.perksToGrant = new List<Perk> { perk };
			obj.pouchesToGrant = 0;
			obj.scoreMultiplierBonus = template.scoreMultiplierBonus;
			obj.scoreBonus = template.scoreBonus;
			obj.comingSoon = false;
			obj.settingBlacklist = ((template.settingBlacklist != null) ? new List<string>(template.settingBlacklist) : new List<string>());
			obj.activateOrder = activateOrder;
			return obj;
		}
	}
	[Serializable]
	public class PerkModule_TheMassEater : PerkModule
	{
		public float NormalEatRecoveryAmount = 25f;

		internal static PerkModule_TheMassEater ActiveInstance;

		private Perk _perkRef;

		private ENT_Player _player;

		private PerkModule_HungerMeter _hunger;

		private readonly List<PerkModule_HungerMeter> _syncedForeignHungerModules = new List<PerkModule_HungerMeter>();

		private int _denizensEaten;

		private int _nextThreshold;

		private int _currentIncrement;

		private float _logTimer;

		private static readonly string[] NegativeTitleKeywords = new string[6] { "shattered", "fractured", "broken", "injury", "injured", "wound" };

		public override void Initialize(Perk perk, bool isNew)
		{
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			((PerkModule)this).Initialize(perk, isNew);
			_perkRef = perk;
			_hunger = perk.modules?.OfType<PerkModule_HungerMeter>().FirstOrDefault();
			_player = Object.FindObjectOfType<ENT_Player>();
			ActiveInstance = this;
			if (isNew)
			{
				_currentIncrement = TheMassPlugin.StartingThreshold.Value;
				_nextThreshold = _currentIncrement;
				if ((Object)(object)_player != (Object)null)
				{
					for (int i = 0; i < TheMassPlugin.StartingMeatCount.Value; i++)
					{
						TheMassBinding.SpawnDenizenMeat(((Component)_player).transform.position, _player);
					}
				}
			}
			if (!TheMassPlugin.SyncForeignHungerModules.Value || _player?.perks == null)
			{
				return;
			}
			foreach (Perk perk2 in _player.perks)
			{
				if ((Object)(object)perk2 == (Object)null || (Object)(object)perk2 == (Object)(object)perk || perk2.modules == null)
				{
					continue;
				}
				foreach (PerkModule_HungerMeter item in perk2.modules.OfType<PerkModule_HungerMeter>())
				{
					SyncForeignHungerModule(item);
				}
			}
		}

		public override string GetCounterString()
		{
			return Mathf.Max(0, _nextThreshold - _denizensEaten).ToString();
		}

		public void SyncForeignHungerModule(PerkModule_HungerMeter foreign)
		{
			if (_hunger != null && foreign != null && foreign != _hunger)
			{
				foreign.hungerMax = _hunger.hungerMax;
				foreign.hungerMeter = _hunger.hungerMeter;
				foreign.hungerDecayRate = _hunger.hungerDecayRate;
				foreign.eatRecovery = _hunger.eatRecovery;
				if (!_syncedForeignHungerModules.Contains(foreign))
				{
					_syncedForeignHungerModules.Add(foreign);
				}
				if (TheMassPlugin.VerboseLogging.Value)
				{
					TheMassPlugin.Logger.LogInfo((object)("[TheMassBinding] Synced a foreign hunger module to match The Mass " + $"(hungerMax={foreign.hungerMax:F1}, decayRate={foreign.hungerDecayRate:F3}, " + $"eatRecovery={foreign.eatRecovery:F1})."));
				}
			}
		}

		public override void Update()
		{
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0162: Unknown result type (might be due to invalid IL or missing references)
			((PerkModule)this).Update();
			if ((Object)(object)_player == (Object)null)
			{
				_player = Object.FindObjectOfType<ENT_Player>();
				if ((Object)(object)_player == (Object)null)
				{
					return;
				}
			}
			bool mouseButton = Input.GetMouseButton(0);
			bool mouseButton2 = Input.GetMouseButton(1);
			bool flag = mouseButton && mouseButton2;
			Denizen val = null;
			string hitDebug = "not grabbing";
			if (flag)
			{
				val = FindDenizenUnderCrosshair(out hitDebug);
			}
			if (TheMassPlugin.VerboseLogging.Value)
			{
				_logTimer += Time.deltaTime;
				if (_logTimer >= 1f)
				{
					_logTimer = 0f;
					TheMassPlugin.Logger.LogInfo((object)($"[TheMassBinding] LMB={mouseButton} RMB={mouseButton2} " + "target=" + (((Object)(object)val != (Object)null) ? ((Object)val).name : "none") + " hits=[" + hitDebug + "] hunger=" + ((_hunger != null) ? _hunger.hungerMeter.ToString("F1") : "n/a")));
				}
			}
			if (!flag || (Object)(object)val == (Object)null)
			{
				return;
			}
			if (Input.GetKeyDown(TheMassPlugin.EatKey.Value))
			{
				try
				{
					EatDenizen(val);
					return;
				}
				catch (Exception arg)
				{
					TheMassPlugin.Logger.LogError((object)$"[TheMassBinding] EatDenizen failed: {arg}");
					return;
				}
			}
			if (!Input.GetKeyDown(TheMassPlugin.FoodMakerKey.Value))
			{
				return;
			}
			try
			{
				MakeFoodFromDenizen(val);
			}
			catch (Exception arg2)
			{
				TheMassPlugin.Logger.LogError((object)$"[TheMassBinding] MakeFoodFromDenizen failed: {arg2}");
			}
		}

		private Denizen FindDenizenUnderCrosshair(out string hitDebug)
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			hitDebug = "";
			if ((Object)(object)_player.cam == (Object)null)
			{
				hitDebug = "no camera";
				return null;
			}
			RaycastHit[] array = Physics.RaycastAll(new Ray(((Component)_player.cam).transform.position, ((Component)_player.cam).transform.forward), TheMassPlugin.EatRange.Value);
			Array.Sort(array, (RaycastHit a, RaycastHit b) => ((RaycastHit)(ref a)).distance.CompareTo(((RaycastHit)(ref b)).distance));
			List<string> list = new List<string>();
			Denizen val = null;
			RaycastHit[] array2 = array;
			for (int num = 0; num < array2.Length; num++)
			{
				RaycastHit val2 = array2[num];
				object obj = ((Component)((RaycastHit)(ref val2)).collider).GetComponentInParent<Denizen>();
				if (obj == null)
				{
					Rigidbody rigidbody = ((RaycastHit)(ref val2)).rigidbody;
					obj = ((rigidbody != null) ? ((Component)rigidbody).GetComponent<Denizen>() : null);
					if (obj == null)
					{
						Rigidbody rigidbody2 = ((RaycastHit)(ref val2)).rigidbody;
						obj = ((rigidbody2 != null) ? ((Component)rigidbody2).GetComponentInParent<Denizen>() : null);
					}
				}
				Denizen val3 = (Denizen)obj;
				bool flag = (Object)(object)val3 != (Object)null && IsBlacklisted(val3);
				list.Add(((Object)(object)val3 != (Object)null) ? (((Object)((Component)((RaycastHit)(ref val2)).collider).gameObject).name + "[Denizen" + (flag ? ",blacklisted" : "") + "]") : ((Object)((Component)((RaycastHit)(ref val2)).collider).gameObject).name);
				if ((Object)(object)val3 != (Object)null && !flag && (Object)(object)val == (Object)null)
				{
					val = val3;
				}
			}
			hitDebug = ((list.Count > 0) ? string.Join(", ", list) : "no hits");
			return val;
		}

		private static bool IsBlacklisted(Denizen denizen)
		{
			return (Object)(object)((Component)denizen).GetComponent<DEN_Roach>() != (Object)null;
		}

		private static bool IsGrub(Denizen denizen)
		{
			if ((Object)(object)denizen != (Object)null)
			{
				return (Object)(object)((Component)denizen).GetComponent<DEN_SlugGrub>() != (Object)null;
			}
			return false;
		}

		private void EatDenizen(Denizen denizen)
		{
			bool flag = IsGrub(denizen);
			if (KillAndRemoveDenizen(denizen))
			{
				RestoreHunger(NormalEatRecoveryAmount * TheMassPlugin.DenizenEatRestoreMultiplier.Value);
				RegisterCredit();
				if (flag)
				{
					TheMassBinding.ApplyGrubSacrificeEquivalent();
				}
			}
		}

		private void MakeFoodFromDenizen(Denizen denizen)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: 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)
			Vector3 position = ((Component)denizen).transform.position;
			bool flag = IsGrub(denizen);
			if (KillAndRemoveDenizen(denizen))
			{
				if (flag)
				{
					TheMassBinding.ApplyGrubSacrificeEquivalent();
				}
				TheMassBinding.SpawnDenizenMeat(position, _player);
			}
		}

		public void OnDenizenMeatEaten()
		{
			RestoreHunger(NormalEatRecoveryAmount * TheMassPlugin.DenizenMeatEatMultiplier.Value);
		}

		private bool KillAndRemoveDenizen(Denizen denizen)
		{
			if ((Object)(object)denizen == (Object)null || ((GameEntity)denizen).dead)
			{
				return false;
			}
			DamageInfo val = DamageInfo.CreateDamageInfo(9999f, (GameEntity)(object)_player, "Eaten");
			((GameEntity)denizen).Kill("Eaten", val);
			Object.Destroy((Object)(object)((Component)denizen).gameObject, 0.15f);
			return true;
		}

		private void RegisterCredit()
		{
			_denizensEaten++;
			if (_nextThreshold <= 0)
			{
				_currentIncrement = TheMassPlugin.StartingThreshold.Value;
				_nextThreshold = _currentIncrement;
			}
			if (_denizensEaten >= _nextThreshold)
			{
				GrantRandomPerk();
				_currentIncrement += TheMassPlugin.ThresholdStep.Value;
				_nextThreshold += _currentIncrement;
			}
		}

		private void RestoreHunger(float amount)
		{
			if (_hunger == null)
			{
				_hunger = _perkRef?.modules?.OfType<PerkModule_HungerMeter>().FirstOrDefault();
			}
			if (_hunger == null)
			{
				return;
			}
			_hunger.hungerMeter = Mathf.Clamp(_hunger.hungerMeter + amount, 0f, _hunger.hungerMax);
			for (int num = _syncedForeignHungerModules.Count - 1; num >= 0; num--)
			{
				PerkModule_HungerMeter val = _syncedForeignHungerModules[num];
				if (val == null)
				{
					_syncedForeignHungerModules.RemoveAt(num);
				}
				else
				{
					val.hungerMeter = Mathf.Clamp(val.hungerMeter + amount, 0f, val.hungerMax);
				}
			}
		}

		private void GrantRandomPerk()
		{
			List<Perk> list = (from p in Resources.FindObjectsOfTypeAll<Perk>()
				where (Object)(object)p != (Object)null && p.id != "Binding_TheMass" && (int)p.perkType != 9 && (int)p.spawnPool != 2 && !IsNegativePerk(p) && !HasHungerModule(p)
				select p).ToList();
			if (list.Count != 0)
			{
				_player.AddPerk(list[Random.Range(0, list.Count)], 1, true);
			}
		}

		private static bool HasHungerModule(Perk p)
		{
			if (p.modules != null)
			{
				return p.modules.OfType<PerkModule_HungerMeter>().Any();
			}
			return false;
		}

		private static bool IsNegativePerk(Perk p)
		{
			if (!string.IsNullOrEmpty(p.id) && p.id.StartsWith("Injury", StringComparison.OrdinalIgnoreCase))
			{
				return true;
			}
			if (!string.IsNullOrEmpty(p.title))
			{
				string title = p.title.ToLowerInvariant();
				if (NegativeTitleKeywords.Any((string k) => title.Contains(k)))
				{
					return true;
				}
			}
			return false;
		}
	}
}