Decompiled source of PerkShop v0.2.1

Thunderstone realese/PerkShop.dll

Decompiled 23 minutes 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.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text.Json;
using BepInEx;
using BepInEx.Core.Logging.Interpolation;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using HarmonyLib;
using Il2CppInterop.Runtime;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSystem;
using Il2CppSystem.Collections.Generic;
using Microsoft.CodeAnalysis;
using PerkShop.Models;
using PerkShop.Services;
using PerkShop.Utilities;
using ProjectM;
using ProjectM.Network;
using ProjectM.Scripting;
using ProjectM.Shared;
using Stunlock.Core;
using Stunlock.Network;
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
using UnityEngine;
using VampireCommandFramework;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("yenko2000")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Chat-command V Rising shop that lets players buy configurable buffs.")]
[assembly: AssemblyFileVersion("0.1.2.0")]
[assembly: AssemblyInformationalVersion("0.1.2")]
[assembly: AssemblyProduct("PerkShop")]
[assembly: AssemblyTitle("PerkShop")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/yenko2000/PerkShop")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.1.2.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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]
	[Microsoft.CodeAnalysis.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]
	[Microsoft.CodeAnalysis.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 PerkShop
{
	internal static class Core
	{
		private static bool _initialized;

		private static World? _server;

		private static EntityManager _entityManager;

		private static ServerScriptMapper? _serverScriptMapper;

		private static DebugEventsSystem? _debugEventsSystem;

		private static SystemService? _systemService;

		public static World Server
		{
			get
			{
				object obj = _server;
				if (obj == null)
				{
					obj = GetWorld("Server") ?? throw new Exception("Server world not found.");
					_server = (World?)obj;
				}
				return (World)obj;
			}
		}

		public static EntityManager EntityManager
		{
			get
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_000d: 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_0025: 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_0015: Unknown result type (might be due to invalid IL or missing references)
				if (!(_entityManager == default(EntityManager)))
				{
					return _entityManager;
				}
				return _entityManager = Server.EntityManager;
			}
		}

		public static ServerScriptMapper ServerScriptMapper => _serverScriptMapper ?? (_serverScriptMapper = Server.GetExistingSystemManaged<ServerScriptMapper>());

		public static ServerGameManager ServerGameManager => ServerScriptMapper.GetServerGameManager();

		public static DebugEventsSystem DebugEventsSystem => _debugEventsSystem ?? (_debugEventsSystem = Server.GetExistingSystemManaged<DebugEventsSystem>());

		public static SystemService Systems => _systemService ?? (_systemService = new SystemService(Server));

		public static ManualLogSource Log => Plugin.PluginLog;

		internal static void InitializeAfterLoaded()
		{
			//IL_002b: 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)
			if (!_initialized)
			{
				_server = GetWorld("Server") ?? throw new Exception("Server world not found.");
				_entityManager = _server.EntityManager;
				_serverScriptMapper = _server.GetExistingSystemManaged<ServerScriptMapper>();
				_debugEventsSystem = _server.GetExistingSystemManaged<DebugEventsSystem>();
				_systemService = new SystemService(_server);
				QueryService.Initialize(_systemService);
				CarrierPrefabService.PrepareConfiguredStatCarrierPrefab();
				_initialized = true;
				Log.LogInfo((object)"Core initialized.");
			}
		}

		internal static World? GetWorld(string name)
		{
			Enumerator<World> enumerator = World.s_AllWorlds.GetEnumerator();
			while (enumerator.MoveNext())
			{
				World current = enumerator.Current;
				if (current != null && current.Name == name)
				{
					return current;
				}
			}
			return null;
		}

		public static void LogDebugIfEnabled(string message)
		{
			try
			{
				if (ConfigService.DebugLoggingEnabled)
				{
					Log.LogInfo((object)message);
				}
			}
			catch
			{
			}
		}

		public static void LogException(Exception e, [CallerMemberName] string? caller = null)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Expected O, but got Unknown
			ManualLogSource log = Log;
			bool flag = default(bool);
			BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(4, 3, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(caller);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral("] ");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(e.Message);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral("\n");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(e.StackTrace);
			}
			log.LogError(val);
		}
	}
	internal static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "yenko2000.PerkShop";

		public const string PLUGIN_NAME = "PerkShop";

		public const string PLUGIN_VERSION = "0.1.2";
	}
	[BepInPlugin("yenko2000.PerkShop", "PerkShop", "0.1.2")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class Plugin : BasePlugin
	{
		internal static ManualLogSource PluginLog { get; private set; }

		internal static Harmony Harmony { get; private set; }

		public override void Load()
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Expected O, but got Unknown
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Expected O, but got Unknown
			if (!(Application.productName != "VRisingServer"))
			{
				PluginLog = ((BasePlugin)this).Log;
				ConfigService.Initialize();
				OwnershipService.Initialize();
				PlayerCacheService.Initialize();
				Harmony = new Harmony("yenko2000.PerkShop");
				Harmony.PatchAll(Assembly.GetExecutingAssembly());
				CommandRegistry.RegisterAll();
				ManualLogSource log = ((BasePlugin)this).Log;
				bool flag = default(bool);
				BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(9, 2, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>("PerkShop");
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>("0.1.2");
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" loaded.");
				}
				log.LogInfo(val);
			}
		}

		public override bool Unload()
		{
			OwnershipService.FlushPendingSaves(force: true);
			PlayerCacheService.FlushPendingSaves(force: true);
			CommandRegistry.UnregisterAssembly();
			Harmony harmony = Harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			return true;
		}

		internal static bool HasLoaded()
		{
			World world = Core.GetWorld("Server");
			if (world == null)
			{
				return false;
			}
			PrefabCollectionSystem existingSystemManaged = world.GetExistingSystemManaged<PrefabCollectionSystem>();
			if (existingSystemManaged == null)
			{
				return false;
			}
			return existingSystemManaged.SpawnableNameToPrefabGuidDictionary.Count > 0;
		}
	}
	public static class ECSExtensions
	{
		public unsafe static T Read<T>(this Entity entity) where T : struct
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			ComponentType val = default(ComponentType);
			((ComponentType)(ref val))..ctor(Il2CppType.Of<T>(), (AccessMode)0);
			EntityManager entityManager = Core.EntityManager;
			return Marshal.PtrToStructure<T>(new IntPtr(((EntityManager)(ref entityManager)).GetComponentDataRawRO(entity, val.TypeIndex)));
		}
	}
}
namespace PerkShop.Utilities
{
	internal static class AdminPlayerLookup
	{
		public static bool TryFindUser(string playerRef, out Entity userEntity, out User user, out string error)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: 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_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: 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_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: 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_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: 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_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_016e: Unknown result type (might be due to invalid IL or missing references)
			//IL_020d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0212: Unknown result type (might be due to invalid IL or missing references)
			//IL_0220: Unknown result type (might be due to invalid IL or missing references)
			//IL_0225: Unknown result type (might be due to invalid IL or missing references)
			//IL_022b: Unknown result type (might be due to invalid IL or missing references)
			userEntity = Entity.Null;
			user = default(User);
			error = string.Empty;
			if (string.IsNullOrWhiteSpace(playerRef))
			{
				error = "Usage: specify an online player name or PlatformId.";
				return false;
			}
			EntityManager entityManager = Core.EntityManager;
			EntityQuery val = ((EntityManager)(ref entityManager)).CreateEntityQuery((ComponentType[])(object)new ComponentType[1] { ComponentType.ReadOnly<User>() });
			NativeArray<Entity> val2 = ((EntityQuery)(ref val)).ToEntityArray(AllocatorHandle.op_Implicit((Allocator)2));
			try
			{
				Enumerator<Entity> enumerator;
				if (ulong.TryParse(playerRef.Trim(), out var result))
				{
					enumerator = val2.GetEnumerator();
					while (enumerator.MoveNext())
					{
						Entity current = enumerator.Current;
						User componentData = ((EntityManager)(ref entityManager)).GetComponentData<User>(current);
						if (componentData.PlatformId == result)
						{
							userEntity = current;
							user = componentData;
							PlayerCacheService.Remember(componentData);
							return true;
						}
					}
					error = $"No online player found with PlatformId {result}.";
					return false;
				}
				string text = playerRef.Trim();
				List<(Entity, User)> list = new List<(Entity, User)>();
				List<(Entity, User)> list2 = new List<(Entity, User)>();
				enumerator = val2.GetEnumerator();
				while (enumerator.MoveNext())
				{
					Entity current2 = enumerator.Current;
					User componentData2 = ((EntityManager)(ref entityManager)).GetComponentData<User>(current2);
					string text2 = ((object)(FixedString64Bytes)(ref componentData2.CharacterName)).ToString();
					if (string.Equals(text2, text, StringComparison.OrdinalIgnoreCase))
					{
						list.Add((current2, componentData2));
					}
					else if (text2.IndexOf(text, StringComparison.OrdinalIgnoreCase) >= 0)
					{
						list2.Add((current2, componentData2));
					}
				}
				List<(Entity, User)> list3 = ((list.Count > 0) ? list : list2);
				if (list3.Count == 0)
				{
					error = "No online player found matching '" + text + "'.";
					return false;
				}
				if (list3.Count > 1)
				{
					error = "Multiple players match that query: " + string.Join(", ", list3.Select<(Entity, User), string>(((Entity entity, User user) x) => ((object)(FixedString64Bytes)(ref x.user.CharacterName)).ToString()));
					return false;
				}
				userEntity = list3[0].Item1;
				user = list3[0].Item2;
				PlayerCacheService.Remember(user);
				return true;
			}
			finally
			{
				if (val2.IsCreated)
				{
					val2.Dispose();
				}
			}
		}
	}
	internal static class InventoryHelper
	{
		public static int Count(Entity characterEntity, PrefabGUID itemPrefab)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: 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_0056: Unknown result type (might be due to invalid IL or missing references)
			EntityManager entityManager = Core.EntityManager;
			Entity val = default(Entity);
			if (!InventoryUtilities.TryGetInventoryEntity<EntityManager>(entityManager, characterEntity, ref val, 0))
			{
				return 0;
			}
			if (!((EntityManager)(ref entityManager)).HasComponent<InventoryBuffer>(val))
			{
				return 0;
			}
			DynamicBuffer<InventoryBuffer> buffer = ((EntityManager)(ref entityManager)).GetBuffer<InventoryBuffer>(val, false);
			int num = 0;
			for (int i = 0; i < buffer.Length; i++)
			{
				InventoryBuffer val2 = buffer[i];
				if (((PrefabGUID)(ref val2.ItemType)).GuidHash == ((PrefabGUID)(ref itemPrefab)).GuidHash)
				{
					num += buffer[i].Amount;
				}
			}
			return num;
		}

		public static bool TryRemove(Entity characterEntity, PrefabGUID itemPrefab, int amount)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: 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_0059: 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_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			EntityManager entityManager = Core.EntityManager;
			Entity val = default(Entity);
			if (!InventoryUtilities.TryGetInventoryEntity<EntityManager>(entityManager, characterEntity, ref val, 0))
			{
				return false;
			}
			if (!((EntityManager)(ref entityManager)).HasComponent<InventoryBuffer>(val))
			{
				return false;
			}
			DynamicBuffer<InventoryBuffer> buffer = ((EntityManager)(ref entityManager)).GetBuffer<InventoryBuffer>(val, false);
			int num = amount;
			int num2 = buffer.Length - 1;
			while (num2 >= 0 && num > 0)
			{
				InventoryBuffer val2 = buffer[num2];
				if (((PrefabGUID)(ref val2.ItemType)).GuidHash == ((PrefabGUID)(ref itemPrefab)).GuidHash && val2.Amount > 0)
				{
					int num3 = math.min(val2.Amount, num);
					val2.Amount -= num3;
					num -= num3;
					if (val2.Amount <= 0)
					{
						val2.ItemType = new PrefabGUID(0);
						val2.Amount = 0;
					}
					buffer[num2] = val2;
				}
				num2--;
			}
			return num == 0;
		}
	}
	internal static class PlayerStateHelper
	{
		private static readonly PrefabGUID[] CombatBuffs = (PrefabGUID[])(object)new PrefabGUID[3]
		{
			new PrefabGUID(581443919),
			new PrefabGUID(697095869),
			new PrefabGUID(698151145)
		};

		public static bool Exists(Entity entity)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			if (entity != Entity.Null)
			{
				EntityManager entityManager = Core.EntityManager;
				return ((EntityManager)(ref entityManager)).Exists(entity);
			}
			return false;
		}

		public static bool IsInCombat(Entity characterEntity)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: 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)
			EntityManager entityManager = Core.EntityManager;
			if (!Exists(characterEntity))
			{
				return false;
			}
			PrefabGUID[] combatBuffs = CombatBuffs;
			foreach (PrefabGUID val in combatBuffs)
			{
				if (BuffUtility.HasBuff<EntityManager>(entityManager, characterEntity, PrefabIdentifier.op_Implicit(val)))
				{
					return true;
				}
			}
			return false;
		}
	}
}
namespace PerkShop.Services
{
	internal static class AccessService
	{
		public static bool CanAccessPerkShop(Entity userEntity, Action<string> reply)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			ShopConfigRoot shop = ConfigService.Shop;
			if (!shop.EnableBuffWhitelist)
			{
				return true;
			}
			if (!TryGetPlatformId(userEntity, out var platformId))
			{
				reply("User component not ready.");
				return false;
			}
			if (shop.BuffWhitelistPlatformIds.Contains(platformId))
			{
				return true;
			}
			reply("You are not whitelisted for the perk shop.");
			return false;
		}

