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()); public static ServerGameManager ServerGameManager => ServerScriptMapper.GetServerGameManager(); public static DebugEventsSystem DebugEventsSystem => _debugEventsSystem ?? (_debugEventsSystem = Server.GetExistingSystemManaged()); 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(); _debugEventsSystem = _server.GetExistingSystemManaged(); _systemService = new SystemService(_server); QueryService.Initialize(_systemService); CarrierPrefabService.PrepareConfiguredStatCarrierPrefab(); _initialized = true; Log.LogInfo((object)"Core initialized."); } } internal static World? GetWorld(string name) { Enumerator 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(caller); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("] "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(e.Message); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("\n"); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(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("PerkShop"); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted("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(); if (existingSystemManaged == null) { return false; } return existingSystemManaged.SpawnableNameToPrefabGuidDictionary.Count > 0; } } public static class ECSExtensions { public unsafe static T Read(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(), (AccessMode)0); EntityManager entityManager = Core.EntityManager; return Marshal.PtrToStructure(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() }); NativeArray val2 = ((EntityQuery)(ref val)).ToEntityArray(AllocatorHandle.op_Implicit((Allocator)2)); try { Enumerator 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(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(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, characterEntity, ref val, 0)) { return 0; } if (!((EntityManager)(ref entityManager)).HasComponent(val)) { return 0; } DynamicBuffer buffer = ((EntityManager)(ref entityManager)).GetBuffer(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, characterEntity, ref val, 0)) { return false; } if (!((EntityManager)(ref entityManager)).HasComponent(val)) { return false; } DynamicBuffer buffer = ((EntityManager)(ref entityManager)).GetBuffer(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, characterEntity, PrefabIdentifier.op_Implicit(val))) { return true; } } return false; } } } namespace PerkShop.Services { internal static class AccessService { public static bool CanAccessPerkShop(Entity userEntity, Action 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 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(userEntity)) { entityManager = Core.EntityManager; User componentData = ((EntityManager)(ref entityManager)).GetComponentData(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 PendingPermanentBuffs = new Dictionary(); private static readonly TimeSpan PendingBuffTimeout = TimeSpan.FromSeconds(30.0); private static readonly HashSet PreparedPermanentBuffPrefabs = new HashSet(); 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 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[] array = PendingPermanentBuffs.ToArray(); bool flag = default(bool); for (int i = 0; i < array.Length; i++) { KeyValuePair 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(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(key.Index); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(":"); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(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(val)) { ((EntityManager)(ref entityManager)).RemoveComponent(val); } if (((EntityManager)(ref entityManager)).HasComponent(val)) { ((EntityManager)(ref entityManager)).RemoveComponent(val); } if (((EntityManager)(ref entityManager)).HasComponent(val)) { ((EntityManager)(ref entityManager)).RemoveComponent(val); } if (persistThroughDeath && !((EntityManager)(ref entityManager)).HasComponent(val)) { ((EntityManager)(ref entityManager)).AddComponent(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(((PrefabGUID)(ref buffPrefab)).GuidHash); ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(": "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted(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(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(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(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(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(val)) { entityManager = Core.EntityManager; ((EntityManager)(ref entityManager)).AddComponent(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 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 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 val = default(NativeArray); try { EntityManager entityManager = Core.EntityManager; EntityQuery val2 = ((EntityManager)(ref entityManager)).CreateEntityQuery((ComponentType[])(object)new ComponentType[2] { ComponentType.ReadOnly(), ComponentType.ReadOnly() }); 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(val3) || !((EntityManager)(ref entityManager)).HasComponent(val3) || ((EntityManager)(ref entityManager)).GetComponentData(val3).Target != characterEntity) { continue; } PrefabGUID componentData = ((EntityManager)(ref entityManager)).GetComponentData(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(val3)) { ((EntityManager)(ref entityManager)).AddComponent(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(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(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(buffEntity)) { ((EntityManager)(ref entityManager)).AddComponent(buffEntity); } if (!preserveVanillaCleanup) { if (((EntityManager)(ref entityManager)).HasComponent(buffEntity)) { ((EntityManager)(ref entityManager)).RemoveComponent(buffEntity); } if (((EntityManager)(ref entityManager)).HasComponent(buffEntity)) { ((EntityManager)(ref entityManager)).RemoveComponent(buffEntity); } } if (durationSeconds < 0) { if (((EntityManager)(ref entityManager)).HasComponent(buffEntity)) { ((EntityManager)(ref entityManager)).RemoveComponent(buffEntity); } } else if (durationSeconds > 0) { if (!((EntityManager)(ref entityManager)).HasComponent(buffEntity)) { ((EntityManager)(ref entityManager)).AddComponent(buffEntity); } ((EntityManager)(ref entityManager)).SetComponentData(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(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(((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(val2)) { ((EntityManager)(ref entityManager)).AddBuffer(val2); } else { ((EntityManager)(ref entityManager)).GetBuffer(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 dictionary2 = (cfg.Buffs = new Dictionary(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 list2 = (cfg.RenewableTimedBuffCategories = new List()); } string[] array = new string[3] { "potion", "elixir", "blood_buff" }; foreach (string text in array) { if (!cfg.RenewableTimedBuffCategories.Contains(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 dictionary2 = (cfg.Categories = new Dictionary(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 statKeyAlias in KeyAliasService.StatKeyAliases) { flag |= RenameStatKey(cfg, statKeyAlias.Key, statKeyAlias.Value); } } if (cfg.Buffs != null) { foreach (KeyValuePair 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 list = (num ? _root.StatWhitelistPlatformIds : _root.BuffWhitelistPlatformIds); Dictionary 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(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(_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 obj = new List { "potion", "elixir", "blood_buff" }; List list = obj; shopConfigRoot2.RenewableTimedBuffCategories = obj; } cfg.RenewableTimedBuffCategories = (from category in cfg.RenewableTimedBuffCategories where !string.IsNullOrWhiteSpace(category) select category.Trim()).Distinct(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 list3 = (shopConfigRoot.BuffWhitelistPlatformIds = new List()); } shopConfigRoot = cfg; if (shopConfigRoot.StatWhitelistPlatformIds == null) { List list3 = (shopConfigRoot.StatWhitelistPlatformIds = new List()); } shopConfigRoot = cfg; if (shopConfigRoot.BuffWhitelistNames == null) { Dictionary dictionary2 = (shopConfigRoot.BuffWhitelistNames = new Dictionary()); } shopConfigRoot = cfg; if (shopConfigRoot.StatWhitelistNames == null) { Dictionary dictionary2 = (shopConfigRoot.StatWhitelistNames = new Dictionary()); } 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 kv) => kv.Key != 0L && !string.IsNullOrWhiteSpace(kv.Value)).ToDictionary((KeyValuePair kv) => kv.Key, (KeyValuePair kv) => kv.Value.Trim()); cfg.StatWhitelistNames = cfg.StatWhitelistNames.Where>((KeyValuePair kv) => kv.Key != 0L && !string.IsNullOrWhiteSpace(kv.Value)).ToDictionary((KeyValuePair kv) => kv.Key, (KeyValuePair 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 dictionary5 = (shopConfigRoot.Stats = new Dictionary(StringComparer.OrdinalIgnoreCase)); } Dictionary dictionary6 = new Dictionary(StringComparer.OrdinalIgnoreCase); string key; foreach (KeyValuePair 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 dictionary8 = (shopConfigRoot.Categories = new Dictionary(StringComparer.OrdinalIgnoreCase)); } Dictionary dictionary9 = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (KeyValuePair 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 dictionary11 = (shopConfigRoot.Buffs = new Dictionary(StringComparer.OrdinalIgnoreCase)); } Dictionary dictionary12 = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (KeyValuePair 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 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 { "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(), BuffWhitelistNames = new Dictionary(), EnableStatWhitelist = false, StatWhitelistPlatformIds = new List(), StatWhitelistNames = new Dictionary(), Stats = new Dictionary(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(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 DefaultBuffEntriesForMigration() { return DefaultBuffEntries(); } private static Dictionary DefaultBuffEntries() { Dictionary obj = new Dictionary(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, DurationSeconds = -1, KeepVisibleTimerFrozen = false, VisibleTimerSeconds = 0, PersistThroughDeath = true, MutateAppliedBuffLifetime = false, PreventDuplicate = true, Notes = "Replace this text with the player-visible effect description." } }; AddBloodBuffs(obj); return obj; } private static void AddBloodBuffs(Dictionary buffs) { AddBloodBuff(buffs, "bruteT1", "Brute Blood Tier 1", -1596803256, 1, "AB_BloodBuff_Brute_Tier1"); AddBloodBuff(buffs, "bruteT2", "Brute Blood Tier 2", 1828387635, 2, "AB_BloodBuff_Brute_Tier2"); AddBloodBuff(buffs, "bruteT3", "Brute Blood Tier 3", -1861657718, 3, "AB_BloodBuff_Brute_Tier3"); AddBloodBuff(buffs, "bruteT4", "Brute Blood Tier 4", -584203677, 4, "AB_BloodBuff_Brute_Tier4"); AddBloodBuff(buffs, "corruptionT1", "Corruption Blood Tier 1", -302908776, 1, "AB_BloodBuff_Corruption_Tier1"); AddBloodBuff(buffs, "corruptionT2", "Corruption Blood Tier 2", -771138642, 2, "AB_BloodBuff_Corruption_Tier2"); AddBloodBuff(buffs, "corruptionT3", "Corruption Blood Tier 3", -1493903943, 3, "AB_BloodBuff_Corruption_Tier3"); AddBloodBuff(buffs, "corruptionT4", "Corruption Blood Tier 4", 1491794137, 4, "AB_BloodBuff_Corruption_Tier4"); AddBloodBuff(buffs, "creatureT1", "Creature Blood Tier 1", 894725875, 1, "AB_BloodBuff_Creature_Tier1"); AddBloodBuff(buffs, "creatureT2", "Creature Blood Tier 2", 475045773, 2, "AB_BloodBuff_Creature_Tier2"); AddBloodBuff(buffs, "creatureT3", "Creature Blood Tier 3", -1055766373, 3, "AB_BloodBuff_Creature_Tier3"); AddBloodBuff(buffs, "creatureT4", "Creature Blood Tier 4", 1643157297, 4, "AB_BloodBuff_Creature_Tier4"); AddBloodBuff(buffs, "draculaT1", "Dracula Blood Tier 1", -488475343, 1, "AB_BloodBuff_Dracula_Tier1", enabled: false); AddBloodBuff(buffs, "draculaT2", "Dracula Blood Tier 2", 2145997375, 2, "AB_BloodBuff_Dracula_Tier2", enabled: false); AddBloodBuff(buffs, "draculaT3", "Dracula Blood Tier 3", 1805033464, 3, "AB_BloodBuff_Dracula_Tier3", enabled: false); AddBloodBuff(buffs, "draculaT4", "Dracula Blood Tier 4", -2079057224, 4, "AB_BloodBuff_Dracula_Tier4", enabled: false); AddBloodBuff(buffs, "draculaT5", "Dracula Blood Tier 5", -1923843097, 5, "AB_BloodBuff_Dracula_Tier5", enabled: false); AddBloodBuff(buffs, "draculinT1", "Draculin Blood Tier 1", 1558171501, 1, "AB_BloodBuff_Draculin_Tier1"); AddBloodBuff(buffs, "draculinT2", "Draculin Blood Tier 2", 997154800, 2, "AB_BloodBuff_Draculin_Tier2"); AddBloodBuff(buffs, "draculinT3", "Draculin Blood Tier 3", 1159173627, 3, "AB_BloodBuff_Draculin_Tier3"); AddBloodBuff(buffs, "draculinT4", "Draculin Blood Tier 4", 1103099361, 4, "AB_BloodBuff_Draculin_Tier4"); AddBloodBuff(buffs, "mutantT1", "Mutant Blood Tier 1", -1266262267, 1, "AB_BloodBuff_Mutant_Tier1"); AddBloodBuff(buffs, "mutantT2", "Mutant Blood Tier 2", -1413561088, 2, "AB_BloodBuff_Mutant_Tier2"); AddBloodBuff(buffs, "mutantT3", "Mutant Blood Tier 3", 946705138, 3, "AB_BloodBuff_Mutant_Tier3"); AddBloodBuff(buffs, "mutantT4", "Mutant Blood Tier 4", -491525099, 4, "AB_BloodBuff_Mutant_Tier4"); AddBloodBuff(buffs, "rogueT1", "Rogue Blood Tier 1", 1201299233, 1, "AB_BloodBuff_Rogue_Tier1"); AddBloodBuff(buffs, "rogueT2", "Rogue Blood Tier 2", -154702686, 2, "AB_BloodBuff_Rogue_Tier2"); AddBloodBuff(buffs, "rogueT3", "Rogue Blood Tier 3", -536284884, 3, "AB_BloodBuff_Rogue_Tier3"); AddBloodBuff(buffs, "rogueT4", "Rogue Blood Tier 4", 210193036, 4, "AB_BloodBuff_Rogue_Tier4"); AddBloodBuff(buffs, "scholarT1", "Scholar Blood Tier 1", 1934870645, 1, "AB_BloodBuff_Scholar_Tier1"); AddBloodBuff(buffs, "scholarT2", "Scholar Blood Tier 2", -993492354, 2, "AB_BloodBuff_Scholar_Tier2"); AddBloodBuff(buffs, "scholarT3", "Scholar Blood Tier 3", -901503997, 3, "AB_BloodBuff_Scholar_Tier3"); AddBloodBuff(buffs, "scholarT4", "Scholar Blood Tier 4", -1859298707, 4, "AB_BloodBuff_Scholar_Tier4"); AddBloodBuff(buffs, "warriorT1", "Warrior Blood Tier 1", -804597757, 1, "AB_BloodBuff_Warrior_Tier1"); AddBloodBuff(buffs, "warriorT2", "Warrior Blood Tier 2", -1510965956, 2, "AB_BloodBuff_Warrior_Tier2"); AddBloodBuff(buffs, "warriorT3", "Warrior Blood Tier 3", -1869022798, 3, "AB_BloodBuff_Warrior_Tier3"); AddBloodBuff(buffs, "warriorT4", "Warrior Blood Tier 4", -397097531, 4, "AB_BloodBuff_Warrior_Tier4"); AddBloodBuff(buffs, "workerT1", "Worker Blood Tier 1", -773025435, 1, "AB_BloodBuff_Worker_Tier1"); AddBloodBuff(buffs, "workerT2", "Worker Blood Tier 2", -2068307944, 2, "AB_BloodBuff_Worker_Tier2"); AddBloodBuff(buffs, "workerT3", "Worker Blood Tier 3", 1359282533, 3, "AB_BloodBuff_Worker_Tier3"); AddBloodBuff(buffs, "workerT4", "Worker Blood Tier 4", 1791009885, 4, "AB_BloodBuff_Worker_Tier4"); AddBloodBuff(buffs, "generalT5", "General Blood Tier 5", 947312310, 5, "AB_BloodBuff_General_Tier5", enabled: false); } private static void AddBloodBuff(Dictionary buffs, string key, string displayName, int prefab, int tier, string prefabName, bool enabled = true) { buffs[key] = new PerkShopEntry { Enabled = enabled, DisplayName = displayName, Category = "blood_buff", BuffPrefab = prefab, Cost = 250 * Math.Max(1, tier), PersistentPurchase = true, DurationSeconds = 7200, KeepVisibleTimerFrozen = false, VisibleTimerSeconds = 0, PersistThroughDeath = false, MutateAppliedBuffLifetime = false, PreventDuplicate = true, Notes = "Applies " + prefabName + " as a renewable 2-hour owned blood buff without permanent lifetime mutation." }; } } internal static class KeyAliasService { internal static readonly IReadOnlyDictionary StatKeyAliases = new Dictionary(StringComparer.OrdinalIgnoreCase) { ["max_health"] = "MH", ["physical_power"] = "PP", ["spell_power"] = "SP", ["movement_speed"] = "MS", ["primary_attack_speed"] = "AS", ["physical_life_leech"] = "phll", ["spell_life_leech"] = "sll", ["primary_life_leech"] = "prll", ["physical_critical_strike_chance"] = "PCC", ["physical_critical_strike_damage"] = "PCD", ["spell_critical_strike_chance"] = "SCC", ["spell_critical_strike_damage"] = "SCD", ["physical_resistance"] = "PR", ["spell_resistance"] = "SR", ["healing_received"] = "HR", ["damage_reduction"] = "DR", ["resource_yield"] = "RY", ["reduced_blood_drain"] = "RBD", ["spell_cooldown_recovery_rate"] = "SCR", ["weapon_cooldown_recovery_rate"] = "WCR", ["ultimate_cooldown_recovery_rate"] = "UCR", ["minion_damage"] = "MD", ["ability_attack_speed"] = "AAS", ["corruption_damage_reduction"] = "CDR" }; internal static readonly IReadOnlyDictionary BuffKeyAliases = new Dictionary(StringComparer.OrdinalIgnoreCase) { ["brute_blood_t1"] = "bruteT1", ["brute_blood_t2"] = "bruteT2", ["brute_blood_t3"] = "bruteT3", ["brute_blood_t4"] = "bruteT4", ["corruption_blood_t1"] = "corruptionT1", ["corruption_blood_t2"] = "corruptionT2", ["corruption_blood_t3"] = "corruptionT3", ["corruption_blood_t4"] = "corruptionT4", ["creature_blood_t1"] = "creatureT1", ["creature_blood_t2"] = "creatureT2", ["creature_blood_t3"] = "creatureT3", ["creature_blood_t4"] = "creatureT4", ["dracula_blood_t1"] = "draculaT1", ["dracula_blood_t2"] = "draculaT2", ["dracula_blood_t3"] = "draculaT3", ["dracula_blood_t4"] = "draculaT4", ["dracula_blood_t5"] = "draculaT5", ["draculin_blood_t1"] = "draculinT1", ["draculin_blood_t2"] = "draculinT2", ["draculin_blood_t3"] = "draculinT3", ["draculin_blood_t4"] = "draculinT4", ["mutant_blood_t1"] = "mutantT1", ["mutant_blood_t2"] = "mutantT2", ["mutant_blood_t3"] = "mutantT3", ["mutant_blood_t4"] = "mutantT4", ["rogue_blood_t1"] = "rogueT1", ["rogue_blood_t2"] = "rogueT2", ["rogue_blood_t3"] = "rogueT3", ["rogue_blood_t4"] = "rogueT4", ["scholar_blood_t1"] = "scholarT1", ["scholar_blood_t2"] = "scholarT2", ["scholar_blood_t3"] = "scholarT3", ["scholar_blood_t4"] = "scholarT4", ["warrior_blood_t1"] = "warriorT1", ["warrior_blood_t2"] = "warriorT2", ["warrior_blood_t3"] = "warriorT3", ["warrior_blood_t4"] = "warriorT4", ["worker_blood_t1"] = "workerT1", ["worker_blood_t2"] = "workerT2", ["worker_blood_t3"] = "workerT3", ["worker_blood_t4"] = "workerT4", ["general_blood_t5"] = "generalT5" }; internal static string NormalizeStatKey(string key) { if (!string.IsNullOrWhiteSpace(key)) { if (!StatKeyAliases.TryGetValue(key.Trim(), out string value)) { return key.Trim(); } return value; } return key; } internal static string NormalizeBuffKey(string key) { if (!string.IsNullOrWhiteSpace(key)) { if (!BuffKeyAliases.TryGetValue(key.Trim(), out string value)) { return key.Trim(); } return value; } return key; } internal static bool IsLegacyStatKey(string key) { if (!string.IsNullOrWhiteSpace(key)) { return StatKeyAliases.ContainsKey(key.Trim()); } return false; } internal static bool IsLegacyBuffKey(string key) { if (!string.IsNullOrWhiteSpace(key)) { return BuffKeyAliases.ContainsKey(key.Trim()); } return false; } } internal static class OwnershipService { public readonly struct UserReapplyResult { public readonly int BuffsChecked; public readonly int BuffsAlreadyActive; public readonly int BuffLifetimeRefreshes; public readonly int BuffApplyAttempts; public readonly int BuffApplySuccesses; public readonly bool StatsAttempted; public readonly bool StatsApplied; public static UserReapplyResult Empty => new UserReapplyResult(0, 0, 0, 0, 0, statsAttempted: false, statsApplied: false); public UserReapplyResult(int buffsChecked, int buffsAlreadyActive, int buffLifetimeRefreshes, int buffApplyAttempts, int buffApplySuccesses, bool statsAttempted, bool statsApplied) { BuffsChecked = buffsChecked; BuffsAlreadyActive = buffsAlreadyActive; BuffLifetimeRefreshes = buffLifetimeRefreshes; BuffApplyAttempts = buffApplyAttempts; BuffApplySuccesses = buffApplySuccesses; StatsAttempted = statsAttempted; StatsApplied = statsApplied; } } public readonly struct OnlineReapplyResult { public readonly int Checked; public readonly int Processed; public readonly int SkippedInvalid; public readonly int BuffsChecked; public readonly int BuffsAlreadyActive; public readonly int BuffLifetimeRefreshes; public readonly int BuffApplyAttempts; public readonly int BuffApplySuccesses; public readonly int StatsAttempted; public readonly int StatsApplied; public static OnlineReapplyResult Empty => new OnlineReapplyResult(0, 0, 0, 0, 0, 0, 0, 0, 0, 0); public OnlineReapplyResult(int checkedUsers, int processed, int skippedInvalid, int buffsChecked, int buffsAlreadyActive, int buffLifetimeRefreshes, int buffApplyAttempts, int buffApplySuccesses, int statsAttempted, int statsApplied) { Checked = checkedUsers; Processed = processed; SkippedInvalid = skippedInvalid; BuffsChecked = buffsChecked; BuffsAlreadyActive = buffsAlreadyActive; BuffLifetimeRefreshes = buffLifetimeRefreshes; BuffApplyAttempts = buffApplyAttempts; BuffApplySuccesses = buffApplySuccesses; StatsAttempted = statsAttempted; StatsApplied = statsApplied; } } private static readonly object Lock = new object(); private static readonly string StoreFile = Path.Combine(ConfigService.ConfigDir, "ownedbuffs.json"); private static readonly Dictionary OnlineUsers = new Dictionary(); private static OwnershipStore _store = new OwnershipStore(); private static bool _loaded; private static bool _dirty; private static DateTime _lastSaveUtc = DateTime.MinValue; private static int _reapplyCursor; public static bool HasPendingSave { get { lock (Lock) { return _dirty; } } } public static string LastSaveUtc { get { if (!(_lastSaveUtc == DateTime.MinValue)) { return _lastSaveUtc.ToString("O"); } return "never"; } } public static int ReapplyCursor { get { lock (Lock) { return _reapplyCursor; } } } public static void Initialize() { Load(); } public static void RegisterOnlineUser(ulong platformId, Entity userEntity) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0004: 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) if (platformId == 0L || userEntity == Entity.Null) { return; } lock (Lock) { OnlineUsers[platformId] = userEntity; } } public static void UnregisterOnlineUser(ulong platformId) { if (platformId == 0L) { return; } lock (Lock) { OnlineUsers.Remove(platformId); } } public static Dictionary GetOnlineUsersSnapshot() { lock (Lock) { return OnlineUsers.ToDictionary((KeyValuePair kv) => kv.Key, (KeyValuePair kv) => kv.Value); } } public static bool PlayerOwns(ulong platformId, string key) { string key2 = key; if (platformId == 0L || string.IsNullOrWhiteSpace(key2)) { return false; } Load(); lock (Lock) { PlayerOwnedBuffs value; return _store.Players.TryGetValue(platformId, out value) && ((value.OwnedBuffKeys != null && value.OwnedBuffKeys.Any((string k) => string.Equals(k, key2, StringComparison.OrdinalIgnoreCase))) || (value.AdminGrantedBuffKeys != null && value.AdminGrantedBuffKeys.Any((string k) => string.Equals(k, key2, StringComparison.OrdinalIgnoreCase)))); } } public static void AddOwnedBuff(ulong platformId, string key) { string key2 = key; if (platformId == 0L || string.IsNullOrWhiteSpace(key2)) { return; } Load(); lock (Lock) { if (!_store.Players.TryGetValue(platformId, out PlayerOwnedBuffs value)) { value = new PlayerOwnedBuffs(); _store.Players[platformId] = value; } if (!value.OwnedBuffKeys.Any((string k) => string.Equals(k, key2, StringComparison.OrdinalIgnoreCase))) { value.OwnedBuffKeys.Add(key2.Trim()); value.OwnedBuffKeys.Sort(StringComparer.OrdinalIgnoreCase); MarkDirty_NoLock(); } } } public static bool EnsureAdminGrantedBuff(ulong platformId, string key) { string key2 = key; if (platformId == 0L || string.IsNullOrWhiteSpace(key2)) { return false; } Load(); lock (Lock) { if (!_store.Players.TryGetValue(platformId, out PlayerOwnedBuffs value)) { value = new PlayerOwnedBuffs(); _store.Players[platformId] = value; } PlayerOwnedBuffs playerOwnedBuffs = value; if (playerOwnedBuffs.AdminGrantedBuffKeys == null) { List list2 = (playerOwnedBuffs.AdminGrantedBuffKeys = new List()); } if (value.AdminGrantedBuffKeys.Any((string k) => string.Equals(k, key2, StringComparison.OrdinalIgnoreCase))) { return false; } value.AdminGrantedBuffKeys.Add(key2.Trim()); value.AdminGrantedBuffKeys.Sort(StringComparer.OrdinalIgnoreCase); MarkDirty_NoLock(); return true; } } public static List GetAdminGrantedBuffKeys(ulong platformId) { if (platformId == 0L) { return new List(); } Load(); lock (Lock) { PlayerOwnedBuffs value; return (_store.Players.TryGetValue(platformId, out value) && value.AdminGrantedBuffKeys != null) ? value.AdminGrantedBuffKeys.Distinct(StringComparer.OrdinalIgnoreCase).OrderBy((string x) => x, StringComparer.OrdinalIgnoreCase).ToList() : new List(); } } public static int CountOwnedBuffsInCategory(ulong platformId, string categoryKey) { string categoryKey2 = categoryKey; if (platformId == 0L || string.IsNullOrWhiteSpace(categoryKey2)) { return 0; } Load(); ShopConfigRoot config = ConfigService.Shop; lock (Lock) { if (!_store.Players.TryGetValue(platformId, out PlayerOwnedBuffs value)) { return 0; } PerkShopEntry value2; return (value.OwnedBuffKeys ?? new List()).Concat(value.AdminGrantedBuffKeys ?? new List()).Distinct(StringComparer.OrdinalIgnoreCase).Count((string key) => config.Buffs.TryGetValue(key, out value2) && string.Equals(value2.Category, categoryKey2, StringComparison.OrdinalIgnoreCase)); } } public static List GetOwnedBuffKeys(ulong platformId) { if (platformId == 0L) { return new List(); } Load(); lock (Lock) { PlayerOwnedBuffs value; return (_store.Players.TryGetValue(platformId, out value) && value.OwnedBuffKeys != null) ? value.OwnedBuffKeys.Distinct(StringComparer.OrdinalIgnoreCase).OrderBy((string x) => x, StringComparer.OrdinalIgnoreCase).ToList() : new List(); } } public static bool EnsureOwnedBuff(ulong platformId, string key) { string key2 = key; if (platformId == 0L || string.IsNullOrWhiteSpace(key2)) { return false; } Load(); lock (Lock) { if (!_store.Players.TryGetValue(platformId, out PlayerOwnedBuffs value)) { value = new PlayerOwnedBuffs(); _store.Players[platformId] = value; } if (value.OwnedBuffKeys.Any((string k) => string.Equals(k, key2, StringComparison.OrdinalIgnoreCase))) { return false; } value.OwnedBuffKeys.Add(key2.Trim()); value.OwnedBuffKeys.Sort(StringComparer.OrdinalIgnoreCase); MarkDirty_NoLock(); return true; } } public static Dictionary GetOwnedStats(ulong platformId) { if (platformId == 0L) { return new Dictionary(StringComparer.OrdinalIgnoreCase); } Load(); lock (Lock) { PlayerOwnedBuffs value; return (_store.Players.TryGetValue(platformId, out value) && value.OwnedStats != null) ? new Dictionary(value.OwnedStats, StringComparer.OrdinalIgnoreCase) : new Dictionary(StringComparer.OrdinalIgnoreCase); } } public static Dictionary GetAdminFlatStats(ulong platformId) { if (platformId == 0L) { return new Dictionary(StringComparer.OrdinalIgnoreCase); } Load(); lock (Lock) { PlayerOwnedBuffs value; return (_store.Players.TryGetValue(platformId, out value) && value.AdminFlatStats != null) ? new Dictionary(value.AdminFlatStats, StringComparer.OrdinalIgnoreCase) : new Dictionary(StringComparer.OrdinalIgnoreCase); } } public static float AddAdminFlatStat(ulong platformId, string unitStat, float amount) { if (platformId == 0L || string.IsNullOrWhiteSpace(unitStat) || Math.Abs(amount) <= 0.0001f) { return 0f; } Load(); lock (Lock) { if (!_store.Players.TryGetValue(platformId, out PlayerOwnedBuffs value)) { value = new PlayerOwnedBuffs(); _store.Players[platformId] = value; } PlayerOwnedBuffs playerOwnedBuffs = value; if (playerOwnedBuffs.AdminFlatStats == null) { Dictionary dictionary2 = (playerOwnedBuffs.AdminFlatStats = new Dictionary(StringComparer.OrdinalIgnoreCase)); } unitStat = unitStat.Trim(); value.AdminFlatStats.TryGetValue(unitStat, out var value2); value.AdminFlatStats[unitStat] = value2 + amount; if (Math.Abs(value.AdminFlatStats[unitStat]) <= 0.0001f) { value.AdminFlatStats.Remove(unitStat); } MarkDirty_NoLock(); float value3; return value.AdminFlatStats.TryGetValue(unitStat, out value3) ? value3 : 0f; } } public static bool ClearAdminFlatStat(ulong platformId, string unitStat) { if (platformId == 0L || string.IsNullOrWhiteSpace(unitStat)) { return false; } Load(); lock (Lock) { if (!_store.Players.TryGetValue(platformId, out PlayerOwnedBuffs value) || value.AdminFlatStats == null) { return false; } bool num = value.AdminFlatStats.Remove(unitStat.Trim()); if (value.OwnedBuffKeys.Count == 0 && value.OwnedStats.Count == 0 && value.AdminFlatStats.Count == 0) { _store.Players.Remove(platformId); } if (num) { MarkDirty_NoLock(); } return num; } } public static int GetOwnedStatCount(ulong platformId, string key) { if (platformId == 0L || string.IsNullOrWhiteSpace(key)) { return 0; } Load(); lock (Lock) { PlayerOwnedBuffs value; int value2; return (_store.Players.TryGetValue(platformId, out value) && value.OwnedStats != null && value.OwnedStats.TryGetValue(key, out value2)) ? value2 : 0; } } public static int CountOwnedStatTypes(ulong platformId) { if (platformId == 0L) { return 0; } Load(); lock (Lock) { PlayerOwnedBuffs value; return (_store.Players.TryGetValue(platformId, out value) && value.OwnedStats != null) ? value.OwnedStats.Count>((KeyValuePair kv) => !string.IsNullOrWhiteSpace(kv.Key) && kv.Value > 0) : 0; } } public static int AddOwnedStat(ulong platformId, string key, int amount = 1) { if (platformId == 0L || string.IsNullOrWhiteSpace(key) || amount <= 0) { return 0; } Load(); lock (Lock) { if (!_store.Players.TryGetValue(platformId, out PlayerOwnedBuffs value)) { value = new PlayerOwnedBuffs(); _store.Players[platformId] = value; } PlayerOwnedBuffs playerOwnedBuffs = value; if (playerOwnedBuffs.OwnedStats == null) { Dictionary dictionary2 = (playerOwnedBuffs.OwnedStats = new Dictionary(StringComparer.OrdinalIgnoreCase)); } key = key.Trim(); value.OwnedStats.TryGetValue(key, out var value2); value.OwnedStats[key] = value2 + amount; MarkDirty_NoLock(); return value.OwnedStats[key]; } } public static bool RemoveOwnedStat(ulong platformId, string key, int amount = 1) { if (platformId == 0L || string.IsNullOrWhiteSpace(key) || amount <= 0) { return false; } Load(); lock (Lock) { if (!_store.Players.TryGetValue(platformId, out PlayerOwnedBuffs value) || value.OwnedStats == null) { return false; } key = key.Trim(); if (!value.OwnedStats.TryGetValue(key, out var value2)) { return false; } int num = value2 - amount; if (num > 0) { value.OwnedStats[key] = num; } else { value.OwnedStats.Remove(key); } if (value.OwnedBuffKeys.Count == 0 && (value.AdminGrantedBuffKeys == null || value.AdminGrantedBuffKeys.Count == 0) && value.OwnedStats.Count == 0 && (value.AdminFlatStats == null || value.AdminFlatStats.Count == 0)) { _store.Players.Remove(platformId); } MarkDirty_NoLock(); return true; } } public static bool RemovePurchasedBuffOnly(ulong platformId, string key) { string key2 = key; if (platformId == 0L || string.IsNullOrWhiteSpace(key2)) { return false; } Load(); lock (Lock) { if (!_store.Players.TryGetValue(platformId, out PlayerOwnedBuffs value)) { return false; } PlayerOwnedBuffs playerOwnedBuffs = value; if (playerOwnedBuffs.OwnedBuffKeys == null) { List list2 = (playerOwnedBuffs.OwnedBuffKeys = new List()); } if (value.OwnedBuffKeys.RemoveAll((string k) => string.Equals(k, key2, StringComparison.OrdinalIgnoreCase)) <= 0) { return false; } if (value.OwnedBuffKeys.Count == 0 && (value.AdminGrantedBuffKeys == null || value.AdminGrantedBuffKeys.Count == 0) && (value.OwnedStats == null || value.OwnedStats.Count == 0) && (value.AdminFlatStats == null || value.AdminFlatStats.Count == 0)) { _store.Players.Remove(platformId); } MarkDirty_NoLock(); return true; } } public static bool RemoveOwnedBuff(ulong platformId, string key) { string key2 = key; if (platformId == 0L || string.IsNullOrWhiteSpace(key2)) { return false; } Load(); lock (Lock) { if (!_store.Players.TryGetValue(platformId, out PlayerOwnedBuffs value)) { return false; } PlayerOwnedBuffs playerOwnedBuffs = value; if (playerOwnedBuffs.OwnedBuffKeys == null) { List list2 = (playerOwnedBuffs.OwnedBuffKeys = new List()); } playerOwnedBuffs = value; if (playerOwnedBuffs.AdminGrantedBuffKeys == null) { List list2 = (playerOwnedBuffs.AdminGrantedBuffKeys = new List()); } int num = value.OwnedBuffKeys.RemoveAll((string k) => string.Equals(k, key2, StringComparison.OrdinalIgnoreCase)); int num2 = value.AdminGrantedBuffKeys.RemoveAll((string k) => string.Equals(k, key2, StringComparison.OrdinalIgnoreCase)); int num3; if (num <= 0) { num3 = ((num2 > 0) ? 1 : 0); if (num3 == 0) { goto IL_011e; } } else { num3 = 1; } if (value.OwnedBuffKeys.Count == 0 && value.AdminGrantedBuffKeys.Count == 0 && (value.OwnedStats == null || value.OwnedStats.Count == 0) && (value.AdminFlatStats == null || value.AdminFlatStats.Count == 0)) { _store.Players.Remove(platformId); } MarkDirty_NoLock(); goto IL_011e; IL_011e: return (byte)num3 != 0; } } public static int RemoveOwnedBuffsInCategory(ulong platformId, string categoryKey, string? exceptKey = null) { string categoryKey2 = categoryKey; string exceptKey2 = exceptKey; if (platformId == 0L || string.IsNullOrWhiteSpace(categoryKey2)) { return 0; } Load(); ShopConfigRoot shop = ConfigService.Shop; HashSet keysToRemove = (from kv in shop.Buffs where kv.Value != null && string.Equals(kv.Value.Category, categoryKey2, StringComparison.OrdinalIgnoreCase) && (string.IsNullOrWhiteSpace(exceptKey2) || !string.Equals(kv.Key, exceptKey2, StringComparison.OrdinalIgnoreCase)) select kv.Key).ToHashSet(StringComparer.OrdinalIgnoreCase); if (keysToRemove.Count == 0) { return 0; } lock (Lock) { if (!_store.Players.TryGetValue(platformId, out PlayerOwnedBuffs value)) { return 0; } PlayerOwnedBuffs playerOwnedBuffs = value; if (playerOwnedBuffs.OwnedBuffKeys == null) { List list2 = (playerOwnedBuffs.OwnedBuffKeys = new List()); } playerOwnedBuffs = value; if (playerOwnedBuffs.AdminGrantedBuffKeys == null) { List list2 = (playerOwnedBuffs.AdminGrantedBuffKeys = new List()); } int num = 0 + value.OwnedBuffKeys.RemoveAll((string k) => keysToRemove.Contains(k)) + value.AdminGrantedBuffKeys.RemoveAll((string k) => keysToRemove.Contains(k)); if (num > 0) { if (value.OwnedBuffKeys.Count == 0 && value.AdminGrantedBuffKeys.Count == 0 && (value.OwnedStats == null || value.OwnedStats.Count == 0) && (value.AdminFlatStats == null || value.AdminFlatStats.Count == 0)) { _store.Players.Remove(platformId); } MarkDirty_NoLock(); } return num; } } public static UserReapplyResult ReapplyOwnedBuffsForUser(Entity userEntity, bool onlyIfMissing = true) { //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_0009: 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_001a: Unknown result type (might be due to invalid IL or missing references) //IL_002e: 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_0037: 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_003d: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0066: 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_0068: 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_007d: Unknown result type (might be due to invalid IL or missing references) //IL_02c3: Unknown result type (might be due to invalid IL or missing references) //IL_01e7: Unknown result type (might be due to invalid IL or missing references) //IL_01e8: Unknown result type (might be due to invalid IL or missing references) //IL_024a: Unknown result type (might be due to invalid IL or missing references) //IL_024b: Unknown result type (might be due to invalid IL or missing references) //IL_024c: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_020b: Unknown result type (might be due to invalid IL or missing references) //IL_020c: Unknown result type (might be due to invalid IL or missing references) try { EntityManager entityManager = Core.EntityManager; if (((EntityManager)(ref entityManager)).Exists(userEntity)) { entityManager = Core.EntityManager; if (((EntityManager)(ref entityManager)).HasComponent(userEntity)) { entityManager = Core.EntityManager; User componentData = ((EntityManager)(ref entityManager)).GetComponentData(userEntity); ulong platformId = componentData.PlatformId; if (platformId == 0L) { return UserReapplyResult.Empty; } RegisterOnlineUser(platformId, userEntity); Entity entity = componentData.LocalCharacter._Entity; if (!(entity == Entity.Null)) { entityManager = Core.EntityManager; if (((EntityManager)(ref entityManager)).Exists(entity)) { ShopConfigRoot shop = ConfigService.Shop; if (!shop.Enabled || !shop.SaveOwnership) { return UserReapplyResult.Empty; } List list; lock (Lock) { Load_NoLock(); list = (_store.Players.TryGetValue(platformId, out PlayerOwnedBuffs value) ? (value.OwnedBuffKeys ?? new List()).Concat(value.AdminGrantedBuffKeys ?? new List()).Distinct(StringComparer.OrdinalIgnoreCase).ToList() : new List()); } int num = 0; int num2 = 0; int num3 = 0; int num4 = 0; int num5 = 0; PrefabGUID buffPrefab = default(PrefabGUID); foreach (string item in list) { if (!shop.Buffs.TryGetValue(item, out PerkShopEntry value2) || !value2.Enabled || !value2.PersistentPurchase || value2.BuffPrefab == 0) { continue; } if (string.Equals(value2.Category, "blood_buff", StringComparison.OrdinalIgnoreCase) && !shop.EnableExperimentalBloodBuffs) { BuffService.RemoveConfiguredCategoryBuffs(entity, "blood_buff"); continue; } num++; ((PrefabGUID)(ref buffPrefab))..ctor(value2.BuffPrefab); bool flag = string.Equals(value2.Category, "blood_buff", StringComparison.OrdinalIgnoreCase); bool flag2 = BuffService.IsRenewableTimedEntry(shop, value2); if (BuffService.HasBuff(entity, buffPrefab)) { num2++; if (flag2) { continue; } if (!flag && value2.MutateAppliedBuffLifetime && BuffService.ForceOwnedBuffLifetime(entity, buffPrefab, BuffService.ResolveDurationSeconds(shop, value2), BuffService.ResolvePersistThroughDeath(shop, value2), value2.KeepVisibleTimerFrozen, value2.VisibleTimerSeconds, BuffService.PreserveVanillaCleanup(shop, value2))) { num3++; } if (onlyIfMissing) { continue; } } num4++; if (BuffService.ApplyPurchasedBuff(userEntity, entity, buffPrefab, preventDuplicate: true, shop.AllowBuffEntityMutation, !flag && value2.MutateAppliedBuffLifetime, BuffService.ResolveDurationSeconds(shop, value2), BuffService.ResolvePersistThroughDeath(shop, value2), value2.KeepVisibleTimerFrozen, value2.VisibleTimerSeconds, BuffService.PreserveVanillaCleanup(shop, value2))) { num5++; } } bool statsAttempted = false; bool statsApplied = false; if (shop.EnableStatShop) { statsAttempted = true; statsApplied = StatService.ApplyOwnedStatsForUser(userEntity); } return new UserReapplyResult(num, num2, num3, num4, num5, statsAttempted, statsApplied); } } return UserReapplyResult.Empty; } } return UserReapplyResult.Empty; } catch (Exception e) { Core.LogException(e, "ReapplyOwnedBuffsForUser"); return UserReapplyResult.Empty; } } public static void ReapplyOnlineOwnedBuffs() { ShopConfigRoot shop = ConfigService.Shop; if (shop.ReapplyOwnedBuffsWhenMissing) { ReapplyOnlineOwnedBuffs(onlyIfMissing: true, shop.ReapplyMaxUsersPerCycle); } } public static OnlineReapplyResult ReapplyOnlineOwnedBuffs(bool onlyIfMissing) { return ReapplyOnlineOwnedBuffs(onlyIfMissing, 0); } public static OnlineReapplyResult ReapplyOnlineOwnedBuffs(bool onlyIfMissing, int maxUsers) { //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: 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_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) ShopConfigRoot shop = ConfigService.Shop; if (!shop.Enabled || !shop.SaveOwnership) { return OnlineReapplyResult.Empty; } List list; lock (Lock) { list = OnlineUsers.Values.ToList(); if (maxUsers > 0 && list.Count > maxUsers) { if (_reapplyCursor >= list.Count) { _reapplyCursor = 0; } list = list.Skip(_reapplyCursor).Concat(list.Take(_reapplyCursor)).Take(maxUsers) .ToList(); _reapplyCursor = (_reapplyCursor + maxUsers) % Math.Max(1, OnlineUsers.Count); } } int num = 0; int num2 = 0; int num3 = 0; int num4 = 0; int num5 = 0; int num6 = 0; int num7 = 0; int num8 = 0; int num9 = 0; int num10 = 0; foreach (Entity item in list) { num++; if (!(item == Entity.Null)) { EntityManager entityManager = Core.EntityManager; if (((EntityManager)(ref entityManager)).Exists(item)) { entityManager = Core.EntityManager; if (((EntityManager)(ref entityManager)).HasComponent(item)) { UserReapplyResult userReapplyResult = ReapplyOwnedBuffsForUser(item, onlyIfMissing); num2++; num4 += userReapplyResult.BuffsChecked; num5 += userReapplyResult.BuffsAlreadyActive; num6 += userReapplyResult.BuffLifetimeRefreshes; num7 += userReapplyResult.BuffApplyAttempts; num8 += userReapplyResult.BuffApplySuccesses; if (userReapplyResult.StatsAttempted) { num9++; } if (userReapplyResult.StatsApplied) { num10++; } continue; } } } num3++; } return new OnlineReapplyResult(num, num2, num3, num4, num5, num6, num7, num8, num9, num10); } private static bool NormalizeStoredKeys_NoLock() { bool flag = false; foreach (PlayerOwnedBuffs value3 in _store.Players.Values) { if (value3 == null) { continue; } flag |= NormalizeBuffKeyList_NoLock(value3.OwnedBuffKeys); flag |= NormalizeBuffKeyList_NoLock(value3.AdminGrantedBuffKeys); if (value3.OwnedStats == null) { continue; } foreach (KeyValuePair statKeyAlias in KeyAliasService.StatKeyAliases) { if (value3.OwnedStats.TryGetValue(statKeyAlias.Key, out var value)) { value3.OwnedStats.Remove(statKeyAlias.Key); value3.OwnedStats.TryGetValue(statKeyAlias.Value, out var value2); value3.OwnedStats[statKeyAlias.Value] = value2 + value; flag = true; } } } return flag; } private static bool NormalizeBuffKeyList_NoLock(List? keys) { if (keys == null || keys.Count == 0) { return false; } bool flag = false; List list = new List(); foreach (string key in keys) { string mapped = KeyAliasService.NormalizeBuffKey(key); if (!string.Equals(key, mapped, StringComparison.Ordinal)) { flag = true; } if (!list.Any((string existing) => string.Equals(existing, mapped, StringComparison.OrdinalIgnoreCase))) { list.Add(mapped); } else { flag = true; } } if (!flag) { return false; } keys.Clear(); keys.AddRange(list.OrderBy((string x) => x, StringComparer.OrdinalIgnoreCase)); return true; } private static void Load() { lock (Lock) { Load_NoLock(); } } private static void Load_NoLock() { if (_loaded) { return; } try { Directory.CreateDirectory(ConfigService.ConfigDir); if (File.Exists(StoreFile)) { _store = JsonSerializer.Deserialize(File.ReadAllText(StoreFile), ConfigService.JsonOptions) ?? new OwnershipStore(); } else { _store = new OwnershipStore(); } OwnershipStore store = _store; if (store.Players == null) { Dictionary dictionary2 = (store.Players = new Dictionary()); } foreach (ulong item in _store.Players.Keys.ToList()) { PlayerOwnedBuffs playerOwnedBuffs = _store.Players[item]; if (playerOwnedBuffs.OwnedBuffKeys == null) { List list2 = (playerOwnedBuffs.OwnedBuffKeys = new List()); } playerOwnedBuffs = _store.Players[item]; if (playerOwnedBuffs.AdminGrantedBuffKeys == null) { List list2 = (playerOwnedBuffs.AdminGrantedBuffKeys = new List()); } playerOwnedBuffs = _store.Players[item]; if (playerOwnedBuffs.OwnedStats == null) { Dictionary dictionary4 = (playerOwnedBuffs.OwnedStats = new Dictionary(StringComparer.OrdinalIgnoreCase)); } playerOwnedBuffs = _store.Players[item]; if (playerOwnedBuffs.AdminFlatStats == null) { Dictionary dictionary6 = (playerOwnedBuffs.AdminFlatStats = new Dictionary(StringComparer.OrdinalIgnoreCase)); } } if (NormalizeStoredKeys_NoLock()) { _dirty = true; } _loaded = true; } catch (Exception e) { Core.LogException(e, "Load_NoLock"); _store = new OwnershipStore(); _loaded = true; } } private static void MarkDirty_NoLock() { _dirty = true; if (ConfigService.Shop.OwnershipSaveDebounceSeconds <= 0f) { Save_NoLock(); } } public static void FlushPendingSaves(bool force = false) { lock (Lock) { if (_dirty) { float ownershipSaveDebounceSeconds = ConfigService.Shop.OwnershipSaveDebounceSeconds; if (force || !(ownershipSaveDebounceSeconds > 0f) || !((DateTime.UtcNow - _lastSaveUtc).TotalSeconds < (double)ownershipSaveDebounceSeconds)) { Save_NoLock(); } } } } private static void Save_NoLock() { Directory.CreateDirectory(ConfigService.ConfigDir); File.WriteAllText(StoreFile, JsonSerializer.Serialize(_store, ConfigService.JsonOptions)); _lastSaveUtc = DateTime.UtcNow; _dirty = false; } } internal static class PlayerCacheService { private static readonly object Lock = new object(); private static readonly string CacheFile = Path.Combine(ConfigService.ConfigDir, "playercache.json"); private static PlayerCacheStore _store = new PlayerCacheStore(); private static bool _loaded; private static bool _dirty; private static DateTime _lastSaveUtc = DateTime.MinValue; public static int CachedPlayerCount { get { Load(); lock (Lock) { return _store.Players?.Count ?? 0; } } } public static bool HasPendingSave { get { lock (Lock) { return _dirty; } } } public static void Initialize() { Load(); } public static void Remember(User user) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) string characterName = ((object)(FixedString64Bytes)(ref user.CharacterName)).ToString(); Remember(user.PlatformId, characterName); } public static void Remember(ulong platformId, string characterName) { if (platformId == 0L || string.IsNullOrWhiteSpace(characterName)) { return; } Load(); lock (Lock) { PlayerCacheStore store = _store; if (store.Players == null) { Dictionary dictionary2 = (store.Players = new Dictionary()); } string text = characterName.Trim(); string lastSeenUtc = DateTime.UtcNow.ToString("O"); PlayerCacheEntry playerCacheEntry = null; bool flag = true; if (_store.Players.TryGetValue(platformId, out PlayerCacheEntry value)) { playerCacheEntry = value; flag = !string.Equals(value.CharacterName, text, StringComparison.Ordinal); } if (flag || playerCacheEntry == null || !DateTime.TryParse(playerCacheEntry.LastSeenUtc, out var result) || !((DateTime.UtcNow - result.ToUniversalTime()).TotalMinutes < 10.0)) { _store.Players[platformId] = new PlayerCacheEntry { CharacterName = text, LastSeenUtc = lastSeenUtc }; MarkDirty_NoLock(); } } } public static bool TryGetName(ulong platformId, out string characterName) { characterName = string.Empty; if (platformId == 0L) { return false; } Load(); lock (Lock) { if (_store.Players != null && _store.Players.TryGetValue(platformId, out PlayerCacheEntry value) && !string.IsNullOrWhiteSpace(value.CharacterName)) { characterName = value.CharacterName.Trim(); return true; } } return false; } public static void FlushPendingSaves(bool force = false) { lock (Lock) { if (_dirty) { float playerCacheSaveDebounceSeconds = ConfigService.Shop.PlayerCacheSaveDebounceSeconds; if (force || !(playerCacheSaveDebounceSeconds > 0f) || !((DateTime.UtcNow - _lastSaveUtc).TotalSeconds < (double)playerCacheSaveDebounceSeconds)) { Save_NoLock(); } } } } private static void Load() { lock (Lock) { if (_loaded) { return; } try { Directory.CreateDirectory(ConfigService.ConfigDir); if (File.Exists(CacheFile)) { _store = JsonSerializer.Deserialize(File.ReadAllText(CacheFile), ConfigService.JsonOptions) ?? new PlayerCacheStore(); } else { _store = new PlayerCacheStore(); } PlayerCacheStore store = _store; if (store.Players == null) { Dictionary dictionary2 = (store.Players = new Dictionary()); } _loaded = true; } catch (Exception e) { Core.LogException(e, "Load"); _store = new PlayerCacheStore(); _loaded = true; } } } private static void MarkDirty_NoLock() { _dirty = true; if (ConfigService.Shop.PlayerCacheSaveDebounceSeconds <= 0f) { Save_NoLock(); } } private static void Save_NoLock() { Directory.CreateDirectory(ConfigService.ConfigDir); File.WriteAllText(CacheFile, JsonSerializer.Serialize(_store, ConfigService.JsonOptions)); _lastSaveUtc = DateTime.UtcNow; _dirty = false; } } internal static class QueryService { private static EntityQuery? _scriptSpawnServerQuery; private static EntityQuery? _buffSystemSpawnServerQuery; internal static void Initialize(SystemService systems) { _scriptSpawnServerQuery = null; _buffSystemSpawnServerQuery = null; } internal static EntityQuery GetScriptSpawnServerQuery(object instance) { //IL_0018: 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_0011: Unknown result type (might be due to invalid IL or missing references) if (_scriptSpawnServerQuery.HasValue) { return _scriptSpawnServerQuery.Value; } _scriptSpawnServerQuery = ResolveScriptSpawnServerQuery(instance); return _scriptSpawnServerQuery.Value; } internal static EntityQuery GetBuffSystemSpawnServerQuery(object instance) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0036: 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) if (_buffSystemSpawnServerQuery.HasValue) { return _buffSystemSpawnServerQuery.Value; } _buffSystemSpawnServerQuery = ResolveSystemQuery(instance, "BuffSystem_Spawn_Server", "_Query"); return _buffSystemSpawnServerQuery.Value; } internal static void ResetRuntimeCaches() { _scriptSpawnServerQuery = null; _buffSystemSpawnServerQuery = null; } private static EntityQuery ResolveScriptSpawnServerQuery(object instance) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) return ResolveSystemQuery(instance, "ScriptSpawnServer", "_EntityQuery"); } private static EntityQuery ResolveSystemQuery(object instance, string systemName, string preferredQueryName) { //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_007a: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: 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_0129: Expected O, but got Unknown //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: Expected O, but got Unknown //IL_01ed: Unknown result type (might be due to invalid IL or missing references) Type type = instance.GetType(); if (type.GetField(preferredQueryName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(instance) is EntityQuery result) { Core.LogDebugIfEnabled($"[QueryService] {systemName} query resolved from {preferredQueryName} field."); return result; } if (type.GetProperty(preferredQueryName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(instance) is EntityQuery result2) { Core.LogDebugIfEnabled($"[QueryService] {systemName} query resolved from {preferredQueryName} property."); return result2; } bool flag = default(bool); if (type.GetProperty("EntityQueries", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(instance) is EntityQuery[] array && array.Length != 0) { ManualLogSource log = Core.Log; BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(67, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[QueryService] "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(systemName); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("."); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(preferredQueryName); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" was unavailable; falling back to EntityQueries[0]."); } log.LogWarning(val); return array[0]; } if (type.GetField("EntityQueries", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(instance) is EntityQuery[] array2 && array2.Length != 0) { ManualLogSource log2 = Core.Log; BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(67, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[QueryService] "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(systemName); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("."); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(preferredQueryName); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" was unavailable; falling back to EntityQueries[0]."); } log2.LogWarning(val); return array2[0]; } throw new InvalidOperationException("Unable to resolve " + systemName + " query."); } } internal static class ShopService { private const int ListPageSize = 8; private const int StatusPageSize = 12; private static string InferStatGroup(string key, string unitStat) { string text = (key + " " + unitStat).ToLowerInvariant(); if (text.Contains("power") || text.Contains("minion")) { return "Power"; } if (text.Contains("health") || text.Contains("resistance") || text.Contains("reduction") || text.Contains("healing")) { return "Health / Defense"; } if (text.Contains("speed") || text.Contains("cooldown")) { return "Speed / Cooldown"; } if (text.Contains("critical") || text.Contains("crit")) { return "Critical"; } if (text.Contains("leech")) { return "Leech"; } return "Utility"; } private static string FormatNumber(float value) { if (!(Math.Abs(value % 1f) <= 0.0001f)) { return value.ToString("0.####"); } return ((int)value).ToString(); } private static (int page, int totalPages) NormalizePage(int requestedPage, int totalItems) { int num = Math.Max(1, (int)Math.Ceiling((double)totalItems / 8.0)); return (Math.Clamp((requestedPage <= 0) ? 1 : requestedPage, 1, num), num); } private static IEnumerable> VisibleCategories(ulong platformId) { ShopConfigRoot config = ConfigService.Shop; return config.Categories.Where>(delegate(KeyValuePair kv) { if (!string.Equals(kv.Key, "potion_elixir", StringComparison.OrdinalIgnoreCase)) { return true; } bool num = config.Buffs.Values.Any((PerkShopEntry entry) => string.Equals(entry.Category, kv.Key, StringComparison.OrdinalIgnoreCase)); bool flag = platformId != 0L && OwnershipService.CountOwnedBuffsInCategory(platformId, kv.Key) > 0; return num || flag; }); } public static void Menu(Entity userEntity, Action reply) { ShopConfigRoot shop = ConfigService.Shop; string text = (shop.EnableBuffWhitelist ? "Whitelist ON" : "Open"); string text2 = (shop.EnableStatWhitelist ? "Whitelist ON" : "Open"); reply("=== PerkShop Menu ===\nCurrency: " + shop.CurrencyName + "\nPerk Shop: " + text + "\n .perk bufflist \n .perk buffdet \n .perk buffbuy \n .perk buffremove \n\nStat Shop: " + text2 + "\n .perk statlist \n .perk statdet \n .perk statbuy \n .perk statremove \n\nUtility:\n .perk status \n .perk search \n .perk sync\n\nOwned perks are restored manually per session with .perk sync."); } public static void Status(Entity userEntity, int page, Action reply) { //IL_0006: 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_0013: 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) //IL_0030: 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_0039: 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_0072: Unknown result type (might be due to invalid IL or missing references) try { if (PlayerStateHelper.Exists(userEntity)) { EntityManager entityManager = Core.EntityManager; if (((EntityManager)(ref entityManager)).HasComponent(userEntity)) { entityManager = Core.EntityManager; User user = ((EntityManager)(ref entityManager)).GetComponentData(userEntity); ShopConfigRoot config = ConfigService.Shop; List list = new List { $"Player: {user.CharacterName}" }; string[] array = (from kv in VisibleCategories(user.PlatformId) orderby kv.Key select kv).Select(delegate(KeyValuePair kv) { int value6 = OwnershipService.CountOwnedBuffsInCategory(user.PlatformId, kv.Key); string value7 = (kv.Value.MaxOwnedSlots.HasValue ? kv.Value.MaxOwnedSlots.Value.ToString() : "∞"); return $"{kv.Value.DisplayName}: {value6}/{value7}"; }).ToArray(); list.Add("Buff Slots: " + ((array.Length == 0) ? "none" : string.Join(" | ", array))); List ownedBuffKeys = OwnershipService.GetOwnedBuffKeys(user.PlatformId); list.Add("Owned Buffs:"); IEnumerable collection; if (ownedBuffKeys.Count != 0) { collection = ownedBuffKeys.Select((string k) => " " + k); } else { IEnumerable enumerable = new string[1] { " none" }; collection = enumerable; } list.AddRange(collection); List adminGrantedBuffKeys = OwnershipService.GetAdminGrantedBuffKeys(user.PlatformId); list.Add("Admin Given Buffs:"); IEnumerable collection2; if (adminGrantedBuffKeys.Count != 0) { collection2 = adminGrantedBuffKeys.Select((string k) => " " + k); } else { IEnumerable enumerable = new string[1] { " none" }; collection2 = enumerable; } list.AddRange(collection2); Dictionary ownedStats = OwnershipService.GetOwnedStats(user.PlatformId); Dictionary adminFlatStats = OwnershipService.GetAdminFlatStats(user.PlatformId); int value = OwnershipService.CountOwnedStatTypes(user.PlatformId); string value2 = ((config.EnableStatTypeSlots && config.MaxOwnedStatTypes > 0) ? config.MaxOwnedStatTypes.ToString() : "∞"); list.Add("Stat Slots:"); list.Add($" {value}/{value2}"); list.Add("Purchased Stats:"); string[] array2 = ownedStats.OrderBy, string>((KeyValuePair kv) => kv.Key, StringComparer.OrdinalIgnoreCase).Select(delegate(KeyValuePair kv) { if (config.Stats.TryGetValue(kv.Key, out StatShopEntry value4)) { float value5 = value4.ValuePerPurchase * (float)kv.Value; return $" {kv.Key} x{kv.Value} = +{FormatNumber(value5)} {value4.UnitStat}"; } return $" {kv.Key} x{kv.Value}"; }).ToArray(); list.AddRange((array2.Length != 0) ? array2 : new string[1] { " none" }); list.Add("Admin Flat Stats:"); string[] array3 = (from kv in adminFlatStats.OrderBy, string>((KeyValuePair kv) => kv.Key, StringComparer.OrdinalIgnoreCase) select " " + kv.Key + " +" + FormatNumber(kv.Value)).ToArray(); list.AddRange((array3.Length != 0) ? array3 : new string[1] { " none" }); (int page, int totalPages) tuple = NormalizePage(page, list.Count); int item = tuple.page; int item2 = tuple.totalPages; string[] value3 = list.Skip((item - 1) * 12).Take(12).ToArray(); reply($"=== PerkShop Status Page {item}/{item2} ===\n" + string.Join("\n", value3) + ((item < item2) ? $"\n\nNext page: .perk status {item + 1}" : "")); return; } } reply("User entity not ready."); } catch (Exception e) { Core.LogException(e, "Status"); reply("Could not read PerkShop status. Check server logs."); } } public static void BuyBuff(Entity userEntity, Entity characterEntity, string key, Action reply) { //IL_001e: 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_0034: 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_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_02b2: Unknown result type (might be due to invalid IL or missing references) //IL_02b3: Unknown result type (might be due to invalid IL or missing references) //IL_027c: Unknown result type (might be due to invalid IL or missing references) //IL_027d: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_0335: Unknown result type (might be due to invalid IL or missing references) //IL_0336: Unknown result type (might be due to invalid IL or missing references) //IL_03b2: Unknown result type (might be due to invalid IL or missing references) //IL_03b3: Unknown result type (might be due to invalid IL or missing references) //IL_03b4: Unknown result type (might be due to invalid IL or missing references) //IL_0418: Unknown result type (might be due to invalid IL or missing references) //IL_041d: Unknown result type (might be due to invalid IL or missing references) //IL_0421: Unknown result type (might be due to invalid IL or missing references) //IL_0422: Unknown result type (might be due to invalid IL or missing references) //IL_042a: Unknown result type (might be due to invalid IL or missing references) try { ShopConfigRoot shop = ConfigService.Shop; if (!shop.Enabled) { reply("Perk shop is disabled."); } else { if (!AccessService.CanAccessPerkShop(userEntity, reply)) { return; } if (!PlayerStateHelper.Exists(userEntity) || !PlayerStateHelper.Exists(characterEntity)) { reply("Player entity not ready."); return; } EntityManager entityManager = Core.EntityManager; if (!((EntityManager)(ref entityManager)).HasComponent(userEntity)) { reply("User component not ready."); return; } if (shop.BlockPurchasesInCombat && PlayerStateHelper.IsInCombat(characterEntity)) { reply("You cannot buy buffs while in combat."); return; } if (string.IsNullOrWhiteSpace(key)) { ListBuffKeys(userEntity, 1, reply); return; } key = KeyAliasService.NormalizeBuffKey(key); if (!shop.Buffs.TryGetValue(key, out PerkShopEntry value) || !value.Enabled) { reply("Unknown buff '" + key + "'. Use .perk bufflist or .perk search ."); return; } if (value.BuffPrefab == 0) { reply("Buff '" + key + "' has BuffPrefab = 0. Configure a valid buff prefab first."); return; } if (string.Equals(value.Category, "blood_buff", StringComparison.OrdinalIgnoreCase) && !shop.EnableExperimentalBloodBuffs) { reply("Blood-buff purchases are disabled by config. Set EnableExperimentalBloodBuffs = true to sell renewable 2-hour blood buffs."); return; } entityManager = Core.EntityManager; ulong platformId = ((EntityManager)(ref entityManager)).GetComponentData(userEntity).PlatformId; OwnershipService.RegisterOnlineUser(platformId, userEntity); if (shop.SaveOwnership && value.PersistentPurchase && OwnershipService.PlayerOwns(platformId, key)) { reply("You already own " + value.DisplayName + "."); OwnershipService.ReapplyOwnedBuffsForUser(userEntity); return; } PrefabGUID buffPrefab = default(PrefabGUID); ((PrefabGUID)(ref buffPrefab))..ctor(value.BuffPrefab); if (shop.SaveOwnership && value.PersistentPurchase && shop.Categories.TryGetValue(value.Category, out BuffCategoryDefinition value2) && value2.MaxOwnedSlots.HasValue) { int num = OwnershipService.CountOwnedBuffsInCategory(platformId, value.Category); if (num >= value2.MaxOwnedSlots.Value) { reply($"Category '{value2.DisplayName}' is full: {num}/{value2.MaxOwnedSlots.Value} owned slots used."); return; } } if (value.PreventDuplicate && BuffService.HasBuff(characterEntity, buffPrefab)) { reply("You already have " + value.DisplayName + "."); return; } PrefabGUID val = default(PrefabGUID); ((PrefabGUID)(ref val))..ctor(shop.CurrencyPrefab); int num2 = InventoryHelper.Count(characterEntity, val); if (num2 < value.Cost) { reply($"Not enough {shop.CurrencyName}: {num2}/{value.Cost}."); return; } if (!InventoryHelper.TryRemove(characterEntity, val, value.Cost)) { reply($"Could not remove {value.Cost} {shop.CurrencyName} from your inventory."); return; } bool flag = string.Equals(value.Category, "blood_buff", StringComparison.OrdinalIgnoreCase); if (!BuffService.ApplyPurchasedBuff(userEntity, characterEntity, buffPrefab, value.PreventDuplicate, flag ? shop.AllowBuffEntityMutation : (shop.ForcePermanentBuffs || shop.AllowBuffEntityMutation), !flag && (shop.ForcePermanentBuffs || value.MutateAppliedBuffLifetime), BuffService.ResolveDurationSeconds(shop, value), BuffService.ResolvePersistThroughDeath(shop, value), value.KeepVisibleTimerFrozen, value.VisibleTimerSeconds, BuffService.PreserveVanillaCleanup(shop, value))) { ServerGameManager serverGameManager = Core.ServerGameManager; ((ServerGameManager)(ref serverGameManager)).TryAddInventoryItem(characterEntity, val, value.Cost); reply("Could not apply " + value.DisplayName + ". Currency was refunded if the inventory accepted it."); return; } if (shop.SaveOwnership && value.PersistentPurchase) { OwnershipService.AddOwnedBuff(platformId, key); } string value3 = ((shop.SaveOwnership && value.PersistentPurchase) ? " Ownership saved." : string.Empty); reply($"Purchased {value.DisplayName} for {value.Cost} {shop.CurrencyName}.{value3}"); } } catch (Exception e) { Core.LogException(e, "BuyBuff"); reply("Buff purchase failed. Check server logs."); } } public static void RemoveBuff(Entity userEntity, Entity characterEntity, string key, Action reply) { //IL_001e: 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_0034: 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_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: 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_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: 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_010a: Unknown result type (might be due to invalid IL or missing references) //IL_028f: Unknown result type (might be due to invalid IL or missing references) //IL_0296: Unknown result type (might be due to invalid IL or missing references) //IL_02a8: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_0186: Unknown result type (might be due to invalid IL or missing references) //IL_0218: Unknown result type (might be due to invalid IL or missing references) //IL_0219: Unknown result type (might be due to invalid IL or missing references) try { ShopConfigRoot shop = ConfigService.Shop; if (!shop.Enabled) { reply("Perk shop is disabled."); } else { if (!AccessService.CanAccessPerkShop(userEntity, reply)) { return; } if (!PlayerStateHelper.Exists(userEntity) || !PlayerStateHelper.Exists(characterEntity)) { reply("Player entity not ready."); return; } EntityManager entityManager = Core.EntityManager; if (!((EntityManager)(ref entityManager)).HasComponent(userEntity)) { reply("User component not ready."); return; } if (shop.BlockRemovalsInCombat && PlayerStateHelper.IsInCombat(characterEntity)) { reply("You cannot remove shop buffs while in combat."); return; } if (string.IsNullOrWhiteSpace(key)) { reply("Usage: .perk buffremove "); return; } key = KeyAliasService.NormalizeBuffKey(key); if (!shop.Buffs.TryGetValue(key, out PerkShopEntry value) || value.BuffPrefab == 0) { reply("Unknown buff '" + key + "'. Use .perk bufflist or .perk search ."); return; } entityManager = Core.EntityManager; User componentData = ((EntityManager)(ref entityManager)).GetComponentData(userEntity); OwnershipService.RegisterOnlineUser(componentData.PlatformId, userEntity); PlayerCacheService.Remember(componentData); bool flag = shop.SaveOwnership && OwnershipService.PlayerOwns(componentData.PlatformId, key); int num = 0; string value2 = value.Category; if (flag && shop.Categories.TryGetValue(value.Category, out BuffCategoryDefinition value3) && value3.SlotFreeCost.HasValue) { num = value3.SlotFreeCost.Value; value2 = value3.DisplayName; } if (flag && num > 0) { PrefabGUID itemPrefab = default(PrefabGUID); ((PrefabGUID)(ref itemPrefab))..ctor(shop.CurrencyPrefab); int num2 = InventoryHelper.Count(characterEntity, itemPrefab); if (num2 < num) { reply($"Not enough {shop.CurrencyName} to free a slot in category '{value2}': {num2}/{num}."); return; } if (!InventoryHelper.TryRemove(characterEntity, itemPrefab, num)) { reply($"Could not remove {num} {shop.CurrencyName} to free a slot in category '{value2}'."); return; } } bool num3 = BuffService.RemoveBuff(characterEntity, new PrefabGUID(value.BuffPrefab)); bool flag2 = shop.SaveOwnership && OwnershipService.RemoveOwnedBuff(componentData.PlatformId, key); if (num3 || flag2) { string value4 = ((flag && num > 0) ? $" Slot freed for {num} {shop.CurrencyName}." : string.Empty); reply($"Removed {value.DisplayName}. Ownership removed: {flag2}.{value4}"); } else { reply("You do not have or own " + value.DisplayName + "."); } } } catch (Exception e) { Core.LogException(e, "RemoveBuff"); reply("Buff removal failed. Check server logs."); } } public static void AdminMenu(Action reply) { ShopConfigRoot shop = ConfigService.Shop; string text = (shop.EnableBuffWhitelist ? $"ON ({shop.BuffWhitelistPlatformIds.Count})" : "OFF"); string text2 = (shop.EnableStatWhitelist ? $"ON ({shop.StatWhitelistPlatformIds.Count})" : "OFF"); reply("=== PerkShop Admin ===\nPlayer Audit:\n .perk info \n .perk wlplayer \n\nBuff Ownership:\n .perk giftbuff \n .perk revokebuff \n .perk addbuff \n .perk clearbuff \n\nStats:\n .perk giftstat \n .perk revokestat \n .perk addflat \n .perk clearflat \n\nWhitelist:\n Buff whitelist: " + text + "\n Stat whitelist: " + text2 + "\n .perk wlstatus\n .perk wlcheckbuff\n .perk wlcheckstat\n .perk wlcheckall\n .perk wladdbuff \n .perk wlremovebuff \n .perk wladdstat \n .perk wlremovestat \n\nConfig / Operations:\n .perk reload\n .perk diag\n .perk validate\n .perk syncall"); } public static void WhitelistList(string scope, Action reply) { ShopConfigRoot shop = ConfigService.Shop; scope = (string.IsNullOrWhiteSpace(scope) ? "all" : scope.Trim().ToLowerInvariant()); if (scope != "buff" && scope != "stat" && scope != "all") { reply("Usage: .perk wlcheck "); } else if (scope == "buff") { reply("=== Buff Whitelist ===\n" + FormatList("Players", shop.BuffWhitelistPlatformIds, shop.BuffWhitelistNames)); } else if (scope == "stat") { reply("=== Stat Whitelist ===\n" + FormatList("Players", shop.StatWhitelistPlatformIds, shop.StatWhitelistNames)); } else { reply("=== Whitelist Lists ===\n" + FormatList("Perk Shop", shop.BuffWhitelistPlatformIds, shop.BuffWhitelistNames) + "\n\n" + FormatList("Stat Shop", shop.StatWhitelistPlatformIds, shop.StatWhitelistNames)); } static string FormatList(string title, List ids, Dictionary names) { Dictionary names2 = names; if (ids.Count == 0) { return title + ":\n none"; } IEnumerable values = ids.Select(delegate(ulong id) { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) if (names2 != null && names2.TryGetValue(id, out string value) && !string.IsNullOrWhiteSpace(value)) { return " " + value; } if (PlayerCacheService.TryGetName(id, out string characterName)) { return " " + characterName; } if (AdminPlayerLookup.TryFindUser(id.ToString(), out Entity _, out User user, out string _)) { PlayerCacheService.Remember(user); return $" {user.CharacterName}"; } return " Unknown/offline"; }); return title + ":\n" + string.Join("\n", values); } } public static void WhitelistPlayer(string playerRef, Action reply) { //IL_0026: 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_005e: 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_0083: Unknown result type (might be due to invalid IL or missing references) try { if (!AdminPlayerLookup.TryFindUser(playerRef, out Entity _, out User user, out string error)) { reply(error); return; } ShopConfigRoot shop = ConfigService.Shop; bool flag = shop.BuffWhitelistPlatformIds.Contains(user.PlatformId); bool flag2 = shop.StatWhitelistPlatformIds.Contains(user.PlatformId); reply($"=== Whitelist: {user.CharacterName} ===\nPlatformId: {user.PlatformId}\nPerk Shop: {((!shop.EnableBuffWhitelist) ? "Open to everyone" : (flag ? "Allowed" : "Blocked"))} | Listed: {(flag ? "yes" : "no")}\nStat Shop: {((!shop.EnableStatWhitelist) ? "Open to everyone" : (flag2 ? "Allowed" : "Blocked"))} | Listed: {(flag2 ? "yes" : "no")}"); } catch (Exception e) { Core.LogException(e, "WhitelistPlayer"); reply("Could not read whitelist status for player. Check server logs."); } } public static void AdminInfo(string playerRef, Action reply) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_029e: Unknown result type (might be due to invalid IL or missing references) try { if (!AdminPlayerLookup.TryFindUser(playerRef, out Entity userEntity, out User user, out string error)) { reply(error); return; } OwnershipService.RegisterOnlineUser(user.PlatformId, userEntity); ShopConfigRoot config = ConfigService.Shop; List ownedBuffKeys = OwnershipService.GetOwnedBuffKeys(user.PlatformId); string[] value = (from kv in VisibleCategories(user.PlatformId) orderby kv.Key select kv).Select(delegate(KeyValuePair kv) { int value7 = OwnershipService.CountOwnedBuffsInCategory(user.PlatformId, kv.Key); string value8 = (kv.Value.MaxOwnedSlots.HasValue ? kv.Value.MaxOwnedSlots.Value.ToString() : "∞"); return $" {kv.Value.DisplayName}: {value7}/{value8}"; }).ToArray(); PerkShopEntry value6; string text = ((ownedBuffKeys.Count == 0) ? " none" : string.Join("\n", ownedBuffKeys.Select((string k) => (!config.Buffs.TryGetValue(k, out value6)) ? (" " + k + " - missing config") : (" " + k + " - " + value6.DisplayName)))); List adminGrantedBuffKeys = OwnershipService.GetAdminGrantedBuffKeys(user.PlatformId); PerkShopEntry value5; string text2 = ((adminGrantedBuffKeys.Count == 0) ? " none" : string.Join("\n", adminGrantedBuffKeys.Select((string k) => (!config.Buffs.TryGetValue(k, out value5)) ? (" " + k + " - missing config") : (" " + k + " - " + value5.DisplayName)))); Dictionary ownedStats = OwnershipService.GetOwnedStats(user.PlatformId); int value2 = OwnershipService.CountOwnedStatTypes(user.PlatformId); string value3 = ((config.EnableStatTypeSlots && config.MaxOwnedStatTypes > 0) ? config.MaxOwnedStatTypes.ToString() : "∞"); StatShopEntry value4; string text3 = ((ownedStats.Count == 0) ? " none" : string.Join("\n", from kv in ownedStats.OrderBy, string>((KeyValuePair kv) => kv.Key, StringComparer.OrdinalIgnoreCase) select config.Stats.TryGetValue(kv.Key, out value4) ? $" {kv.Key} x{kv.Value} = +{FormatNumber(value4.ValuePerPurchase * (float)kv.Value)} {value4.UnitStat}" : $" {kv.Key} x{kv.Value} - missing config")); Dictionary adminFlatStats = OwnershipService.GetAdminFlatStats(user.PlatformId); string text4 = ((adminFlatStats.Count == 0) ? " none" : string.Join("\n", from kv in adminFlatStats.OrderBy, string>((KeyValuePair kv) => kv.Key, StringComparer.OrdinalIgnoreCase) select " " + kv.Key + " +" + FormatNumber(kv.Value))); bool flag = config.BuffWhitelistPlatformIds.Contains(user.PlatformId); bool flag2 = config.StatWhitelistPlatformIds.Contains(user.PlatformId); reply($"=== Player Info: {user.CharacterName} ===\nPlatformId: {user.PlatformId}\n\n" + "Buff Slots:\n" + string.Join("\n", value) + "\n\nOwned Buffs:\n" + text + "\n\nAdmin Given Buffs:\n" + text2 + "\n\n" + $"Stat Type Slots: {value2}/{value3}\n" + "Purchased Stats:\n" + text3 + "\n\nAdmin Flat Stats:\n" + text4 + "\n\nWhitelist:\n" + $" Perk Shop: {((!config.EnableBuffWhitelist) ? "Open" : (flag ? "Allowed" : "Blocked"))} | Listed: {(flag ? "yes" : "no")}\n" + " Stat Shop: " + ((!config.EnableStatWhitelist) ? "Open" : (flag2 ? "Allowed" : "Blocked")) + " | Listed: " + (flag2 ? "yes" : "no")); } catch (Exception e) { Core.LogException(e, "AdminInfo"); reply("Could not read target player ownership. Check server logs."); } } public static void AdminAdd(string playerRef, string buffKey, Action reply) { //IL_006b: 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_007f: 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_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_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_01b3: Unknown result type (might be due to invalid IL or missing references) //IL_01b4: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_0148: 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) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) try { if (!AdminPlayerLookup.TryFindUser(playerRef, out Entity userEntity, out User user, out string error)) { reply(error); return; } ShopConfigRoot shop = ConfigService.Shop; if (string.IsNullOrWhiteSpace(buffKey)) { reply("Usage: .perk addbuff "); return; } buffKey = KeyAliasService.NormalizeBuffKey(buffKey); if (!shop.Buffs.TryGetValue(buffKey, out PerkShopEntry value)) { reply("Unknown buff '" + buffKey + "'. Use .perk bufflist or .perk search ."); return; } bool flag = OwnershipService.EnsureAdminGrantedBuff(user.PlatformId, buffKey); OwnershipService.RegisterOnlineUser(user.PlatformId, userEntity); if (value.BuffPrefab != 0) { Entity entity = user.LocalCharacter._Entity; if (PlayerStateHelper.Exists(entity)) { bool flag2 = string.Equals(value.Category, "blood_buff", StringComparison.OrdinalIgnoreCase); BuffService.ApplyPurchasedBuff(userEntity, entity, new PrefabGUID(value.BuffPrefab), value.PreventDuplicate, flag2 ? shop.AllowBuffEntityMutation : (shop.ForcePermanentBuffs || shop.AllowBuffEntityMutation), !flag2 && (shop.ForcePermanentBuffs || value.MutateAppliedBuffLifetime), BuffService.ResolveDurationSeconds(shop, value), BuffService.ResolvePersistThroughDeath(shop, value), value.KeepVisibleTimerFrozen, value.VisibleTimerSeconds, BuffService.PreserveVanillaCleanup(shop, value)); } } reply(flag ? $"Admin granted {value.DisplayName} to {user.CharacterName}." : $"{user.CharacterName} already had {value.DisplayName} as an admin-given buff."); } catch (Exception e) { Core.LogException(e, "AdminAdd"); reply("Could not add buff to target player. Check server logs."); } } public static void AdminRemove(string playerRef, string buffKey, Action reply) { //IL_006b: 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_007f: 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_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_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_0155: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: 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_00b0: Unknown result type (might be due to invalid IL or missing references) try { if (!AdminPlayerLookup.TryFindUser(playerRef, out Entity userEntity, out User user, out string error)) { reply(error); return; } ShopConfigRoot shop = ConfigService.Shop; if (string.IsNullOrWhiteSpace(buffKey)) { reply("Usage: .perk clearbuff "); return; } buffKey = KeyAliasService.NormalizeBuffKey(buffKey); if (!shop.Buffs.TryGetValue(buffKey, out PerkShopEntry value)) { reply("Unknown buff '" + buffKey + "'. Use .perk bufflist or .perk search ."); return; } bool flag = OwnershipService.RemoveOwnedBuff(user.PlatformId, buffKey); OwnershipService.RegisterOnlineUser(user.PlatformId, userEntity); bool flag2 = false; if (value.BuffPrefab != 0) { Entity entity = user.LocalCharacter._Entity; if (PlayerStateHelper.Exists(entity)) { flag2 = BuffService.RemoveBuff(entity, new PrefabGUID(value.BuffPrefab)); } } if (flag || flag2) { reply($"Admin cleared {value.DisplayName} from {user.CharacterName}. Saved ownership removed: {flag}, active buff removed: {flag2}."); } else { reply($"{user.CharacterName} did not have {value.DisplayName} as a saved/admin buff or as an active buff."); } } catch (Exception e) { Core.LogException(e, "AdminRemove"); reply("Could not remove buff from target player. Check server logs."); } } public static void AdminGiftBuff(string playerRef, string buffKey, Action reply) { //IL_0172: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_0186: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Unknown result type (might be due to invalid IL or missing references) //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_0297: Unknown result type (might be due to invalid IL or missing references) //IL_0298: 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) //IL_022c: Unknown result type (might be due to invalid IL or missing references) //IL_01b4: Unknown result type (might be due to invalid IL or missing references) //IL_01b5: Unknown result type (might be due to invalid IL or missing references) //IL_01be: Unknown result type (might be due to invalid IL or missing references) try { if (!AdminPlayerLookup.TryFindUser(playerRef, out Entity userEntity, out User user, out string error)) { reply(error); return; } ShopConfigRoot shop = ConfigService.Shop; if (string.IsNullOrWhiteSpace(buffKey)) { reply("Usage: .perk giftbuff "); return; } buffKey = KeyAliasService.NormalizeBuffKey(buffKey); if (!shop.Buffs.TryGetValue(buffKey, out PerkShopEntry value) || !value.Enabled) { reply("Unknown or disabled buff '" + buffKey + "'."); return; } if (shop.Categories.TryGetValue(value.Category, out BuffCategoryDefinition value2) && value2.MaxOwnedSlots.HasValue) { int num = OwnershipService.CountOwnedBuffsInCategory(user.PlatformId, value.Category); if (!OwnershipService.PlayerOwns(user.PlatformId, buffKey) && num >= value2.MaxOwnedSlots.Value) { reply($"Cannot gift {value.DisplayName}. Category '{value2.DisplayName}' is full: {num}/{value2.MaxOwnedSlots.Value}."); return; } } bool flag = OwnershipService.EnsureOwnedBuff(user.PlatformId, buffKey); OwnershipService.RegisterOnlineUser(user.PlatformId, userEntity); PlayerCacheService.Remember(user); bool value3 = false; if (value.BuffPrefab != 0) { Entity entity = user.LocalCharacter._Entity; if (PlayerStateHelper.Exists(entity)) { value3 = BuffService.ApplyPurchasedBuff(userEntity, entity, new PrefabGUID(value.BuffPrefab), value.PreventDuplicate, shop.ForcePermanentBuffs || shop.AllowBuffEntityMutation, shop.ForcePermanentBuffs || value.MutateAppliedBuffLifetime, BuffService.ResolveDurationSeconds(shop, value), BuffService.ResolvePersistThroughDeath(shop, value), value.KeepVisibleTimerFrozen, value.VisibleTimerSeconds, BuffService.PreserveVanillaCleanup(shop, value)); } } reply(flag ? $"Gifted purchased buff {value.DisplayName} to {user.CharacterName}. Counts toward slot usage. Applied now: {value3}." : $"{user.CharacterName} already has purchased/admin ownership for {value.DisplayName}. No additional slot was consumed."); } catch (Exception e) { Core.LogException(e, "AdminGiftBuff"); reply("Could not gift purchased buff. Check server logs."); } } public static void AdminRevokePurchasedBuff(string playerRef, string buffKey, Action reply) { //IL_006b: 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_007f: 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_0151: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: 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_00a7: 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_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) try { if (!AdminPlayerLookup.TryFindUser(playerRef, out Entity userEntity, out User user, out string error)) { reply(error); return; } ShopConfigRoot shop = ConfigService.Shop; if (string.IsNullOrWhiteSpace(buffKey)) { reply("Usage: .perk revokebuff "); return; } buffKey = KeyAliasService.NormalizeBuffKey(buffKey); if (!shop.Buffs.TryGetValue(buffKey, out PerkShopEntry value)) { reply("Unknown buff '" + buffKey + "'. Use .perk bufflist or .perk search ."); return; } bool flag = OwnershipService.RemovePurchasedBuffOnly(user.PlatformId, buffKey); OwnershipService.RegisterOnlineUser(user.PlatformId, userEntity); PlayerCacheService.Remember(user); bool value2 = false; if (flag && value.BuffPrefab != 0) { bool num = OwnershipService.PlayerOwns(user.PlatformId, buffKey); Entity entity = user.LocalCharacter._Entity; if (!num && PlayerStateHelper.Exists(entity)) { value2 = BuffService.RemoveBuff(entity, new PrefabGUID(value.BuffPrefab)); } } reply(flag ? $"Revoked purchased buff {value.DisplayName} from {user.CharacterName}. Active buff removed: {value2}." : $"{user.CharacterName} did not have purchased ownership for {value.DisplayName}. Admin-given buffs are not removed by revokebuff."); } catch (Exception e) { Core.LogException(e, "AdminRevokePurchasedBuff"); reply("Could not revoke purchased buff. Check server logs."); } } public static void AdminGiftStat(string playerRef, string statKey, int ranks, Action reply) { //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_01e5: Unknown result type (might be due to invalid IL or missing references) //IL_01f4: Unknown result type (might be due to invalid IL or missing references) //IL_01fa: Unknown result type (might be due to invalid IL or missing references) //IL_0200: Unknown result type (might be due to invalid IL or missing references) //IL_0209: Unknown result type (might be due to invalid IL or missing references) //IL_020a: Unknown result type (might be due to invalid IL or missing references) //IL_020f: Unknown result type (might be due to invalid IL or missing references) //IL_0214: Unknown result type (might be due to invalid IL or missing references) //IL_0216: Unknown result type (might be due to invalid IL or missing references) //IL_0276: Unknown result type (might be due to invalid IL or missing references) //IL_0277: Unknown result type (might be due to invalid IL or missing references) //IL_021f: 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_0222: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) try { if (!AdminPlayerLookup.TryFindUser(playerRef, out Entity userEntity, out User user, out string error)) { reply(error); return; } ShopConfigRoot shop = ConfigService.Shop; if (!shop.EnableStatShop) { reply("Permanent stat shop is disabled."); return; } if (string.IsNullOrWhiteSpace(statKey) || ranks <= 0) { reply("Usage: .perk giftstat "); return; } statKey = KeyAliasService.NormalizeStatKey(statKey); if (!shop.Stats.TryGetValue(statKey, out StatShopEntry value) || !value.Enabled) { reply("Unknown or disabled stat key '" + statKey + "'. Use .perk statlist or .perk search ."); return; } int ownedStatCount = OwnershipService.GetOwnedStatCount(user.PlatformId, statKey); if (value.MaxPurchases > 0 && ownedStatCount + ranks > value.MaxPurchases) { reply($"Cannot gift {ranks} rank(s) of {value.DisplayName}. Purchase cap would be exceeded: {ownedStatCount}/{value.MaxPurchases} owned."); return; } if (shop.EnableStatTypeSlots && shop.MaxOwnedStatTypes > 0 && ownedStatCount == 0) { int num = OwnershipService.CountOwnedStatTypes(user.PlatformId); if (num >= shop.MaxOwnedStatTypes) { reply($"Cannot gift {value.DisplayName}. Stat type slots are full: {num}/{shop.MaxOwnedStatTypes}."); return; } } int value2 = OwnershipService.AddOwnedStat(user.PlatformId, statKey, ranks); OwnershipService.RegisterOnlineUser(user.PlatformId, userEntity); PlayerCacheService.Remember(user); bool value3 = false; Entity entity = user.LocalCharacter._Entity; if (PlayerStateHelper.Exists(entity)) { value3 = StatService.RebuildOwnedStats(userEntity, entity, user.PlatformId); } reply($"Gifted {ranks} purchased rank(s) of {value.DisplayName} to {user.CharacterName}. Total purchased ranks: {value2}. Counts toward stat slots and MaxPurchases. Applied now: {value3}."); } catch (Exception e) { Core.LogException(e, "AdminGiftStat"); reply("Could not gift purchased stat. Check server logs."); } } public static void AdminRevokePurchasedStat(string playerRef, string statKey, int ranks, Action reply) { //IL_006d: 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_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: 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_016d: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) try { if (!AdminPlayerLookup.TryFindUser(playerRef, out Entity userEntity, out User user, out string error)) { reply(error); return; } if (string.IsNullOrWhiteSpace(statKey) || ranks <= 0) { reply("Usage: .perk revokestat "); return; } statKey = KeyAliasService.NormalizeStatKey(statKey); if (!ConfigService.Shop.Stats.TryGetValue(statKey, out StatShopEntry value)) { reply("Unknown stat key '" + statKey + "'. Use .perk statlist or .perk search ."); return; } int ownedStatCount = OwnershipService.GetOwnedStatCount(user.PlatformId, statKey); bool flag = OwnershipService.RemoveOwnedStat(user.PlatformId, statKey, ranks); int ownedStatCount2 = OwnershipService.GetOwnedStatCount(user.PlatformId, statKey); OwnershipService.RegisterOnlineUser(user.PlatformId, userEntity); PlayerCacheService.Remember(user); bool value2 = false; Entity entity = user.LocalCharacter._Entity; if (PlayerStateHelper.Exists(entity)) { value2 = StatService.RebuildOwnedStats(userEntity, entity, user.PlatformId); } reply(flag ? $"Revoked {Math.Min(ranks, ownedStatCount)} purchased rank(s) of {value.DisplayName} from {user.CharacterName}. Remaining ranks: {ownedStatCount2}. Applied now: {value2}." : $"{user.CharacterName} did not have purchased ranks of {value.DisplayName}."); } catch (Exception e) { Core.LogException(e, "AdminRevokePurchasedStat"); reply("Could not revoke purchased stat. Check server logs."); } } private static bool TryResolveUnitStatOrShopKey(string input, out UnitStatType unitStat, out string canonicalStat, out string sourceLabel) { unitStat = (UnitStatType)0; canonicalStat = string.Empty; sourceLabel = string.Empty; if (string.IsNullOrWhiteSpace(input)) { return false; } input = input.Trim(); if (ConfigService.Shop.Stats.TryGetValue(input, out StatShopEntry value) && Enum.TryParse(value.UnitStat, ignoreCase: true, out unitStat)) { canonicalStat = ((object)(UnitStatType)(ref unitStat)).ToString(); sourceLabel = input + " -> " + canonicalStat; return true; } if (Enum.TryParse(input, ignoreCase: true, out unitStat)) { canonicalStat = ((object)(UnitStatType)(ref unitStat)).ToString(); sourceLabel = canonicalStat; return true; } return false; } public static void AdminAddFlatStat(string playerRef, string unitStatName, float amount, Action reply) { //IL_0068: 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_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0086: 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_008c: 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_0093: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: 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_009d: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) try { if (!AdminPlayerLookup.TryFindUser(playerRef, out Entity userEntity, out User user, out string error)) { reply(error); return; } if (string.IsNullOrWhiteSpace(unitStatName) || Math.Abs(amount) <= 0.0001f) { reply("Usage: .perk addflat "); return; } if (!TryResolveUnitStatOrShopKey(unitStatName, out UnitStatType _, out string canonicalStat, out string sourceLabel)) { reply("Unknown stat '" + unitStatName + "'. Use a UnitStat like PhysicalPower or a stat key from .perk statlist like PP."); return; } float value = OwnershipService.AddAdminFlatStat(user.PlatformId, canonicalStat, amount); OwnershipService.RegisterOnlineUser(user.PlatformId, userEntity); bool value2 = false; Entity entity = user.LocalCharacter._Entity; if (PlayerStateHelper.Exists(entity)) { value2 = StatService.RebuildOwnedStats(userEntity, entity, user.PlatformId); } reply($"Admin added {amount} flat {canonicalStat} to {user.CharacterName}. Source: {sourceLabel}. Total admin flat {canonicalStat}: +{value}. Applied now: {value2}. This does not consume stat slots or MaxPurchases."); } catch (Exception e) { Core.LogException(e, "AdminAddFlatStat"); reply("Could not add admin flat stat. Check server logs."); } } public static void AdminClearFlatStat(string playerRef, string unitStatName, Action reply) { //IL_005b: 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_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0079: 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_0083: 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_008e: 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_0091: 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_0125: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) try { if (!AdminPlayerLookup.TryFindUser(playerRef, out Entity userEntity, out User user, out string error)) { reply(error); return; } if (string.IsNullOrWhiteSpace(unitStatName)) { reply("Usage: .perk clearflat "); return; } if (!TryResolveUnitStatOrShopKey(unitStatName, out UnitStatType _, out string canonicalStat, out string sourceLabel)) { reply("Unknown stat '" + unitStatName + "'. Use a UnitStat like PhysicalPower or a stat key from .perk statlist like PP."); return; } bool flag = OwnershipService.ClearAdminFlatStat(user.PlatformId, canonicalStat); OwnershipService.RegisterOnlineUser(user.PlatformId, userEntity); bool value = false; Entity entity = user.LocalCharacter._Entity; if (PlayerStateHelper.Exists(entity)) { value = StatService.RebuildOwnedStats(userEntity, entity, user.PlatformId); } reply(flag ? $"Admin cleared flat {canonicalStat} from {user.CharacterName}. Source: {sourceLabel}. Applied now: {value}." : $"{user.CharacterName} did not have admin flat {canonicalStat}. Source: {sourceLabel}."); } catch (Exception e) { Core.LogException(e, "AdminClearFlatStat"); reply("Could not clear admin flat stat. Check server logs."); } } public static void ListStatKeys(Entity userEntity, int page, Action reply) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) if (!AccessService.CanAccessStatShop(userEntity, reply)) { return; } ShopConfigRoot config = ConfigService.Shop; if (!config.EnableStatShop) { reply("Permanent stat shop is disabled."); return; } string[] array = (from kv in config.Stats where kv.Value.Enabled && StatDefinitionService.IsClientUnsupportedStatAllowed(config, kv.Value) orderby InferStatGroup(kv.Key, kv.Value.UnitStat), kv.Key select $" {kv.Key} [{InferStatGroup(kv.Key, kv.Value.UnitStat)}]").ToArray(); if (array.Length == 0) { reply("No permanent stats configured."); return; } (int page, int totalPages) tuple = NormalizePage(page, array.Length); int item = tuple.page; int item2 = tuple.totalPages; string[] value = array.Skip((item - 1) * 8).Take(8).ToArray(); string text = ((item < item2) ? $"\nNext page: .perk statlist {item + 1}" : string.Empty); reply($"=== Stat Keys Page {item}/{item2} ===\n" + string.Join("\n", value) + "\n\nDetails: .perk statdet " + text); } public static void StatDetails(Entity userEntity, string key, Action reply) { //IL_0000: 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_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_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) if (!AccessService.CanAccessStatShop(userEntity, reply)) { return; } ShopConfigRoot shop = ConfigService.Shop; if (!shop.EnableStatShop) { reply("Permanent stat shop is disabled."); return; } if (string.IsNullOrWhiteSpace(key)) { reply("Usage: .perk statdet "); return; } key = KeyAliasService.NormalizeStatKey(key); if (!shop.Stats.TryGetValue(key, out StatShopEntry value) || !value.Enabled) { reply("Unknown stat '" + key + "'. Use .perk statlist or .perk search ."); return; } int num = 0; int value2 = 0; string value3 = "∞"; if (PlayerStateHelper.Exists(userEntity)) { EntityManager entityManager = Core.EntityManager; if (((EntityManager)(ref entityManager)).HasComponent(userEntity)) { entityManager = Core.EntityManager; User componentData = ((EntityManager)(ref entityManager)).GetComponentData(userEntity); num = OwnershipService.GetOwnedStatCount(componentData.PlatformId, key); value2 = OwnershipService.CountOwnedStatTypes(componentData.PlatformId); value3 = ((shop.EnableStatTypeSlots && shop.MaxOwnedStatTypes > 0) ? shop.MaxOwnedStatTypes.ToString() : "∞"); } } string value4 = ((value.MaxPurchases > 0) ? value.MaxPurchases.ToString() : "∞"); float value5 = (float)num * value.ValuePerPurchase; string value6 = ((num > 0) ? "Uses 1 existing stat type slot" : "Would use 1 new stat type slot"); string value7 = (StatDefinitionService.IsClientUnsupportedStatAllowed(shop, value) ? "Enabled" : "Disabled by client-compatibility guard. Set EnableClientUnsupportedStats=true to allow gameplay-only/non-TAB stats."); reply($"=== {value.DisplayName} ===\nKey: {key}\nGroup: {InferStatGroup(key, value.UnitStat)}\nEffect: +{FormatNumber(value.ValuePerPurchase)} {value.UnitStat} per purchase\nCost: {value.Cost} {shop.CurrencyName}\nOwned: {num}/{value4}\nCurrent total: +{FormatNumber(value5)} {value.UnitStat}\nStat slots: {value2}/{value3}. {value6}.\nClient compatibility: {value7}\nNotes: {value.Notes}\nBuy: .perk statbuy {key}"); } public static void Search(Entity userEntity, string query, Action reply) { string query2 = query; ShopConfigRoot config = ConfigService.Shop; if (string.IsNullOrWhiteSpace(query2)) { reply("Usage: .perk search "); return; } query2 = query2.Trim(); string[] array = (config.Enabled ? (from kv in config.Buffs where kv.Value.Enabled && (config.EnableExperimentalBloodBuffs || !string.Equals(kv.Value.Category, "blood_buff", StringComparison.OrdinalIgnoreCase)) && (kv.Key.Contains(query2, StringComparison.OrdinalIgnoreCase) || kv.Value.DisplayName.Contains(query2, StringComparison.OrdinalIgnoreCase) || kv.Value.Category.Contains(query2, StringComparison.OrdinalIgnoreCase) || kv.Value.Notes.Contains(query2, StringComparison.OrdinalIgnoreCase)) orderby kv.Key select kv).Select(delegate(KeyValuePair kv) { BuffCategoryDefinition value; string value2 = (config.Categories.TryGetValue(kv.Value.Category, out value) ? value.DisplayName : kv.Value.Category); return $" buff:{kv.Key} [{value2}]"; }).Take(8).ToArray() : Array.Empty()); string[] array2 = (config.EnableStatShop ? (from kv in config.Stats where kv.Value.Enabled && StatDefinitionService.IsClientUnsupportedStatAllowed(config, kv.Value) && (kv.Key.Contains(query2, StringComparison.OrdinalIgnoreCase) || kv.Value.DisplayName.Contains(query2, StringComparison.OrdinalIgnoreCase) || kv.Value.UnitStat.Contains(query2, StringComparison.OrdinalIgnoreCase) || kv.Value.Notes.Contains(query2, StringComparison.OrdinalIgnoreCase)) orderby kv.Key select $" stat:{kv.Key} [{InferStatGroup(kv.Key, kv.Value.UnitStat)}]").Take(8).ToArray() : Array.Empty()); if (array.Length == 0 && array2.Length == 0) { reply("No PerkShop results for '" + query2 + "'."); return; } reply("=== Search: " + query2 + " ===\nBuffs:\n" + ((array.Length == 0) ? " none" : string.Join("\n", array)) + "\n\nStats:\n" + ((array2.Length == 0) ? " none" : string.Join("\n", array2)) + "\n\nDetails: .perk buffdet or .perk statdet "); } public static void BuyStat(Entity userEntity, Entity characterEntity, string key, Action reply) { //IL_001e: 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_006d: 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_008a: 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_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_01e8: Unknown result type (might be due to invalid IL or missing references) //IL_01ed: Unknown result type (might be due to invalid IL or missing references) //IL_01f1: Unknown result type (might be due to invalid IL or missing references) //IL_01f2: Unknown result type (might be due to invalid IL or missing references) //IL_01fe: Unknown result type (might be due to invalid IL or missing references) //IL_0319: Unknown result type (might be due to invalid IL or missing references) //IL_031a: Unknown result type (might be due to invalid IL or missing references) //IL_039c: Unknown result type (might be due to invalid IL or missing references) //IL_039d: Unknown result type (might be due to invalid IL or missing references) //IL_0410: Unknown result type (might be due to invalid IL or missing references) //IL_0411: Unknown result type (might be due to invalid IL or missing references) //IL_0424: Unknown result type (might be due to invalid IL or missing references) //IL_042b: Expected O, but got Unknown try { ShopConfigRoot shop = ConfigService.Shop; if (!shop.Enabled) { reply("Perk shop is disabled."); } else { if (!AccessService.CanAccessStatShop(userEntity, reply)) { return; } if (!shop.EnableStatShop) { reply("Permanent stat shop is disabled."); return; } if (!StatService.IsCarrierConfigured(out PrefabGUID _, out string error)) { reply("Permanent stat shop is not ready: " + error); return; } if (!PlayerStateHelper.Exists(userEntity) || !PlayerStateHelper.Exists(characterEntity)) { reply("Player entity not ready."); return; } EntityManager entityManager = Core.EntityManager; if (!((EntityManager)(ref entityManager)).HasComponent(userEntity)) { reply("User component not ready."); return; } if (shop.BlockPurchasesInCombat && PlayerStateHelper.IsInCombat(characterEntity)) { reply("You cannot buy stats while in combat."); return; } if (string.IsNullOrWhiteSpace(key)) { ListStatKeys(userEntity, 1, reply); return; } key = KeyAliasService.NormalizeStatKey(key); if (!shop.Stats.TryGetValue(key, out StatShopEntry value) || !value.Enabled) { reply("Unknown stat '" + key + "'. Use .perk statlist or .perk search ."); return; } if (!Enum.TryParse(value.UnitStat, ignoreCase: true, out UnitStatType _)) { reply($"Stat '{key}' has invalid UnitStat '{value.UnitStat}'. Check config."); return; } if (!StatDefinitionService.IsClientUnsupportedStatAllowed(shop, value)) { reply($"Stat '{value.DisplayName}' ({value.UnitStat}) is disabled by the client-compatibility guard. " + "It has gameplay effect, but Bloodcraft/Eclipse/VampireAttributes can spam NotImplementedException and it may not appear in TAB. Set EnableClientUnsupportedStats = true only if you accept that risk."); return; } entityManager = Core.EntityManager; ulong platformId = ((EntityManager)(ref entityManager)).GetComponentData(userEntity).PlatformId; OwnershipService.RegisterOnlineUser(platformId, userEntity); int ownedStatCount = OwnershipService.GetOwnedStatCount(platformId, key); if (value.MaxPurchases > 0 && ownedStatCount >= value.MaxPurchases) { reply($"You already reached the purchase cap for {value.DisplayName}: {ownedStatCount}/{value.MaxPurchases}."); return; } if (shop.EnableStatTypeSlots && ownedStatCount == 0 && shop.MaxOwnedStatTypes > 0) { int num = OwnershipService.CountOwnedStatTypes(platformId); if (num >= shop.MaxOwnedStatTypes) { reply($"Stat type slots are full: {num}/{shop.MaxOwnedStatTypes}. You can still buy more ranks of already-owned stats, but not a new stat type."); return; } } PrefabGUID itemPrefab = default(PrefabGUID); ((PrefabGUID)(ref itemPrefab))..ctor(shop.CurrencyPrefab); int num2 = InventoryHelper.Count(characterEntity, itemPrefab); if (num2 < value.Cost) { reply($"Not enough {shop.CurrencyName}: {num2}/{value.Cost}."); return; } if (!InventoryHelper.TryRemove(characterEntity, itemPrefab, value.Cost)) { reply($"Could not remove {value.Cost} {shop.CurrencyName} from your inventory."); return; } int num3 = OwnershipService.AddOwnedStat(platformId, key); if (!StatService.RebuildOwnedStats(userEntity, characterEntity, platformId)) { ManualLogSource log = Core.Log; bool flag = default(bool); BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(88, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[StatShop] Stat purchase '"); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(key); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("' was saved but stat carrier could not be applied immediately."); } log.LogWarning(val); reply("Purchased " + value.DisplayName + ". Ownership saved, but immediate application failed. It may apply on relog/reapply."); } else { float value2 = (float)num3 * value.ValuePerPurchase; reply($"Purchased {value.DisplayName} for {value.Cost} {shop.CurrencyName}. Owned: {num3}. Total {value.UnitStat}: +{value2}."); } } } catch (Exception e) { Core.LogException(e, "BuyStat"); reply("Permanent stat purchase failed. Check server logs."); } } public static void Sync(Entity userEntity, Action reply) { //IL_0000: 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_001a: 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) try { bool flag = AccessService.CanAccessPerkShop(userEntity, reply); bool num = AccessService.CanAccessStatShop(userEntity, reply); OwnershipService.UserReapplyResult userReapplyResult = default(OwnershipService.UserReapplyResult); if (flag) { userReapplyResult = OwnershipService.ReapplyOwnedBuffsForUser(userEntity, onlyIfMissing: false); } bool value = false; bool value2 = false; if (num) { value = true; value2 = StatService.ApplyOwnedStatsForUser(userEntity); } reply("PerkShop sync complete.\n" + $"Buff access: {flag}\n" + $"Buffs checked: {userReapplyResult.BuffsChecked}\n" + $"Buffs already active: {userReapplyResult.BuffsAlreadyActive}\n" + $"Buff apply attempts: {userReapplyResult.BuffApplyAttempts}\n" + $"Buff apply successes: {userReapplyResult.BuffApplySuccesses}\n" + $"Stats attempted: {value}\n" + $"Stats applied or already current: {value2}"); } catch (Exception e) { Core.LogException(e, "Sync"); reply("Could not sync PerkShop buffs/stats. Check server logs."); } } public static void RemoveStat(Entity userEntity, Entity characterEntity, string key, Action reply) { //IL_0006: 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_0034: 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_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: 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_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_0248: Unknown result type (might be due to invalid IL or missing references) //IL_0249: Unknown result type (might be due to invalid IL or missing references) //IL_01bc: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Unknown result type (might be due to invalid IL or missing references) try { ShopConfigRoot shop = ConfigService.Shop; if (!AccessService.CanAccessStatShop(userEntity, reply)) { return; } if (!shop.EnableStatShop) { reply("Permanent stat shop is disabled."); return; } if (!PlayerStateHelper.Exists(userEntity) || !PlayerStateHelper.Exists(characterEntity)) { reply("Player entity not ready."); return; } EntityManager entityManager = Core.EntityManager; if (!((EntityManager)(ref entityManager)).HasComponent(userEntity)) { reply("User component not ready."); return; } if (shop.BlockRemovalsInCombat && PlayerStateHelper.IsInCombat(characterEntity)) { reply("You cannot remove stats while in combat."); return; } if (string.IsNullOrWhiteSpace(key)) { reply("Usage: .perk statremove "); return; } key = KeyAliasService.NormalizeStatKey(key); if (!shop.Stats.TryGetValue(key, out StatShopEntry value)) { reply("Unknown stat '" + key + "'. Use .perk statlist or .perk search ."); return; } entityManager = Core.EntityManager; ulong platformId = ((EntityManager)(ref entityManager)).GetComponentData(userEntity).PlatformId; int ownedStatCount = OwnershipService.GetOwnedStatCount(platformId, key); if (ownedStatCount <= 0) { reply("You do not own " + value.DisplayName + "."); return; } int num = (shop.EnableStatTypeSlots ? shop.StatTypeSlotFreeCost : 0); if (num > 0) { PrefabGUID itemPrefab = default(PrefabGUID); ((PrefabGUID)(ref itemPrefab))..ctor(shop.CurrencyPrefab); int num2 = InventoryHelper.Count(characterEntity, itemPrefab); if (num2 < num) { reply($"Not enough {shop.CurrencyName} to free stat type slot: {num2}/{num}."); return; } if (!InventoryHelper.TryRemove(characterEntity, itemPrefab, num)) { reply($"Could not remove {num} {shop.CurrencyName} to free stat type slot."); return; } } if (!OwnershipService.RemoveOwnedStat(platformId, key, ownedStatCount)) { reply("Could not remove " + value.DisplayName + "."); return; } StatService.RebuildOwnedStats(userEntity, characterEntity, platformId); string value2 = ((num > 0) ? $" Stat type slot freed for {num} {shop.CurrencyName}." : " Stat type slot freed."); reply($"Removed all {ownedStatCount} rank(s) of {value.DisplayName}.{value2}"); } catch (Exception e) { Core.LogException(e, "RemoveStat"); reply("Could not remove permanent stat. Check server logs."); } } public static void ReloadConfig(Action reply) { try { OwnershipService.FlushPendingSaves(force: true); ConfigService.Reload(); QueryService.Initialize(Core.Systems); CarrierPrefabService.Reset(); CarrierPrefabService.PrepareConfiguredStatCarrierPrefab(); StatService.ResetRuntimeStateAfterConfigReload(); BuffService.ResetRuntimeStateAfterConfigReload(); ShopConfigRoot shop = ConfigService.Shop; string text = StatService.DescribeConfiguredCarrier(); reply("PerkShop config reloaded.\nVersion: 0.1.2\n" + $"ConfigVersion: {shop.ConfigVersion}\n" + $"Debug logging: {shop.EnableDebugLogging}\n" + $"Permanent buffs: {shop.ForcePermanentBuffs}\n" + "Stat carrier: " + text + "\nRun .perk syncall to force reapply for online players."); } catch (Exception e) { Core.LogException(e, "ReloadConfig"); reply("PerkShop config reload failed. Check server logs."); } } public static void Diagnostics(Action reply) { try { ShopConfigRoot shop = ConfigService.Shop; Dictionary onlineUsersSnapshot = OwnershipService.GetOnlineUsersSnapshot(); reply("=== PerkShop Diagnostics ===\nVersion: 0.1.2\n" + $"ConfigVersion: {shop.ConfigVersion}\n" + $"Enabled: {shop.Enabled}\n" + "Buff shop access: " + (shop.EnableBuffWhitelist ? $"Whitelist ON ({shop.BuffWhitelistPlatformIds.Count})" : "Open") + "\n" + $"Stat shop enabled: {shop.EnableStatShop}\n" + "Stat shop access: " + (shop.EnableStatWhitelist ? $"Whitelist ON ({shop.StatWhitelistPlatformIds.Count})" : "Open") + "\n" + $"Currency: {shop.CurrencyName} ({shop.CurrencyPrefab})\n" + $"Permanent buffs: {shop.ForcePermanentBuffs}\n" + $"Debug logging: {shop.EnableDebugLogging}\n" + $"Config auto-detect: {shop.AutoDetectConfigChanges}, check interval: {shop.ConfigFileCheckIntervalSeconds:0.##}s\n" + $"Reapply missing: {shop.ReapplyOwnedBuffsWhenMissing}, every {shop.ReapplyCheckIntervalSeconds}s, max users/cycle: {((shop.ReapplyMaxUsersPerCycle <= 0) ? "unlimited" : shop.ReapplyMaxUsersPerCycle.ToString())}, cursor: {OwnershipService.ReapplyCursor}\n" + $"Carrier finalize interval: {shop.CarrierFinalizeCheckIntervalSeconds:0.##}s\n" + $"Ownership save debounce: {shop.OwnershipSaveDebounceSeconds:0.##}s, pending: {OwnershipService.HasPendingSave}, last save: {OwnershipService.LastSaveUtc}\n" + $"Player cache entries: {PlayerCacheService.CachedPlayerCount}, save debounce: {shop.PlayerCacheSaveDebounceSeconds:0.##}s, pending: {PlayerCacheService.HasPendingSave}\n" + $"Online cached users: {onlineUsersSnapshot.Count}\n" + $"Pending stat carriers: {StatService.PendingCarrierCount}\n" + $"Pending permanent buffs: {BuffService.PendingPermanentBuffCount}\n" + $"Cached stat hashes: {StatService.CachedStatHashCount}\n" + $"Parsed stat definitions: {StatDefinitionService.ParsedStatCount}\n" + "Max health behavior: " + shop.MaxHealthPurchaseBehavior + "\nStat carrier: " + StatService.DescribeConfiguredCarrier() + "\n" + $"Client attribute aliases: {shop.UseClientAttributeStatAliases}\n" + $"Blood buffs enabled: {shop.EnableExperimentalBloodBuffs}\n" + $"Unsupported client stats allowed: {shop.EnableClientUnsupportedStats}"); } catch (Exception e) { Core.LogException(e, "Diagnostics"); reply("Could not build PerkShop diagnostics. Check server logs."); } } public static void SyncAll(Action reply) { try { OwnershipService.FlushPendingSaves(force: true); PlayerCacheService.FlushPendingSaves(force: true); OwnershipService.OnlineReapplyResult onlineReapplyResult = OwnershipService.ReapplyOnlineOwnedBuffs(onlyIfMissing: false); OwnershipService.FlushPendingSaves(force: true); PlayerCacheService.FlushPendingSaves(force: true); reply("PerkShop syncall complete.\n" + $"Online cached users checked: {onlineReapplyResult.Checked}\n" + $"Valid users processed: {onlineReapplyResult.Processed}\n" + $"Invalid/stale users skipped: {onlineReapplyResult.SkippedInvalid}\n" + $"Buffs checked: {onlineReapplyResult.BuffsChecked}\n" + $"Buffs already active: {onlineReapplyResult.BuffsAlreadyActive}\n" + $"Buff lifetime refreshes: {onlineReapplyResult.BuffLifetimeRefreshes}\n" + $"Buff apply attempts: {onlineReapplyResult.BuffApplyAttempts}\n" + $"Buff apply successes: {onlineReapplyResult.BuffApplySuccesses}\n" + $"Stats attempted: {onlineReapplyResult.StatsAttempted}\n" + $"Stats applied or already current: {onlineReapplyResult.StatsApplied}\n" + $"Pending stat carriers after sync: {StatService.PendingCarrierCount}"); } catch (Exception e) { Core.LogException(e, "SyncAll"); reply("PerkShop syncall failed. Check server logs."); } } public static void Validate(Action reply) { try { ShopConfigRoot shop = ConfigService.Shop; List list = new List(); List list2 = new List(); if (!StatService.IsCarrierConfigured(out PrefabGUID _, out string error)) { list2.Add(error); } if (shop.StatCarrierBuffPrefab == 1774716596) { list2.Add("StatCarrierBuffPrefab uses Bloodcraft's SetBonus_AllLeech_T09 carrier."); } if (shop.CurrencyPrefab == 0) { list2.Add("CurrencyPrefab is 0."); } string key; foreach (KeyValuePair stat in shop.Stats) { stat.Deconstruct(out key, out var value); string text = key; StatShopEntry statShopEntry = value; if (statShopEntry == null) { list2.Add("Stat '" + text + "' has a null entry."); } else if (statShopEntry.Enabled) { if (!StatDefinitionService.TryGetParsedStat(text, out StatDefinitionService.ParsedStatDefinition _)) { list2.Add("Stat '" + text + "' is enabled but has invalid UnitStat/ModificationType/AttributeCapType."); } if (statShopEntry.Cost <= 0) { list2.Add("Stat '" + text + "' has non-positive cost."); } if (statShopEntry.MaxPurchases < 0) { list2.Add("Stat '" + text + "' has negative MaxPurchases."); } if (StatDefinitionService.IsDangerousModifierCombo(statShopEntry)) { list.Add("Stat '" + text + "' uses MovementSpeed + Add. Prefer MultiplyBaseAdd."); } if (!shop.EnableClientUnsupportedStats && !StatDefinitionService.IsClientTabSafeStat(shop, statShopEntry)) { list.Add($"Stat '{text}' ({statShopEntry.UnitStat}) is enabled in config but suppressed by EnableClientUnsupportedStats=false to avoid Bloodcraft/Eclipse VampireAttributes spam. If aliases are enabled, PerkShop will test the resolved client-attribute stat instead."); } } } Dictionary dictionary = new Dictionary(); foreach (KeyValuePair buff in shop.Buffs) { buff.Deconstruct(out key, out var value2); string text2 = key; PerkShopEntry perkShopEntry = value2; if (perkShopEntry == null) { list2.Add("Buff '" + text2 + "' has a null entry."); } else { if (!perkShopEntry.Enabled) { continue; } if (perkShopEntry.BuffPrefab == 0) { list2.Add("Buff '" + text2 + "' has BuffPrefab 0."); } if (!shop.Categories.ContainsKey(perkShopEntry.Category)) { list2.Add($"Buff '{text2}' references missing category '{perkShopEntry.Category}'."); } if (perkShopEntry.Cost <= 0) { list2.Add("Buff '" + text2 + "' has non-positive cost."); } if (perkShopEntry.BuffPrefab != 0) { if (dictionary.TryGetValue(perkShopEntry.BuffPrefab, out var value3)) { list.Add($"Buff '{text2}' and '{value3}' share prefab {perkShopEntry.BuffPrefab}."); } else { dictionary[perkShopEntry.BuffPrefab] = text2; } } string text3 = (text2 + " " + perkShopEntry.DisplayName).ToLowerInvariant(); if (text3.Contains("shapeshift") || text3.Contains("travel") || text3.Contains("summon") || text3.Contains("channel")) { list.Add("Buff '" + text2 + "' may be a scripted/exotic buff. Test carefully before selling it permanently."); } } } string text4 = ((list.Count == 0) ? "none" : string.Join("\n", from w in list.Take(15) select " - " + w)); string text5 = ((list2.Count == 0) ? "none" : string.Join("\n", from e in list2.Take(15) select " - " + e)); reply("=== PerkShop Config Validation ===\n" + $"Errors: {list2.Count}\n" + text5 + "\n" + $"Warnings: {list.Count}\n" + text4 + "\n" + $"Parsed stat definitions: {StatDefinitionService.ParsedStatCount}\n" + $"Buff entries: {shop.Buffs.Count}\n" + $"Stat entries: {shop.Stats.Count}"); } catch (Exception e2) { Core.LogException(e2, "Validate"); reply("PerkShop validation failed. Check server logs."); } } public static void WhitelistStatus(Action reply) { ShopConfigRoot shop = ConfigService.Shop; string text = (shop.EnableBuffWhitelist ? $"ON ({shop.BuffWhitelistPlatformIds.Count} player(s))" : $"OFF ({shop.BuffWhitelistPlatformIds.Count} saved player(s))"); string text2 = (shop.EnableStatWhitelist ? $"ON ({shop.StatWhitelistPlatformIds.Count} player(s))" : $"OFF ({shop.StatWhitelistPlatformIds.Count} saved player(s))"); reply("=== Whitelist Status ===\nBuff Whitelist: " + text + "\nStat Whitelist: " + text2); } public static void WhitelistAdd(string shopType, string playerRef, Action reply) { if (!TryResolveWhitelistTarget(playerRef, out ulong platformId, out string characterName, out string error)) { reply(error); return; } bool flag = ConfigService.UpdateWhitelist(shopType, platformId, add: true, characterName); string text = (string.IsNullOrWhiteSpace(characterName) ? platformId.ToString() : $"{characterName} ({platformId})"); reply(flag ? $"Added {text} to {shopType} whitelist." : (text + " is already in " + shopType + " whitelist.")); } public static void WhitelistRemove(string shopType, string playerRef, Action reply) { if (!TryResolveWhitelistTarget(playerRef, out ulong platformId, out string characterName, out string error)) { reply(error); return; } bool flag = ConfigService.UpdateWhitelist(shopType, platformId, add: false); string text = (string.IsNullOrWhiteSpace(characterName) ? platformId.ToString() : $"{characterName} ({platformId})"); reply(flag ? $"Removed {text} from {shopType} whitelist." : (text + " was not in " + shopType + " whitelist.")); } private static bool TryResolveWhitelistTarget(string playerRef, out ulong platformId, out string characterName, out string error) { //IL_0086: Unknown result type (might be due to invalid IL or missing references) platformId = 0uL; characterName = string.Empty; error = string.Empty; if (string.IsNullOrWhiteSpace(playerRef)) { error = "Usage: .perk wladdbuff , .perk wladdstat , .perk wlremovebuff , or .perk wlremovestat "; return false; } playerRef = playerRef.Trim(); Entity userEntity; if (ulong.TryParse(playerRef, out platformId) && platformId != 0L) { string characterName2; if (AdminPlayerLookup.TryFindUser(playerRef, out userEntity, out User user, out string _)) { characterName = ((object)(FixedString64Bytes)(ref user.CharacterName)).ToString(); PlayerCacheService.Remember(platformId, characterName); } else if (PlayerCacheService.TryGetName(platformId, out characterName2)) { characterName = characterName2; } return true; } if (!AdminPlayerLookup.TryFindUser(playerRef, out userEntity, out var user2, out error)) { return false; } platformId = user2.PlatformId; characterName = ((object)(FixedString64Bytes)(ref user2.CharacterName)).ToString(); PlayerCacheService.Remember(platformId, characterName); if (platformId == 0L) { error = "Could not resolve PlatformId for '" + playerRef + "'."; return false; } return true; } public static void ListBuffKeys(Entity userEntity, int page, Action reply) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) if (!AccessService.CanAccessPerkShop(userEntity, reply)) { return; } ShopConfigRoot config = ConfigService.Shop; if (!config.Enabled) { reply("Perk shop is disabled."); return; } BuffCategoryDefinition value4; string[] array = (from kv in config.Buffs where kv.Value.Enabled where config.EnableExperimentalBloodBuffs || !string.Equals(kv.Value.Category, "blood_buff", StringComparison.OrdinalIgnoreCase) orderby (!config.Categories.TryGetValue(kv.Value.Category, out value4)) ? kv.Value.Category : value4.DisplayName, kv.Key select kv).Select(delegate(KeyValuePair kv) { BuffCategoryDefinition value2; string value3 = (config.Categories.TryGetValue(kv.Value.Category, out value2) ? value2.DisplayName : kv.Value.Category); return $" {kv.Key} [{value3}]"; }).ToArray(); if (array.Length == 0) { reply("No buffs configured."); return; } (int page, int totalPages) tuple = NormalizePage(page, array.Length); int item = tuple.page; int item2 = tuple.totalPages; string[] value = array.Skip((item - 1) * 8).Take(8).ToArray(); string text = ((item < item2) ? $"\nNext page: .perk bufflist {item + 1}" : string.Empty); reply($"=== Buff Keys Page {item}/{item2} ===\n" + string.Join("\n", value) + "\n\nDetails: .perk buffdet " + text); } public static void BuffDetails(Entity userEntity, string key, Action reply) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: 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_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) if (!AccessService.CanAccessPerkShop(userEntity, reply)) { return; } ShopConfigRoot shop = ConfigService.Shop; if (!shop.Enabled) { reply("Perk shop is disabled."); return; } if (string.IsNullOrWhiteSpace(key)) { reply("Usage: .perk buffdet "); return; } key = KeyAliasService.NormalizeBuffKey(key); if (!shop.Buffs.TryGetValue(key, out PerkShopEntry value) || !value.Enabled) { reply("Unknown buff '" + key + "'. Use .perk bufflist or .perk search ."); return; } BuffCategoryDefinition value2; string value3 = (shop.Categories.TryGetValue(value.Category, out value2) ? value2.DisplayName : value.Category); string value4 = (string.IsNullOrWhiteSpace(value.Notes) ? "No effect description configured." : value.Notes); bool flag = false; string value5 = "Slot: no cap configured"; if (PlayerStateHelper.Exists(userEntity)) { EntityManager entityManager = Core.EntityManager; if (((EntityManager)(ref entityManager)).HasComponent(userEntity)) { entityManager = Core.EntityManager; User componentData = ((EntityManager)(ref entityManager)).GetComponentData(userEntity); flag = OwnershipService.PlayerOwns(componentData.PlatformId, key); if (shop.Categories.TryGetValue(value.Category, out BuffCategoryDefinition value6)) { int value7 = OwnershipService.CountOwnedBuffsInCategory(componentData.PlatformId, value.Category); string value8 = (value6.MaxOwnedSlots.HasValue ? value6.MaxOwnedSlots.Value.ToString() : "∞"); value5 = $"Slot: {value6.DisplayName} {value7}/{value8}"; } } } reply($"=== {value.DisplayName} ===\nKey: {key}\nCategory: {value3}\nEffect: {value4}\nCost: {value.Cost} {shop.CurrencyName}\nOwned: {(flag ? "Yes" : "No")}\n{value5}\nBuy: .perk buffbuy {key}\nRemove: .perk buffremove {key}"); } } internal static class StatDefinitionService { internal sealed class ParsedStatDefinition { public string Key { get; init; } = string.Empty; public StatShopEntry Entry { get; init; } = new StatShopEntry(); public UnitStatType UnitStat { get; init; } public ModificationType ModificationType { get; init; } public AttributeCapType AttributeCapType { get; init; } } public const int CurrentConfigVersion = 14; private static readonly object Lock = new object(); private static readonly Dictionary ParsedStats = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly HashSet WarnedInvalidDefinitions = new HashSet(StringComparer.OrdinalIgnoreCase); public static int ParsedStatCount { get { lock (Lock) { return ParsedStats.Count; } } } public static void RebuildCache(ShopConfigRoot config) { //IL_0183: Unknown result type (might be due to invalid IL or missing references) //IL_0202: Unknown result type (might be due to invalid IL or missing references) //IL_020a: Unknown result type (might be due to invalid IL or missing references) //IL_020f: Unknown result type (might be due to invalid IL or missing references) //IL_022e: Unknown result type (might be due to invalid IL or missing references) //IL_0236: Unknown result type (might be due to invalid IL or missing references) //IL_023e: Unknown result type (might be due to invalid IL or missing references) //IL_0200: Unknown result type (might be due to invalid IL or missing references) lock (Lock) { ParsedStats.Clear(); WarnedInvalidDefinitions.Clear(); if (config?.Stats == null) { return; } foreach (var (text2, statShopEntry2) in config.Stats) { if (string.IsNullOrWhiteSpace(text2) || statShopEntry2 == null || !statShopEntry2.Enabled) { continue; } string text3 = text2.Trim(); if (!Enum.TryParse(statShopEntry2.UnitStat, ignoreCase: true, out UnitStatType result)) { WarnOnce("UnitStat:" + text3 + ":" + statShopEntry2.UnitStat, $"[StatDefinitionService] Stat '{text3}' has invalid UnitStat '{statShopEntry2.UnitStat}' and will be skipped."); continue; } if (!Enum.TryParse(statShopEntry2.ModificationType, ignoreCase: true, out ModificationType result2)) { WarnOnce("ModificationType:" + text3 + ":" + statShopEntry2.ModificationType, $"[StatDefinitionService] Stat '{text3}' has invalid ModificationType '{statShopEntry2.ModificationType}'. Falling back to Add."); result2 = (ModificationType)3; } if (!Enum.TryParse(statShopEntry2.AttributeCapType, ignoreCase: true, out AttributeCapType result3)) { WarnOnce("AttributeCapType:" + text3 + ":" + statShopEntry2.AttributeCapType, $"[StatDefinitionService] Stat '{text3}' has invalid AttributeCapType '{statShopEntry2.AttributeCapType}'. Falling back to Uncapped."); result3 = (AttributeCapType)(-1); } UnitStatType unitStat = ResolveClientAttributeAlias(result, config.UseClientAttributeStatAliases); ParsedStats[text3] = new ParsedStatDefinition { Key = text3, Entry = statShopEntry2, UnitStat = unitStat, ModificationType = result2, AttributeCapType = result3 }; } } } public static bool TryGetParsedStat(string key, out ParsedStatDefinition definition) { definition = null; if (string.IsNullOrWhiteSpace(key)) { return false; } lock (Lock) { if (ParsedStats.TryGetValue(key, out ParsedStatDefinition value)) { definition = value; return true; } return false; } } public static bool IsClientTabSafeUnitStat(string unitStat) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrWhiteSpace(unitStat)) { return false; } if (!Enum.TryParse(unitStat.Trim(), ignoreCase: true, out UnitStatType result)) { return false; } return IsClientTabSafeUnitStat(result); } public static bool IsClientTabSafeUnitStat(UnitStatType unitStat) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Invalid comparison between Unknown and I4 //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Invalid comparison between Unknown and I4 if ((int)unitStat != 0) { if ((int)unitStat != 4) { if ((int)unitStat == 25) { return true; } return false; } return true; } return true; } public static UnitStatType ResolveClientAttributeAlias(UnitStatType unitStat, bool useAliases) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) if (!useAliases) { return unitStat; } if ((int)unitStat == 5) { return (UnitStatType)83; } return unitStat; } public static bool IsClientTabSafeStat(ShopConfigRoot config, StatShopEntry entry) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) if (entry == null) { return false; } if (!Enum.TryParse(entry.UnitStat, ignoreCase: true, out UnitStatType result)) { return false; } return IsClientTabSafeUnitStat(ResolveClientAttributeAlias(result, config.UseClientAttributeStatAliases)); } public static bool IsClientUnsupportedStatAllowed(ShopConfigRoot config, StatShopEntry entry) { if (!config.EnableClientUnsupportedStats) { return IsClientTabSafeStat(config, entry); } return true; } public static bool IsDangerousModifierCombo(StatShopEntry entry) { if (entry == null) { return false; } if (string.Equals(entry.UnitStat, "MovementSpeed", StringComparison.OrdinalIgnoreCase)) { return string.Equals(entry.ModificationType, "Add", StringComparison.OrdinalIgnoreCase); } return false; } public static void NormalizeKnownStatEntry(string key, StatShopEntry entry) { if (entry != null) { if (string.Equals(key, "MS", StringComparison.OrdinalIgnoreCase) && string.Equals(entry.UnitStat, "MovementSpeed", StringComparison.OrdinalIgnoreCase) && string.Equals(entry.ModificationType, "Add", StringComparison.OrdinalIgnoreCase)) { entry.ModificationType = "MultiplyBaseAdd"; } if (TryGetDefaultStatNote(key, entry.ValuePerPurchase, entry.UnitStat, out string note) && (string.IsNullOrWhiteSpace(entry.Notes) || entry.Notes.StartsWith("Permanently increases ", StringComparison.OrdinalIgnoreCase) || string.Equals(entry.Notes, "Permanent stat purchase.", StringComparison.OrdinalIgnoreCase))) { entry.Notes = note; } } } public static bool TryGetDefaultStatNote(string key, float valuePerPurchase, string unitStat, out string note) { note = string.Empty; if (string.IsNullOrWhiteSpace(key)) { return false; } string text = FormatNumber(valuePerPurchase); string text2 = ((!(key.Trim().ToLowerInvariant() == "MS")) ? $"Permanently increases {ToTitle(unitStat)} by {text} per purchase." : ("Permanently increases Movement Speed by " + text + " per purchase. Uses MultiplyBaseAdd for HUD/stat compatibility.")); note = text2; return true; } private static string ToTitle(string value) { if (string.IsNullOrWhiteSpace(value)) { return "the configured stat"; } string text = string.Empty; string text2 = value.Trim(); for (int i = 0; i < text2.Length; i++) { char c = text2[i]; if (char.IsUpper(c) && text.Length > 0) { text += " "; } text += c; } return text; } private static string FormatNumber(float value) { if (!(Math.Abs(value % 1f) < 0.0001f)) { return value.ToString("0.####"); } return ((int)value).ToString(); } private static void WarnOnce(string key, string message) { if (WarnedInvalidDefinitions.Add(key)) { Core.Log.LogWarning((object)message); } } } internal static class StatService { private sealed class PendingCarrierInfo { public DateTime CreatedAtUtc { get; init; } = DateTime.UtcNow; public Entity CharacterEntity { get; init; } = Entity.Null; public float? HealthRatioBeforeRebuild { get; init; } } private static readonly PrefabGUID BloodcraftBonusStatsCarrier = new PrefabGUID(1774716596); private static readonly Dictionary PendingPermanentCarriers = new Dictionary(); private static readonly Dictionary PendingHealthRatiosByPlatformId = new Dictionary(); private static readonly Dictionary LastAppliedStatHashes = new Dictionary(); private static readonly HashSet SpawnValidatedPlatformIds = new HashSet(); private static readonly HashSet WarnedInvalidStatConfig = new HashSet(StringComparer.OrdinalIgnoreCase); private static readonly TimeSpan PendingCarrierTimeout = TimeSpan.FromSeconds(30.0); public static int PendingCarrierCount => PendingPermanentCarriers.Count; public static int CachedStatHashCount => LastAppliedStatHashes.Count; public static void ResetRuntimeStateAfterConfigReload() { SpawnValidatedPlatformIds.Clear(); PendingPermanentCarriers.Clear(); PendingHealthRatiosByPlatformId.Clear(); LastAppliedStatHashes.Clear(); WarnedInvalidStatConfig.Clear(); } public static string DescribeConfiguredCarrier() { if (!IsCarrierConfigured(out PrefabGUID _, out string error)) { return "invalid (" + error + ")"; } return $"SetBonus_ShieldPowerAndHealingReceived_T08 ({ConfigService.Shop.StatCarrierBuffPrefab})"; } private static void WarnInvalidStatOnce(string warningKey, string message) { if (WarnedInvalidStatConfig.Add(warningKey)) { Core.Log.LogWarning((object)message); } } public static bool IsCarrierConfigured(out PrefabGUID carrier, out string error) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_001c: 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) int statCarrierBuffPrefab = ConfigService.Shop.StatCarrierBuffPrefab; carrier = (PrefabGUID)((statCarrierBuffPrefab != 0) ? new PrefabGUID(statCarrierBuffPrefab) : PrefabGUID.Empty); error = string.Empty; if (statCarrierBuffPrefab == 0) { error = "StatCarrierBuffPrefab is 0. Configure a dedicated carrier buff prefab before using the permanent stat shop."; return false; } if (((PrefabGUID)(ref carrier)).Equals(BloodcraftBonusStatsCarrier)) { error = "StatCarrierBuffPrefab is set to SetBonus_AllLeech_T09, which Bloodcraft uses as its bonus stats carrier. Use a different dedicated carrier buff."; return false; } return true; } public static bool ApplyOwnedStatsForUser(Entity userEntity) { //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_0008: Unknown result type (might be due to invalid IL or missing references) //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_0018: 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_002c: 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_0032: 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_0034: 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_003e: 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_0040: 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_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) try { EntityManager entityManager = Core.EntityManager; if (((EntityManager)(ref entityManager)).Exists(userEntity)) { entityManager = Core.EntityManager; if (((EntityManager)(ref entityManager)).HasComponent(userEntity)) { entityManager = Core.EntityManager; User componentData = ((EntityManager)(ref entityManager)).GetComponentData(userEntity); Entity entity = componentData.LocalCharacter._Entity; if (!(entity == Entity.Null)) { entityManager = Core.EntityManager; if (((EntityManager)(ref entityManager)).Exists(entity)) { return ApplyOwnedStats(userEntity, entity, componentData.PlatformId); } } return false; } } return false; } catch (Exception e) { Core.LogException(e, "ApplyOwnedStatsForUser"); return false; } } public static bool ApplyOwnedStats(Entity userEntity, Entity characterEntity, ulong platformId) { //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) return ApplyOwnedStats(userEntity, characterEntity, platformId, forceRebuildCarrier: false); } public static bool RebuildOwnedStats(Entity userEntity, Entity characterEntity, ulong platformId) { //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) return ApplyOwnedStats(userEntity, characterEntity, platformId, forceRebuildCarrier: true); } private static bool ApplyOwnedStats(Entity userEntity, Entity characterEntity, ulong platformId, bool forceRebuildCarrier) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown //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_00df: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_0108: 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_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_0167: 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_016f: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_0191: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_019a: Unknown result type (might be due to invalid IL or missing references) //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Unknown result type (might be due to invalid IL or missing references) //IL_01af: Unknown result type (might be due to invalid IL or missing references) //IL_01b1: Unknown result type (might be due to invalid IL or missing references) //IL_01b2: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Unknown result type (might be due to invalid IL or missing references) ShopConfigRoot shop = ConfigService.Shop; if (!shop.EnableStatShop) { return false; } if (!IsCarrierConfigured(out PrefabGUID carrier, out string error)) { ManualLogSource log = Core.Log; bool flag = default(bool); BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(14, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[StatService] "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(error); } log.LogWarning(val); return false; } CarrierPrefabService.PrepareConfiguredStatCarrierPrefab(); Dictionary ownedStats = OwnershipService.GetOwnedStats(platformId); Dictionary adminFlatStats = OwnershipService.GetAdminFlatStats(platformId); if (ownedStats.Count <= 0 && adminFlatStats.Count <= 0) { BuffService.RemoveBuff(characterEntity, carrier); SpawnValidatedPlatformIds.Remove(platformId); PendingHealthRatiosByPlatformId.Remove(platformId); LastAppliedStatHashes.Remove(platformId); return true; } int num = ComputeStatOwnershipHash(platformId, ownedStats, adminFlatStats, shop); bool enabled = string.Equals(shop.MaxHealthPurchaseBehavior, "PreserveRatio", StringComparison.OrdinalIgnoreCase); if (forceRebuildCarrier || !LastAppliedStatHashes.TryGetValue(platformId, out var value) || value != num) { CaptureHealthRatioIfNeeded(characterEntity, platformId, enabled); } Entity buffEntity = default(Entity); if (forceRebuildCarrier) { SpawnValidatedPlatformIds.Remove(platformId); BuffService.RemoveBuff(characterEntity, carrier); } else if (BuffUtility.TryGetBuff(Core.EntityManager, characterEntity, PrefabIdentifier.op_Implicit(carrier), ref buffEntity)) { if (SpawnValidatedPlatformIds.Contains(platformId) && LastAppliedStatHashes.TryGetValue(platformId, out value) && value == num) { MarkStatCarrierPendingPermanent(buffEntity, characterEntity, TryConsumeHealthRatio(platformId)); return true; } SpawnValidatedPlatformIds.Remove(platformId); BuffService.RemoveBuff(characterEntity, carrier); } FromCharacter val2 = default(FromCharacter); val2.User = userEntity; val2.Character = characterEntity; FromCharacter val3 = val2; ApplyBuffDebugEvent val4 = default(ApplyBuffDebugEvent); val4.BuffPrefabGUID = carrier; ApplyBuffDebugEvent val5 = val4; Core.DebugEventsSystem.ApplyBuff(val3, val5); Entity buffEntity2 = default(Entity); if (BuffUtility.TryGetBuff(Core.EntityManager, characterEntity, PrefabIdentifier.op_Implicit(carrier), ref buffEntity2)) { TryPopulateOwnedStatBuffer(buffEntity2, characterEntity, userEntity); } return true; } public static bool TryPopulateOwnedStatBuffer(Entity buffEntity, Entity characterEntity, Entity userEntity) { //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_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) //IL_0033: 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_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_006c: 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_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0074: 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_009f: 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) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: 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) try { EntityManager entityManager = Core.EntityManager; if (buffEntity == Entity.Null || !((EntityManager)(ref entityManager)).Exists(buffEntity)) { return false; } if (characterEntity == Entity.Null || !((EntityManager)(ref entityManager)).Exists(characterEntity)) { return false; } if (userEntity == Entity.Null || !((EntityManager)(ref entityManager)).Exists(userEntity) || !((EntityManager)(ref entityManager)).HasComponent(userEntity)) { return false; } User componentData = ((EntityManager)(ref entityManager)).GetComponentData(userEntity); if (!TryPopulateOwnedStatBuffer(buffEntity, componentData.PlatformId)) { return false; } SpawnValidatedPlatformIds.Add(componentData.PlatformId); LastAppliedStatHashes[componentData.PlatformId] = ComputeStatOwnershipHash(componentData.PlatformId); TryAddSyncToUser(buffEntity, userEntity); float? healthRatioBeforeRebuild = TryConsumeHealthRatio(componentData.PlatformId); if (!healthRatioBeforeRebuild.HasValue && string.Equals(ConfigService.Shop.MaxHealthPurchaseBehavior, "PreserveRatio", StringComparison.OrdinalIgnoreCase)) { healthRatioBeforeRebuild = CaptureHealthRatio(characterEntity); } MarkStatCarrierPendingPermanent(buffEntity, characterEntity, healthRatioBeforeRebuild); return true; } catch (Exception e) { Core.LogException(e, "TryPopulateOwnedStatBuffer"); return false; } } public static bool TryPopulateOwnedStatBuffer(Entity buffEntity, ulong platformId, bool clearExisting = true) { //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_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) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0051: 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_005b: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_0187: Unknown result type (might be due to invalid IL or missing references) //IL_01d4: Unknown result type (might be due to invalid IL or missing references) //IL_01d9: Unknown result type (might be due to invalid IL or missing references) //IL_01dd: Unknown result type (might be due to invalid IL or missing references) //IL_01e2: Unknown result type (might be due to invalid IL or missing references) //IL_01e7: Unknown result type (might be due to invalid IL or missing references) //IL_02d8: Unknown result type (might be due to invalid IL or missing references) //IL_02e0: Unknown result type (might be due to invalid IL or missing references) //IL_02e2: Unknown result type (might be due to invalid IL or missing references) //IL_02ea: Unknown result type (might be due to invalid IL or missing references) //IL_02f2: Unknown result type (might be due to invalid IL or missing references) //IL_0336: Unknown result type (might be due to invalid IL or missing references) //IL_033b: Unknown result type (might be due to invalid IL or missing references) //IL_033f: Unknown result type (might be due to invalid IL or missing references) //IL_0344: Unknown result type (might be due to invalid IL or missing references) //IL_0349: Unknown result type (might be due to invalid IL or missing references) EntityManager entityManager = Core.EntityManager; ShopConfigRoot shop = ConfigService.Shop; if (!shop.EnableStatShop) { return false; } if (buffEntity == Entity.Null || !((EntityManager)(ref entityManager)).Exists(buffEntity)) { return false; } Dictionary ownedStats = OwnershipService.GetOwnedStats(platformId); Dictionary adminFlatStats = OwnershipService.GetAdminFlatStats(platformId); if (ownedStats.Count == 0 && adminFlatStats.Count == 0) { return false; } DynamicBuffer val = (((EntityManager)(ref entityManager)).HasBuffer(buffEntity) ? ((EntityManager)(ref entityManager)).GetBuffer(buffEntity, false) : ((EntityManager)(ref entityManager)).AddBuffer(buffEntity)); if (clearExisting) { val.Clear(); } string key; ModificationIDs val3; foreach (KeyValuePair item in ownedStats.OrderBy, string>((KeyValuePair kv) => kv.Key, StringComparer.OrdinalIgnoreCase)) { item.Deconstruct(out key, out var value); string text = key; int num = value; if (num > 0 && StatDefinitionService.TryGetParsedStat(text, out StatDefinitionService.ParsedStatDefinition definition)) { StatShopEntry entry = definition.Entry; if (!StatDefinitionService.IsClientUnsupportedStatAllowed(shop, entry)) { WarnInvalidStatOnce("ClientUnsupported:" + text, $"[StatService] Stat '{text}' ({entry.UnitStat}) is disabled by EnableClientUnsupportedStats=false because Bloodcraft/Eclipse/VampireAttributes can spam NotImplementedException for it. It remains owned but is not applied."); continue; } ModifyUnitStatBuff_DOTS val2 = new ModifyUnitStatBuff_DOTS { StatType = definition.UnitStat, ModificationType = definition.ModificationType, AttributeCapType = definition.AttributeCapType, SoftCapValue = 0f, Value = entry.ValuePerPurchase * (float)num, Modifier = 1f, IncreaseByStacks = false, ValueByStacks = 0f, Priority = 0 }; val3 = ModificationIDs.Create(); val2.Id = ((ModificationIDs)(ref val3)).NewModificationId(); val.Add(val2); } } foreach (KeyValuePair item2 in adminFlatStats.OrderBy, string>((KeyValuePair kv) => kv.Key, StringComparer.OrdinalIgnoreCase)) { item2.Deconstruct(out key, out var value2); string text2 = key; float value3 = value2; if (!(Math.Abs(value3) <= 0.0001f)) { if (!Enum.TryParse(text2, ignoreCase: true, out UnitStatType result)) { WarnInvalidStatOnce("AdminFlat:" + text2, "[StatService] Unknown admin flat UnitStat '" + text2 + "'."); continue; } if (!shop.EnableClientUnsupportedStats && !StatDefinitionService.IsClientTabSafeUnitStat(text2)) { WarnInvalidStatOnce("ClientUnsupportedAdminFlat:" + text2, "[StatService] Admin flat stat '" + text2 + "' is disabled by EnableClientUnsupportedStats=false because Bloodcraft/Eclipse/VampireAttributes can spam NotImplementedException for it."); continue; } ModifyUnitStatBuff_DOTS val2 = new ModifyUnitStatBuff_DOTS { StatType = result, ModificationType = (ModificationType)3, AttributeCapType = (AttributeCapType)(-1), SoftCapValue = 0f, Value = value3, Modifier = 1f, IncreaseByStacks = false, ValueByStacks = 0f, Priority = 0 }; val3 = ModificationIDs.Create(); val2.Id = ((ModificationIDs)(ref val3)).NewModificationId(); val.Add(val2); } } return val.Length > 0; } public static void TryAddSyncToUser(Entity buffEntity, Entity userEntity) { //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_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0014: 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_002d: 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_004c: 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_0042: 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_0053: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0060: 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_0083: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_008c: 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) EntityManager entityManager = Core.EntityManager; if (buffEntity == Entity.Null || userEntity == Entity.Null || !((EntityManager)(ref entityManager)).Exists(buffEntity) || !((EntityManager)(ref entityManager)).Exists(userEntity)) { return; } DynamicBuffer val = (((EntityManager)(ref entityManager)).HasBuffer(buffEntity) ? ((EntityManager)(ref entityManager)).GetBuffer(buffEntity, false) : ((EntityManager)(ref entityManager)).AddBuffer(buffEntity)); SyncToUserBuffer val2; for (int i = 0; i < val.Length; i++) { val2 = val[i]; if (((Entity)(ref val2.UserEntity)).Equals(userEntity)) { return; } } val2 = new SyncToUserBuffer { UserEntity = userEntity }; val.Add(val2); } public static void MarkStatCarrierPendingPermanent(Entity buffEntity) { //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) MarkStatCarrierPendingPermanent(buffEntity, Entity.Null, null); } public static void MarkStatCarrierPendingPermanent(Entity buffEntity, Entity characterEntity, float? healthRatioBeforeRebuild) { //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) //IL_001a: Unknown result type (might be due to invalid IL or missing references) if (!(buffEntity == Entity.Null)) { PendingPermanentCarriers[buffEntity] = new PendingCarrierInfo { CharacterEntity = characterEntity, HealthRatioBeforeRebuild = healthRatioBeforeRebuild }; } } public static void ProcessPendingPermanentCarriers() { //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_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Expected O, but got Unknown //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) if (PendingPermanentCarriers.Count == 0) { return; } EntityManager entityManager = Core.EntityManager; KeyValuePair[] array = PendingPermanentCarriers.ToArray(); bool flag = default(bool); for (int i = 0; i < array.Length; i++) { KeyValuePair keyValuePair = array[i]; Entity key = keyValuePair.Key; PendingCarrierInfo value = keyValuePair.Value; if (key == Entity.Null || !((EntityManager)(ref entityManager)).Exists(key)) { PendingPermanentCarriers.Remove(key); } else if (((EntityManager)(ref entityManager)).HasComponent(key)) { if (DateTime.UtcNow - value.CreatedAtUtc > PendingCarrierTimeout) { PendingPermanentCarriers.Remove(key); ManualLogSource log = Core.Log; BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(65, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[StatService] Timed out waiting to finalize stat carrier entity:"); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(key.Index); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(":"); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(key.Version); } log.LogWarning(val); } } else { BuffService.MakeBuffPermanent(key); Entity val2 = value.CharacterEntity; if ((val2 == Entity.Null || !((EntityManager)(ref entityManager)).Exists(val2)) && ((EntityManager)(ref entityManager)).HasComponent(key)) { val2 = ((EntityManager)(ref entityManager)).GetComponentData(key).Target; } NormalizeHealthAfterStatCarrierUpdate(val2, value.HealthRatioBeforeRebuild); PendingPermanentCarriers.Remove(key); Core.LogDebugIfEnabled($"[StatService] Finalized permanent stat carrier entity:{key.Index}:{key.Version}"); } } } public static void NormalizeHealthAfterStatCarrierUpdate(Entity characterEntity) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) NormalizeHealthAfterStatCarrierUpdate(characterEntity, null); } public static void NormalizeHealthAfterStatCarrierUpdate(Entity characterEntity, float? previousHealthRatio) { //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_001f: 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_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) //IL_0031: 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_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: 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) //IL_00bc: 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) EntityManager entityManager = Core.EntityManager; if (characterEntity == Entity.Null || !((EntityManager)(ref entityManager)).Exists(characterEntity) || !((EntityManager)(ref entityManager)).HasComponent(characterEntity)) { return; } Health componentData = ((EntityManager)(ref entityManager)).GetComponentData(characterEntity); float value = componentData.MaxHealth._Value; string maxHealthPurchaseBehavior = ConfigService.Shop.MaxHealthPurchaseBehavior; if (value <= 0f) { return; } if (string.Equals(maxHealthPurchaseBehavior, "FillToMax", StringComparison.OrdinalIgnoreCase)) { componentData.Value = value; ((EntityManager)(ref entityManager)).SetComponentData(characterEntity, componentData); } else if (string.Equals(maxHealthPurchaseBehavior, "PreserveRatio", StringComparison.OrdinalIgnoreCase) && previousHealthRatio.HasValue) { float num = previousHealthRatio.Value; if (num < 0f) { num = 0f; } if (num > 1f) { num = 1f; } componentData.Value = value * num; if (componentData.Value > value) { componentData.Value = value; } ((EntityManager)(ref entityManager)).SetComponentData(characterEntity, componentData); } else if (componentData.Value > value) { componentData.Value = value; ((EntityManager)(ref entityManager)).SetComponentData(characterEntity, componentData); } } private static void CaptureHealthRatioIfNeeded(Entity characterEntity, ulong platformId, bool enabled) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) if (enabled) { float? num = CaptureHealthRatio(characterEntity); if (num.HasValue) { PendingHealthRatiosByPlatformId[platformId] = num.Value; } } } private static float? TryConsumeHealthRatio(ulong platformId) { if (!PendingHealthRatiosByPlatformId.TryGetValue(platformId, out var value)) { return null; } PendingHealthRatiosByPlatformId.Remove(platformId); return value; } private static float? CaptureHealthRatio(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_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_001f: 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_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_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_005a: Unknown result type (might be due to invalid IL or missing references) EntityManager entityManager = Core.EntityManager; if (characterEntity == Entity.Null || !((EntityManager)(ref entityManager)).Exists(characterEntity) || !((EntityManager)(ref entityManager)).HasComponent(characterEntity)) { return null; } Health componentData = ((EntityManager)(ref entityManager)).GetComponentData(characterEntity); float value = componentData.MaxHealth._Value; if (value <= 0f) { return null; } float num = componentData.Value / value; if (num < 0f) { num = 0f; } if (num > 1f) { num = 1f; } return num; } private static int ComputeStatOwnershipHash(ulong platformId) { return ComputeStatOwnershipHash(platformId, OwnershipService.GetOwnedStats(platformId), OwnershipService.GetAdminFlatStats(platformId), ConfigService.Shop); } private static int ComputeStatOwnershipHash(ulong platformId, Dictionary ownedStats, Dictionary adminFlatStats, ShopConfigRoot config) { int num = 17; num = num * 31 + config.StatCarrierBuffPrefab; num = num * 31 + (config.EnableClientUnsupportedStats ? 1 : 0); num = num * 31 + (config.UseClientAttributeStatAliases ? 1 : 0); string key; float value3; foreach (KeyValuePair item in ownedStats.OrderBy, string>((KeyValuePair kv) => kv.Key, StringComparer.OrdinalIgnoreCase)) { item.Deconstruct(out key, out var value); string text = key; int num2 = value; if (num2 > 0) { num = num * 31 + StringComparer.OrdinalIgnoreCase.GetHashCode(text); num = num * 31 + num2; if (config.Stats.TryGetValue(text, out StatShopEntry value2) && value2 != null) { num = num * 31 + StringComparer.OrdinalIgnoreCase.GetHashCode(value2.UnitStat ?? string.Empty); num = num * 31 + StringComparer.OrdinalIgnoreCase.GetHashCode(value2.ModificationType ?? string.Empty); num = num * 31 + StringComparer.OrdinalIgnoreCase.GetHashCode(value2.AttributeCapType ?? string.Empty); int num3 = num * 31; value3 = value2.ValuePerPurchase; num = num3 + value3.GetHashCode(); } } } foreach (KeyValuePair item2 in adminFlatStats.OrderBy, string>((KeyValuePair kv) => kv.Key, StringComparer.OrdinalIgnoreCase)) { item2.Deconstruct(out key, out value3); string obj = key; float value4 = value3; if (!(Math.Abs(value4) <= 0.0001f)) { num = num * 31 + StringComparer.OrdinalIgnoreCase.GetHashCode(obj); num = num * 31 + value4.GetHashCode(); } } return num; } } internal sealed class SystemService { internal World ServerWorld { get; } internal ServerScriptMapper ServerScriptMapper { get; } internal DebugEventsSystem DebugEventsSystem { get; } internal PrefabCollectionSystem PrefabCollectionSystem { get; } internal SystemService(World serverWorld) { ServerWorld = serverWorld; ServerScriptMapper = serverWorld.GetExistingSystemManaged(); DebugEventsSystem = serverWorld.GetExistingSystemManaged(); PrefabCollectionSystem = serverWorld.GetExistingSystemManaged(); } } } namespace PerkShop.Patches { [HarmonyPatch] internal static class BuffSystemSpawnServerPatch { private static MethodBase TargetMethod() { Type type = AccessTools.TypeByName("BuffSystem_Spawn_Server") ?? AccessTools.TypeByName("ProjectM.BuffSystem_Spawn_Server"); if (type == null) { throw new MissingMethodException("Unable to find BuffSystem_Spawn_Server type."); } MethodInfo methodInfo = AccessTools.Method(type, "OnUpdate", (Type[])null, (Type[])null); if (methodInfo == null) { throw new MissingMethodException(type.FullName, "OnUpdate"); } return methodInfo; } [HarmonyPrefix] private static void OnUpdatePrefix(object __instance) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) try { if (Plugin.HasLoaded() && BuffService.PendingPermanentBuffCount != 0) { BuffService.ProcessPendingPermanentBuffsDuringSpawn(QueryService.GetBuffSystemSpawnServerQuery(__instance)); } } catch (Exception e) { Core.LogException(e, "OnUpdatePrefix"); } } } [HarmonyPatch(typeof(ServerBootstrapSystem), "OnUpdate")] internal static class InitializationPatch { private static bool _initialized; private static float _nextReapplyCheck; private static float _nextCarrierFinalizeCheck; [HarmonyPostfix] private static void OneShot_AfterServerBootstrap() { if (!_initialized) { if (!Plugin.HasLoaded()) { return; } _initialized = true; Core.InitializeAfterLoaded(); OwnershipService.Initialize(); PlayerCacheService.Initialize(); } ShopConfigRoot shop = ConfigService.Shop; if (Time.time >= _nextCarrierFinalizeCheck) { _nextCarrierFinalizeCheck = Time.time + shop.CarrierFinalizeCheckIntervalSeconds; StatService.ProcessPendingPermanentCarriers(); BuffService.ProcessPendingPermanentBuffs(); OwnershipService.FlushPendingSaves(); PlayerCacheService.FlushPendingSaves(); } } } [HarmonyPatch] internal static class ScriptSpawnServerPatch { private static MethodBase TargetMethod() { Type type = AccessTools.TypeByName("ScriptSpawnServer") ?? AccessTools.TypeByName("ProjectM.ScriptSpawnServer") ?? AccessTools.TypeByName("ProjectM.Shared.Systems.ScriptSpawnServer"); if (type == null) { throw new MissingMethodException("Unable to find ScriptSpawnServer type."); } MethodInfo methodInfo = AccessTools.Method(type, "OnUpdate", (Type[])null, (Type[])null); if (methodInfo == null) { throw new MissingMethodException(type.FullName, "OnUpdate"); } return methodInfo; } [HarmonyPrefix] private static void OnUpdatePrefix(object __instance) { //IL_001d: 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_0026: Unknown result type (might be due to invalid IL or missing references) //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) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: 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_004b: 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_005e: 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_007a: 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_0081: 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_0092: 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_00a2: 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_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: 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_00dd: 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) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: 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) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) try { if (!Plugin.HasLoaded() || !StatService.IsCarrierConfigured(out PrefabGUID carrier, out string _)) { return; } EntityQuery scriptSpawnServerQuery = QueryService.GetScriptSpawnServerQuery(__instance); NativeArray val = ((EntityQuery)(ref scriptSpawnServerQuery)).ToEntityArray(AllocatorHandle.op_Implicit((Allocator)2)); try { EntityManager entityManager = Core.EntityManager; for (int i = 0; i < val.Length; i++) { Entity val2 = val[i]; if (val2 == Entity.Null || !((EntityManager)(ref entityManager)).Exists(val2) || !((EntityManager)(ref entityManager)).HasComponent(val2)) { continue; } PrefabGUID componentData = ((EntityManager)(ref entityManager)).GetComponentData(val2); if (!((PrefabGUID)(ref componentData)).Equals(carrier) || !((EntityManager)(ref entityManager)).HasComponent(val2)) { continue; } Entity target = ((EntityManager)(ref entityManager)).GetComponentData(val2).Target; if (!(target == Entity.Null) && ((EntityManager)(ref entityManager)).Exists(target) && ((EntityManager)(ref entityManager)).HasComponent(target)) { Entity userEntity = ((EntityManager)(ref entityManager)).GetComponentData(target).UserEntity; if (!(userEntity == Entity.Null) && ((EntityManager)(ref entityManager)).Exists(userEntity) && ((EntityManager)(ref entityManager)).HasComponent(userEntity) && StatService.TryPopulateOwnedStatBuffer(val2, target, userEntity)) { User componentData2 = ((EntityManager)(ref entityManager)).GetComponentData(userEntity); Core.LogDebugIfEnabled($"[ScriptSpawnServerPatch] Populated PerkShop stat carrier entity:{val2.Index}:{val2.Version} player:{componentData2.CharacterName} platformId:{componentData2.PlatformId}"); } } } } finally { if (val.IsCreated) { val.Dispose(); } } } catch (Exception e) { Core.LogException(e, "OnUpdatePrefix"); } } } [HarmonyPatch] internal static class UserConnectionPatch { [HarmonyPatch(typeof(ServerBootstrapSystem), "OnUserConnected")] [HarmonyPostfix] private static void OnUserConnectedPostfix(ServerBootstrapSystem __instance, NetConnectionId netConnectionId) { //IL_001d: 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_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: 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_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0055: 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) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_005e: 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_0069: Unknown result type (might be due to invalid IL or missing references) int num = default(int); if (!__instance._NetEndPointToApprovedUserIndex.TryGetValue(netConnectionId, ref num)) { return; } Entity userEntity = ((Il2CppArrayBase)(object)__instance._ApprovedUsersLookup)[num].UserEntity; if (userEntity == Entity.Null) { return; } EntityManager entityManager = ((ComponentSystemBase)__instance).EntityManager; if (!((EntityManager)(ref entityManager)).Exists(userEntity)) { return; } entityManager = ((ComponentSystemBase)__instance).EntityManager; if (((EntityManager)(ref entityManager)).HasComponent(userEntity)) { entityManager = ((ComponentSystemBase)__instance).EntityManager; User componentData = ((EntityManager)(ref entityManager)).GetComponentData(userEntity); OwnershipService.RegisterOnlineUser(componentData.PlatformId, userEntity); PlayerCacheService.Remember(componentData); if (ConfigService.Shop.SaveOwnership && Plugin.HasLoaded()) { Core.InitializeAfterLoaded(); } } } [HarmonyPatch(typeof(ServerBootstrapSystem), "OnUserDisconnected")] [HarmonyPrefix] private static void OnUserDisconnectedPrefix(ServerBootstrapSystem __instance, NetConnectionId netConnectionId) { //IL_001d: 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_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: 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_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0055: 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) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) int num = default(int); if (!__instance._NetEndPointToApprovedUserIndex.TryGetValue(netConnectionId, ref num)) { return; } Entity userEntity = ((Il2CppArrayBase)(object)__instance._ApprovedUsersLookup)[num].UserEntity; if (userEntity == Entity.Null) { return; } EntityManager entityManager = ((ComponentSystemBase)__instance).EntityManager; if (((EntityManager)(ref entityManager)).Exists(userEntity)) { entityManager = ((ComponentSystemBase)__instance).EntityManager; if (((EntityManager)(ref entityManager)).HasComponent(userEntity)) { entityManager = ((ComponentSystemBase)__instance).EntityManager; OwnershipService.UnregisterOnlineUser(((EntityManager)(ref entityManager)).GetComponentData(userEntity).PlatformId); } } } } } namespace PerkShop.Models { public sealed class ShopConfigRoot { public int ConfigVersion { get; set; } = 14; public bool Enabled { get; set; } = true; public bool EnableDebugLogging { get; set; } public int CurrencyPrefab { get; set; } = 576389135; public string CurrencyName { get; set; } = "Greater Stygian Shards"; public bool BlockPurchasesInCombat { get; set; } = true; public bool BlockRemovalsInCombat { get; set; } = true; public bool SaveOwnership { get; set; } = true; public bool ReapplyOwnedBuffsOnLogin { get; set; } = true; public bool ReapplyOwnedBuffsWhenMissing { get; set; } = true; public int ReapplyCheckIntervalSeconds { get; set; } = 60; public int ReapplyMaxUsersPerCycle { get; set; } = 5; public float CarrierFinalizeCheckIntervalSeconds { get; set; } = 0.25f; public float OwnershipSaveDebounceSeconds { get; set; } = 2f; public bool AutoDetectConfigChanges { get; set; } public float ConfigFileCheckIntervalSeconds { get; set; } = 5f; public float PlayerCacheSaveDebounceSeconds { get; set; } = 30f; public bool ForcePermanentBuffs { get; set; } = true; public bool AllowBuffEntityMutation { get; set; } = true; public bool UseRenewableTimedBuffs { get; set; } = true; public int RenewableTimedBuffSeconds { get; set; } = 7200; public List RenewableTimedBuffCategories { get; set; } = new List { "potion", "elixir", "blood_buff" }; public bool RenewableTimedBuffsPersistThroughDeath { get; set; } public bool EnableBuffWhitelist { get; set; } public List BuffWhitelistPlatformIds { get; set; } = new List(); public Dictionary BuffWhitelistNames { get; set; } = new Dictionary(); public bool EnableStatWhitelist { get; set; } public List StatWhitelistPlatformIds { get; set; } = new List(); public Dictionary StatWhitelistNames { get; set; } = new Dictionary(); public Dictionary Categories { get; set; } = new Dictionary(); public bool EnableStatShop { get; set; } = true; public bool EnableClientUnsupportedStats { get; set; } = true; public bool UseClientAttributeStatAliases { get; set; } public bool EnableExperimentalBloodBuffs { get; set; } public int StatCarrierBuffPrefab { get; set; } = -809648681; public bool EnableStatTypeSlots { get; set; } = true; public int MaxOwnedStatTypes { get; set; } = 4; public int StatTypeSlotFreeCost { get; set; } = 500; public string MaxHealthPurchaseBehavior { get; set; } = "ClampOnly"; public Dictionary Stats { get; set; } = new Dictionary(); public Dictionary Buffs { get; set; } = new Dictionary(); } public sealed class BuffCategoryDefinition { public string DisplayName { get; set; } = "Unnamed Category"; public string Documentation { get; set; } = string.Empty; public int? MaxOwnedSlots { get; set; } public int? SlotFreeCost { get; set; } } public sealed class PerkShopEntry { public bool Enabled { get; set; } = true; public bool EnableDebugLogging { get; set; } public string DisplayName { get; set; } = "Unnamed Buff"; public string Category { get; set; } = "misc"; public int BuffPrefab { get; set; } public int Cost { get; set; } = 100; public bool PersistentPurchase { get; set; } = true; public int DurationSeconds { get; set; } = -1; public bool KeepVisibleTimerFrozen { get; set; } public int VisibleTimerSeconds { get; set; } public bool PersistThroughDeath { get; set; } = true; public bool MutateAppliedBuffLifetime { get; set; } = true; public bool PreventDuplicate { get; set; } = true; public string Notes { get; set; } = "Use an isolated carrier buff prefab for best compatibility."; } public sealed class StatShopEntry { public bool Enabled { get; set; } = true; public bool EnableDebugLogging { get; set; } public string DisplayName { get; set; } = "Unnamed Stat"; public string UnitStat { get; set; } = "PhysicalPower"; public string ModificationType { get; set; } = "Add"; public string AttributeCapType { get; set; } = "Uncapped"; public float ValuePerPurchase { get; set; } = 1f; public int Cost { get; set; } = 100; public int MaxPurchases { get; set; } = 1; public string Notes { get; set; } = "Permanent stat purchase."; } public sealed class OwnershipStore { public Dictionary Players { get; set; } = new Dictionary(); } public sealed class PlayerOwnedBuffs { public List OwnedBuffKeys { get; set; } = new List(); public List AdminGrantedBuffKeys { get; set; } = new List(); public Dictionary OwnedStats { get; set; } = new Dictionary(); public Dictionary AdminFlatStats { get; set; } = new Dictionary(); } public sealed class PlayerCacheStore { public Dictionary Players { get; set; } = new Dictionary(); } public sealed class PlayerCacheEntry { public string CharacterName { get; set; } = string.Empty; public string LastSeenUtc { get; set; } = string.Empty; } } namespace PerkShop.Commands { [CommandGroup("perkshop", "perk")] internal static class PerkShopCommands { [Command("menu", null, null, null, null, false)] public static void Menu(ChatCommandContext ctx) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) ShopService.Menu(ctx.Event.SenderUserEntity, (Action)ctx.Reply); } [Command("status", null, null, null, null, false)] public static void Status(ChatCommandContext ctx, int page = 1) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) ShopService.Status(ctx.Event.SenderUserEntity, page, (Action)ctx.Reply); } [Command("search", null, null, null, null, false)] public static void Search(ChatCommandContext ctx, string query = "") { //IL_0006: Unknown result type (might be due to invalid IL or missing references) ShopService.Search(ctx.Event.SenderUserEntity, query, (Action)ctx.Reply); } [Command("bufflist", null, null, null, null, false)] public static void BuffList(ChatCommandContext ctx, int page = 1) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) ShopService.ListBuffKeys(ctx.Event.SenderUserEntity, page, (Action)ctx.Reply); } [Command("buffdet", null, null, null, null, false)] public static void BuffDetail(ChatCommandContext ctx, string buffKey = "") { //IL_0006: Unknown result type (might be due to invalid IL or missing references) ShopService.BuffDetails(ctx.Event.SenderUserEntity, buffKey, (Action)ctx.Reply); } [Command("buffbuy", null, null, null, null, false)] public static void BuffBuy(ChatCommandContext ctx, string buffKey = "") { //IL_0006: 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) ShopService.BuyBuff(ctx.Event.SenderUserEntity, ctx.Event.SenderCharacterEntity, buffKey, (Action)ctx.Reply); } [Command("buffremove", null, null, null, null, false)] public static void BuffRemove(ChatCommandContext ctx, string buffKey = "") { //IL_0006: 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) ShopService.RemoveBuff(ctx.Event.SenderUserEntity, ctx.Event.SenderCharacterEntity, buffKey, (Action)ctx.Reply); } [Command("statlist", null, null, null, null, false)] public static void StatList(ChatCommandContext ctx, int page = 1) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) ShopService.ListStatKeys(ctx.Event.SenderUserEntity, page, (Action)ctx.Reply); } [Command("statdet", null, null, null, null, false)] public static void StatDetail(ChatCommandContext ctx, string statKey = "") { //IL_0006: Unknown result type (might be due to invalid IL or missing references) ShopService.StatDetails(ctx.Event.SenderUserEntity, statKey, (Action)ctx.Reply); } [Command("statbuy", null, null, null, null, false)] public static void StatBuy(ChatCommandContext ctx, string statKey = "") { //IL_0006: 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) ShopService.BuyStat(ctx.Event.SenderUserEntity, ctx.Event.SenderCharacterEntity, statKey, (Action)ctx.Reply); } [Command("statremove", null, null, null, null, false)] public static void StatRemove(ChatCommandContext ctx, string statKey = "") { //IL_0006: 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) ShopService.RemoveStat(ctx.Event.SenderUserEntity, ctx.Event.SenderCharacterEntity, statKey, (Action)ctx.Reply); } [Command("sync", null, null, null, null, false)] public static void Sync(ChatCommandContext ctx) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) ShopService.Sync(ctx.Event.SenderUserEntity, (Action)ctx.Reply); } [Command("admin", null, null, null, null, true)] public static void Admin(ChatCommandContext ctx) { ShopService.AdminMenu((Action)ctx.Reply); } [Command("info", null, null, null, null, true)] public static void Info(ChatCommandContext ctx, string playerRef = "") { ShopService.AdminInfo(playerRef, (Action)ctx.Reply); } [Command("giftbuff", null, null, null, null, true)] public static void GiftBuff(ChatCommandContext ctx, string playerRef = "", string buffKey = "") { ShopService.AdminGiftBuff(playerRef, buffKey, (Action)ctx.Reply); } [Command("revokebuff", null, null, null, null, true)] public static void RevokeBuff(ChatCommandContext ctx, string playerRef = "", string buffKey = "") { ShopService.AdminRevokePurchasedBuff(playerRef, buffKey, (Action)ctx.Reply); } [Command("giftstat", null, null, null, null, true)] public static void GiftStat(ChatCommandContext ctx, string playerRef = "", string statKey = "", int ranks = 1) { ShopService.AdminGiftStat(playerRef, statKey, ranks, (Action)ctx.Reply); } [Command("revokestat", null, null, null, null, true)] public static void RevokeStat(ChatCommandContext ctx, string playerRef = "", string statKey = "", int ranks = 1) { ShopService.AdminRevokePurchasedStat(playerRef, statKey, ranks, (Action)ctx.Reply); } [Command("addbuff", null, null, null, null, true)] public static void AddBuff(ChatCommandContext ctx, string playerRef = "", string buffKey = "") { ShopService.AdminAdd(playerRef, buffKey, (Action)ctx.Reply); } [Command("clearbuff", null, null, null, null, true)] public static void ClearBuff(ChatCommandContext ctx, string playerRef = "", string buffKey = "") { ShopService.AdminRemove(playerRef, buffKey, (Action)ctx.Reply); } [Command("addflat", null, null, null, null, true)] public static void AddFlat(ChatCommandContext ctx, string playerRef = "", string unitStat = "", float amount = 0f) { ShopService.AdminAddFlatStat(playerRef, unitStat, amount, (Action)ctx.Reply); } [Command("clearflat", null, null, null, null, true)] public static void ClearFlat(ChatCommandContext ctx, string playerRef = "", string unitStat = "") { ShopService.AdminClearFlatStat(playerRef, unitStat, (Action)ctx.Reply); } [Command("wlstatus", null, null, null, null, true)] public static void WhitelistStatus(ChatCommandContext ctx) { ShopService.WhitelistStatus((Action)ctx.Reply); } [Command("wlcheckbuff", null, null, null, null, true)] public static void WhitelistCheckBuff(ChatCommandContext ctx) { ShopService.WhitelistList("buff", (Action)ctx.Reply); } [Command("wlcheckstat", null, null, null, null, true)] public static void WhitelistCheckStat(ChatCommandContext ctx) { ShopService.WhitelistList("stat", (Action)ctx.Reply); } [Command("wlcheckall", null, null, null, null, true)] public static void WhitelistCheckAll(ChatCommandContext ctx) { ShopService.WhitelistList("all", (Action)ctx.Reply); } [Command("wlplayer", null, null, null, null, true)] public static void WhitelistPlayer(ChatCommandContext ctx, string playerRef = "") { ShopService.WhitelistPlayer(playerRef, (Action)ctx.Reply); } [Command("wladdbuff", null, null, null, null, true)] public static void WhitelistAddBuff(ChatCommandContext ctx, string playerRef = "") { ShopService.WhitelistAdd("buff", playerRef, (Action)ctx.Reply); } [Command("wlremovebuff", null, null, null, null, true)] public static void WhitelistRemoveBuff(ChatCommandContext ctx, string playerRef = "") { ShopService.WhitelistRemove("buff", playerRef, (Action)ctx.Reply); } [Command("wladdstat", null, null, null, null, true)] public static void WhitelistAddStat(ChatCommandContext ctx, string playerRef = "") { ShopService.WhitelistAdd("stat", playerRef, (Action)ctx.Reply); } [Command("wlremovestat", null, null, null, null, true)] public static void WhitelistRemoveStat(ChatCommandContext ctx, string playerRef = "") { ShopService.WhitelistRemove("stat", playerRef, (Action)ctx.Reply); } [Command("reload", null, null, null, null, true)] public static void Reload(ChatCommandContext ctx) { ShopService.ReloadConfig((Action)ctx.Reply); } [Command("diag", null, null, null, null, true)] public static void Diagnostics(ChatCommandContext ctx) { ShopService.Diagnostics((Action)ctx.Reply); } [Command("validate", null, null, null, null, true)] public static void Validate(ChatCommandContext ctx) { ShopService.Validate((Action)ctx.Reply); } [Command("syncall", null, null, null, null, true)] public static void SyncAll(ChatCommandContext ctx) { ShopService.SyncAll((Action)ctx.Reply); } } }