		public static bool CanAccessStatShop(Entity userEntity, Action<string> reply)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			ShopConfigRoot shop = ConfigService.Shop;
			if (!shop.EnableStatWhitelist)
			{
				return true;
			}
			if (!TryGetPlatformId(userEntity, out var platformId))
			{
				reply("User component not ready.");
				return false;
			}
			if (shop.StatWhitelistPlatformIds.Contains(platformId))
			{
				return true;
			}
			reply("You are not whitelisted for the stat shop.");
			return false;
		}

		private static bool TryGetPlatformId(Entity userEntity, out ulong platformId)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//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_0029: 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_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: 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)
			platformId = 0uL;
			if (!(userEntity == Entity.Null))
			{
				EntityManager entityManager = Core.EntityManager;
				if (((EntityManager)(ref entityManager)).Exists(userEntity))
				{
					entityManager = Core.EntityManager;
					if (((EntityManager)(ref entityManager)).HasComponent<User>(userEntity))
					{
						entityManager = Core.EntityManager;
						User componentData = ((EntityManager)(ref entityManager)).GetComponentData<User>(userEntity);
						platformId = componentData.PlatformId;
						return platformId != 0;
					}
				}
			}
			return false;
		}
	}
	internal static class BuffService
	{
		private sealed class PendingPermanentBuffInfo
		{
			public DateTime CreatedAtUtc { get; init; } = DateTime.UtcNow;


			public int DurationSeconds { get; init; }

			public bool PersistThroughDeath { get; init; }

			public bool PreserveVanillaCleanup { get; init; }
		}

		private static readonly Dictionary<Entity, PendingPermanentBuffInfo> PendingPermanentBuffs = new Dictionary<Entity, PendingPermanentBuffInfo>();

		private static readonly TimeSpan PendingBuffTimeout = TimeSpan.FromSeconds(30.0);

		private static readonly HashSet<int> PreparedPermanentBuffPrefabs = new HashSet<int>();

		public static int PendingPermanentBuffCount => PendingPermanentBuffs.Count;

		public static bool IsRenewableTimedEntry(ShopConfigRoot config, PerkShopEntry entry)
		{
			return ConfigService.IsRenewableTimedCategory(config, entry.Category);
		}

		public static int ResolveDurationSeconds(ShopConfigRoot config, PerkShopEntry entry)
		{
			return ConfigService.ResolveBuffDuration(config, entry);
		}

		public static bool ResolvePersistThroughDeath(ShopConfigRoot config, PerkShopEntry entry)
		{
			return ConfigService.ResolveBuffPersistThroughDeath(config, entry);
		}

		public static bool PreserveVanillaCleanup(ShopConfigRoot config, PerkShopEntry entry)
		{
			return ConfigService.PreserveVanillaBuffCleanup(config, entry);
		}

		public static void ResetRuntimeStateAfterConfigReload()
		{
			PendingPermanentBuffs.Clear();
			PreparedPermanentBuffPrefabs.Clear();
		}

		public static void ProcessPendingPermanentBuffsDuringSpawn(EntityQuery spawnQuery)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: 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)
			if (PendingPermanentBuffs.Count == 0)
			{
				return;
			}
			NativeArray<Entity> val = ((EntityQuery)(ref spawnQuery)).ToEntityArray(AllocatorHandle.op_Implicit((Allocator)2));
			try
			{
				for (int i = 0; i < val.Length; i++)
				{
					Entity val2 = val[i];
					if (PendingPermanentBuffs.TryGetValue(val2, out PendingPermanentBuffInfo value))
					{
						TrySetLifetime(val2, value.DurationSeconds, value.PersistThroughDeath, value.PreserveVanillaCleanup);
						PendingPermanentBuffs.Remove(val2);
					}
				}
			}
			finally
			{
				if (val.IsCreated)
				{
					val.Dispose();
				}
			}
		}

		private static void MarkPendingLifetimeMutation(Entity buffEntity, int durationSeconds, bool persistThroughDeath, bool preserveVanillaCleanup)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			if (!(buffEntity == Entity.Null))
			{
				PendingPermanentBuffs[buffEntity] = new PendingPermanentBuffInfo
				{
					DurationSeconds = durationSeconds,
					PersistThroughDeath = persistThroughDeath,
					PreserveVanillaCleanup = preserveVanillaCleanup
				};
			}
		}

		public static void ProcessPendingPermanentBuffs()
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: 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_004f: 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_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Expected O, but got Unknown
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			if (PendingPermanentBuffs.Count == 0)
			{
				return;
			}
			EntityManager entityManager = Core.EntityManager;
			KeyValuePair<Entity, PendingPermanentBuffInfo>[] array = PendingPermanentBuffs.ToArray();
			bool flag = default(bool);
			for (int i = 0; i < array.Length; i++)
			{
				KeyValuePair<Entity, PendingPermanentBuffInfo> keyValuePair = array[i];
				Entity key = keyValuePair.Key;
				PendingPermanentBuffInfo value = keyValuePair.Value;
				if (key == Entity.Null || !((EntityManager)(ref entityManager)).Exists(key))
				{
					PendingPermanentBuffs.Remove(key);
				}
				else if (((EntityManager)(ref entityManager)).HasComponent<SpawnTag>(key))
				{
					if (DateTime.UtcNow - value.CreatedAtUtc > PendingBuffTimeout)
					{
						PendingPermanentBuffs.Remove(key);
						ManualLogSource log = Core.Log;
						BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(67, 2, ref flag);
						if (flag)
						{
							((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[BuffService] Timed out waiting to finalize permanent buff entity:");
							((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(key.Index);
							((BepInExLogInterpolatedStringHandler)val).AppendLiteral(":");
							((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(key.Version);
						}
						log.LogWarning(val);
					}
				}
				else
				{
					TrySetLifetime(key, value.DurationSeconds, value.PersistThroughDeath, value.PreserveVanillaCleanup);
					PendingPermanentBuffs.Remove(key);
				}
			}
		}

		private static void PreparePermanentBuffPrefab(PrefabGUID buffPrefab, int durationSeconds, bool persistThroughDeath)
		{
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Expected O, but got Unknown
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: 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_0041: 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_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: 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)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			if (durationSeconds >= 0 || PreparedPermanentBuffPrefabs.Contains(((PrefabGUID)(ref buffPrefab)).GuidHash))
			{
				return;
			}
			try
			{
				Entity val = default(Entity);
				if (!Core.Systems.PrefabCollectionSystem._PrefabGuidToEntityMap.TryGetValue(buffPrefab, ref val))
				{
					return;
				}
				EntityManager entityManager = Core.EntityManager;
				if (!(val == Entity.Null) && ((EntityManager)(ref entityManager)).Exists(val))
				{
					if (((EntityManager)(ref entityManager)).HasComponent<LifeTime>(val))
					{
						((EntityManager)(ref entityManager)).RemoveComponent<LifeTime>(val);
					}
					if (((EntityManager)(ref entityManager)).HasComponent<RemoveBuffOnGameplayEvent>(val))
					{
						((EntityManager)(ref entityManager)).RemoveComponent<RemoveBuffOnGameplayEvent>(val);
					}
					if (((EntityManager)(ref entityManager)).HasComponent<RemoveBuffOnGameplayEventEntry>(val))
					{
						((EntityManager)(ref entityManager)).RemoveComponent<RemoveBuffOnGameplayEventEntry>(val);
					}
					if (persistThroughDeath && !((EntityManager)(ref entityManager)).HasComponent<Buff_Persists_Through_Death>(val))
					{
						((EntityManager)(ref entityManager)).AddComponent<Buff_Persists_Through_Death>(val);
					}
					PreparedPermanentBuffPrefabs.Add(((PrefabGUID)(ref buffPrefab)).GuidHash);
					Core.LogDebugIfEnabled($"[BuffService] Prepared permanent buff prefab {((PrefabGUID)(ref buffPrefab)).GuidHash} with no LifeTime.");
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = Core.Log;
				bool flag = default(bool);
				BepInExWarningLogInterpolatedStringHandler val2 = new BepInExWarningLogInterpolatedStringHandler(56, 2, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("[BuffService] Failed to prepare permanent buff prefab ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<int>(((PrefabGUID)(ref buffPrefab)).GuidHash);
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(": ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(ex.Message);
				}
				log.LogWarning(val2);
			}
		}

		public static bool HasBuff(Entity characterEntity, PrefabGUID buffPrefab)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			Entity val = default(Entity);
			return BuffUtility.TryGetBuff<EntityManager>(Core.EntityManager, characterEntity, PrefabIdentifier.op_Implicit(buffPrefab), ref val);
		}

		public static bool ApplyPurchasedBuff(Entity userEntity, Entity characterEntity, PrefabGUID buffPrefab, bool preventDuplicate, bool allowMutation, bool mutateLifetime, int durationSeconds, bool persistThroughDeath, bool keepVisibleTimerFrozen = false, int visibleTimerSeconds = 0, bool preserveVanillaCleanup = false)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: 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_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: 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_0052: 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_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: 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_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: 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_001f: 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)
			Entity buffEntity = default(Entity);
			if (BuffUtility.TryGetBuff<EntityManager>(Core.EntityManager, characterEntity, PrefabIdentifier.op_Implicit(buffPrefab), ref buffEntity))
			{
				if (allowMutation && mutateLifetime)
				{
					MarkPendingLifetimeMutation(buffEntity, durationSeconds, persistThroughDeath, preserveVanillaCleanup);
				}
				return true;
			}
			if (preventDuplicate && HasBuff(characterEntity, buffPrefab))
			{
				return false;
			}
			FromCharacter val = default(FromCharacter);
			val.User = userEntity;
			val.Character = characterEntity;
			FromCharacter val2 = val;
			ApplyBuffDebugEvent val3 = default(ApplyBuffDebugEvent);
			val3.BuffPrefabGUID = buffPrefab;
			ApplyBuffDebugEvent val4 = val3;
			Core.DebugEventsSystem.ApplyBuff(val2, val4);
			Entity buffEntity2 = default(Entity);
			if (!BuffUtility.TryGetBuff<EntityManager>(Core.EntityManager, characterEntity, PrefabIdentifier.op_Implicit(buffPrefab), ref buffEntity2))
			{
				return false;
			}
			if (allowMutation && mutateLifetime)
			{
				MarkPendingLifetimeMutation(buffEntity2, durationSeconds, persistThroughDeath, preserveVanillaCleanup);
			}
			return true;
		}

		public static bool RemoveBuff(Entity characterEntity, PrefabGUID buffPrefab)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: 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_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_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			Entity val = default(Entity);
			if (!BuffUtility.TryGetBuff<EntityManager>(Core.EntityManager, characterEntity, PrefabIdentifier.op_Implicit(buffPrefab), ref val))
			{
				return false;
			}
			PendingPermanentBuffs.Remove(val);
			DestroyUtility.Destroy(Core.EntityManager, val, (DestroyDebugReason)13, (string)null, 0);
			EntityManager entityManager = Core.EntityManager;
			if (((EntityManager)(ref entityManager)).Exists(val))
			{
				entityManager = Core.EntityManager;
				if (!((EntityManager)(ref entityManager)).HasComponent<DestroyTag>(val))
				{
					entityManager = Core.EntityManager;
					((EntityManager)(ref entityManager)).AddComponent<DestroyTag>(val);
				}
			}
			return true;
		}

		public static void MakeBuffPermanent(Entity buffEntity, bool persistThroughDeath = true)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			TrySetLifetime(buffEntity, -1, persistThroughDeath, preserveVanillaCleanup: false);
		}

		public static int RemoveConfiguredCategoryBuffs(Entity characterEntity, string category)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			string category2 = category;
			if (characterEntity == Entity.Null || string.IsNullOrWhiteSpace(category2))
			{
				return 0;
			}
			HashSet<int> hashSet = (from entry in ConfigService.Shop.Buffs.Values
				where entry != null && entry.BuffPrefab != 0 && string.Equals(entry.Category, category2, StringComparison.OrdinalIgnoreCase)
				select entry.BuffPrefab).ToHashSet();
			if (hashSet.Count == 0)
			{
				return 0;
			}
			int num = 0;
			foreach (int item in hashSet)
			{
				if (RemoveBuff(characterEntity, new PrefabGUID(item)))
				{
					num++;
				}
			}
			return num + RemoveLiveBuffsByPrefabSet(characterEntity, hashSet);
		}

		private static int RemoveLiveBuffsByPrefabSet(Entity characterEntity, HashSet<int> prefabSet)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Expected O, but got Unknown
			//IL_0019: 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_0024: 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_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: 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_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: 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_0070: 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_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: 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_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			if (characterEntity == Entity.Null || prefabSet.Count == 0)
			{
				return 0;
			}
			NativeArray<Entity> val = default(NativeArray<Entity>);
			try
			{
				EntityManager entityManager = Core.EntityManager;
				EntityQuery val2 = ((EntityManager)(ref entityManager)).CreateEntityQuery((ComponentType[])(object)new ComponentType[2]
				{
					ComponentType.ReadOnly<Buff>(),
					ComponentType.ReadOnly<PrefabGUID>()
				});
				val = ((EntityQuery)(ref val2)).ToEntityArray(AllocatorHandle.op_Implicit((Allocator)2));
				int num = 0;
				for (int i = 0; i < val.Length; i++)
				{
					Entity val3 = val[i];
					if (!((EntityManager)(ref entityManager)).Exists(val3) || !((EntityManager)(ref entityManager)).HasComponent<Buff>(val3) || !((EntityManager)(ref entityManager)).HasComponent<PrefabGUID>(val3) || ((EntityManager)(ref entityManager)).GetComponentData<Buff>(val3).Target != characterEntity)
					{
						continue;
					}
					PrefabGUID componentData = ((EntityManager)(ref entityManager)).GetComponentData<PrefabGUID>(val3);
					if (prefabSet.Contains(((PrefabGUID)(ref componentData)).GuidHash))
					{
						PendingPermanentBuffs.Remove(val3);
						DestroyUtility.Destroy(entityManager, val3, (DestroyDebugReason)13, (string)null, 0);
						if (((EntityManager)(ref entityManager)).Exists(val3) && !((EntityManager)(ref entityManager)).HasComponent<DestroyTag>(val3))
						{
							((EntityManager)(ref entityManager)).AddComponent<DestroyTag>(val3);
						}
						num++;
					}
				}
				return num;
			}
			catch (Exception ex)
			{
				ManualLogSource log = Core.Log;
				bool flag = default(bool);
				BepInExWarningLogInterpolatedStringHandler val4 = new BepInExWarningLogInterpolatedStringHandler(63, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val4).AppendLiteral("[BuffService] Failed scanning live category buffs for removal: ");
					((BepInExLogInterpolatedStringHandler)val4).AppendFormatted<string>(ex.Message);
				}
				log.LogWarning(val4);
				return 0;
			}
			finally
			{
				if (val.IsCreated)
				{
					val.Dispose();
				}
			}
		}

		public static bool ForceOwnedBuffLifetime(Entity characterEntity, PrefabGUID buffPrefab, int durationSeconds, bool persistThroughDeath, bool keepVisibleTimerFrozen = false, int visibleTimerSeconds = 0, bool preserveVanillaCleanup = false)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			Entity buffEntity = default(Entity);
			if (!BuffUtility.TryGetBuff<EntityManager>(Core.EntityManager, characterEntity, PrefabIdentifier.op_Implicit(buffPrefab), ref buffEntity))
			{
				return false;
			}
			MarkPendingLifetimeMutation(buffEntity, durationSeconds, persistThroughDeath, preserveVanillaCleanup);
			return true;
		}

		private static void TrySetLifetime(Entity buffEntity, int durationSeconds, bool persistThroughDeath, bool preserveVanillaCleanup)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: 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)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			EntityManager entityManager = Core.EntityManager;
			if (buffEntity == Entity.Null || !((EntityManager)(ref entityManager)).Exists(buffEntity))
			{
				return;
			}
			if (persistThroughDeath && !((EntityManager)(ref entityManager)).HasComponent<Buff_Persists_Through_Death>(buffEntity))
			{
				((EntityManager)(ref entityManager)).AddComponent<Buff_Persists_Through_Death>(buffEntity);
			}
			if (!preserveVanillaCleanup)
			{
				if (((EntityManager)(ref entityManager)).HasComponent<RemoveBuffOnGameplayEvent>(buffEntity))
				{
					((EntityManager)(ref entityManager)).RemoveComponent<RemoveBuffOnGameplayEvent>(buffEntity);
				}
				if (((EntityManager)(ref entityManager)).HasComponent<RemoveBuffOnGameplayEventEntry>(buffEntity))
				{
					((EntityManager)(ref entityManager)).RemoveComponent<RemoveBuffOnGameplayEventEntry>(buffEntity);
				}
			}
			if (durationSeconds < 0)
			{
				if (((EntityManager)(ref entityManager)).HasComponent<LifeTime>(buffEntity))
				{
					((EntityManager)(ref entityManager)).RemoveComponent<LifeTime>(buffEntity);
				}
			}
			else if (durationSeconds > 0)
			{
				if (!((EntityManager)(ref entityManager)).HasComponent<LifeTime>(buffEntity))
				{
					((EntityManager)(ref entityManager)).AddComponent<LifeTime>(buffEntity);
				}
				((EntityManager)(ref entityManager)).SetComponentData<LifeTime>(buffEntity, new LifeTime
				{
					Duration = durationSeconds,
					EndAction = (LifeTimeEndAction)2
				});
			}
		}
	}
	internal static class CarrierPrefabService
	{
		private const string ScriptSpawnTypeName = "ProjectM.Scripting.ScriptSpawn";

		private static int _preparedCarrierGuid;

		private static Type? _scriptSpawnManagedType;

		private static Type? _scriptSpawnIl2CppType;

		internal static void Reset()
		{
			_preparedCarrierGuid = 0;
		}

		internal static bool PrepareConfiguredStatCarrierPrefab()
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Expected O, but got Unknown
			//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)
			//IL_006c: 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_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Expected O, but got Unknown
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!StatService.IsCarrierConfigured(out PrefabGUID carrier, out string error))
				{
					ManualLogSource log = Core.Log;
					bool flag = default(bool);
					BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(23, 1, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[CarrierPrefabService] ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(error);
					}
					log.LogWarning(val);
					return false;
				}
				if (_preparedCarrierGuid == ((PrefabGUID)(ref carrier)).GuidHash)
				{
					return true;
				}
				Entity val2 = default(Entity);
				if (!Core.Systems.PrefabCollectionSystem._PrefabGuidToEntityMap.TryGetValue(carrier, ref val2))
				{
					ManualLogSource log2 = Core.Log;
					bool flag2 = default(bool);
					BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(102, 1, ref flag2);
					if (flag2)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[CarrierPrefabService] Could not find stat carrier prefab entity for ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(((PrefabGUID)(ref carrier)).GuidHash);
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral(". Stats will not spawn correctly.");
					}
					log2.LogWarning(val);
					return false;
				}
				EntityManager entityManager = Core.EntityManager;
				if (!TryEnsureScriptSpawn(entityManager, val2))
				{
					Core.Log.LogWarning((object)"[CarrierPrefabService] Could not resolve/add ScriptSpawn dynamically. Immediate stat-buffer fallback will still be attempted, but HUD/stat sync may be less reliable.");
				}
				if (!((EntityManager)(ref entityManager)).HasBuffer<ModifyUnitStatBuff_DOTS>(val2))
				{
					((EntityManager)(ref entityManager)).AddBuffer<ModifyUnitStatBuff_DOTS>(val2);
				}
				else
				{
					((EntityManager)(ref entityManager)).GetBuffer<ModifyUnitStatBuff_DOTS>(val2, false).Clear();
				}
				_preparedCarrierGuid = ((PrefabGUID)(ref carrier)).GuidHash;
				Core.LogDebugIfEnabled($"[CarrierPrefabService] Prepared stat carrier prefab {((PrefabGUID)(ref carrier)).GuidHash} with dynamic ScriptSpawn + ModifyUnitStatBuff_DOTS.");
				return true;
			}
			catch (Exception e)
			{
				Core.LogException(e, "PrepareConfiguredStatCarrierPrefab");
				return false;
			}
		}

		private static bool TryEnsureScriptSpawn(EntityManager em, Entity prefabEntity)
		{
			//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)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			Type val = ResolveScriptSpawnIl2CppType();
			if (val == (Type)null)
			{
				return false;
			}
			ComponentType val2 = default(ComponentType);
			((ComponentType)(ref val2))..ctor(val, (AccessMode)0);
			if (!((EntityManager)(ref em)).HasComponent(prefabEntity, val2))
			{
				((EntityManager)(ref em)).AddComponent(prefabEntity, val2);
			}
			return true;
		}

		private static Type? ResolveScriptSpawnIl2CppType()
		{
			if (_scriptSpawnIl2CppType != (Type)null)
			{
				return _scriptSpawnIl2CppType;
			}
			if ((object)_scriptSpawnManagedType == null)
			{
				_scriptSpawnManagedType = AccessTools.TypeByName("ProjectM.Scripting.ScriptSpawn") ?? AccessTools.TypeByName("ProjectM.Gameplay.Scripting.ScriptSpawn") ?? AccessTools.TypeByName("ProjectM.ScriptSpawn") ?? AccessTools.TypeByName("ScriptSpawn");
			}
			if (_scriptSpawnManagedType == null)
			{
				return null;
			}
			MethodInfo methodInfo = typeof(Il2CppType).GetMethods(BindingFlags.Static | BindingFlags.Public).FirstOrDefault((MethodInfo method) => method.Name == "Of" && method.IsGenericMethodDefinition && method.GetParameters().Length == 0);
			if (methodInfo == null)
			{
				return null;
			}
			object? obj = methodInfo.MakeGenericMethod(_scriptSpawnManagedType).Invoke(null, null);
			_scriptSpawnIl2CppType = (Type?)((obj is Type) ? obj : null);
			return _scriptSpawnIl2CppType;
		}
	}
	internal static class ConfigMigrationService
	{
		internal static bool TryMigrate(ShopConfigRoot cfg, out ShopConfigRoot migrated)
		{
			migrated = cfg ?? ConfigService.DefaultConfigForMigration();
			bool flag = false;
			if (migrated.ConfigVersion < 2)
			{
				flag |= FixMovementSpeed(migrated);
				flag |= ClampOldOvertunedDefaults(migrated);
			}
			if (migrated.ConfigVersion < 4)
			{
				flag |= DisableVisibleTimerResetLoop(migrated);
				flag |= EnsureDefaultBloodBuffs(migrated);
			}
			if (migrated.ConfigVersion < 6)
			{
				flag |= MigrateDefaultStatCarrier(migrated);
			}
			if (migrated.ConfigVersion < 7)
			{
				flag |= FixDefaultStatNotes(migrated);
			}
			if (migrated.ConfigVersion < 8)
			{
				flag |= ApplyConfigVersion8RuntimeDefaults(migrated);
			}
			if (migrated.ConfigVersion < 11)
			{
				flag |= ApplyConfigVersion11CompatibilityDefaults(migrated);
			}
			if (migrated.ConfigVersion < 12)
			{
				flag |= ApplyConfigVersion12RenewableTimedBuffDefaults(migrated);
			}
			if (migrated.ConfigVersion < 13)
			{
				flag |= ApplyConfigVersion13AllStatsAndBloodSlots(migrated);
			}
			if (migrated.ConfigVersion < 14)
			{
				flag |= ApplyConfigVersion14ShortKeys(migrated);
			}
			if (migrated.ConfigVersion < 14)
			{
				migrated.ConfigVersion = 14;
				flag = true;
			}
			return flag;
		}

		private static bool MigrateDefaultStatCarrier(ShopConfigRoot cfg)
		{
			if (cfg.StatCarrierBuffPrefab != -1469378405)
			{
				return false;
			}
			cfg.StatCarrierBuffPrefab = -809648681;
			return true;
		}

		private static bool FixMovementSpeed(ShopConfigRoot cfg)
		{
			if (cfg.Stats == null || !cfg.Stats.TryGetValue("movement_speed", out StatShopEntry value) || value == null)
			{
				return false;
			}
			if (!StatDefinitionService.IsDangerousModifierCombo(value))
			{
				return false;
			}
			value.ModificationType = "MultiplyBaseAdd";
			value.Notes = "Permanently increases Movement Speed by 0.05 per purchase. Uses MultiplyBaseAdd for HUD/stat compatibility.";
			return true;
		}

		private static bool ClampOldOvertunedDefaults(ShopConfigRoot cfg)
		{
			if (cfg.Stats == null)
			{
				return false;
			}
			return false | SetIfOldValue(cfg, "primary_attack_speed", 0.05f, 0.02f) | SetIfOldValue(cfg, "physical_resistance", 0.05f, 0.02f) | SetIfOldValue(cfg, "spell_resistance", 0.05f, 0.02f) | SetIfOldValue(cfg, "healing_received", 0.05f, 0.03f) | SetIfOldValue(cfg, "damage_reduction", 0.02f, 0.01f) | SetIfOldValue(cfg, "spell_cooldown_recovery_rate", 0.05f, 0.02f) | SetIfOldValue(cfg, "weapon_cooldown_recovery_rate", 0.05f, 0.02f) | SetIfOldValue(cfg, "ability_attack_speed", 0.05f, 0.02f) | SetIfOldValue(cfg, "corruption_damage_reduction", 0.05f, 0.02f) | SetIfOldValue(cfg, "physical_power", 1f, 2f) | SetIfOldValue(cfg, "primary_life_leech", 0.02f, 0.03f) | SetIfOldValue(cfg, "physical_critical_strike_damage", 0.05f, 0.1f) | SetIfOldValue(cfg, "spell_critical_strike_damage", 0.05f, 0.1f) | SetIfOldValue(cfg, "reduced_blood_drain", 0.05f, 0.1f);
		}

		private static bool SetIfOldValue(ShopConfigRoot cfg, string key, float oldValue, float newValue)
		{
			if (!cfg.Stats.TryGetValue(key, out StatShopEntry value) || value == null)
			{
				return false;
			}
			if (Math.Abs(value.ValuePerPurchase - oldValue) > 0.0001f)
			{
				return false;
			}
			value.ValuePerPurchase = newValue;
			return true;
		}

		private static bool DisableVisibleTimerResetLoop(ShopConfigRoot cfg)
		{
			if (cfg.Buffs == null)
			{
				return false;
			}
			bool result = false;
			foreach (var (_, perkShopEntry2) in cfg.Buffs)
			{
				if (perkShopEntry2 != null && (perkShopEntry2.KeepVisibleTimerFrozen || perkShopEntry2.VisibleTimerSeconds != 0 || perkShopEntry2.DurationSeconds != -1))
				{
					perkShopEntry2.KeepVisibleTimerFrozen = false;
					perkShopEntry2.VisibleTimerSeconds = 0;
					perkShopEntry2.DurationSeconds = -1;
					perkShopEntry2.MutateAppliedBuffLifetime = true;
					perkShopEntry2.PersistThroughDeath = true;
					result = true;
				}
			}
			return result;
		}

		private static bool EnsureDefaultBloodBuffs(ShopConfigRoot cfg)
		{
			if (cfg.Buffs == null)
			{
				Dictionary<string, PerkShopEntry> dictionary2 = (cfg.Buffs = new Dictionary<string, PerkShopEntry>(StringComparer.OrdinalIgnoreCase));
			}
			bool result = false;
			foreach (var (key, perkShopEntry2) in ConfigService.DefaultBuffEntriesForMigration())
			{
				if (perkShopEntry2 != null && string.Equals(perkShopEntry2.Category, "blood_buff", StringComparison.OrdinalIgnoreCase) && !cfg.Buffs.ContainsKey(key))
				{
					cfg.Buffs[key] = perkShopEntry2;
					result = true;
				}
			}
			return result;
		}

		private static bool FixDefaultStatNotes(ShopConfigRoot cfg)
		{
			if (cfg.Stats == null)
			{
				return false;
			}
			bool result = false;
			foreach (var (key, statShopEntry2) in cfg.Stats)
			{
				if (statShopEntry2 != null && StatDefinitionService.TryGetDefaultStatNote(key, statShopEntry2.ValuePerPurchase, statShopEntry2.UnitStat, out string note) && !string.Equals(statShopEntry2.Notes, note, StringComparison.Ordinal) && (string.IsNullOrWhiteSpace(statShopEntry2.Notes) || statShopEntry2.Notes.StartsWith("Permanently increases ", StringComparison.OrdinalIgnoreCase) || string.Equals(statShopEntry2.Notes, "Permanent stat purchase.", StringComparison.OrdinalIgnoreCase)))
				{
					statShopEntry2.Notes = note;
					result = true;
				}
			}
			return result;
		}

		private static bool ApplyConfigVersion8RuntimeDefaults(ShopConfigRoot cfg)
		{
			bool result = false;
			if (cfg.ConfigFileCheckIntervalSeconds <= 0f)
			{
				cfg.ConfigFileCheckIntervalSeconds = 5f;
				result = true;
			}
			if (cfg.PlayerCacheSaveDebounceSeconds <= 0f)
			{
				cfg.PlayerCacheSaveDebounceSeconds = 30f;
				result = true;
			}
			_ = cfg.AutoDetectConfigChanges;
			return result;
		}

		private static bool ApplyConfigVersion11CompatibilityDefaults(ShopConfigRoot cfg)
		{
			bool result = false;
			if (cfg.UseClientAttributeStatAliases)
			{
				cfg.UseClientAttributeStatAliases = false;
				result = true;
			}
			_ = cfg.EnableExperimentalBloodBuffs;
			return result;
		}

		private static bool ApplyConfigVersion12RenewableTimedBuffDefaults(ShopConfigRoot cfg)
		{
			bool result = false;
			if (!cfg.UseRenewableTimedBuffs)
			{
				cfg.UseRenewableTimedBuffs = true;
				result = true;
			}
			if (cfg.RenewableTimedBuffSeconds < 60)
			{
				cfg.RenewableTimedBuffSeconds = 7200;
				result = true;
			}
			if (cfg.RenewableTimedBuffCategories == null)
			{
				List<string> list2 = (cfg.RenewableTimedBuffCategories = new List<string>());
			}
			string[] array = new string[3] { "potion", "elixir", "blood_buff" };
			foreach (string text in array)
			{
				if (!cfg.RenewableTimedBuffCategories.Contains<string>(text, StringComparer.OrdinalIgnoreCase))
				{
					cfg.RenewableTimedBuffCategories.Add(text);
					result = true;
				}
			}
			if (!cfg.EnableExperimentalBloodBuffs)
			{
				cfg.EnableExperimentalBloodBuffs = true;
				result = true;
			}
			if (cfg.Buffs != null)
			{
				foreach (var (_, perkShopEntry2) in cfg.Buffs)
				{
					if (perkShopEntry2 != null && (string.Equals(perkShopEntry2.Category, "potion", StringComparison.OrdinalIgnoreCase) || string.Equals(perkShopEntry2.Category, "elixir", StringComparison.OrdinalIgnoreCase) || string.Equals(perkShopEntry2.Category, "blood_buff", StringComparison.OrdinalIgnoreCase)))
					{
						if (perkShopEntry2.DurationSeconds != cfg.RenewableTimedBuffSeconds)
						{
							perkShopEntry2.DurationSeconds = cfg.RenewableTimedBuffSeconds;
							result = true;
						}
						if (perkShopEntry2.KeepVisibleTimerFrozen || perkShopEntry2.VisibleTimerSeconds != 0)
						{
							perkShopEntry2.KeepVisibleTimerFrozen = false;
							perkShopEntry2.VisibleTimerSeconds = 0;
							result = true;
						}
						if (perkShopEntry2.PersistThroughDeath)
						{
							perkShopEntry2.PersistThroughDeath = false;
							result = true;
						}
						if (!perkShopEntry2.MutateAppliedBuffLifetime)
						{
							perkShopEntry2.MutateAppliedBuffLifetime = true;
							result = true;
						}
					}
				}
			}
			return result;
		}

		private static bool ApplyConfigVersion13AllStatsAndBloodSlots(ShopConfigRoot cfg)
		{
			bool result = false;
			if (!cfg.EnableClientUnsupportedStats)
			{
				cfg.EnableClientUnsupportedStats = true;
				result = true;
			}
			if (cfg.Stats != null)
			{
				foreach (var (_, statShopEntry2) in cfg.Stats)
				{
					if (statShopEntry2 != null && !statShopEntry2.Enabled)
					{
						statShopEntry2.Enabled = true;
						result = true;
					}
				}
			}
			if (cfg.Categories == null)
			{
				Dictionary<string, BuffCategoryDefinition> dictionary2 = (cfg.Categories = new Dictionary<string, BuffCategoryDefinition>(StringComparer.OrdinalIgnoreCase));
			}
			if (!cfg.Categories.TryGetValue("blood_buff", out BuffCategoryDefinition value) || value == null)
			{
				cfg.Categories["blood_buff"] = new BuffCategoryDefinition
				{
					DisplayName = "Blood Buff",
					Documentation = "Renewable 2-hour blood-buff effects. Default limit: five blood packages.",
					MaxOwnedSlots = 5,
					SlotFreeCost = 250
				};
				result = true;
			}
			else
			{
				if (value.MaxOwnedSlots.GetValueOrDefault() != 5)
				{
					value.MaxOwnedSlots = 5;
					result = true;
				}
				if (string.IsNullOrWhiteSpace(value.Documentation) || value.Documentation.Contains("one blood package", StringComparison.OrdinalIgnoreCase) || value.Documentation.Contains("Recommended limit", StringComparison.OrdinalIgnoreCase))
				{
					value.Documentation = "Renewable 2-hour blood-buff effects. Default limit: five blood packages.";
					result = true;
				}
			}
			return result;
		}

		private static bool ApplyConfigVersion14ShortKeys(ShopConfigRoot cfg)
		{
			bool flag = false;
			if (cfg.Stats != null)
			{
				foreach (KeyValuePair<string, string> statKeyAlias in KeyAliasService.StatKeyAliases)
				{
					flag |= RenameStatKey(cfg, statKeyAlias.Key, statKeyAlias.Value);
				}
			}
			if (cfg.Buffs != null)
			{
				foreach (KeyValuePair<string, string> buffKeyAlias in KeyAliasService.BuffKeyAliases)
				{
					flag |= RenameBuffKey(cfg, buffKeyAlias.Key, buffKeyAlias.Value);
				}
				string[] array = new string[6] { "draculaT1", "draculaT2", "draculaT3", "draculaT4", "draculaT5", "generalT5" };
				foreach (string key in array)
				{
					if (cfg.Buffs.TryGetValue(key, out PerkShopEntry value) && value != null && value.Enabled)
					{
						value.Enabled = false;
						flag = true;
					}
				}
			}
			return flag;
		}

		private static bool RenameStatKey(ShopConfigRoot cfg, string oldKey, string newKey)
		{
			if (cfg.Stats == null || !cfg.Stats.TryGetValue(oldKey, out StatShopEntry value) || value == null)
			{
				return false;
			}
			if (!cfg.Stats.ContainsKey(newKey))
			{
				cfg.Stats[newKey] = value;
			}
			cfg.Stats.Remove(oldKey);
			return true;
		}

		private static bool RenameBuffKey(ShopConfigRoot cfg, string oldKey, string newKey)
		{
			if (cfg.Buffs == null || !cfg.Buffs.TryGetValue(oldKey, out PerkShopEntry value) || value == null)
			{
				return false;
			}
			if (!cfg.Buffs.ContainsKey(newKey))
			{
				cfg.Buffs[newKey] = value;
			}
			cfg.Buffs.Remove(oldKey);
			return true;
		}
	}
	internal static class ConfigService
	{
		private static readonly object Lock = new object();

		internal static readonly string ConfigDir = Path.Combine(Paths.ConfigPath, "PerkShop");

		private static readonly string ConfigFile = Path.Combine(ConfigDir, "perkconfig.json");

		private static DateTime _lastWrite = DateTime.MinValue;

		private static DateTime _nextConfigFileCheckUtc = DateTime.MinValue;

		private static ShopConfigRoot _root = DefaultConfig();

		internal static readonly JsonSerializerOptions JsonOptions = new JsonSerializerOptions
		{
			AllowTrailingCommas = true,
			PropertyNameCaseInsensitive = true,
			ReadCommentHandling = JsonCommentHandling.Skip,
			WriteIndented = true
		};

		public static ShopConfigRoot Shop => GetShop();

		public static bool DebugLoggingEnabled
		{
			get
			{
				try
				{
					return Shop.EnableDebugLogging;
				}
				catch
				{
					return false;
				}
			}
		}

		public static void Initialize()
		{
			Load(force: true);
		}

		public static void Reload()
		{
			Load(force: true);
		}

		public static bool UpdateWhitelist(string shopType, ulong platformId, bool add, string displayName = "")
		{
			if (platformId == 0L)
			{
				return false;
			}
			lock (Lock)
			{
				Load(force: true);
				_root = Normalize(_root);
				bool num = string.Equals(shopType, "stat", StringComparison.OrdinalIgnoreCase);
				List<ulong> list = (num ? _root.StatWhitelistPlatformIds : _root.BuffWhitelistPlatformIds);
				Dictionary<ulong, string> dictionary = (num ? _root.StatWhitelistNames : _root.BuffWhitelistNames);
				bool flag = list.Contains(platformId);
				if (add)
				{
					if (!flag)
					{
						list.Add(platformId);
						list.Sort();
					}
					if (!string.IsNullOrWhiteSpace(displayName))
					{
						dictionary[platformId] = displayName.Trim();
					}
					File.WriteAllText(ConfigFile, JsonSerializer.Serialize(_root, JsonOptions));
					_lastWrite = File.GetLastWriteTime(ConfigFile);
					return !flag;
				}
				if (!flag)
				{
					return false;
				}
				list.Remove(platformId);
				dictionary.Remove(platformId);
				File.WriteAllText(ConfigFile, JsonSerializer.Serialize(_root, JsonOptions));
				_lastWrite = File.GetLastWriteTime(ConfigFile);
				return true;
			}
		}

		private static ShopConfigRoot GetShop()
		{
			Load(force: false);
			lock (Lock)
			{
				return _root;
			}
		}

		private static void Load(bool force)
		{
			//IL_017d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0184: Expected O, but got Unknown
			lock (Lock)
			{
				try
				{
					Directory.CreateDirectory(ConfigDir);
					if (!File.Exists(ConfigFile))
					{
						_root = Normalize(DefaultConfig());
						StatDefinitionService.RebuildCache(_root);
						File.WriteAllText(ConfigFile, JsonSerializer.Serialize(_root, JsonOptions));
						_lastWrite = File.GetLastWriteTime(ConfigFile);
						_nextConfigFileCheckUtc = DateTime.UtcNow.AddSeconds(Math.Max(1f, _root.ConfigFileCheckIntervalSeconds));
						return;
					}
					if (!force)
					{
						if (!_root.AutoDetectConfigChanges || DateTime.UtcNow < _nextConfigFileCheckUtc)
						{
							return;
						}
						_nextConfigFileCheckUtc = DateTime.UtcNow.AddSeconds(Math.Max(1f, _root.ConfigFileCheckIntervalSeconds));
					}
					DateTime lastWriteTime = File.GetLastWriteTime(ConfigFile);
					if (!force && lastWriteTime <= _lastWrite)
					{
						return;
					}
					_root = JsonSerializer.Deserialize<ShopConfigRoot>(File.ReadAllText(ConfigFile), JsonOptions) ?? DefaultConfig();
					if (ConfigMigrationService.TryMigrate(_root, out ShopConfigRoot migrated))
					{
						_root = Normalize(migrated);
						StatDefinitionService.RebuildCache(_root);
						File.WriteAllText(ConfigFile, JsonSerializer.Serialize(_root, JsonOptions));
						lastWriteTime = File.GetLastWriteTime(ConfigFile);
						ManualLogSource log = Core.Log;
						bool flag = default(bool);
						BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(53, 1, ref flag);
						if (flag)
						{
							((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[ConfigService] Migrated perkconfig.json to version ");
							((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(_root.ConfigVersion);
							((BepInExLogInterpolatedStringHandler)val).AppendLiteral(".");
						}
						log.LogInfo(val);
					}
					else
					{
						_root = Normalize(_root);
						StatDefinitionService.RebuildCache(_root);
					}
					_lastWrite = lastWriteTime;
					_nextConfigFileCheckUtc = DateTime.UtcNow.AddSeconds(Math.Max(1f, _root.ConfigFileCheckIntervalSeconds));
				}
				catch (Exception e)
				{
					Core.LogException(e, "Load");
					_root = Normalize(DefaultConfig());
					StatDefinitionService.RebuildCache(_root);
					_nextConfigFileCheckUtc = DateTime.UtcNow.AddSeconds(Math.Max(1f, _root.ConfigFileCheckIntervalSeconds));
				}
			}
		}

		private static ShopConfigRoot Normalize(ShopConfigRoot cfg)
		{
			if (cfg == null)
			{
				cfg = DefaultConfig();
			}
			if (cfg.ConfigVersion <= 0)
			{
				cfg.ConfigVersion = 14;
			}
			cfg.CurrencyName = (string.IsNullOrWhiteSpace(cfg.CurrencyName) ? "Currency" : cfg.CurrencyName.Trim());
			if (cfg.CurrencyPrefab == 0)
			{
				cfg.CurrencyPrefab = 576389135;
			}
			if (cfg.ReapplyCheckIntervalSeconds < 3)
			{
				cfg.ReapplyCheckIntervalSeconds = 3;
			}
			if (cfg.ReapplyMaxUsersPerCycle < 0)
			{
				cfg.ReapplyMaxUsersPerCycle = 0;
			}
			if (cfg.CarrierFinalizeCheckIntervalSeconds < 0.05f)
			{
				cfg.CarrierFinalizeCheckIntervalSeconds = 0.05f;
			}
			if (cfg.OwnershipSaveDebounceSeconds < 0f)
			{
				cfg.OwnershipSaveDebounceSeconds = 0f;
			}
			if (cfg.ConfigFileCheckIntervalSeconds < 1f)
			{
				cfg.ConfigFileCheckIntervalSeconds = 5f;
			}
			if (cfg.PlayerCacheSaveDebounceSeconds < 0f)
			{
				cfg.PlayerCacheSaveDebounceSeconds = 0f;
			}
			if (cfg.RenewableTimedBuffSeconds < 60)
			{
				cfg.RenewableTimedBuffSeconds = 7200;
			}
			ShopConfigRoot shopConfigRoot = cfg;
			if (shopConfigRoot.RenewableTimedBuffCategories == null)
			{
				ShopConfigRoot shopConfigRoot2 = shopConfigRoot;
				List<string> obj = new List<string> { "potion", "elixir", "blood_buff" };
				List<string> list = obj;
				shopConfigRoot2.RenewableTimedBuffCategories = obj;
			}
			cfg.RenewableTimedBuffCategories = (from category in cfg.RenewableTimedBuffCategories
				where !string.IsNullOrWhiteSpace(category)
				select category.Trim()).Distinct<string>(StringComparer.OrdinalIgnoreCase).ToList();
			if (cfg.RenewableTimedBuffCategories.Count == 0)
			{
				cfg.RenewableTimedBuffCategories.AddRange(new string[3] { "potion", "elixir", "blood_buff" });
			}
			if (cfg.ForcePermanentBuffs)
			{
				cfg.AllowBuffEntityMutation = true;
			}
			shopConfigRoot = cfg;
			if (shopConfigRoot.BuffWhitelistPlatformIds == null)
			{
				List<ulong> list3 = (shopConfigRoot.BuffWhitelistPlatformIds = new List<ulong>());
			}
			shopConfigRoot = cfg;
			if (shopConfigRoot.StatWhitelistPlatformIds == null)
			{
				List<ulong> list3 = (shopConfigRoot.StatWhitelistPlatformIds = new List<ulong>());
			}
			shopConfigRoot = cfg;
			if (shopConfigRoot.BuffWhitelistNames == null)
			{
				Dictionary<ulong, string> dictionary2 = (shopConfigRoot.BuffWhitelistNames = new Dictionary<ulong, string>());
			}
			shopConfigRoot = cfg;
			if (shopConfigRoot.StatWhitelistNames == null)
			{
				Dictionary<ulong, string> dictionary2 = (shopConfigRoot.StatWhitelistNames = new Dictionary<ulong, string>());
			}
			cfg.BuffWhitelistPlatformIds = (from id in cfg.BuffWhitelistPlatformIds.Where((ulong id) => id != 0).Distinct()
				orderby id
				select id).ToList();
			cfg.StatWhitelistPlatformIds = (from id in cfg.StatWhitelistPlatformIds.Where((ulong id) => id != 0).Distinct()
				orderby id
				select id).ToList();
			cfg.BuffWhitelistNames = cfg.BuffWhitelistNames.Where<KeyValuePair<ulong, string>>((KeyValuePair<ulong, string> kv) => kv.Key != 0L && !string.IsNullOrWhiteSpace(kv.Value)).ToDictionary((KeyValuePair<ulong, string> kv) => kv.Key, (KeyValuePair<ulong, string> kv) => kv.Value.Trim());
			cfg.StatWhitelistNames = cfg.StatWhitelistNames.Where<KeyValuePair<ulong, string>>((KeyValuePair<ulong, string> kv) => kv.Key != 0L && !string.IsNullOrWhiteSpace(kv.Value)).ToDictionary((KeyValuePair<ulong, string> kv) => kv.Key, (KeyValuePair<ulong, string> kv) => kv.Value.Trim());
			if (cfg.MaxOwnedStatTypes < 0)
			{
				cfg.MaxOwnedStatTypes = 0;
			}
			if (cfg.StatTypeSlotFreeCost < 0)
			{
				cfg.StatTypeSlotFreeCost = 0;
			}
			cfg.MaxHealthPurchaseBehavior = (string.IsNullOrWhiteSpace(cfg.MaxHealthPurchaseBehavior) ? "ClampOnly" : cfg.MaxHealthPurchaseBehavior.Trim());
			if (!string.Equals(cfg.MaxHealthPurchaseBehavior, "ClampOnly", StringComparison.OrdinalIgnoreCase) && !string.Equals(cfg.MaxHealthPurchaseBehavior, "FillToMax", StringComparison.OrdinalIgnoreCase) && !string.Equals(cfg.MaxHealthPurchaseBehavior, "PreserveRatio", StringComparison.OrdinalIgnoreCase))
			{
				cfg.MaxHealthPurchaseBehavior = "ClampOnly";
			}
			shopConfigRoot = cfg;
			if (shopConfigRoot.Stats == null)
			{
				Dictionary<string, StatShopEntry> dictionary5 = (shopConfigRoot.Stats = new Dictionary<string, StatShopEntry>(StringComparer.OrdinalIgnoreCase));
			}
			Dictionary<string, StatShopEntry> dictionary6 = new Dictionary<string, StatShopEntry>(StringComparer.OrdinalIgnoreCase);
			string key;
			foreach (KeyValuePair<string, StatShopEntry> stat in cfg.Stats)
			{
				stat.Deconstruct(out key, out var value);
				string text = key;
				StatShopEntry statShopEntry = value;
				if (!string.IsNullOrWhiteSpace(text) && statShopEntry != null)
				{
					statShopEntry.DisplayName = (string.IsNullOrWhiteSpace(statShopEntry.DisplayName) ? text.Trim() : statShopEntry.DisplayName.Trim());
					statShopEntry.UnitStat = (string.IsNullOrWhiteSpace(statShopEntry.UnitStat) ? "PhysicalPower" : statShopEntry.UnitStat.Trim());
					statShopEntry.ModificationType = (string.IsNullOrWhiteSpace(statShopEntry.ModificationType) ? "Add" : statShopEntry.ModificationType.Trim());
					statShopEntry.AttributeCapType = (string.IsNullOrWhiteSpace(statShopEntry.AttributeCapType) ? "Uncapped" : statShopEntry.AttributeCapType.Trim());
					if (statShopEntry.Cost <= 0)
					{
						statShopEntry.Cost = 1;
					}
					if (statShopEntry.ValuePerPurchase == 0f)
					{
						statShopEntry.ValuePerPurchase = 1f;
					}
					if (statShopEntry.MaxPurchases < 0)
					{
						statShopEntry.MaxPurchases = 0;
					}
					statShopEntry.Notes = (string.IsNullOrWhiteSpace(statShopEntry.Notes) ? "Permanent stat purchase." : statShopEntry.Notes.Trim());
					StatDefinitionService.NormalizeKnownStatEntry(text.Trim(), statShopEntry);
					dictionary6[text.Trim()] = statShopEntry;
				}
			}
			cfg.Stats = dictionary6;
			shopConfigRoot = cfg;
			if (shopConfigRoot.Categories == null)
			{
				Dictionary<string, BuffCategoryDefinition> dictionary8 = (shopConfigRoot.Categories = new Dictionary<string, BuffCategoryDefinition>(StringComparer.OrdinalIgnoreCase));
			}
			Dictionary<string, BuffCategoryDefinition> dictionary9 = new Dictionary<string, BuffCategoryDefinition>(StringComparer.OrdinalIgnoreCase);
			foreach (KeyValuePair<string, BuffCategoryDefinition> category in cfg.Categories)
			{
				category.Deconstruct(out key, out var value2);
				string text2 = key;
				BuffCategoryDefinition buffCategoryDefinition = value2;
				if (!string.IsNullOrWhiteSpace(text2) && buffCategoryDefinition != null)
				{
					buffCategoryDefinition.DisplayName = (string.IsNullOrWhiteSpace(buffCategoryDefinition.DisplayName) ? text2.Trim() : buffCategoryDefinition.DisplayName.Trim());
					buffCategoryDefinition.Documentation = (string.IsNullOrWhiteSpace(buffCategoryDefinition.Documentation) ? string.Empty : buffCategoryDefinition.Documentation.Trim());
					if (buffCategoryDefinition.MaxOwnedSlots.HasValue && buffCategoryDefinition.MaxOwnedSlots.Value <= 0)
					{
						buffCategoryDefinition.MaxOwnedSlots = null;
					}
					if (buffCategoryDefinition.SlotFreeCost.HasValue && buffCategoryDefinition.SlotFreeCost.Value <= 0)
					{
						buffCategoryDefinition.SlotFreeCost = null;
					}
					dictionary9[text2.Trim()] = buffCategoryDefinition;
				}
			}
			EnsureCategory(dictionary9, "blood_buff", "Blood Buff", "Renewable 2-hour blood-buff effects. Default limit: five blood packages.", 5, 250);
			EnsureCategory(dictionary9, "set_bonus", "Set Bonus", "Armor set style bonuses or set-derived effects.", null, null);
			EnsureCategory(dictionary9, "potion", "Potion", "Potion and brew-style buffs. Safer for multi-slot ownership.", 3, 100);
			EnsureCategory(dictionary9, "elixir", "Elixir", "Elixir-style buffs. Bloodcraft compatibility default: one owned elixir slot.", 1, 500);
			EnsureCategory(dictionary9, "misc", "Misc", "Fallback category for uncategorized buffs.", null, null);
			if (dictionary9.TryGetValue("blood_buff", out var value3) && value3.MaxOwnedSlots.GetValueOrDefault() == 1)
			{
				value3.MaxOwnedSlots = 5;
				value3.Documentation = "Renewable 2-hour blood-buff effects. Default limit: five blood packages.";
			}
			cfg.Categories = dictionary9;
			shopConfigRoot = cfg;
			if (shopConfigRoot.Buffs == null)
			{
				Dictionary<string, PerkShopEntry> dictionary11 = (shopConfigRoot.Buffs = new Dictionary<string, PerkShopEntry>(StringComparer.OrdinalIgnoreCase));
			}
			Dictionary<string, PerkShopEntry> dictionary12 = new Dictionary<string, PerkShopEntry>(StringComparer.OrdinalIgnoreCase);
			foreach (KeyValuePair<string, PerkShopEntry> buff in cfg.Buffs)
			{
				buff.Deconstruct(out key, out var value4);
				string text3 = key;
				PerkShopEntry perkShopEntry = value4;
				if (string.IsNullOrWhiteSpace(text3) || perkShopEntry == null)
				{
					continue;
				}
				perkShopEntry.DisplayName = (string.IsNullOrWhiteSpace(perkShopEntry.DisplayName) ? text3.Trim() : perkShopEntry.DisplayName.Trim());
				perkShopEntry.Category = (string.IsNullOrWhiteSpace(perkShopEntry.Category) ? "misc" : perkShopEntry.Category.Trim());
				if (!cfg.Categories.ContainsKey(perkShopEntry.Category))
				{
					perkShopEntry.Category = "misc";
				}
				if (perkShopEntry.Cost <= 0)
				{
					perkShopEntry.Cost = 1;
				}
				if (cfg.ForcePermanentBuffs)
				{
					perkShopEntry.PersistentPurchase = true;
					perkShopEntry.KeepVisibleTimerFrozen = false;
					perkShopEntry.VisibleTimerSeconds = 0;
					perkShopEntry.MutateAppliedBuffLifetime = true;
					if (IsRenewableTimedCategory(cfg, perkShopEntry.Category))
					{
						perkShopEntry.DurationSeconds = cfg.RenewableTimedBuffSeconds;
						perkShopEntry.PersistThroughDeath = cfg.RenewableTimedBuffsPersistThroughDeath;
					}
					else
					{
						perkShopEntry.DurationSeconds = -1;
						perkShopEntry.PersistThroughDeath = true;
					}
				}
				else
				{
					if (perkShopEntry.DurationSeconds < -1)
					{
						perkShopEntry.DurationSeconds = -1;
					}
					if (perkShopEntry.VisibleTimerSeconds < 0)
					{
						perkShopEntry.VisibleTimerSeconds = 0;
					}
					if (!perkShopEntry.MutateAppliedBuffLifetime)
					{
						perkShopEntry.PersistThroughDeath = false;
					}
				}
				dictionary12[text3.Trim()] = perkShopEntry;
			}
			cfg.Buffs = dictionary12;
			return cfg;
		}

		internal static bool IsRenewableTimedCategory(ShopConfigRoot cfg, string category)
		{
			string category2 = category;
			if (cfg == null || !cfg.UseRenewableTimedBuffs || string.IsNullOrWhiteSpace(category2))
			{
				return false;
			}
			if (cfg.RenewableTimedBuffCategories != null)
			{
				return cfg.RenewableTimedBuffCategories.Any((string c) => string.Equals(c, category2, StringComparison.OrdinalIgnoreCase));
			}
			return false;
		}

		internal static int ResolveBuffDuration(ShopConfigRoot cfg, PerkShopEntry entry)
		{
			if (cfg != null && entry != null && IsRenewableTimedCategory(cfg, entry.Category))
			{
				return Math.Max(60, cfg.RenewableTimedBuffSeconds);
			}
			if (cfg == null || !cfg.ForcePermanentBuffs || (entry.KeepVisibleTimerFrozen && entry.VisibleTimerSeconds > 0))
			{
				return entry.DurationSeconds;
			}
			return -1;
		}

		internal static bool ResolveBuffPersistThroughDeath(ShopConfigRoot cfg, PerkShopEntry entry)
		{
			if (cfg != null && entry != null && IsRenewableTimedCategory(cfg, entry.Category))
			{
				return cfg.RenewableTimedBuffsPersistThroughDeath;
			}
			if (cfg == null || !cfg.ForcePermanentBuffs)
			{
				return entry.PersistThroughDeath;
			}
			return true;
		}

		internal static bool PreserveVanillaBuffCleanup(ShopConfigRoot cfg, PerkShopEntry entry)
		{
			if (cfg != null && entry != null)
			{
				return IsRenewableTimedCategory(cfg, entry.Category);
			}
			return false;
		}

		private static void EnsureCategory(Dictionary<string, BuffCategoryDefinition> categories, string key, string displayName, string documentation, int? maxOwnedSlots, int? slotFreeCost)
		{
			if (!categories.TryGetValue(key, out BuffCategoryDefinition value) || value == null)
			{
				categories[key] = new BuffCategoryDefinition
				{
					DisplayName = displayName,
					Documentation = documentation,
					MaxOwnedSlots = maxOwnedSlots,
					SlotFreeCost = slotFreeCost
				};
				return;
			}
			value.DisplayName = (string.IsNullOrWhiteSpace(value.DisplayName) ? displayName : value.DisplayName.Trim());
			value.Documentation = (string.IsNullOrWhiteSpace(value.Documentation) ? documentation : value.Documentation.Trim());
			if (value.MaxOwnedSlots.HasValue && value.MaxOwnedSlots.Value <= 0)
			{
				value.MaxOwnedSlots = null;
			}
			if (value.SlotFreeCost.HasValue && value.SlotFreeCost.Value <= 0)
			{
				value.SlotFreeCost = null;
			}
		}

		internal static ShopConfigRoot DefaultConfigForMigration()
		{
			return DefaultConfig();
		}

		private static ShopConfigRoot DefaultConfig()
		{
			return new ShopConfigRoot
			{
				ConfigVersion = 14,
				Enabled = true,
				EnableDebugLogging = false,
				CurrencyPrefab = 576389135,
				CurrencyName = "Greater Stygian Shards",
				BlockPurchasesInCombat = true,
				BlockRemovalsInCombat = true,
				SaveOwnership = true,
				ReapplyOwnedBuffsOnLogin = false,
				ReapplyOwnedBuffsWhenMissing = false,
				ReapplyCheckIntervalSeconds = 60,
				ReapplyMaxUsersPerCycle = 5,
				CarrierFinalizeCheckIntervalSeconds = 0.25f,
				OwnershipSaveDebounceSeconds = 2f,
				AutoDetectConfigChanges = false,
				ConfigFileCheckIntervalSeconds = 5f,
				PlayerCacheSaveDebounceSeconds = 30f,
				ForcePermanentBuffs = true,
				AllowBuffEntityMutation = true,
				UseRenewableTimedBuffs = true,
				RenewableTimedBuffSeconds = 7200,
				RenewableTimedBuffCategories = new List<string> { "potion", "elixir", "blood_buff" },
				RenewableTimedBuffsPersistThroughDeath = false,
				EnableStatShop = true,
				EnableClientUnsupportedStats = true,
				UseClientAttributeStatAliases = false,
				EnableExperimentalBloodBuffs = true,
				StatCarrierBuffPrefab = -809648681,
				EnableStatTypeSlots = true,
				MaxOwnedStatTypes = 4,
				StatTypeSlotFreeCost = 500,
				MaxHealthPurchaseBehavior = "ClampOnly",
				EnableBuffWhitelist = false,
				BuffWhitelistPlatformIds = new List<ulong>(),
				BuffWhitelistNames = new Dictionary<ulong, string>(),
				EnableStatWhitelist = false,
				StatWhitelistPlatformIds = new List<ulong>(),
				StatWhitelistNames = new Dictionary<ulong, string>(),
				Stats = new Dictionary<string, StatShopEntry>(StringComparer.OrdinalIgnoreCase)
				{
					["MH"] = new StatShopEntry
					{
						DisplayName = "Max Health",
						UnitStat = "MaxHealth",
						ModificationType = "Add",
						AttributeCapType = "Uncapped",
						ValuePerPurchase = 25f,
						Cost = 500,
						MaxPurchases = 10,
						Notes = "Permanently increases Max Health by 25 per purchase."
					},
					["PP"] = new StatShopEntry
					{
						DisplayName = "Physical Power",
						UnitStat = "PhysicalPower",
						ModificationType = "Add",
						AttributeCapType = "Uncapped",
						ValuePerPurchase = 2f,
						Cost = 500,
						MaxPurchases = 10,
						Notes = "Permanently increases Physical Power by 2 per purchase."
					},
					["SP"] = new StatShopEntry
					{
						DisplayName = "Spell Power",
						UnitStat = "SpellPower",
						ModificationType = "Add",
						AttributeCapType = "Uncapped",
						ValuePerPurchase = 1f,
						Cost = 500,
						MaxPurchases = 10,
						Notes = "Permanently increases Spell Power by 1 per purchase."
					},
					["MS"] = new StatShopEntry
					{
						DisplayName = "Movement Speed",
						UnitStat = "MovementSpeed",
						ModificationType = "MultiplyBaseAdd",
						AttributeCapType = "Uncapped",
						ValuePerPurchase = 0.05f,
						Cost = 500,
						MaxPurchases = 5,
						Notes = "Permanently increases Movement Speed by 0.05 per purchase. Uses MultiplyBaseAdd for HUD/stat compatibility."
					},
					["AS"] = new StatShopEntry
					{
						DisplayName = "Primary Attack Speed",
						UnitStat = "PrimaryAttackSpeed",
						ModificationType = "Add",
						AttributeCapType = "Uncapped",
						ValuePerPurchase = 0.02f,
						Cost = 500,
						MaxPurchases = 5,
						Notes = "Permanently increases Primary Attack Speed by 0.02 per purchase."
					},
					["phll"] = new StatShopEntry
					{
						DisplayName = "Physical Life Leech",
						UnitStat = "PhysicalLifeLeech",
						ModificationType = "Add",
						AttributeCapType = "Uncapped",
						ValuePerPurchase = 0.02f,
						Cost = 500,
						MaxPurchases = 5,
						Notes = "Permanently increases Physical Life Leech by 0.02 per purchase."
					},
					["sll"] = new StatShopEntry
					{
						DisplayName = "Spell Life Leech",
						UnitStat = "SpellLifeLeech",
						ModificationType = "Add",
						AttributeCapType = "Uncapped",
						ValuePerPurchase = 0.02f,
						Cost = 500,
						MaxPurchases = 5,
						Notes = "Permanently increases Spell Life Leech by 0.02 per purchase."
					},
					["prll"] = new StatShopEntry
					{
						DisplayName = "Primary Life Leech",
						UnitStat = "PrimaryLifeLeech",
						ModificationType = "Add",
						AttributeCapType = "Uncapped",
						ValuePerPurchase = 0.03f,
						Cost = 500,
						MaxPurchases = 5,
						Notes = "Permanently increases Primary Life Leech by 0.03 per purchase."
					},
					["PCC"] = new StatShopEntry
					{
						DisplayName = "Physical Critical Strike Chance",
						UnitStat = "PhysicalCriticalStrikeChance",
						ModificationType = "Add",
						AttributeCapType = "Uncapped",
						ValuePerPurchase = 0.02f,
						Cost = 500,
						MaxPurchases = 5,
						Notes = "Permanently increases Physical Critical Strike Chance by 0.02 per purchase."
					},
					["PCD"] = new StatShopEntry
					{
						DisplayName = "Physical Critical Strike Damage",
						UnitStat = "PhysicalCriticalStrikeDamage",
						ModificationType = "Add",
						AttributeCapType = "Uncapped",
						ValuePerPurchase = 0.1f,
						Cost = 500,
						MaxPurchases = 5,
						Notes = "Permanently increases Physical Critical Strike Damage by 0.1 per purchase."
					},
					["SCC"] = new StatShopEntry
					{
						DisplayName = "Spell Critical Strike Chance",
						UnitStat = "SpellCriticalStrikeChance",
						ModificationType = "Add",
						AttributeCapType = "Uncapped",
						ValuePerPurchase = 0.02f,
						Cost = 500,
						MaxPurchases = 5,
						Notes = "Permanently increases Spell Critical Strike Chance by 0.02 per purchase."
					},
					["SCD"] = new StatShopEntry
					{
						DisplayName = "Spell Critical Strike Damage",
						UnitStat = "SpellCriticalStrikeDamage",
						ModificationType = "Add",
						AttributeCapType = "Uncapped",
						ValuePerPurchase = 0.1f,
						Cost = 500,
						MaxPurchases = 5,
						Notes = "Permanently increases Spell Critical Strike Damage by 0.1 per purchase."
					},
					["PR"] = new StatShopEntry
					{
						DisplayName = "Physical Resistance",
						UnitStat = "PhysicalResistance",
						ModificationType = "Add",
						AttributeCapType = "Uncapped",
						ValuePerPurchase = 0.02f,
						Cost = 500,
						MaxPurchases = 5,
						Notes = "Permanently increases Physical Resistance by 0.02 per purchase."
					},
					["SR"] = new StatShopEntry
					{
						DisplayName = "Spell Resistance",
						UnitStat = "SpellResistance",
						ModificationType = "Add",
						AttributeCapType = "Uncapped",
						ValuePerPurchase = 0.02f,
						Cost = 500,
						MaxPurchases = 5,
						Notes = "Permanently increases Spell Resistance by 0.02 per purchase."
					},
					["HR"] = new StatShopEntry
					{
						DisplayName = "Healing Received",
						UnitStat = "HealingReceived",
						ModificationType = "Add",
						AttributeCapType = "Uncapped",
						ValuePerPurchase = 0.03f,
						Cost = 500,
						MaxPurchases = 5,
						Notes = "Permanently increases Healing Received by 0.03 per purchase."
					},
					["DR"] = new StatShopEntry
					{
						DisplayName = "Damage Reduction",
						UnitStat = "DamageReduction",
						ModificationType = "Add",
						AttributeCapType = "Uncapped",
						ValuePerPurchase = 0.01f,
						Cost = 750,
						MaxPurchases = 5,
						Notes = "Permanently increases Damage Reduction by 0.01 per purchase."
					},
					["RY"] = new StatShopEntry
					{
						DisplayName = "Resource Yield",
						UnitStat = "ResourceYield",
						ModificationType = "Add",
						AttributeCapType = "Uncapped",
						ValuePerPurchase = 0.05f,
						Cost = 500,
						MaxPurchases = 5,
						Notes = "Permanently increases Resource Yield by 0.05 per purchase."
					},
					["RBD"] = new StatShopEntry
					{
						DisplayName = "Reduced Blood Drain",
						UnitStat = "ReducedBloodDrain",
						ModificationType = "Add",
						AttributeCapType = "Uncapped",
						ValuePerPurchase = 0.1f,
						Cost = 500,
						MaxPurchases = 5,
						Notes = "Permanently increases Reduced Blood Drain by 0.1 per purchase."
					},
					["SCR"] = new StatShopEntry
					{
						DisplayName = "Spell Cooldown Recovery Rate",
						UnitStat = "SpellCooldownRecoveryRate",
						ModificationType = "Add",
						AttributeCapType = "Uncapped",
						ValuePerPurchase = 0.02f,
						Cost = 500,
						MaxPurchases = 5,
						Notes = "Permanently increases Spell Cooldown Recovery Rate by 0.02 per purchase."
					},
					["WCR"] = new StatShopEntry
					{
						DisplayName = "Weapon Cooldown Recovery Rate",
						UnitStat = "WeaponCooldownRecoveryRate",
						ModificationType = "Add",
						AttributeCapType = "Uncapped",
						ValuePerPurchase = 0.02f,
						Cost = 500,
						MaxPurchases = 5,
						Notes = "Permanently increases Weapon Cooldown Recovery Rate by 0.02 per purchase."
					},
					["UCR"] = new StatShopEntry
					{
						DisplayName = "Ultimate Cooldown Recovery Rate",
						UnitStat = "UltimateCooldownRecoveryRate",
						ModificationType = "Add",
						AttributeCapType = "Uncapped",
						ValuePerPurchase = 0.04f,
						Cost = 500,
						MaxPurchases = 5,
						Notes = "Permanently increases Ultimate Cooldown Recovery Rate by 0.04 per purchase."
					},
					["MD"] = new StatShopEntry
					{
						DisplayName = "Minion Damage",
						UnitStat = "MinionDamage",
						ModificationType = "Add",
						AttributeCapType = "Uncapped",
						ValuePerPurchase = 0.05f,
						Cost = 500,
						MaxPurchases = 5,
						Notes = "Permanently increases Minion Damage by 0.05 per purchase."
					},
					["AAS"] = new StatShopEntry
					{
						DisplayName = "Ability Attack Speed",
						UnitStat = "AbilityAttackSpeed",
						ModificationType = "Add",
						AttributeCapType = "Uncapped",
						ValuePerPurchase = 0.02f,
						Cost = 500,
						MaxPurchases = 5,
						Notes = "Permanently increases Ability Attack Speed by 0.02 per purchase."
					},
					["CDR"] = new StatShopEntry
					{
						DisplayName = "Corruption Damage Reduction",
						UnitStat = "CorruptionDamageReduction",
						ModificationType = "Add",
						AttributeCapType = "Uncapped",
						ValuePerPurchase = 0.02f,
						Cost = 500,
						MaxPurchases = 5,
						Notes = "Permanently increases Corruption Damage Reduction by 0.02 per purchase."
					}
				},
				Categories = new Dictionary<string, BuffCategoryDefinition>(StringComparer.OrdinalIgnoreCase)
				{
					["blood_buff"] = new BuffCategoryDefinition
					{
						DisplayName = "Blood Buff",
						Documentation = "Renewable 2-hour blood-buff effects. Default limit: five blood packages.",
						MaxOwnedSlots = 5,
						SlotFreeCost = 250
					},
					["set_bonus"] = new BuffCategoryDefinition
					{
						DisplayName = "Set Bonus",
						Documentation = "Armor set style bonuses or set-derived effects.",
						MaxOwnedSlots = null,
						SlotFreeCost = null
					},
					["potion"] = new BuffCategoryDefinition
					{
						DisplayName = "Potion",
						Documentation = "Potion and brew-style buffs. Safer for multi-slot ownership.",
						MaxOwnedSlots = 3,
						SlotFreeCost = 100
					},
					["elixir"] = new BuffCategoryDefinition
					{
						DisplayName = "Elixir",
						Documentation = "Elixir-style buffs. Bloodcraft compatibility default: one owned elixir slot.",
						MaxOwnedSlots = 1,
						SlotFreeCost = 500
					},
					["misc"] = new BuffCategoryDefinition
					{
						DisplayName = "Misc",
						Documentation = "Fallback category for uncategorized buffs.",
						MaxOwnedSlots = null,
						SlotFreeCost = null
					}
				},
				Buffs = DefaultBuffEntries()
			};
		}

		internal static Dictionary<string, PerkShopEntry> DefaultBuffEntriesForMigration()
		{
			return DefaultBuffEntries();
		}

		private static Dictionary<string, PerkShopEntry> DefaultBuffEntries()
		{
			Dictionary<string, PerkShopEntry> obj = new Dictionary<string, PerkShopEntry>(StringComparer.OrdinalIgnoreCase)
			{
				["sun_immunity"] = new PerkShopEntry
				{
					DisplayName = "Sun Immunity",
					Category = "misc",
					BuffPrefab = 32681348,
					Cost = 100,
					PersistentPurchase = true,
					DurationSeconds = -1,
					KeepVisibleTimerFrozen = false,
					VisibleTimerSeconds = 0,
					PersistThroughDeath = true,
					MutateAppliedBuffLifetime = false,
					PreventDuplicate = true,
					Notes = "Grants complete immunity to sun exposure."
				},
				["potion_of_rage"] = new PerkShopEntry
				{
					DisplayName = "Potion of Rage",
					Category = "potion",
					BuffPrefab = -1591883586,
					Cost = 400,
					PersistentPurchase = true,
					DurationSeconds = 7200,
					KeepVisibleTimerFrozen = false,
					VisibleTimerSeconds = 0,
					PersistThroughDeath = false,
					MutateAppliedBuffLifetime = false,
					PreventDuplicate = true,
					Notes = "Applies AB_Consumable_PhysicalPowerPotion_T02_Buff as a renewable 2-hour owned buff."
				},
				["elixir_of_the_crow"] = new PerkShopEntry
				{
					DisplayName = "Elixir of the Crow",
					Category = "elixir",
					BuffPrefab = -262239794,
					Cost = 750,
					PersistentPurchase = true,
					DurationSeconds = -1,
					KeepVisibleTimerFrozen = false,
					VisibleTimerSeconds = 0,
					PersistThroughDeath = true,
					MutateAppliedBuffLifetime = false,
					PreventDuplicate = true,
					Notes = "Applies AB_Elixir_Crow_T01_Buff as a renewable 2-hour owned buff."
				},
				["empty_layout"] = new PerkShopEntry
				{
					Enabled = false,
					DisplayName = "Empty Buff Layout",
					Category = "misc",
					BuffPrefab = 32681348,
					Cost = 100,
					PersistentPurchase = true,
					Durati

PerkShop.dll

Decompiled 23 minutes 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.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text.Json;
using BepInEx;
using BepInEx.Core.Logging.Interpolation;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using HarmonyLib;
using Il2CppInterop.Runtime;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSystem;
using Il2CppSystem.Collections.Generic;
using Microsoft.CodeAnalysis;
using PerkShop.Models;
using PerkShop.Services;
using PerkShop.Utilities;
using ProjectM;
using ProjectM.Network;
using ProjectM.Scripting;
using ProjectM.Shared;
using Stunlock.Core;
using Stunlock.Network;
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
using UnityEngine;
using VampireCommandFramework;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("yenko2000")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Chat-command V Rising shop that lets players buy configurable buffs.")]
[assembly: AssemblyFileVersion("0.1.2.0")]
[assembly: AssemblyInformationalVersion("0.1.2")]
[assembly: AssemblyProduct("PerkShop")]
[assembly: AssemblyTitle("PerkShop")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/yenko2000/PerkShop")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.1.2.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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]
	[Microsoft.CodeAnalysis.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]
	[Microsoft.CodeAnalysis.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 PerkShop
{
	internal static class Core
	{
		private static bool _initialized;

		private static World? _server;

		private static EntityManager _entityManager;

		private static ServerScriptMapper? _serverScriptMapper;

		private static DebugEventsSystem? _debugEventsSystem;

		private static SystemService? _systemService;

		public static World Server
		{
			get
			{
				object obj = _server;
				if (obj == null)
				{
					obj = GetWorld("Server") ?? throw new Exception("Server world not found.");
					_server = (World?)obj;
				}
				return (World)obj;
			}
		}

		public static EntityManager EntityManager
		{
			get
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_000d: 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_0025: 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_0015: Unknown result type (might be due to invalid IL or missing references)
				if (!(_entityManager == default(EntityManager)))
				{
					return _entityManager;
				}
				return _entityManager = Server.EntityManager;
			}
		}

		public static ServerScriptMapper ServerScriptMapper => _serverScriptMapper ?? (_serverScriptMapper = Server.GetExistingSystemManaged<ServerScriptMapper>());

		public static ServerGameManager ServerGameManager => ServerScriptMapper.GetServerGameManager();

		public static DebugEventsSystem DebugEventsSystem => _debugEventsSystem ?? (_debugEventsSystem = Server.GetExistingSystemManaged<DebugEventsSystem>());

		public static SystemService Systems => _systemService ?? (_systemService = new SystemService(Server));

		public static ManualLogSource Log => Plugin.PluginLog;

		internal static void InitializeAfterLoaded()
		{
			//IL_002b: 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)
			if (!_initialized)
			{
				_server = GetWorld("Server") ?? throw new Exception("Server world not found.");
				_entityManager = _server.EntityManager;
				_serverScriptMapper = _server.GetExistingSystemManaged<ServerScriptMapper>();
				_debugEventsSystem = _server.GetExistingSystemManaged<DebugEventsSystem>();
				_systemService = new SystemService(_server);
				QueryService.Initialize(_systemService);
				CarrierPrefabService.PrepareConfiguredStatCarrierPrefab();
				_initialized = true;
				Log.LogInfo((object)"Core initialized.");
			}
		}

		internal static World? GetWorld(string name)
		{
			Enumerator<World> enumerator = World.s_AllWorlds.GetEnumerator();
			while (enumerator.MoveNext())
			{
				World current = enumerator.Current;
				if (current != null && current.Name == name)
				{
					return current;
				}
			}
			return null;
		}

		public static void LogDebugIfEnabled(string message)
		{
			try
			{
				if (ConfigService.DebugLoggingEnabled)
				{
					Log.LogInfo((object)message);
				}
			}
			catch
			{
			}
		}

		public static void LogException(Exception e, [CallerMemberName] string? caller = null)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Expected O, but got Unknown
			ManualLogSource log = Log;
			bool flag = default(bool);
			BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(4, 3, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(caller);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral("] ");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(e.Message);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral("\n");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(e.StackTrace);
			}
			log.LogError(val);
		}
	}
	internal static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "yenko2000.PerkShop";

		public const string PLUGIN_NAME = "PerkShop";

		public const string PLUGIN_VERSION = "0.1.2";
	}
	[BepInPlugin("yenko2000.PerkShop", "PerkShop", "0.1.2")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class Plugin : BasePlugin
	{
		internal static ManualLogSource PluginLog { get; private set; }

		internal static Harmony Harmony { get; private set; }

		public override void Load()
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Expected O, but got Unknown
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Expected O, but got Unknown
			if (!(Application.productName != "VRisingServer"))
			{
				PluginLog = ((BasePlugin)this).Log;
				ConfigService.Initialize();
				OwnershipService.Initialize();
				PlayerCacheService.Initialize();
				Harmony = new Harmony("yenko2000.PerkShop");
				Harmony.PatchAll(Assembly.GetExecutingAssembly());
				CommandRegistry.RegisterAll();
				ManualLogSource log = ((BasePlugin)this).Log;
				bool flag = default(bool);
				BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(9, 2, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>("PerkShop");
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>("0.1.2");
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" loaded.");
				}
				log.LogInfo(val);
			}
		}

		public override bool Unload()
		{
			OwnershipService.FlushPendingSaves(force: true);
			PlayerCacheService.FlushPendingSaves(force: true);
			CommandRegistry.UnregisterAssembly();
			Harmony harmony = Harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			return true;
		}

		internal static bool HasLoaded()
		{
			World world = Core.GetWorld("Server");
			if (world == null)
			{
				return false;
			}
			PrefabCollectionSystem existingSystemManaged = world.GetExistingSystemManaged<PrefabCollectionSystem>();
			if (existingSystemManaged == null)
			{
				return false;
			}
			return existingSystemManaged.SpawnableNameToPrefabGuidDictionary.Count > 0;
		}
	}
	public static class ECSExtensions
	{
		public unsafe static T Read<T>(this Entity entity) where T : struct
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			ComponentType val = default(ComponentType);
			((ComponentType)(ref val))..ctor(Il2CppType.Of<T>(), (AccessMode)0);
			EntityManager entityManager = Core.EntityManager;
			return Marshal.PtrToStructure<T>(new IntPtr(((EntityManager)(ref entityManager)).GetComponentDataRawRO(entity, val.TypeIndex)));
		}
	}
}
namespace PerkShop.Utilities
{
	internal static class AdminPlayerLookup
	{
		public static bool TryFindUser(string playerRef, out Entity userEntity, out User user, out string error)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: 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_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: 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_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: 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_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: 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_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_016e: Unknown result type (might be due to invalid IL or missing references)
			//IL_020d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0212: Unknown result type (might be due to invalid IL or missing references)
			//IL_0220: Unknown result type (might be due to invalid IL or missing references)
			//IL_0225: Unknown result type (might be due to invalid IL or missing references)
			//IL_022b: Unknown result type (might be due to invalid IL or missing references)
			userEntity = Entity.Null;
			user = default(User);
			error = string.Empty;
			if (string.IsNullOrWhiteSpace(playerRef))
			{
				error = "Usage: specify an online player name or PlatformId.";
				return false;
			}
			EntityManager entityManager = Core.EntityManager;
			EntityQuery val = ((EntityManager)(ref entityManager)).CreateEntityQuery((ComponentType[])(object)new ComponentType[1] { ComponentType.ReadOnly<User>() });
			NativeArray<Entity> val2 = ((EntityQuery)(ref val)).ToEntityArray(AllocatorHandle.op_Implicit((Allocator)2));
			try
			{
				Enumerator<Entity> enumerator;
				if (ulong.TryParse(playerRef.Trim(), out var result))
				{
					enumerator = val2.GetEnumerator();
					while (enumerator.MoveNext())
					{
						Entity current = enumerator.Current;
						User componentData = ((EntityManager)(ref entityManager)).GetComponentData<User>(current);
						if (componentData.PlatformId == result)
						{
							userEntity = current;
							user = componentData;
							PlayerCacheService.Remember(componentData);
							return true;
						}
					}
					error = $"No online player found with PlatformId {result}.";
					return false;
				}
				string text = playerRef.Trim();
				List<(Entity, User)> list = new List<(Entity, User)>();
				List<(Entity, User)> list2 = new List<(Entity, User)>();
				enumerator = val2.GetEnumerator();
				while (enumerator.MoveNext())
				{
					Entity current2 = enumerator.Current;
					User componentData2 = ((EntityManager)(ref entityManager)).GetComponentData<User>(current2);
					string text2 = ((object)(FixedString64Bytes)(ref componentData2.CharacterName)).ToString();
					if (string.Equals(text2, text, StringComparison.OrdinalIgnoreCase))
					{
						list.Add((current2, componentData2));
					}
					else if (text2.IndexOf(text, StringComparison.OrdinalIgnoreCase) >= 0)
					{
						list2.Add((current2, componentData2));
					}
				}
				List<(Entity, User)> list3 = ((list.Count > 0) ? list : list2);
				if (list3.Count == 0)
				{
					error = "No online player found matching '" + text + "'.";
					return false;
				}
				if (list3.Count > 1)
				{
					error = "Multiple players match that query: " + string.Join(", ", list3.Select<(Entity, User), string>(((Entity entity, User user) x) => ((object)(FixedString64Bytes)(ref x.user.CharacterName)).ToString()));
					return false;
				}
				userEntity = list3[0].Item1;
				user = list3[0].Item2;
				PlayerCacheService.Remember(user);
				return true;
			}
			finally
			{
				if (val2.IsCreated)
				{
					val2.Dispose();
				}
			}
		}
	}
	internal static class InventoryHelper
	{
		public static int Count(Entity characterEntity, PrefabGUID itemPrefab)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: 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_0056: Unknown result type (might be due to invalid IL or missing references)
			EntityManager entityManager = Core.EntityManager;
			Entity val = default(Entity);
			if (!InventoryUtilities.TryGetInventoryEntity<EntityManager>(entityManager, characterEntity, ref val, 0))
			{
				return 0;
			}
			if (!((EntityManager)(ref entityManager)).HasComponent<InventoryBuffer>(val))
			{
				return 0;
			}
			DynamicBuffer<InventoryBuffer> buffer = ((EntityManager)(ref entityManager)).GetBuffer<InventoryBuffer>(val, false);
			int num = 0;
			for (int i = 0; i < buffer.Length; i++)
			{
				InventoryBuffer val2 = buffer[i];
				if (((PrefabGUID)(ref val2.ItemType)).GuidHash == ((PrefabGUID)(ref itemPrefab)).GuidHash)
				{
					num += buffer[i].Amount;
				}
			}
			return num;
		}

		public static bool TryRemove(Entity characterEntity, PrefabGUID itemPrefab, int amount)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: 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_0059: 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_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			EntityManager entityManager = Core.EntityManager;
			Entity val = default(Entity);
			if (!InventoryUtilities.TryGetInventoryEntity<EntityManager>(entityManager, characterEntity, ref val, 0))
			{
				return false;
			}
			if (!((EntityManager)(ref entityManager)).HasComponent<InventoryBuffer>(val))
			{
				return false;
			}
			DynamicBuffer<InventoryBuffer> buffer = ((EntityManager)(ref entityManager)).GetBuffer<InventoryBuffer>(val, false);
			int num = amount;
			int num2 = buffer.Length - 1;
			while (num2 >= 0 && num > 0)
			{
				InventoryBuffer val2 = buffer[num2];
				if (((PrefabGUID)(ref val2.ItemType)).GuidHash == ((PrefabGUID)(ref itemPrefab)).GuidHash && val2.Amount > 0)
				{
					int num3 = math.min(val2.Amount, num);
					val2.Amount -= num3;
					num -= num3;
					if (val2.Amount <= 0)
					{
						val2.ItemType = new PrefabGUID(0);
						val2.Amount = 0;
					}
					buffer[num2] = val2;
				}
				num2--;
			}
			return num == 0;
		}
	}
	internal static class PlayerStateHelper
	{
		private static readonly PrefabGUID[] CombatBuffs = (PrefabGUID[])(object)new PrefabGUID[3]
		{
			new PrefabGUID(581443919),
			new PrefabGUID(697095869),
			new PrefabGUID(698151145)
		};

		public static bool Exists(Entity entity)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			if (entity != Entity.Null)
			{
				EntityManager entityManager = Core.EntityManager;
				return ((EntityManager)(ref entityManager)).Exists(entity);
			}
			return false;
		}

		public static bool IsInCombat(Entity characterEntity)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: 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)
			EntityManager entityManager = Core.EntityManager;
			if (!Exists(characterEntity))
			{
				return false;
			}
			PrefabGUID[] combatBuffs = CombatBuffs;
			foreach (PrefabGUID val in combatBuffs)
			{
				if (BuffUtility.HasBuff<EntityManager>(entityManager, characterEntity, PrefabIdentifier.op_Implicit(val)))
				{
					return true;
				}
			}
			return false;
		}
	}
}
namespace PerkShop.Services
{
	internal static class AccessService
	{
		public static bool CanAccessPerkShop(Entity userEntity, Action<string> reply)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			ShopConfigRoot shop = ConfigService.Shop;
			if (!shop.EnableBuffWhitelist)
			{
				return true;
			}
			if (!TryGetPlatformId(userEntity, out var platformId))
			{
				reply("User component not ready.");
				return false;
			}
			if (shop.BuffWhitelistPlatformIds.Contains(platformId))
			{
				return true;
			}
			reply("You are not whitelisted for the perk shop.");
			return false;
		}

		public static bool CanAccessStatShop(Entity userEntity, Action<string> reply)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			ShopConfigRoot shop = ConfigService.Shop;
			if (!shop.EnableStatWhitelist)
			{
				return true;
			}
			if (!TryGetPlatformId(userEntity, out var platformId))
			{
				reply("User component not ready.");
				return false;
			}
			if (shop.StatWhitelistPlatformIds.Contains(platformId))
			{
				return true;
			}
			reply("You are not whitelisted for the stat shop.");
			return false;
		}

		private static bool TryGetPlatformId(Entity userEntity, out ulong platformId)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//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_0029: 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_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: 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)
			platformId = 0uL;
			if (!(userEntity == Entity.Null))
			{
				EntityManager entityManager = Core.EntityManager;
				if (((EntityManager)(ref entityManager)).Exists(userEntity))
				{
					entityManager = Core.EntityManager;
					if (((EntityManager)(ref entityManager)).HasComponent<User>(userEntity))
					{
						entityManager = Core.EntityManager;
						User componentData = ((EntityManager)(ref entityManager)).GetComponentData<User>(userEntity);
						platformId = componentData.PlatformId;
						return platformId != 0;
					}
				}
			}
			return false;
		}
	}
	internal static class BuffService
	{
		private sealed class PendingPermanentBuffInfo
		{
			public DateTime CreatedAtUtc { get; init; } = DateTime.UtcNow;


			public int DurationSeconds { get; init; }

			public bool PersistThroughDeath { get; init; }

			public bool PreserveVanillaCleanup { get; init; }
		}

		private static readonly Dictionary<Entity, PendingPermanentBuffInfo> PendingPermanentBuffs = new Dictionary<Entity, PendingPermanentBuffInfo>();

		private static readonly TimeSpan PendingBuffTimeout = TimeSpan.FromSeconds(30.0);

		private static readonly HashSet<int> PreparedPermanentBuffPrefabs = new HashSet<int>();

		public static int PendingPermanentBuffCount => PendingPermanentBuffs.Count;

		public static bool IsRenewableTimedEntry(ShopConfigRoot config, PerkShopEntry entry)
		{
			return ConfigService.IsRenewableTimedCategory(config, entry.Category);
		}

		public static int ResolveDurationSeconds(ShopConfigRoot config, PerkShopEntry entry)
		{
			return ConfigService.ResolveBuffDuration(config, entry);
		}

		public static bool ResolvePersistThroughDeath(ShopConfigRoot config, PerkShopEntry entry)
		{
			return ConfigService.ResolveBuffPersistThroughDeath(config, entry);
		}

		public static bool PreserveVanillaCleanup(ShopConfigRoot config, PerkShopEntry entry)
		{
			return ConfigService.PreserveVanillaBuffCleanup(config, entry);
		}

		public static void ResetRuntimeStateAfterConfigReload()
		{
			PendingPermanentBuffs.Clear();
			PreparedPermanentBuffPrefabs.Clear();
		}

		public static void ProcessPendingPermanentBuffsDuringSpawn(EntityQuery spawnQuery)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: 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)
			if (PendingPermanentBuffs.Count == 0)
			{
				return;
			}
			NativeArray<Entity> val = ((EntityQuery)(ref spawnQuery)).ToEntityArray(AllocatorHandle.op_Implicit((Allocator)2));
			try
			{
				for (int i = 0; i < val.Length; i++)
				{
					Entity val2 = val[i];
					if (PendingPermanentBuffs.TryGetValue(val2, out PendingPermanentBuffInfo value))
					{
						TrySetLifetime(val2, value.DurationSeconds, value.PersistThroughDeath, value.PreserveVanillaCleanup);
						PendingPermanentBuffs.Remove(val2);
					}
				}
			}
			finally
			{
				if (val.IsCreated)
				{
					val.Dispose();
				}
			}
		}

		private static void MarkPendingLifetimeMutation(Entity buffEntity, int durationSeconds, bool persistThroughDeath, bool preserveVanillaCleanup)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			if (!(buffEntity == Entity.Null))
			{
				PendingPermanentBuffs[buffEntity] = new PendingPermanentBuffInfo
				{
					DurationSeconds = durationSeconds,
					PersistThroughDeath = persistThroughDeath,
					PreserveVanillaCleanup = preserveVanillaCleanup
				};
			}
		}

		public static void ProcessPendingPermanentBuffs()
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: 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_004f: 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_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Expected O, but got Unknown
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			if (PendingPermanentBuffs.Count == 0)
			{
				return;
			}
			EntityManager entityManager = Core.EntityManager;
			KeyValuePair<Entity, PendingPermanentBuffInfo>[] array = PendingPermanentBuffs.ToArray();
			bool flag = default(bool);
			for (int i = 0; i < array.Length; i++)
			{
				KeyValuePair<Entity, PendingPermanentBuffInfo> keyValuePair = array[i];
				Entity key = keyValuePair.Key;
				PendingPermanentBuffInfo value = keyValuePair.Value;
				if (key == Entity.Null || !((EntityManager)(ref entityManager)).Exists(key))
				{
					PendingPermanentBuffs.Remove(key);
				}
				else if (((EntityManager)(ref entityManager)).HasComponent<SpawnTag>(key))
				{
					if (DateTime.UtcNow - value.CreatedAtUtc > PendingBuffTimeout)
					{
						PendingPermanentBuffs.Remove(key);
						ManualLogSource log = Core.Log;
						BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(67, 2, ref flag);
						if (flag)
						{
							((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[BuffService] Timed out waiting to finalize permanent buff entity:");
							((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(key.Index);
							((BepInExLogInterpolatedStringHandler)val).AppendLiteral(":");
							((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(key.Version);
						}
						log.LogWarning(val);
					}
				}
				else
				{
					TrySetLifetime(key, value.DurationSeconds, value.PersistThroughDeath, value.PreserveVanillaCleanup);
					PendingPermanentBuffs.Remove(key);
				}
			}
		}

		private static void PreparePermanentBuffPrefab(PrefabGUID buffPrefab, int durationSeconds, bool persistThroughDeath)
		{
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Expected O, but got Unknown
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: 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_0041: 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_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: 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)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			if (durationSeconds >= 0 || PreparedPermanentBuffPrefabs.Contains(((PrefabGUID)(ref buffPrefab)).GuidHash))
			{
				return;
			}
			try
			{
				Entity val = default(Entity);
				if (!Core.Systems.PrefabCollectionSystem._PrefabGuidToEntityMap.TryGetValue(buffPrefab, ref val))
				{
					return;
				}
				EntityManager entityManager = Core.EntityManager;
				if (!(val == Entity.Null) && ((EntityManager)(ref entityManager)).Exists(val))
				{
					if (((EntityManager)(ref entityManager)).HasComponent<LifeTime>(val))
					{
						((EntityManager)(ref entityManager)).RemoveComponent<LifeTime>(val);
					}
					if (((EntityManager)(ref entityManager)).HasComponent<RemoveBuffOnGameplayEvent>(val))
					{
						((EntityManager)(ref entityManager)).RemoveComponent<RemoveBuffOnGameplayEvent>(val);
					}
					if (((EntityManager)(ref entityManager)).HasComponent<RemoveBuffOnGameplayEventEntry>(val))
					{
						((EntityManager)(ref entityManager)).RemoveComponent<RemoveBuffOnGameplayEventEntry>(val);
					}
					if (persistThroughDeath && !((EntityManager)(ref entityManager)).HasComponent<Buff_Persists_Through_Death>(val))
					{
						((EntityManager)(ref entityManager)).AddComponent<Buff_Persists_Through_Death>(val);
					}
					PreparedPermanentBuffPrefabs.Add(((PrefabGUID)(ref buffPrefab)).GuidHash);
					Core.LogDebugIfEnabled($"[BuffService] Prepared permanent buff prefab {((PrefabGUID)(ref buffPrefab)).GuidHash} with no LifeTime.");
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = Core.Log;
				bool flag = default(bool);
				BepInExWarningLogInterpolatedStringHandler val2 = new BepInExWarningLogInterpolatedStringHandler(56, 2, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("[BuffService] Failed to prepare permanent buff prefab ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<int>(((PrefabGUID)(ref buffPrefab)).GuidHash);
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(": ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(ex.Message);
				}
				log.LogWarning(val2);
			}
		}

		public static bool HasBuff(Entity characterEntity, PrefabGUID buffPrefab)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			Entity val = default(Entity);
			return BuffUtility.TryGetBuff<EntityManager>(Core.EntityManager, characterEntity, PrefabIdentifier.op_Implicit(buffPrefab), ref val);
		}

		public static bool ApplyPurchasedBuff(Entity userEntity, Entity characterEntity, PrefabGUID buffPrefab, bool preventDuplicate, bool allowMutation, bool mutateLifetime, int durationSeconds, bool persistThroughDeath, bool keepVisibleTimerFrozen = false, int visibleTimerSeconds = 0, bool preserveVanillaCleanup = false)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: 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_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: 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_0052: 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_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: 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_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: 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_001f: 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)
			Entity buffEntity = default(Entity);
			if (BuffUtility.TryGetBuff<EntityManager>(Core.EntityManager, characterEntity, PrefabIdentifier.op_Implicit(buffPrefab), ref buffEntity))
			{
				if (allowMutation && mutateLifetime)
				{
					MarkPendingLifetimeMutation(buffEntity, durationSeconds, persistThroughDeath, preserveVanillaCleanup);
				}
				return true;
			}
			if (preventDuplicate && HasBuff(characterEntity, buffPrefab))
			{
				return false;
			}
			FromCharacter val = default(FromCharacter);
			val.User = userEntity;
			val.Character = characterEntity;
			FromCharacter val2 = val;
			ApplyBuffDebugEvent val3 = default(ApplyBuffDebugEvent);
			val3.BuffPrefabGUID = buffPrefab;
			ApplyBuffDebugEvent val4 = val3;
			Core.DebugEventsSystem.ApplyBuff(val2, val4);
			Entity buffEntity2 = default(Entity);
			if (!BuffUtility.TryGetBuff<EntityManager>(Core.EntityManager, characterEntity, PrefabIdentifier.op_Implicit(buffPrefab), ref buffEntity2))
			{
				return false;
			}
			if (allowMutation && mutateLifetime)
			{
				MarkPendingLifetimeMutation(buffEntity2, durationSeconds, persistThroughDeath, preserveVanillaCleanup);
			}
			return true;
		}

		public static bool RemoveBuff(Entity characterEntity, PrefabGUID buffPrefab)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: 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_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_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			Entity val = default(Entity);
			if (!BuffUtility.TryGetBuff<EntityManager>(Core.EntityManager, characterEntity, PrefabIdentifier.op_Implicit(buffPrefab), ref val))
			{
				return false;
			}
			PendingPermanentBuffs.Remove(val);
			DestroyUtility.Destroy(Core.EntityManager, val, (DestroyDebugReason)13, (string)null, 0);
			EntityManager entityManager = Core.EntityManager;
			if (((EntityManager)(ref entityManager)).Exists(val))
			{
				entityManager = Core.EntityManager;
				if (!((EntityManager)(ref entityManager)).HasComponent<DestroyTag>(val))
				{
					entityManager = Core.EntityManager;
					((EntityManager)(ref entityManager)).AddComponent<DestroyTag>(val);
				}
			}
			return true;
		}

		public static void MakeBuffPermanent(Entity buffEntity, bool persistThroughDeath = true)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			TrySetLifetime(buffEntity, -1, persistThroughDeath, preserveVanillaCleanup: false);
		}

		public static int RemoveConfiguredCategoryBuffs(Entity characterEntity, string category)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			string category2 = category;
			if (characterEntity == Entity.Null || string.IsNullOrWhiteSpace(category2))
			{
				return 0;
			}
			HashSet<int> hashSet = (from entry in ConfigService.Shop.Buffs.Values
				where entry != null && entry.BuffPrefab != 0 && string.Equals(entry.Category, category2, StringComparison.OrdinalIgnoreCase)
				select entry.BuffPrefab).ToHashSet();
			if (hashSet.Count == 0)
			{
				return 0;
			}
			int num = 0;
			foreach (int item in hashSet)
			{
				if (RemoveBuff(characterEntity, new PrefabGUID(item)))
				{
					num++;
				}
			}
			return num + RemoveLiveBuffsByPrefabSet(characterEntity, hashSet);
		}

		private static int RemoveLiveBuffsByPrefabSet(Entity characterEntity, HashSet<int> prefabSet)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Expected O, but got Unknown
			//IL_0019: 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_0024: 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_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: 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_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: 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_0070: 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_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: 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_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			if (characterEntity == Entity.Null || prefabSet.Count == 0)
			{
				return 0;
			}
			NativeArray<Entity> val = default(NativeArray<Entity>);
			try
			{
				EntityManager entityManager = Core.EntityManager;
				EntityQuery val2 = ((EntityManager)(ref entityManager)).CreateEntityQuery((ComponentType[])(object)new ComponentType[2]
				{
					ComponentType.ReadOnly<Buff>(),
					ComponentType.ReadOnly<PrefabGUID>()
				});
				val = ((EntityQuery)(ref val2)).ToEntityArray(AllocatorHandle.op_Implicit((Allocator)2));
				int num = 0;
				for (int i = 0; i < val.Length; i++)
				{
					Entity val3 = val[i];
					if (!((EntityManager)(ref entityManager)).Exists(val3) || !((EntityManager)(ref entityManager)).HasComponent<Buff>(val3) || !((EntityManager)(ref entityManager)).HasComponent<PrefabGUID>(val3) || ((EntityManager)(ref entityManager)).GetComponentData<Buff>(val3).Target != characterEntity)
					{
						continue;
					}
					PrefabGUID componentData = ((EntityManager)(ref entityManager)).GetComponentData<PrefabGUID>(val3);
					if (prefabSet.Contains(((PrefabGUID)(ref componentData)).GuidHash))
					{
						PendingPermanentBuffs.Remove(val3);
						DestroyUtility.Destroy(entityManager, val3, (DestroyDebugReason)13, (string)null, 0);
						if (((EntityManager)(ref entityManager)).Exists(val3) && !((EntityManager)(ref entityManager)).HasComponent<DestroyTag>(val3))
						{
							((EntityManager)(ref entityManager)).AddComponent<DestroyTag>(val3);
						}
						num++;
					}
				}
				return num;
			}
			catch (Exception ex)
			{
				ManualLogSource log = Core.Log;
				bool flag = default(bool);
				BepInExWarningLogInterpolatedStringHandler val4 = new BepInExWarningLogInterpolatedStringHandler(63, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val4).AppendLiteral("[BuffService] Failed scanning live category buffs for removal: ");
					((BepInExLogInterpolatedStringHandler)val4).AppendFormatted<string>(ex.Message);
				}
				log.LogWarning(val4);
				return 0;
			}
			finally
			{
				if (val.IsCreated)
				{
					val.Dispose();
				}
			}
		}

		public static bool ForceOwnedBuffLifetime(Entity characterEntity, PrefabGUID buffPrefab, int durationSeconds, bool persistThroughDeath, bool keepVisibleTimerFrozen = false, int visibleTimerSeconds = 0, bool preserveVanillaCleanup = false)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			Entity buffEntity = default(Entity);
			if (!BuffUtility.TryGetBuff<EntityManager>(Core.EntityManager, characterEntity, PrefabIdentifier.op_Implicit(buffPrefab), ref buffEntity))
			{
				return false;
			}
			MarkPendingLifetimeMutation(buffEntity, durationSeconds, persistThroughDeath, preserveVanillaCleanup);
			return true;
		}

		private static void TrySetLifetime(Entity buffEntity, int durationSeconds, bool persistThroughDeath, bool preserveVanillaCleanup)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: 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)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			EntityManager entityManager = Core.EntityManager;
			if (buffEntity == Entity.Null || !((EntityManager)(ref entityManager)).Exists(buffEntity))
			{
				return;
			}
			if (persistThroughDeath && !((EntityManager)(ref entityManager)).HasComponent<Buff_Persists_Through_Death>(buffEntity))
			{
				((EntityManager)(ref entityManager)).AddComponent<Buff_Persists_Through_Death>(buffEntity);
			}
			if (!preserveVanillaCleanup)
			{
				if (((EntityManager)(ref entityManager)).HasComponent<RemoveBuffOnGameplayEvent>(buffEntity))
				{
					((EntityManager)(ref entityManager)).RemoveComponent<RemoveBuffOnGameplayEvent>(buffEntity);
				}
				if (((EntityManager)(ref entityManager)).HasComponent<RemoveBuffOnGameplayEventEntry>(buffEntity))
				{
					((EntityManager)(ref entityManager)).RemoveComponent<RemoveBuffOnGameplayEventEntry>(buffEntity);
				}
			}
			if (durationSeconds < 0)
			{
				if (((EntityManager)(ref entityManager)).HasComponent<LifeTime>(buffEntity))
				{
					((EntityManager)(ref entityManager)).RemoveComponent<LifeTime>(buffEntity);
				}
			}
			else if (durationSeconds > 0)
			{
				if (!((EntityManager)(ref entityManager)).HasComponent<LifeTime>(buffEntity))
				{
					((EntityManager)(ref entityManager)).AddComponent<LifeTime>(buffEntity);
				}
				((EntityManager)(ref entityManager)).SetComponentData<LifeTime>(buffEntity, new LifeTime
				{
					Duration = durationSeconds,
					EndAction = (LifeTimeEndAction)2
				});
			}
		}
	}
	internal static class CarrierPrefabService
	{
		private const string ScriptSpawnTypeName = "ProjectM.Scripting.ScriptSpawn";

		private static int _preparedCarrierGuid;

		private static Type? _scriptSpawnManagedType;

		private static Type? _scriptSpawnIl2CppType;

		internal static void Reset()
		{
			_preparedCarrierGuid = 0;
		}

		internal static bool PrepareConfiguredStatCarrierPrefab()
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Expected O, but got Unknown
			//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)
			//IL_006c: 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_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Expected O, but got Unknown
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!StatService.IsCarrierConfigured(out PrefabGUID carrier, out string error))
				{
					ManualLogSource log = Core.Log;
					bool flag = default(bool);
					BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(23, 1, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[CarrierPrefabService] ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(error);
					}
					log.LogWarning(val);
					return false;
				}
				if (_preparedCarrierGuid == ((PrefabGUID)(ref carrier)).GuidHash)
				{
					return true;
				}
				Entity val2 = default(Entity);
				if (!Core.Systems.PrefabCollectionSystem._PrefabGuidToEntityMap.TryGetValue(carrier, ref val2))
				{
					ManualLogSource log2 = Core.Log;
					bool flag2 = default(bool);
					BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(102, 1, ref flag2);
					if (flag2)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[CarrierPrefabService] Could not find stat carrier prefab entity for ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(((PrefabGUID)(ref carrier)).GuidHash);
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral(". Stats will not spawn correctly.");
					}
					log2.LogWarning(val);
					return false;
				}
				EntityManager entityManager = Core.EntityManager;
				if (!TryEnsureScriptSpawn(entityManager, val2))
				{
					Core.Log.LogWarning((object)"[CarrierPrefabService] Could not resolve/add ScriptSpawn dynamically. Immediate stat-buffer fallback will still be attempted, but HUD/stat sync may be less reliable.");
				}
				if (!((EntityManager)(ref entityManager)).HasBuffer<ModifyUnitStatBuff_DOTS>(val2))
				{
					((EntityManager)(ref entityManager)).AddBuffer<ModifyUnitStatBuff_DOTS>(val2);
				}
				else
				{
					((EntityManager)(ref entityManager)).GetBuffer<ModifyUnitStatBuff_DOTS>(val2, false).Clear();
				}
				_preparedCarrierGuid = ((PrefabGUID)(ref carrier)).GuidHash;
				Core.LogDebugIfEnabled($"[CarrierPrefabService] Prepared stat carrier prefab {((PrefabGUID)(ref carrier)).GuidHash} with dynamic ScriptSpawn + ModifyUnitStatBuff_DOTS.");
				return true;
			}
			catch (Exception e)
			{
				Core.LogException(e, "PrepareConfiguredStatCarrierPrefab");
				return false;
			}
		}

		private static bool TryEnsureScriptSpawn(EntityManager em, Entity prefabEntity)
		{
			//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)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			Type val = ResolveScriptSpawnIl2CppType();
			if (val == (Type)null)
			{
				return false;
			}
			ComponentType val2 = default(ComponentType);
			((ComponentType)(ref val2))..ctor(val, (AccessMode)0);
			if (!((EntityManager)(ref em)).HasComponent(prefabEntity, val2))
			{
				((EntityManager)(ref em)).AddComponent(prefabEntity, val2);
			}
			return true;
		}

		private static Type? ResolveScriptSpawnIl2CppType()
		{
			if (_scriptSpawnIl2CppType != (Type)null)
			{
				return _scriptSpawnIl2CppType;
			}
			if ((object)_scriptSpawnManagedType == null)
			{
				_scriptSpawnManagedType = AccessTools.TypeByName("ProjectM.Scripting.ScriptSpawn") ?? AccessTools.TypeByName("ProjectM.Gameplay.Scripting.ScriptSpawn") ?? AccessTools.TypeByName("ProjectM.ScriptSpawn") ?? AccessTools.TypeByName("ScriptSpawn");
			}
			if (_scriptSpawnManagedType == null)
			{
				return null;
			}
			MethodInfo methodInfo = typeof(Il2CppType).GetMethods(BindingFlags.Static | BindingFlags.Public).FirstOrDefault((MethodInfo method) => method.Name == "Of" && method.IsGenericMethodDefinition && method.GetParameters().Length == 0);
			if (methodInfo == null)
			{
				return null;
			}
			object? obj = methodInfo.MakeGenericMethod(_scriptSpawnManagedType).Invoke(null, null);
			_scriptSpawnIl2CppType = (Type?)((obj is Type) ? obj : null);
			return _scriptSpawnIl2CppType;
		}
	}
	internal static class ConfigMigrationService
	{
		internal static bool TryMigrate(ShopConfigRoot cfg, out ShopConfigRoot migrated)
		{
			migrated = cfg ?? ConfigService.DefaultConfigForMigration();
			bool flag = false;
			if (migrated.ConfigVersion < 2)
			{
				flag |= FixMovementSpeed(migrated);
				flag |= ClampOldOvertunedDefaults(migrated);
			}
			if (migrated.ConfigVersion < 4)
			{
				flag |= DisableVisibleTimerResetLoop(migrated);
				flag |= EnsureDefaultBloodBuffs(migrated);
			}
			if (migrated.ConfigVersion < 6)
			{
				flag |= MigrateDefaultStatCarrier(migrated);
			}
			if (migrated.ConfigVersion < 7)
			{
				flag |= FixDefaultStatNotes(migrated);
			}
			if (migrated.ConfigVersion < 8)
			{
				flag |= ApplyConfigVersion8RuntimeDefaults(migrated);
			}
			if (migrated.ConfigVersion < 11)
			{
				flag |= ApplyConfigVersion11CompatibilityDefaults(migrated);
			}
			if (migrated.ConfigVersion < 12)
			{
				flag |= ApplyConfigVersion12RenewableTimedBuffDefaults(migrated);
			}
			if (migrated.ConfigVersion < 13)
			{
				flag |= ApplyConfigVersion13AllStatsAndBloodSlots(migrated);
			}
			if (migrated.ConfigVersion < 14)
			{
				flag |= ApplyConfigVersion14ShortKeys(migrated);
			}
			if (migrated.ConfigVersion < 14)
			{
				migrated.ConfigVersion = 14;
				flag = true;
			}
			return flag;
		}

		private static bool MigrateDefaultStatCarrier(ShopConfigRoot cfg)
		{
			if (cfg.StatCarrierBuffPrefab != -1469378405)
			{
				return false;
			}
			cfg.StatCarrierBuffPrefab = -809648681;
			return true;
		}

		private static bool FixMovementSpeed(ShopConfigRoot cfg)
		{
			if (cfg.Stats == null || !cfg.Stats.TryGetValue("movement_speed", out StatShopEntry value) || value == null)
			{
				return false;
			}
			if (!StatDefinitionService.IsDangerousModifierCombo(value))
			{
				return false;
			}
			value.ModificationType = "MultiplyBaseAdd";
			value.Notes = "Permanently increases Movement Speed by 0.05 per purchase. Uses MultiplyBaseAdd for HUD/stat compatibility.";
			return true;
		}

		private static bool ClampOldOvertunedDefaults(ShopConfigRoot cfg)
		{
			if (cfg.Stats == null)
			{
				return false;
			}
			return false | SetIfOldValue(cfg, "primary_attack_speed", 0.05f, 0.02f) | SetIfOldValue(cfg, "physical_resistance", 0.05f, 0.02f) | SetIfOldValue(cfg, "spell_resistance", 0.05f, 0.02f) | SetIfOldValue(cfg, "healing_received", 0.05f, 0.03f) | SetIfOldValue(cfg, "damage_reduction", 0.02f, 0.01f) | SetIfOldValue(cfg, "spell_cooldown_recovery_rate", 0.05f, 0.02f) | SetIfOldValue(cfg, "weapon_cooldown_recovery_rate", 0.05f, 0.02f) | SetIfOldValue(cfg, "ability_attack_speed", 0.05f, 0.02f) | SetIfOldValue(cfg, "corruption_damage_reduction", 0.05f, 0.02f) | SetIfOldValue(cfg, "physical_power", 1f, 2f) | SetIfOldValue(cfg, "primary_life_leech", 0.02f, 0.03f) | SetIfOldValue(cfg, "physical_critical_strike_damage", 0.05f, 0.1f) | SetIfOldValue(cfg, "spell_critical_strike_damage", 0.05f, 0.1f) | SetIfOldValue(cfg, "reduced_blood_drain", 0.05f, 0.1f);
		}

		private static bool SetIfOldValue(ShopConfigRoot cfg, string key, float oldValue, float newValue)
		{
			if (!cfg.Stats.TryGetValue(key, out StatShopEntry value) || value == null)
			{
				return false;
			}
			if (Math.Abs(value.ValuePerPurchase - oldValue) > 0.0001f)
			{
				return false;
			}
			value.ValuePerPurchase = newValue;
			return true;
		}

		private static bool DisableVisibleTimerResetLoop(ShopConfigRoot cfg)
		{
			if (cfg.Buffs == null)
			{
				return false;
			}
			bool result = false;
			foreach (var (_, perkShopEntry2) in cfg.Buffs)
			{
				if (perkShopEntry2 != null && (perkShopEntry2.KeepVisibleTimerFrozen || perkShopEntry2.VisibleTimerSeconds != 0 || perkShopEntry2.DurationSeconds != -1))
				{
					perkShopEntry2.KeepVisibleTimerFrozen = false;
					perkShopEntry2.VisibleTimerSeconds = 0;
					perkShopEntry2.DurationSeconds = -1;
					perkShopEntry2.MutateAppliedBuffLifetime = true;
					perkShopEntry2.PersistThroughDeath = true;
					result = true;
				}
			}
			return result;
		}

		private static bool EnsureDefaultBloodBuffs(ShopConfigRoot cfg)
		{
			if (cfg.Buffs == null)
			{
				Dictionary<string, PerkShopEntry> dictionary2 = (cfg.Buffs = new Dictionary<string, PerkShopEntry>(StringComparer.OrdinalIgnoreCase));
			}
			bool result = false;
			foreach (var (key, perkShopEntry2) in ConfigService.DefaultBuffEntriesForMigration())
			{
				if (perkShopEntry2 != null && string.Equals(perkShopEntry2.Category, "blood_buff", StringComparison.OrdinalIgnoreCase) && !cfg.Buffs.ContainsKey(key))
				{
					cfg.Buffs[key] = perkShopEntry2;
					result = true;
				}
			}
			return result;
		}

		private static bool FixDefaultStatNotes(ShopConfigRoot cfg)
		{
			if (cfg.Stats == null)
			{
				return false;
			}
			bool result = false;
			foreach (var (key, statShopEntry2) in cfg.Stats)
			{
				if (statShopEntry2 != null && StatDefinitionService.TryGetDefaultStatNote(key, statShopEntry2.ValuePerPurchase, statShopEntry2.UnitStat, out string note) && !string.Equals(statShopEntry2.Notes, note, StringComparison.Ordinal) && (string.IsNullOrWhiteSpace(statShopEntry2.Notes) || statShopEntry2.Notes.StartsWith("Permanently increases ", StringComparison.OrdinalIgnoreCase) || string.Equals(statShopEntry2.Notes, "Permanent stat purchase.", StringComparison.OrdinalIgnoreCase)))
				{
					statShopEntry2.Notes = note;
					result = true;
				}
			}
			return result;
		}

		private static bool ApplyConfigVersion8RuntimeDefaults(ShopConfigRoot cfg)
		{
			bool result = false;
			if (cfg.ConfigFileCheckIntervalSeconds <= 0f)
			{
				cfg.ConfigFileCheckIntervalSeconds = 5f;
				result = true;
			}
			if (cfg.PlayerCacheSaveDebounceSeconds <= 0f)
			{
				cfg.PlayerCacheSaveDebounceSeconds = 30f;
				result = true;
			}
			_ = cfg.AutoDetectConfigChanges;
			return result;
		}

		private static bool ApplyConfigVersion11CompatibilityDefaults(ShopConfigRoot cfg)
		{
			bool result = false;
			if (cfg.UseClientAttributeStatAliases)
			{
				cfg.UseClientAttributeStatAliases = false;
				result = true;
			}
			_ = cfg.EnableExperimentalBloodBuffs;
			return result;
		}

		private static bool ApplyConfigVersion12RenewableTimedBuffDefaults(ShopConfigRoot cfg)
		{
			bool result = false;
			if (!cfg.UseRenewableTimedBuffs)
			{
				cfg.UseRenewableTimedBuffs = true;
				result = true;
			}
			if (cfg.RenewableTimedBuffSeconds < 60)
			{
				cfg.RenewableTimedBuffSeconds = 7200;
				result = true;
			}
			if (cfg.RenewableTimedBuffCategories == null)
			{
				List<string> list2 = (cfg.RenewableTimedBuffCategories = new List<string>());
			}
			string[] array = new string[3] { "potion", "elixir", "blood_buff" };
			foreach (string text in array)
			{
				if (!cfg.RenewableTimedBuffCategories.Contains<string>(text, StringComparer.OrdinalIgnoreCase))
				{
					cfg.RenewableTimedBuffCategories.Add(text);
					result = true;
				}
			}
			if (!cfg.EnableExperimentalBloodBuffs)
			{
				cfg.EnableExperimentalBloodBuffs = true;
				result = true;
			}
			if (cfg.Buffs != null)
			{
				foreach (var (_, perkShopEntry2) in cfg.Buffs)
				{
					if (perkShopEntry2 != null && (string.Equals(perkShopEntry2.Category, "potion", StringComparison.OrdinalIgnoreCase) || string.Equals(perkShopEntry2.Category, "elixir", StringComparison.OrdinalIgnoreCase) || string.Equals(perkShopEntry2.Category, "blood_buff", StringComparison.OrdinalIgnoreCase)))
					{
						if (perkShopEntry2.DurationSeconds != cfg.RenewableTimedBuffSeconds)
						{
							perkShopEntry2.DurationSeconds = cfg.RenewableTimedBuffSeconds;
							result = true;
						}
						if (perkShopEntry2.KeepVisibleTimerFrozen || perkShopEntry2.VisibleTimerSeconds != 0)
						{
							perkShopEntry2.KeepVisibleTimerFrozen = false;
							perkShopEntry2.VisibleTimerSeconds = 0;
							result = true;
						}
						if (perkShopEntry2.PersistThroughDeath)
						{
							perkShopEntry2.PersistThroughDeath = false;
							result = true;
						}
						if (!perkShopEntry2.MutateAppliedBuffLifetime)
						{
							perkShopEntry2.MutateAppliedBuffLifetime = true;
							result = true;
						}
					}
				}
			}
			return result;
		}

		private static bool ApplyConfigVersion13AllStatsAndBloodSlots(ShopConfigRoot cfg)
		{
			bool result = false;
			if (!cfg.EnableClientUnsupportedStats)
			{
				cfg.EnableClientUnsupportedStats = true;
				result = true;
			}
			if (cfg.Stats != null)
			{
				foreach (var (_, statShopEntry2) in cfg.Stats)
				{
					if (statShopEntry2 != null && !statShopEntry2.Enabled)
					{
						statShopEntry2.Enabled = true;
						result = true;
					}
				}
			}
			if (cfg.Categories == null)
			{
				Dictionary<string, BuffCategoryDefinition> dictionary2 = (cfg.Categories = new Dictionary<string, BuffCategoryDefinition>(StringComparer.OrdinalIgnoreCase));
			}
			if (!cfg.Categories.TryGetValue("blood_buff", out BuffCategoryDefinition value) || value == null)
			{
				cfg.Categories["blood_buff"] = new BuffCategoryDefinition
				{
					DisplayName = "Blood Buff",
					Documentation = "Renewable 2-hour blood-buff effects. Default limit: five blood packages.",
					MaxOwnedSlots = 5,
					SlotFreeCost = 250
				};
				result = true;
			}
			else
			{
				if (value.MaxOwnedSlots.GetValueOrDefault() != 5)
				{
					value.MaxOwnedSlots = 5;
					result = true;
				}
				if (string.IsNullOrWhiteSpace(value.Documentation) || value.Documentation.Contains("one blood package", StringComparison.OrdinalIgnoreCase) || value.Documentation.Contains("Recommended limit", StringComparison.OrdinalIgnoreCase))
				{
					value.Documentation = "Renewable 2-hour blood-buff effects. Default limit: five blood packages.";
					result = true;
				}
			}
			return result;
		}

		private static bool ApplyConfigVersion14ShortKeys(ShopConfigRoot cfg)
		{
			bool flag = false;
			if (cfg.Stats != null)
			{
				foreach (KeyValuePair<string, string> statKeyAlias in KeyAliasService.StatKeyAliases)
				{
					flag |= RenameStatKey(cfg, statKeyAlias.Key, statKeyAlias.Value);
				}
			}
			if (cfg.Buffs != null)
			{
				foreach (KeyValuePair<string, string> buffKeyAlias in KeyAliasService.BuffKeyAliases)
				{
					flag |= RenameBuffKey(cfg, buffKeyAlias.Key, buffKeyAlias.Value);
				}
				string[] array = new string[6] { "draculaT1", "draculaT2", "draculaT3", "draculaT4", "draculaT5", "generalT5" };
				foreach (string key in array)
				{
					if (cfg.Buffs.TryGetValue(key, out PerkShopEntry value) && value != null && value.Enabled)
					{
						value.Enabled = false;
						flag = true;
					}
				}
			}
			return flag;
		}

		private static bool RenameStatKey(ShopConfigRoot cfg, string oldKey, string newKey)
		{
			if (cfg.Stats == null || !cfg.Stats.TryGetValue(oldKey, out StatShopEntry value) || value == null)
			{
				return false;
			}
			if (!cfg.Stats.ContainsKey(newKey))
			{
				cfg.Stats[newKey] = value;
			}
			cfg.Stats.Remove(oldKey);
			return true;
		}

		private static bool RenameBuffKey(ShopConfigRoot cfg, string oldKey, string newKey)
		{
			if (cfg.Buffs == null || !cfg.Buffs.TryGetValue(oldKey, out PerkShopEntry value) || value == null)
			{
				return false;
			}
			if (!cfg.Buffs.ContainsKey(newKey))
			{
				cfg.Buffs[newKey] = value;
			}
			cfg.Buffs.Remove(oldKey);
			return true;
		}
	}
	internal static class ConfigService
	{
		private static readonly object Lock = new object();

		internal static readonly string ConfigDir = Path.Combine(Paths.ConfigPath, "PerkShop");

		private static readonly string ConfigFile = Path.Combine(ConfigDir, "perkconfig.json");

		private static DateTime _lastWrite = DateTime.MinValue;

		private static DateTime _nextConfigFileCheckUtc = DateTime.MinValue;

		private static ShopConfigRoot _root = DefaultConfig();

		internal static readonly JsonSerializerOptions JsonOptions = new JsonSerializerOptions
		{
			AllowTrailingCommas = true,
			PropertyNameCaseInsensitive = true,
			ReadCommentHandling = JsonCommentHandling.Skip,
			WriteIndented = true
		};

		public static ShopConfigRoot Shop => GetShop();

		public static bool DebugLoggingEnabled
		{
			get
			{
				try
				{
					return Shop.EnableDebugLogging;
				}
				catch
				{
					return false;
				}
			}
		}

		public static void Initialize()
		{
			Load(force: true);
		}

		public static void Reload()
		{
			Load(force: true);
		}

		public static bool UpdateWhitelist(string shopType, ulong platformId, bool add, string displayName = "")
		{
			if (platformId == 0L)
			{
				return false;
			}
			lock (Lock)
			{
				Load(force: true);
				_root = Normalize(_root);
				bool num = string.Equals(shopType, "stat", StringComparison.OrdinalIgnoreCase);
				List<ulong> list = (num ? _root.StatWhitelistPlatformIds : _root.BuffWhitelistPlatformIds);
				Dictionary<ulong, string> dictionary = (num ? _root.StatWhitelistNames : _root.BuffWhitelistNames);
				bool flag = list.Contains(platformId);
				if (add)
				{
					if (!flag)
					{
						list.Add(platformId);
						list.Sort();
					}
					if (!string.IsNullOrWhiteSpace(displayName))
					{
						dictionary[platformId] = displayName.Trim();
					}
					File.WriteAllText(ConfigFile, JsonSerializer.Serialize(_root, JsonOptions));
					_lastWrite = File.GetLastWriteTime(ConfigFile);
					return !flag;
				}
				if (!flag)
				{
					return false;
				}
				list.Remove(platformId);
				dictionary.Remove(platformId);
				File.WriteAllText(ConfigFile, JsonSerializer.Serialize(_root, JsonOptions));
				_lastWrite = File.GetLastWriteTime(ConfigFile);
				return true;
			}
		}

		private static ShopConfigRoot GetShop()
		{
			Load(force: false);
			lock (Lock)
			{
				return _root;
			}
		}

		private static void Load(bool force)
		{
			//IL_017d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0184: Expected O, but got Unknown
			lock (Lock)
			{
				try
				{
					Directory.CreateDirectory(ConfigDir);
					if (!File.Exists(ConfigFile))
					{
						_root = Normalize(DefaultConfig());
						StatDefinitionService.RebuildCache(_root);
						File.WriteAllText(ConfigFile, JsonSerializer.Serialize(_root, JsonOptions));
						_lastWrite = File.GetLastWriteTime(ConfigFile);
						_nextConfigFileCheckUtc = DateTime.UtcNow.AddSeconds(Math.Max(1f, _root.ConfigFileCheckIntervalSeconds));
						return;
					}
					if (!force)
					{
						if (!_root.AutoDetectConfigChanges || DateTime.UtcNow < _nextConfigFileCheckUtc)
						{
							return;
						}
						_nextConfigFileCheckUtc = DateTime.UtcNow.AddSeconds(Math.Max(1f, _root.ConfigFileCheckIntervalSeconds));
					}
					DateTime lastWriteTime = File.GetLastWriteTime(ConfigFile);
					if (!force && lastWriteTime <= _lastWrite)
					{
						return;
					}
					_root = JsonSerializer.Deserialize<ShopConfigRoot>(File.ReadAllText(ConfigFile), JsonOptions) ?? DefaultConfig();
					if (ConfigMigrationService.TryMigrate(_root, out ShopConfigRoot migrated))
					{
						_root = Normalize(migrated);
						StatDefinitionService.RebuildCache(_root);
						File.WriteAllText(ConfigFile, JsonSerializer.Serialize(_root, JsonOptions));
						lastWriteTime = File.GetLastWriteTime(ConfigFile);
						ManualLogSource log = Core.Log;
						bool flag = default(bool);
						BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(53, 1, ref flag);
						if (flag)
						{
							((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[ConfigService] Migrated perkconfig.json to version ");
							((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(_root.ConfigVersion);
							((BepInExLogInterpolatedStringHandler)val).AppendLiteral(".");
						}
						log.LogInfo(val);
					}
					else
					{
						_root = Normalize(_root);
						StatDefinitionService.RebuildCache(_root);
					}
					_lastWrite = lastWriteTime;
					_nextConfigFileCheckUtc = DateTime.UtcNow.AddSeconds(Math.Max(1f, _root.ConfigFileCheckIntervalSeconds));
				}
				catch (Exception e)
				{
					Core.LogException(e, "Load");
					_root = Normalize(DefaultConfig());
					StatDefinitionService.RebuildCache(_root);
					_nextConfigFileCheckUtc = DateTime.UtcNow.AddSeconds(Math.Max(1f, _root.ConfigFileCheckIntervalSeconds));
				}
			}
		}

		private static ShopConfigRoot Normalize(ShopConfigRoot cfg)
		{
			if (cfg == null)
			{
				cfg = DefaultConfig();
			}
			if (cfg.ConfigVersion <= 0)
			{
				cfg.ConfigVersion = 14;
			}
			cfg.CurrencyName = (string.IsNullOrWhiteSpace(cfg.CurrencyName) ? "Currency" : cfg.CurrencyName.Trim());
			if (cfg.CurrencyPrefab == 0)
			{
				cfg.CurrencyPrefab = 576389135;
			}
			if (cfg.ReapplyCheckIntervalSeconds < 3)
			{
				cfg.ReapplyCheckIntervalSeconds = 3;
			}
			if (cfg.ReapplyMaxUsersPerCycle < 0)
			{
				cfg.ReapplyMaxUsersPerCycle = 0;
			}
			if (cfg.CarrierFinalizeCheckIntervalSeconds < 0.05f)
			{
				cfg.CarrierFinalizeCheckIntervalSeconds = 0.05f;
			}
			if (cfg.OwnershipSaveDebounceSeconds < 0f)
			{
				cfg.OwnershipSaveDebounceSeconds = 0f;
			}
			if (cfg.ConfigFileCheckIntervalSeconds < 1f)
			{
				cfg.ConfigFileCheckIntervalSeconds = 5f;
			}
			if (cfg.PlayerCacheSaveDebounceSeconds < 0f)
			{
				cfg.PlayerCacheSaveDebounceSeconds = 0f;
			}
			if (cfg.RenewableTimedBuffSeconds < 60)
			{
				cfg.RenewableTimedBuffSeconds = 7200;
			}
			ShopConfigRoot shopConfigRoot = cfg;
			if (shopConfigRoot.RenewableTimedBuffCategories == null)
			{
				ShopConfigRoot shopConfigRoot2 = shopConfigRoot;
				List<string> obj = new List<string> { "potion", "elixir", "blood_buff" };
				List<string> list = obj;
				shopConfigRoot2.RenewableTimedBuffCategories = obj;
			}
			cfg.RenewableTimedBuffCategories = (from category in cfg.RenewableTimedBuffCategories
				where !string.IsNullOrWhiteSpace(category)
				select category.Trim()).Distinct<string>(StringComparer.OrdinalIgnoreCase).ToList();
			if (cfg.RenewableTimedBuffCategories.Count == 0)
			{
				cfg.RenewableTimedBuffCategories.AddRange(new string[3] { "potion", "elixir", "blood_buff" });
			}
			if (cfg.ForcePermanentBuffs)
			{
				cfg.AllowBuffEntityMutation = true;
			}
			shopConfigRoot = cfg;
			if (shopConfigRoot.BuffWhitelistPlatformIds == null)
			{
				List<ulong> list3 = (shopConfigRoot.BuffWhitelistPlatformIds = new List<ulong>());
			}
			shopConfigRoot = cfg;
			if (shopConfigRoot.StatWhitelistPlatformIds == null)
			{
				List<ulong> list3 = (shopConfigRoot.StatWhitelistPlatformIds = new List<ulong>());
			}
			shopConfigRoot = cfg;
			if (shopConfigRoot.BuffWhitelistNames == null)
			{
				Dictionary<ulong, string> dictionary2 = (shopConfigRoot.BuffWhitelistNames = new Dictionary<ulong, string>());
			}
			shopConfigRoot = cfg;
			if (shopConfigRoot.StatWhitelistNames == null)
			{
				Dictionary<ulong, string> dictionary2 = (shopConfigRoot.StatWhitelistNames = new Dictionary<ulong, string>());
			}
			cfg.BuffWhitelistPlatformIds = (from id in cfg.BuffWhitelistPlatformIds.Where((ulong id) => id != 0).Distinct()
				orderby id
				select id).ToList();
			cfg.StatWhitelistPlatformIds = (from id in cfg.StatWhitelistPlatformIds.Where((ulong id) => id != 0).Distinct()
				orderby id
				select id).ToList();
			cfg.BuffWhitelistNames = cfg.BuffWhitelistNames.Where<KeyValuePair<ulong, string>>((KeyValuePair<ulong, string> kv) => kv.Key != 0L && !string.IsNullOrWhiteSpace(kv.Value)).ToDictionary((KeyValuePair<ulong, string> kv) => kv.Key, (KeyValuePair<ulong, string> kv) => kv.Value.Trim());
			cfg.StatWhitelistNames = cfg.StatWhitelistNames.Where<KeyValuePair<ulong, string>>((KeyValuePair<ulong, string> kv) => kv.Key != 0L && !string.IsNullOrWhiteSpace(kv.Value)).ToDictionary((KeyValuePair<ulong, string> kv) => kv.Key, (KeyValuePair<ulong, string> kv) => kv.Value.Trim());
			if (cfg.MaxOwnedStatTypes < 0)
			{
				cfg.MaxOwnedStatTypes = 0;
			}
			if (cfg.StatTypeSlotFreeCost < 0)
			{
				cfg.StatTypeSlotFreeCost = 0;
			}
			cfg.MaxHealthPurchaseBehavior = (string.IsNullOrWhiteSpace(cfg.MaxHealthPurchaseBehavior) ? "ClampOnly" : cfg.MaxHealthPurchaseBehavior.Trim());
			if (!string.Equals(cfg.MaxHealthPurchaseBehavior, "ClampOnly", StringComparison.OrdinalIgnoreCase) && !string.Equals(cfg.MaxHealthPurchaseBehavior, "FillToMax", StringComparison.OrdinalIgnoreCase) && !string.Equals(cfg.MaxHealthPurchaseBehavior, "PreserveRatio", StringComparison.OrdinalIgnoreCase))
			{
				cfg.MaxHealthPurchaseBehavior = "ClampOnly";
			}
			shopConfigRoot = cfg;
			if (shopConfigRoot.Stats == null)
			{
				Dictionary<string, StatShopEntry> dictionary5 = (shopConfigRoot.Stats = new Dictionary<string, StatShopEntry>(StringComparer.OrdinalIgnoreCase));
			}
			Dictionary<string, StatShopEntry> dictionary6 = new Dictionary<string, StatShopEntry>(StringComparer.OrdinalIgnoreCase);
			string key;
			foreach (KeyValuePair<string, StatShopEntry> stat in cfg.Stats)
			{
				stat.Deconstruct(out key, out var value);
				string text = key;
				StatShopEntry statShopEntry = value;
				if (!string.IsNullOrWhiteSpace(text) && statShopEntry != null)
				{
					statShopEntry.DisplayName = (string.IsNullOrWhiteSpace(statShopEntry.DisplayName) ? text.Trim() : statShopEntry.DisplayName.Trim());
					statShopEntry.UnitStat = (string.IsNullOrWhiteSpace(statShopEntry.UnitStat) ? "PhysicalPower" : statShopEntry.UnitStat.Trim());
					statShopEntry.ModificationType = (string.IsNullOrWhiteSpace(statShopEntry.ModificationType) ? "Add" : statShopEntry.ModificationType.Trim());
					statShopEntry.AttributeCapType = (string.IsNullOrWhiteSpace(statShopEntry.AttributeCapType) ? "Uncapped" : statShopEntry.AttributeCapType.Trim());
					if (statShopEntry.Cost <= 0)
					{
						statShopEntry.Cost = 1;
					}
					if (statShopEntry.ValuePerPurchase == 0f)
					{
						statShopEntry.ValuePerPurchase = 1f;
					}
					if (statShopEntry.MaxPurchases < 0)
					{
						statShopEntry.MaxPurchases = 0;
					}
					statShopEntry.Notes = (string.IsNullOrWhiteSpace(statShopEntry.Notes) ? "Permanent stat purchase." : statShopEntry.Notes.Trim());
					StatDefinitionService.NormalizeKnownStatEntry(text.Trim(), statShopEntry);
					dictionary6[text.Trim()] = statShopEntry;
				}
			}
			cfg.Stats = dictionary6;
			shopConfigRoot = cfg;
			if (shopConfigRoot.Categories == null)
			{
				Dictionary<string, BuffCategoryDefinition> dictionary8 = (shopConfigRoot.Categories = new Dictionary<string, BuffCategoryDefinition>(StringComparer.OrdinalIgnoreCase));
			}
			Dictionary<string, BuffCategoryDefinition> dictionary9 = new Dictionary<string, BuffCategoryDefinition>(StringComparer.OrdinalIgnoreCase);
			foreach (KeyValuePair<string, BuffCategoryDefinition> category in cfg.Categories)
			{
				category.Deconstruct(out key, out var value2);
				string text2 = key;
				BuffCategoryDefinition buffCategoryDefinition = value2;
				if (!string.IsNullOrWhiteSpace(text2) && buffCategoryDefinition != null)
				{
					buffCategoryDefinition.DisplayName = (string.IsNullOrWhiteSpace(buffCategoryDefinition.DisplayName) ? text2.Trim() : buffCategoryDefinition.DisplayName.Trim());
					buffCategoryDefinition.Documentation = (string.IsNullOrWhiteSpace(buffCategoryDefinition.Documentation) ? string.Empty : buffCategoryDefinition.Documentation.Trim());
					if (buffCategoryDefinition.MaxOwnedSlots.HasValue && buffCategoryDefinition.MaxOwnedSlots.Value <= 0)
					{
						buffCategoryDefinition.MaxOwnedSlots = null;
					}
					if (buffCategoryDefinition.SlotFreeCost.HasValue && buffCategoryDefinition.SlotFreeCost.Value <= 0)
					{
						buffCategoryDefinition.SlotFreeCost = null;
					}
					dictionary9[text2.Trim()] = buffCategoryDefinition;
				}
			}
			EnsureCategory(dictionary9, "blood_buff", "Blood Buff", "Renewable 2-hour blood-buff effects. Default limit: five blood packages.", 5, 250);
			EnsureCategory(dictionary9, "set_bonus", "Set Bonus", "Armor set style bonuses or set-derived effects.", null, null);
			EnsureCategory(dictionary9, "potion", "Potion", "Potion and brew-style buffs. Safer for multi-slot ownership.", 3, 100);
			EnsureCategory(dictionary9, "elixir", "Elixir", "Elixir-style buffs. Bloodcraft compatibility default: one owned elixir slot.", 1, 500);
			EnsureCategory(dictionary9, "misc", "Misc", "Fallback category for uncategorized buffs.", null, null);
			if (dictionary9.TryGetValue("blood_buff", out var value3) && value3.MaxOwnedSlots.GetValueOrDefault() == 1)
			{
				value3.MaxOwnedSlots = 5;
				value3.Documentation = "Renewable 2-hour blood-buff effects. Default limit: five blood packages.";
			}
			cfg.Categories = dictionary9;
			shopConfigRoot = cfg;
			if (shopConfigRoot.Buffs == null)
			{
				Dictionary<string, PerkShopEntry> dictionary11 = (shopConfigRoot.Buffs = new Dictionary<string, PerkShopEntry>(StringComparer.OrdinalIgnoreCase));
			}
			Dictionary<string, PerkShopEntry> dictionary12 = new Dictionary<string, PerkShopEntry>(StringComparer.OrdinalIgnoreCase);
			foreach (KeyValuePair<string, PerkShopEntry> buff in cfg.Buffs)
			{
				buff.Deconstruct(out key, out var value4);
				string text3 = key;
				PerkShopEntry perkShopEntry = value4;
				if (string.IsNullOrWhiteSpace(text3) || perkShopEntry == null)
				{
					continue;
				}
				perkShopEntry.DisplayName = (string.IsNullOrWhiteSpace(perkShopEntry.DisplayName) ? text3.Trim() : perkShopEntry.DisplayName.Trim());
				perkShopEntry.Category = (string.IsNullOrWhiteSpace(perkShopEntry.Category) ? "misc" : perkShopEntry.Category.Trim());
				if (!cfg.Categories.ContainsKey(perkShopEntry.Category))
				{
					perkShopEntry.Category = "misc";
				}
				if (perkShopEntry.Cost <= 0)
				{
					perkShopEntry.Cost = 1;
				}
				if (cfg.ForcePermanentBuffs)
				{
					perkShopEntry.PersistentPurchase = true;
					perkShopEntry.KeepVisibleTimerFrozen = false;
					perkShopEntry.VisibleTimerSeconds = 0;
					perkShopEntry.MutateAppliedBuffLifetime = true;
					if (IsRenewableTimedCategory(cfg, perkShopEntry.Category))
					{
						perkShopEntry.DurationSeconds = cfg.RenewableTimedBuffSeconds;
						perkShopEntry.PersistThroughDeath = cfg.RenewableTimedBuffsPersistThroughDeath;
					}
					else
					{
						perkShopEntry.DurationSeconds = -1;
						perkShopEntry.PersistThroughDeath = true;
					}
				}
				else
				{
					if (perkShopEntry.DurationSeconds < -1)
					{
						perkShopEntry.DurationSeconds = -1;
					}
					if (perkShopEntry.VisibleTimerSeconds < 0)
					{
						perkShopEntry.VisibleTimerSeconds = 0;
					}
					if (!perkShopEntry.MutateAppliedBuffLifetime)
					{
						perkShopEntry.PersistThroughDeath = false;
					}
				}
				dictionary12[text3.Trim()] = perkShopEntry;
			}
			cfg.Buffs = dictionary12;
			return cfg;
		}

		internal static bool IsRenewableTimedCategory(ShopConfigRoot cfg, string category)
		{
			string category2 = category;
			if (cfg == null || !cfg.UseRenewableTimedBuffs || string.IsNullOrWhiteSpace(category2))
			{
				return false;
			}
			if (cfg.RenewableTimedBuffCategories != null)
			{
				return cfg.RenewableTimedBuffCategories.Any((string c) => string.Equals(c, category2, StringComparison.OrdinalIgnoreCase));
			}
			return false;
		}

		internal static int ResolveBuffDuration(ShopConfigRoot cfg, PerkShopEntry entry)
		{
			if (cfg != null && entry != null && IsRenewableTimedCategory(cfg, entry.Category))
			{
				return Math.Max(60, cfg.RenewableTimedBuffSeconds);
			}
			if (cfg == null || !cfg.ForcePermanentBuffs || (entry.KeepVisibleTimerFrozen && entry.VisibleTimerSeconds > 0))
			{
				return entry.DurationSeconds;
			}
			return -1;
		}

		internal static bool ResolveBuffPersistThroughDeath(ShopConfigRoot cfg, PerkShopEntry entry)
		{
			if (cfg != null && entry != null && IsRenewableTimedCategory(cfg, entry.Category))
			{
				return cfg.RenewableTimedBuffsPersistThroughDeath;
			}
			if (cfg == null || !cfg.ForcePermanentBuffs)
			{
				return entry.PersistThroughDeath;
			}
			return true;
		}

		internal static bool PreserveVanillaBuffCleanup(ShopConfigRoot cfg, PerkShopEntry entry)
		{
			if (cfg != null && entry != null)
			{
				return IsRenewableTimedCategory(cfg, entry.Category);
			}
			return false;
		}

		private static void EnsureCategory(Dictionary<string, BuffCategoryDefinition> categories, string key, string displayName, string documentation, int? maxOwnedSlots, int? slotFreeCost)
		{
			if (!categories.TryGetValue(key, out BuffCategoryDefinition value) || value == null)
			{
				categories[key] = new BuffCategoryDefinition
				{
					DisplayName = displayName,
					Documentation = documentation,
					MaxOwnedSlots = maxOwnedSlots,
					SlotFreeCost = slotFreeCost
				};
				return;
			}
			value.DisplayName = (string.IsNullOrWhiteSpace(value.DisplayName) ? displayName : value.DisplayName.Trim());
			value.Documentation = (string.IsNullOrWhiteSpace(value.Documentation) ? documentation : value.Documentation.Trim());
			if (value.MaxOwnedSlots.HasValue && value.MaxOwnedSlots.Value <= 0)
			{
				value.MaxOwnedSlots = null;
			}
			if (value.SlotFreeCost.HasValue && value.SlotFreeCost.Value <= 0)
			{
				value.SlotFreeCost = null;
			}
		}

		internal static ShopConfigRoot DefaultConfigForMigration()
		{
			return DefaultConfig();
		}

		private static ShopConfigRoot DefaultConfig()
		{
			return new ShopConfigRoot
			{
				ConfigVersion = 14,
				Enabled = true,
				EnableDebugLogging = false,
				CurrencyPrefab = 576389135,
				CurrencyName = "Greater Stygian Shards",
				BlockPurchasesInCombat = true,
				BlockRemovalsInCombat = true,
				SaveOwnership = true,
				ReapplyOwnedBuffsOnLogin = false,
				ReapplyOwnedBuffsWhenMissing = false,
				ReapplyCheckIntervalSeconds = 60,
				ReapplyMaxUsersPerCycle = 5,
				CarrierFinalizeCheckIntervalSeconds = 0.25f,
				OwnershipSaveDebounceSeconds = 2f,
				AutoDetectConfigChanges = false,
				ConfigFileCheckIntervalSeconds = 5f,
				PlayerCacheSaveDebounceSeconds = 30f,
				ForcePermanentBuffs = true,
				AllowBuffEntityMutation = true,
				UseRenewableTimedBuffs = true,
				RenewableTimedBuffSeconds = 7200,
				RenewableTimedBuffCategories = new List<string> { "potion", "elixir", "blood_buff" },
				RenewableTimedBuffsPersistThroughDeath = false,
				EnableStatShop = true,
				EnableClientUnsupportedStats = true,
				UseClientAttributeStatAliases = false,
				EnableExperimentalBloodBuffs = true,
				StatCarrierBuffPrefab = -809648681,
				EnableStatTypeSlots = true,
				MaxOwnedStatTypes = 4,
				StatTypeSlotFreeCost = 500,
				MaxHealthPurchaseBehavior = "ClampOnly",
				EnableBuffWhitelist = false,
				BuffWhitelistPlatformIds = new List<ulong>(),
				BuffWhitelistNames = new Dictionary<ulong, string>(),
				EnableStatWhitelist = false,
				StatWhitelistPlatformIds = new List<ulong>(),
				StatWhitelistNames = new Dictionary<ulong, string>(),
				Stats = new Dictionary<string, StatShopEntry>(StringComparer.OrdinalIgnoreCase)
				{
					["MH"] = new StatShopEntry
					{
						DisplayName = "Max Health",
						UnitStat = "MaxHealth",
						ModificationType = "Add",
						AttributeCapType = "Uncapped",
						ValuePerPurchase = 25f,
						Cost = 500,
						MaxPurchases = 10,
						Notes = "Permanently increases Max Health by 25 per purchase."
					},
					["PP"] = new StatShopEntry
					{
						DisplayName = "Physical Power",
						UnitStat = "PhysicalPower",
						ModificationType = "Add",
						AttributeCapType = "Uncapped",
						ValuePerPurchase = 2f,
						Cost = 500,
						MaxPurchases = 10,
						Notes = "Permanently increases Physical Power by 2 per purchase."
					},
					["SP"] = new StatShopEntry
					{
						DisplayName = "Spell Power",
						UnitStat = "SpellPower",
						ModificationType = "Add",
						AttributeCapType = "Uncapped",
						ValuePerPurchase = 1f,
						Cost = 500,
						MaxPurchases = 10,
						Notes = "Permanently increases Spell Power by 1 per purchase."
					},
					["MS"] = new StatShopEntry
					{
						DisplayName = "Movement Speed",
						UnitStat = "MovementSpeed",
						ModificationType = "MultiplyBaseAdd",
						AttributeCapType = "Uncapped",
						ValuePerPurchase = 0.05f,
						Cost = 500,
						MaxPurchases = 5,
						Notes = "Permanently increases Movement Speed by 0.05 per purchase. Uses MultiplyBaseAdd for HUD/stat compatibility."
					},
					["AS"] = new StatShopEntry
					{
						DisplayName = "Primary Attack Speed",
						UnitStat = "PrimaryAttackSpeed",
						ModificationType = "Add",
						AttributeCapType = "Uncapped",
						ValuePerPurchase = 0.02f,
						Cost = 500,
						MaxPurchases = 5,
						Notes = "Permanently increases Primary Attack Speed by 0.02 per purchase."
					},
					["phll"] = new StatShopEntry
					{
						DisplayName = "Physical Life Leech",
						UnitStat = "PhysicalLifeLeech",
						ModificationType = "Add",
						AttributeCapType = "Uncapped",
						ValuePerPurchase = 0.02f,
						Cost = 500,
						MaxPurchases = 5,
						Notes = "Permanently increases Physical Life Leech by 0.02 per purchase."
					},
					["sll"] = new StatShopEntry
					{
						DisplayName = "Spell Life Leech",
						UnitStat = "SpellLifeLeech",
						ModificationType = "Add",
						AttributeCapType = "Uncapped",
						ValuePerPurchase = 0.02f,
						Cost = 500,
						MaxPurchases = 5,
						Notes = "Permanently increases Spell Life Leech by 0.02 per purchase."
					},
					["prll"] = new StatShopEntry
					{
						DisplayName = "Primary Life Leech",
						UnitStat = "PrimaryLifeLeech",
						ModificationType = "Add",
						AttributeCapType = "Uncapped",
						ValuePerPurchase = 0.03f,
						Cost = 500,
						MaxPurchases = 5,
						Notes = "Permanently increases Primary Life Leech by 0.03 per purchase."
					},
					["PCC"] = new StatShopEntry
					{
						DisplayName = "Physical Critical Strike Chance",
						UnitStat = "PhysicalCriticalStrikeChance",
						ModificationType = "Add",
						AttributeCapType = "Uncapped",
						ValuePerPurchase = 0.02f,
						Cost = 500,
						MaxPurchases = 5,
						Notes = "Permanently increases Physical Critical Strike Chance by 0.02 per purchase."
					},
					["PCD"] = new StatShopEntry
					{
						DisplayName = "Physical Critical Strike Damage",
						UnitStat = "PhysicalCriticalStrikeDamage",
						ModificationType = "Add",
						AttributeCapType = "Uncapped",
						ValuePerPurchase = 0.1f,
						Cost = 500,
						MaxPurchases = 5,
						Notes = "Permanently increases Physical Critical Strike Damage by 0.1 per purchase."
					},
					["SCC"] = new StatShopEntry
					{
						DisplayName = "Spell Critical Strike Chance",
						UnitStat = "SpellCriticalStrikeChance",
						ModificationType = "Add",
						AttributeCapType = "Uncapped",
						ValuePerPurchase = 0.02f,
						Cost = 500,
						MaxPurchases = 5,
						Notes = "Permanently increases Spell Critical Strike Chance by 0.02 per purchase."
					},
					["SCD"] = new StatShopEntry
					{
						DisplayName = "Spell Critical Strike Damage",
						UnitStat = "SpellCriticalStrikeDamage",
						ModificationType = "Add",
						AttributeCapType = "Uncapped",
						ValuePerPurchase = 0.1f,
						Cost = 500,
						MaxPurchases = 5,
						Notes = "Permanently increases Spell Critical Strike Damage by 0.1 per purchase."
					},
					["PR"] = new StatShopEntry
					{
						DisplayName = "Physical Resistance",
						UnitStat = "PhysicalResistance",
						ModificationType = "Add",
						AttributeCapType = "Uncapped",
						ValuePerPurchase = 0.02f,
						Cost = 500,
						MaxPurchases = 5,
						Notes = "Permanently increases Physical Resistance by 0.02 per purchase."
					},
					["SR"] = new StatShopEntry
					{
						DisplayName = "Spell Resistance",
						UnitStat = "SpellResistance",
						ModificationType = "Add",
						AttributeCapType = "Uncapped",
						ValuePerPurchase = 0.02f,
						Cost = 500,
						MaxPurchases = 5,
						Notes = "Permanently increases Spell Resistance by 0.02 per purchase."
					},
					["HR"] = new StatShopEntry
					{
						DisplayName = "Healing Received",
						UnitStat = "HealingReceived",
						ModificationType = "Add",
						AttributeCapType = "Uncapped",
						ValuePerPurchase = 0.03f,
						Cost = 500,
						MaxPurchases = 5,
						Notes = "Permanently increases Healing Received by 0.03 per purchase."
					},
					["DR"] = new StatShopEntry
					{
						DisplayName = "Damage Reduction",
						UnitStat = "DamageReduction",
						ModificationType = "Add",
						AttributeCapType = "Uncapped",
						ValuePerPurchase = 0.01f,
						Cost = 750,
						MaxPurchases = 5,
						Notes = "Permanently increases Damage Reduction by 0.01 per purchase."
					},
					["RY"] = new StatShopEntry
					{
						DisplayName = "Resource Yield",
						UnitStat = "ResourceYield",
						ModificationType = "Add",
						AttributeCapType = "Uncapped",
						ValuePerPurchase = 0.05f,
						Cost = 500,
						MaxPurchases = 5,
						Notes = "Permanently increases Resource Yield by 0.05 per purchase."
					},
					["RBD"] = new StatShopEntry
					{
						DisplayName = "Reduced Blood Drain",
						UnitStat = "ReducedBloodDrain",
						ModificationType = "Add",
						AttributeCapType = "Uncapped",
						ValuePerPurchase = 0.1f,
						Cost = 500,
						MaxPurchases = 5,
						Notes = "Permanently increases Reduced Blood Drain by 0.1 per purchase."
					},
					["SCR"] = new StatShopEntry
					{
						DisplayName = "Spell Cooldown Recovery Rate",
						UnitStat = "SpellCooldownRecoveryRate",
						ModificationType = "Add",
						AttributeCapType = "Uncapped",
						ValuePerPurchase = 0.02f,
						Cost = 500,
						MaxPurchases = 5,
						Notes = "Permanently increases Spell Cooldown Recovery Rate by 0.02 per purchase."
					},
					["WCR"] = new StatShopEntry
					{
						DisplayName = "Weapon Cooldown Recovery Rate",
						UnitStat = "WeaponCooldownRecoveryRate",
						ModificationType = "Add",
						AttributeCapType = "Uncapped",
						ValuePerPurchase = 0.02f,
						Cost = 500,
						MaxPurchases = 5,
						Notes = "Permanently increases Weapon Cooldown Recovery Rate by 0.02 per purchase."
					},
					["UCR"] = new StatShopEntry
					{
						DisplayName = "Ultimate Cooldown Recovery Rate",
						UnitStat = "UltimateCooldownRecoveryRate",
						ModificationType = "Add",
						AttributeCapType = "Uncapped",
						ValuePerPurchase = 0.04f,
						Cost = 500,
						MaxPurchases = 5,
						Notes = "Permanently increases Ultimate Cooldown Recovery Rate by 0.04 per purchase."
					},
					["MD"] = new StatShopEntry
					{
						DisplayName = "Minion Damage",
						UnitStat = "MinionDamage",
						ModificationType = "Add",
						AttributeCapType = "Uncapped",
						ValuePerPurchase = 0.05f,
						Cost = 500,
						MaxPurchases = 5,
						Notes = "Permanently increases Minion Damage by 0.05 per purchase."
					},
					["AAS"] = new StatShopEntry
					{
						DisplayName = "Ability Attack Speed",
						UnitStat = "AbilityAttackSpeed",
						ModificationType = "Add",
						AttributeCapType = "Uncapped",
						ValuePerPurchase = 0.02f,
						Cost = 500,
						MaxPurchases = 5,
						Notes = "Permanently increases Ability Attack Speed by 0.02 per purchase."
					},
					["CDR"] = new StatShopEntry
					{
						DisplayName = "Corruption Damage Reduction",
						UnitStat = "CorruptionDamageReduction",
						ModificationType = "Add",
						AttributeCapType = "Uncapped",
						ValuePerPurchase = 0.02f,
						Cost = 500,
						MaxPurchases = 5,
						Notes = "Permanently increases Corruption Damage Reduction by 0.02 per purchase."
					}
				},
				Categories = new Dictionary<string, BuffCategoryDefinition>(StringComparer.OrdinalIgnoreCase)
				{
					["blood_buff"] = new BuffCategoryDefinition
					{
						DisplayName = "Blood Buff",
						Documentation = "Renewable 2-hour blood-buff effects. Default limit: five blood packages.",
						MaxOwnedSlots = 5,
						SlotFreeCost = 250
					},
					["set_bonus"] = new BuffCategoryDefinition
					{
						DisplayName = "Set Bonus",
						Documentation = "Armor set style bonuses or set-derived effects.",
						MaxOwnedSlots = null,
						SlotFreeCost = null
					},
					["potion"] = new BuffCategoryDefinition
					{
						DisplayName = "Potion",
						Documentation = "Potion and brew-style buffs. Safer for multi-slot ownership.",
						MaxOwnedSlots = 3,
						SlotFreeCost = 100
					},
					["elixir"] = new BuffCategoryDefinition
					{
						DisplayName = "Elixir",
						Documentation = "Elixir-style buffs. Bloodcraft compatibility default: one owned elixir slot.",
						MaxOwnedSlots = 1,
						SlotFreeCost = 500
					},
					["misc"] = new BuffCategoryDefinition
					{
						DisplayName = "Misc",
						Documentation = "Fallback category for uncategorized buffs.",
						MaxOwnedSlots = null,
						SlotFreeCost = null
					}
				},
				Buffs = DefaultBuffEntries()
			};
		}

		internal static Dictionary<string, PerkShopEntry> DefaultBuffEntriesForMigration()
		{
			return DefaultBuffEntries();
		}

		private static Dictionary<string, PerkShopEntry> DefaultBuffEntries()
		{
			Dictionary<string, PerkShopEntry> obj = new Dictionary<string, PerkShopEntry>(StringComparer.OrdinalIgnoreCase)
			{
				["sun_immunity"] = new PerkShopEntry
				{
					DisplayName = "Sun Immunity",
					Category = "misc",
					BuffPrefab = 32681348,
					Cost = 100,
					PersistentPurchase = true,
					DurationSeconds = -1,
					KeepVisibleTimerFrozen = false,
					VisibleTimerSeconds = 0,
					PersistThroughDeath = true,
					MutateAppliedBuffLifetime = false,
					PreventDuplicate = true,
					Notes = "Grants complete immunity to sun exposure."
				},
				["potion_of_rage"] = new PerkShopEntry
				{
					DisplayName = "Potion of Rage",
					Category = "potion",
					BuffPrefab = -1591883586,
					Cost = 400,
					PersistentPurchase = true,
					DurationSeconds = 7200,
					KeepVisibleTimerFrozen = false,
					VisibleTimerSeconds = 0,
					PersistThroughDeath = false,
					MutateAppliedBuffLifetime = false,
					PreventDuplicate = true,
					Notes = "Applies AB_Consumable_PhysicalPowerPotion_T02_Buff as a renewable 2-hour owned buff."
				},
				["elixir_of_the_crow"] = new PerkShopEntry
				{
					DisplayName = "Elixir of the Crow",
					Category = "elixir",
					BuffPrefab = -262239794,
					Cost = 750,
					PersistentPurchase = true,
					DurationSeconds = -1,
					KeepVisibleTimerFrozen = false,
					VisibleTimerSeconds = 0,
					PersistThroughDeath = true,
					MutateAppliedBuffLifetime = false,
					PreventDuplicate = true,
					Notes = "Applies AB_Elixir_Crow_T01_Buff as a renewable 2-hour owned buff."
				},
				["empty_layout"] = new PerkShopEntry
				{
					Enabled = false,
					DisplayName = "Empty Buff Layout",
					Category = "misc",
					BuffPrefab = 32681348,
					Cost = 100,
					PersistentPurchase = true,
					Durati