Decompiled source of PerkShop v0.2.1
Thunderstone realese/PerkShop.dll
Decompiled 23 minutes ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text.Json; using BepInEx; using BepInEx.Core.Logging.Interpolation; using BepInEx.Logging; using BepInEx.Unity.IL2CPP; using HarmonyLib; using Il2CppInterop.Runtime; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppSystem; using Il2CppSystem.Collections.Generic; using Microsoft.CodeAnalysis; using PerkShop.Models; using PerkShop.Services; using PerkShop.Utilities; using ProjectM; using ProjectM.Network; using ProjectM.Scripting; using ProjectM.Shared; using Stunlock.Core; using Stunlock.Network; using Unity.Collections; using Unity.Entities; using Unity.Mathematics; using UnityEngine; using VampireCommandFramework; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("yenko2000")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Chat-command V Rising shop that lets players buy configurable buffs.")] [assembly: AssemblyFileVersion("0.1.2.0")] [assembly: AssemblyInformationalVersion("0.1.2")] [assembly: AssemblyProduct("PerkShop")] [assembly: AssemblyTitle("PerkShop")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/yenko2000/PerkShop")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.1.2.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace PerkShop { internal static class Core { private static bool _initialized; private static World? _server; private static EntityManager _entityManager; private static ServerScriptMapper? _serverScriptMapper; private static DebugEventsSystem? _debugEventsSystem; private static SystemService? _systemService; public static World Server { get { object obj = _server; if (obj == null) { obj = GetWorld("Server") ?? throw new Exception("Server world not found."); _server = (World?)obj; } return (World)obj; } } public static EntityManager EntityManager { get { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) if (!(_entityManager == default(EntityManager))) { return _entityManager; } return _entityManager = Server.EntityManager; } } public static ServerScriptMapper ServerScriptMapper => _serverScriptMapper ?? (_serverScriptMapper = Server.GetExistingSystemManaged<ServerScriptMapper>()); public static ServerGameManager ServerGameManager => ServerScriptMapper.GetServerGameManager(); public static DebugEventsSystem DebugEventsSystem => _debugEventsSystem ?? (_debugEventsSystem = Server.GetExistingSystemManaged<DebugEventsSystem>()); public static SystemService Systems => _systemService ?? (_systemService = new SystemService(Server)); public static ManualLogSource Log => Plugin.PluginLog; internal static void InitializeAfterLoaded() { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) if (!_initialized) { _server = GetWorld("Server") ?? throw new Exception("Server world not found."); _entityManager = _server.EntityManager; _serverScriptMapper = _server.GetExistingSystemManaged<ServerScriptMapper>(); _debugEventsSystem = _server.GetExistingSystemManaged<DebugEventsSystem>(); _systemService = new SystemService(_server); QueryService.Initialize(_systemService); CarrierPrefabService.PrepareConfiguredStatCarrierPrefab(); _initialized = true; Log.LogInfo((object)"Core initialized."); } } internal static World? GetWorld(string name) { Enumerator<World> enumerator = World.s_AllWorlds.GetEnumerator(); while (enumerator.MoveNext()) { World current = enumerator.Current; if (current != null && current.Name == name) { return current; } } return null; } public static void LogDebugIfEnabled(string message) { try { if (ConfigService.DebugLoggingEnabled) { Log.LogInfo((object)message); } } catch { } } public static void LogException(Exception e, [CallerMemberName] string? caller = null) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Expected O, but got Unknown ManualLogSource log = Log; bool flag = default(bool); BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(4, 3, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("["); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(caller); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("] "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(e.Message); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("\n"); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(e.StackTrace); } log.LogError(val); } } internal static class MyPluginInfo { public const string PLUGIN_GUID = "yenko2000.PerkShop"; public const string PLUGIN_NAME = "PerkShop"; public const string PLUGIN_VERSION = "0.1.2"; } [BepInPlugin("yenko2000.PerkShop", "PerkShop", "0.1.2")] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class Plugin : BasePlugin { internal static ManualLogSource PluginLog { get; private set; } internal static Harmony Harmony { get; private set; } public override void Load() { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Expected O, but got Unknown //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Expected O, but got Unknown if (!(Application.productName != "VRisingServer")) { PluginLog = ((BasePlugin)this).Log; ConfigService.Initialize(); OwnershipService.Initialize(); PlayerCacheService.Initialize(); Harmony = new Harmony("yenko2000.PerkShop"); Harmony.PatchAll(Assembly.GetExecutingAssembly()); CommandRegistry.RegisterAll(); ManualLogSource log = ((BasePlugin)this).Log; bool flag = default(bool); BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(9, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>("PerkShop"); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>("0.1.2"); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" loaded."); } log.LogInfo(val); } } public override bool Unload() { OwnershipService.FlushPendingSaves(force: true); PlayerCacheService.FlushPendingSaves(force: true); CommandRegistry.UnregisterAssembly(); Harmony harmony = Harmony; if (harmony != null) { harmony.UnpatchSelf(); } return true; } internal static bool HasLoaded() { World world = Core.GetWorld("Server"); if (world == null) { return false; } PrefabCollectionSystem existingSystemManaged = world.GetExistingSystemManaged<PrefabCollectionSystem>(); if (existingSystemManaged == null) { return false; } return existingSystemManaged.SpawnableNameToPrefabGuidDictionary.Count > 0; } } public static class ECSExtensions { public unsafe static T Read<T>(this Entity entity) where T : struct { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) ComponentType val = default(ComponentType); ((ComponentType)(ref val))..ctor(Il2CppType.Of<T>(), (AccessMode)0); EntityManager entityManager = Core.EntityManager; return Marshal.PtrToStructure<T>(new IntPtr(((EntityManager)(ref entityManager)).GetComponentDataRawRO(entity, val.TypeIndex))); } } } namespace PerkShop.Utilities { internal static class AdminPlayerLookup { public static bool TryFindUser(string playerRef, out Entity userEntity, out User user, out string error) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_020d: Unknown result type (might be due to invalid IL or missing references) //IL_0212: Unknown result type (might be due to invalid IL or missing references) //IL_0220: Unknown result type (might be due to invalid IL or missing references) //IL_0225: Unknown result type (might be due to invalid IL or missing references) //IL_022b: Unknown result type (might be due to invalid IL or missing references) userEntity = Entity.Null; user = default(User); error = string.Empty; if (string.IsNullOrWhiteSpace(playerRef)) { error = "Usage: specify an online player name or PlatformId."; return false; } EntityManager entityManager = Core.EntityManager; EntityQuery val = ((EntityManager)(ref entityManager)).CreateEntityQuery((ComponentType[])(object)new ComponentType[1] { ComponentType.ReadOnly<User>() }); NativeArray<Entity> val2 = ((EntityQuery)(ref val)).ToEntityArray(AllocatorHandle.op_Implicit((Allocator)2)); try { Enumerator<Entity> enumerator; if (ulong.TryParse(playerRef.Trim(), out var result)) { enumerator = val2.GetEnumerator(); while (enumerator.MoveNext()) { Entity current = enumerator.Current; User componentData = ((EntityManager)(ref entityManager)).GetComponentData<User>(current); if (componentData.PlatformId == result) { userEntity = current; user = componentData; PlayerCacheService.Remember(componentData); return true; } } error = $"No online player found with PlatformId {result}."; return false; } string text = playerRef.Trim(); List<(Entity, User)> list = new List<(Entity, User)>(); List<(Entity, User)> list2 = new List<(Entity, User)>(); enumerator = val2.GetEnumerator(); while (enumerator.MoveNext()) { Entity current2 = enumerator.Current; User componentData2 = ((EntityManager)(ref entityManager)).GetComponentData<User>(current2); string text2 = ((object)(FixedString64Bytes)(ref componentData2.CharacterName)).ToString(); if (string.Equals(text2, text, StringComparison.OrdinalIgnoreCase)) { list.Add((current2, componentData2)); } else if (text2.IndexOf(text, StringComparison.OrdinalIgnoreCase) >= 0) { list2.Add((current2, componentData2)); } } List<(Entity, User)> list3 = ((list.Count > 0) ? list : list2); if (list3.Count == 0) { error = "No online player found matching '" + text + "'."; return false; } if (list3.Count > 1) { error = "Multiple players match that query: " + string.Join(", ", list3.Select<(Entity, User), string>(((Entity entity, User user) x) => ((object)(FixedString64Bytes)(ref x.user.CharacterName)).ToString())); return false; } userEntity = list3[0].Item1; user = list3[0].Item2; PlayerCacheService.Remember(user); return true; } finally { if (val2.IsCreated) { val2.Dispose(); } } } } internal static class InventoryHelper { public static int Count(Entity characterEntity, PrefabGUID itemPrefab) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) EntityManager entityManager = Core.EntityManager; Entity val = default(Entity); if (!InventoryUtilities.TryGetInventoryEntity<EntityManager>(entityManager, characterEntity, ref val, 0)) { return 0; } if (!((EntityManager)(ref entityManager)).HasComponent<InventoryBuffer>(val)) { return 0; } DynamicBuffer<InventoryBuffer> buffer = ((EntityManager)(ref entityManager)).GetBuffer<InventoryBuffer>(val, false); int num = 0; for (int i = 0; i < buffer.Length; i++) { InventoryBuffer val2 = buffer[i]; if (((PrefabGUID)(ref val2.ItemType)).GuidHash == ((PrefabGUID)(ref itemPrefab)).GuidHash) { num += buffer[i].Amount; } } return num; } public static bool TryRemove(Entity characterEntity, PrefabGUID itemPrefab, int amount) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) EntityManager entityManager = Core.EntityManager; Entity val = default(Entity); if (!InventoryUtilities.TryGetInventoryEntity<EntityManager>(entityManager, characterEntity, ref val, 0)) { return false; } if (!((EntityManager)(ref entityManager)).HasComponent<InventoryBuffer>(val)) { return false; } DynamicBuffer<InventoryBuffer> buffer = ((EntityManager)(ref entityManager)).GetBuffer<InventoryBuffer>(val, false); int num = amount; int num2 = buffer.Length - 1; while (num2 >= 0 && num > 0) { InventoryBuffer val2 = buffer[num2]; if (((PrefabGUID)(ref val2.ItemType)).GuidHash == ((PrefabGUID)(ref itemPrefab)).GuidHash && val2.Amount > 0) { int num3 = math.min(val2.Amount, num); val2.Amount -= num3; num -= num3; if (val2.Amount <= 0) { val2.ItemType = new PrefabGUID(0); val2.Amount = 0; } buffer[num2] = val2; } num2--; } return num == 0; } } internal static class PlayerStateHelper { private static readonly PrefabGUID[] CombatBuffs = (PrefabGUID[])(object)new PrefabGUID[3] { new PrefabGUID(581443919), new PrefabGUID(697095869), new PrefabGUID(698151145) }; public static bool Exists(Entity entity) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) if (entity != Entity.Null) { EntityManager entityManager = Core.EntityManager; return ((EntityManager)(ref entityManager)).Exists(entity); } return false; } public static bool IsInCombat(Entity characterEntity) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) EntityManager entityManager = Core.EntityManager; if (!Exists(characterEntity)) { return false; } PrefabGUID[] combatBuffs = CombatBuffs; foreach (PrefabGUID val in combatBuffs) { if (BuffUtility.HasBuff<EntityManager>(entityManager, characterEntity, PrefabIdentifier.op_Implicit(val))) { return true; } } return false; } } } namespace PerkShop.Services { internal static class AccessService { public static bool CanAccessPerkShop(Entity userEntity, Action<string> reply) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) ShopConfigRoot shop = ConfigService.Shop; if (!shop.EnableBuffWhitelist) { return true; } if (!TryGetPlatformId(userEntity, out var platformId)) { reply("User component not ready."); return false; } if (shop.BuffWhitelistPlatformIds.Contains(platformId)) { return true; } reply("You are not whitelisted for the perk shop."); return false; } public static bool CanAccessStatShop(Entity userEntity, Action<string> reply) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) ShopConfigRoot shop = ConfigService.Shop; if (!shop.EnableStatWhitelist) { return true; } if (!TryGetPlatformId(userEntity, out var platformId)) { reply("User component not ready."); return false; } if (shop.StatWhitelistPlatformIds.Contains(platformId)) { return true; } reply("You are not whitelisted for the stat shop."); return false; } private static bool TryGetPlatformId(Entity userEntity, out ulong platformId) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) platformId = 0uL; if (!(userEntity == Entity.Null)) { EntityManager entityManager = Core.EntityManager; if (((EntityManager)(ref entityManager)).Exists(userEntity)) { entityManager = Core.EntityManager; if (((EntityManager)(ref entityManager)).HasComponent<User>(userEntity)) { entityManager = Core.EntityManager; User componentData = ((EntityManager)(ref entityManager)).GetComponentData<User>(userEntity); platformId = componentData.PlatformId; return platformId != 0; } } } return false; } } internal static class BuffService { private sealed class PendingPermanentBuffInfo { public DateTime CreatedAtUtc { get; init; } = DateTime.UtcNow; public int DurationSeconds { get; init; } public bool PersistThroughDeath { get; init; } public bool PreserveVanillaCleanup { get; init; } } private static readonly Dictionary<Entity, PendingPermanentBuffInfo> PendingPermanentBuffs = new Dictionary<Entity, PendingPermanentBuffInfo>(); private static readonly TimeSpan PendingBuffTimeout = TimeSpan.FromSeconds(30.0); private static readonly HashSet<int> PreparedPermanentBuffPrefabs = new HashSet<int>(); public static int PendingPermanentBuffCount => PendingPermanentBuffs.Count; public static bool IsRenewableTimedEntry(ShopConfigRoot config, PerkShopEntry entry) { return ConfigService.IsRenewableTimedCategory(config, entry.Category); } public static int ResolveDurationSeconds(ShopConfigRoot config, PerkShopEntry entry) { return ConfigService.ResolveBuffDuration(config, entry); } public static bool ResolvePersistThroughDeath(ShopConfigRoot config, PerkShopEntry entry) { return ConfigService.ResolveBuffPersistThroughDeath(config, entry); } public static bool PreserveVanillaCleanup(ShopConfigRoot config, PerkShopEntry entry) { return ConfigService.PreserveVanillaBuffCleanup(config, entry); } public static void ResetRuntimeStateAfterConfigReload() { PendingPermanentBuffs.Clear(); PreparedPermanentBuffPrefabs.Clear(); } public static void ProcessPendingPermanentBuffsDuringSpawn(EntityQuery spawnQuery) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) if (PendingPermanentBuffs.Count == 0) { return; } NativeArray<Entity> val = ((EntityQuery)(ref spawnQuery)).ToEntityArray(AllocatorHandle.op_Implicit((Allocator)2)); try { for (int i = 0; i < val.Length; i++) { Entity val2 = val[i]; if (PendingPermanentBuffs.TryGetValue(val2, out PendingPermanentBuffInfo value)) { TrySetLifetime(val2, value.DurationSeconds, value.PersistThroughDeath, value.PreserveVanillaCleanup); PendingPermanentBuffs.Remove(val2); } } } finally { if (val.IsCreated) { val.Dispose(); } } } private static void MarkPendingLifetimeMutation(Entity buffEntity, int durationSeconds, bool persistThroughDeath, bool preserveVanillaCleanup) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) if (!(buffEntity == Entity.Null)) { PendingPermanentBuffs[buffEntity] = new PendingPermanentBuffInfo { DurationSeconds = durationSeconds, PersistThroughDeath = persistThroughDeath, PreserveVanillaCleanup = preserveVanillaCleanup }; } } public static void ProcessPendingPermanentBuffs() { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Expected O, but got Unknown //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) if (PendingPermanentBuffs.Count == 0) { return; } EntityManager entityManager = Core.EntityManager; KeyValuePair<Entity, PendingPermanentBuffInfo>[] array = PendingPermanentBuffs.ToArray(); bool flag = default(bool); for (int i = 0; i < array.Length; i++) { KeyValuePair<Entity, PendingPermanentBuffInfo> keyValuePair = array[i]; Entity key = keyValuePair.Key; PendingPermanentBuffInfo value = keyValuePair.Value; if (key == Entity.Null || !((EntityManager)(ref entityManager)).Exists(key)) { PendingPermanentBuffs.Remove(key); } else if (((EntityManager)(ref entityManager)).HasComponent<SpawnTag>(key)) { if (DateTime.UtcNow - value.CreatedAtUtc > PendingBuffTimeout) { PendingPermanentBuffs.Remove(key); ManualLogSource log = Core.Log; BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(67, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[BuffService] Timed out waiting to finalize permanent buff entity:"); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(key.Index); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(":"); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(key.Version); } log.LogWarning(val); } } else { TrySetLifetime(key, value.DurationSeconds, value.PersistThroughDeath, value.PreserveVanillaCleanup); PendingPermanentBuffs.Remove(key); } } } private static void PreparePermanentBuffPrefab(PrefabGUID buffPrefab, int durationSeconds, bool persistThroughDeath) { //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Expected O, but got Unknown //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) if (durationSeconds >= 0 || PreparedPermanentBuffPrefabs.Contains(((PrefabGUID)(ref buffPrefab)).GuidHash)) { return; } try { Entity val = default(Entity); if (!Core.Systems.PrefabCollectionSystem._PrefabGuidToEntityMap.TryGetValue(buffPrefab, ref val)) { return; } EntityManager entityManager = Core.EntityManager; if (!(val == Entity.Null) && ((EntityManager)(ref entityManager)).Exists(val)) { if (((EntityManager)(ref entityManager)).HasComponent<LifeTime>(val)) { ((EntityManager)(ref entityManager)).RemoveComponent<LifeTime>(val); } if (((EntityManager)(ref entityManager)).HasComponent<RemoveBuffOnGameplayEvent>(val)) { ((EntityManager)(ref entityManager)).RemoveComponent<RemoveBuffOnGameplayEvent>(val); } if (((EntityManager)(ref entityManager)).HasComponent<RemoveBuffOnGameplayEventEntry>(val)) { ((EntityManager)(ref entityManager)).RemoveComponent<RemoveBuffOnGameplayEventEntry>(val); } if (persistThroughDeath && !((EntityManager)(ref entityManager)).HasComponent<Buff_Persists_Through_Death>(val)) { ((EntityManager)(ref entityManager)).AddComponent<Buff_Persists_Through_Death>(val); } PreparedPermanentBuffPrefabs.Add(((PrefabGUID)(ref buffPrefab)).GuidHash); Core.LogDebugIfEnabled($"[BuffService] Prepared permanent buff prefab {((PrefabGUID)(ref buffPrefab)).GuidHash} with no LifeTime."); } } catch (Exception ex) { ManualLogSource log = Core.Log; bool flag = default(bool); BepInExWarningLogInterpolatedStringHandler val2 = new BepInExWarningLogInterpolatedStringHandler(56, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("[BuffService] Failed to prepare permanent buff prefab "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<int>(((PrefabGUID)(ref buffPrefab)).GuidHash); ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(": "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(ex.Message); } log.LogWarning(val2); } } public static bool HasBuff(Entity characterEntity, PrefabGUID buffPrefab) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) Entity val = default(Entity); return BuffUtility.TryGetBuff<EntityManager>(Core.EntityManager, characterEntity, PrefabIdentifier.op_Implicit(buffPrefab), ref val); } public static bool ApplyPurchasedBuff(Entity userEntity, Entity characterEntity, PrefabGUID buffPrefab, bool preventDuplicate, bool allowMutation, bool mutateLifetime, int durationSeconds, bool persistThroughDeath, bool keepVisibleTimerFrozen = false, int visibleTimerSeconds = 0, bool preserveVanillaCleanup = false) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) Entity buffEntity = default(Entity); if (BuffUtility.TryGetBuff<EntityManager>(Core.EntityManager, characterEntity, PrefabIdentifier.op_Implicit(buffPrefab), ref buffEntity)) { if (allowMutation && mutateLifetime) { MarkPendingLifetimeMutation(buffEntity, durationSeconds, persistThroughDeath, preserveVanillaCleanup); } return true; } if (preventDuplicate && HasBuff(characterEntity, buffPrefab)) { return false; } FromCharacter val = default(FromCharacter); val.User = userEntity; val.Character = characterEntity; FromCharacter val2 = val; ApplyBuffDebugEvent val3 = default(ApplyBuffDebugEvent); val3.BuffPrefabGUID = buffPrefab; ApplyBuffDebugEvent val4 = val3; Core.DebugEventsSystem.ApplyBuff(val2, val4); Entity buffEntity2 = default(Entity); if (!BuffUtility.TryGetBuff<EntityManager>(Core.EntityManager, characterEntity, PrefabIdentifier.op_Implicit(buffPrefab), ref buffEntity2)) { return false; } if (allowMutation && mutateLifetime) { MarkPendingLifetimeMutation(buffEntity2, durationSeconds, persistThroughDeath, preserveVanillaCleanup); } return true; } public static bool RemoveBuff(Entity characterEntity, PrefabGUID buffPrefab) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) Entity val = default(Entity); if (!BuffUtility.TryGetBuff<EntityManager>(Core.EntityManager, characterEntity, PrefabIdentifier.op_Implicit(buffPrefab), ref val)) { return false; } PendingPermanentBuffs.Remove(val); DestroyUtility.Destroy(Core.EntityManager, val, (DestroyDebugReason)13, (string)null, 0); EntityManager entityManager = Core.EntityManager; if (((EntityManager)(ref entityManager)).Exists(val)) { entityManager = Core.EntityManager; if (!((EntityManager)(ref entityManager)).HasComponent<DestroyTag>(val)) { entityManager = Core.EntityManager; ((EntityManager)(ref entityManager)).AddComponent<DestroyTag>(val); } } return true; } public static void MakeBuffPermanent(Entity buffEntity, bool persistThroughDeath = true) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) TrySetLifetime(buffEntity, -1, persistThroughDeath, preserveVanillaCleanup: false); } public static int RemoveConfiguredCategoryBuffs(Entity characterEntity, string category) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) string category2 = category; if (characterEntity == Entity.Null || string.IsNullOrWhiteSpace(category2)) { return 0; } HashSet<int> hashSet = (from entry in ConfigService.Shop.Buffs.Values where entry != null && entry.BuffPrefab != 0 && string.Equals(entry.Category, category2, StringComparison.OrdinalIgnoreCase) select entry.BuffPrefab).ToHashSet(); if (hashSet.Count == 0) { return 0; } int num = 0; foreach (int item in hashSet) { if (RemoveBuff(characterEntity, new PrefabGUID(item))) { num++; } } return num + RemoveLiveBuffsByPrefabSet(characterEntity, hashSet); } private static int RemoveLiveBuffsByPrefabSet(Entity characterEntity, HashSet<int> prefabSet) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Expected O, but got Unknown //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) if (characterEntity == Entity.Null || prefabSet.Count == 0) { return 0; } NativeArray<Entity> val = default(NativeArray<Entity>); try { EntityManager entityManager = Core.EntityManager; EntityQuery val2 = ((EntityManager)(ref entityManager)).CreateEntityQuery((ComponentType[])(object)new ComponentType[2] { ComponentType.ReadOnly<Buff>(), ComponentType.ReadOnly<PrefabGUID>() }); val = ((EntityQuery)(ref val2)).ToEntityArray(AllocatorHandle.op_Implicit((Allocator)2)); int num = 0; for (int i = 0; i < val.Length; i++) { Entity val3 = val[i]; if (!((EntityManager)(ref entityManager)).Exists(val3) || !((EntityManager)(ref entityManager)).HasComponent<Buff>(val3) || !((EntityManager)(ref entityManager)).HasComponent<PrefabGUID>(val3) || ((EntityManager)(ref entityManager)).GetComponentData<Buff>(val3).Target != characterEntity) { continue; } PrefabGUID componentData = ((EntityManager)(ref entityManager)).GetComponentData<PrefabGUID>(val3); if (prefabSet.Contains(((PrefabGUID)(ref componentData)).GuidHash)) { PendingPermanentBuffs.Remove(val3); DestroyUtility.Destroy(entityManager, val3, (DestroyDebugReason)13, (string)null, 0); if (((EntityManager)(ref entityManager)).Exists(val3) && !((EntityManager)(ref entityManager)).HasComponent<DestroyTag>(val3)) { ((EntityManager)(ref entityManager)).AddComponent<DestroyTag>(val3); } num++; } } return num; } catch (Exception ex) { ManualLogSource log = Core.Log; bool flag = default(bool); BepInExWarningLogInterpolatedStringHandler val4 = new BepInExWarningLogInterpolatedStringHandler(63, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val4).AppendLiteral("[BuffService] Failed scanning live category buffs for removal: "); ((BepInExLogInterpolatedStringHandler)val4).AppendFormatted<string>(ex.Message); } log.LogWarning(val4); return 0; } finally { if (val.IsCreated) { val.Dispose(); } } } public static bool ForceOwnedBuffLifetime(Entity characterEntity, PrefabGUID buffPrefab, int durationSeconds, bool persistThroughDeath, bool keepVisibleTimerFrozen = false, int visibleTimerSeconds = 0, bool preserveVanillaCleanup = false) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) Entity buffEntity = default(Entity); if (!BuffUtility.TryGetBuff<EntityManager>(Core.EntityManager, characterEntity, PrefabIdentifier.op_Implicit(buffPrefab), ref buffEntity)) { return false; } MarkPendingLifetimeMutation(buffEntity, durationSeconds, persistThroughDeath, preserveVanillaCleanup); return true; } private static void TrySetLifetime(Entity buffEntity, int durationSeconds, bool persistThroughDeath, bool preserveVanillaCleanup) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) EntityManager entityManager = Core.EntityManager; if (buffEntity == Entity.Null || !((EntityManager)(ref entityManager)).Exists(buffEntity)) { return; } if (persistThroughDeath && !((EntityManager)(ref entityManager)).HasComponent<Buff_Persists_Through_Death>(buffEntity)) { ((EntityManager)(ref entityManager)).AddComponent<Buff_Persists_Through_Death>(buffEntity); } if (!preserveVanillaCleanup) { if (((EntityManager)(ref entityManager)).HasComponent<RemoveBuffOnGameplayEvent>(buffEntity)) { ((EntityManager)(ref entityManager)).RemoveComponent<RemoveBuffOnGameplayEvent>(buffEntity); } if (((EntityManager)(ref entityManager)).HasComponent<RemoveBuffOnGameplayEventEntry>(buffEntity)) { ((EntityManager)(ref entityManager)).RemoveComponent<RemoveBuffOnGameplayEventEntry>(buffEntity); } } if (durationSeconds < 0) { if (((EntityManager)(ref entityManager)).HasComponent<LifeTime>(buffEntity)) { ((EntityManager)(ref entityManager)).RemoveComponent<LifeTime>(buffEntity); } } else if (durationSeconds > 0) { if (!((EntityManager)(ref entityManager)).HasComponent<LifeTime>(buffEntity)) { ((EntityManager)(ref entityManager)).AddComponent<LifeTime>(buffEntity); } ((EntityManager)(ref entityManager)).SetComponentData<LifeTime>(buffEntity, new LifeTime { Duration = durationSeconds, EndAction = (LifeTimeEndAction)2 }); } } } internal static class CarrierPrefabService { private const string ScriptSpawnTypeName = "ProjectM.Scripting.ScriptSpawn"; private static int _preparedCarrierGuid; private static Type? _scriptSpawnManagedType; private static Type? _scriptSpawnIl2CppType; internal static void Reset() { _preparedCarrierGuid = 0; } internal static bool PrepareConfiguredStatCarrierPrefab() { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Expected O, but got Unknown //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) try { if (!StatService.IsCarrierConfigured(out PrefabGUID carrier, out string error)) { ManualLogSource log = Core.Log; bool flag = default(bool); BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(23, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[CarrierPrefabService] "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(error); } log.LogWarning(val); return false; } if (_preparedCarrierGuid == ((PrefabGUID)(ref carrier)).GuidHash) { return true; } Entity val2 = default(Entity); if (!Core.Systems.PrefabCollectionSystem._PrefabGuidToEntityMap.TryGetValue(carrier, ref val2)) { ManualLogSource log2 = Core.Log; bool flag2 = default(bool); BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(102, 1, ref flag2); if (flag2) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[CarrierPrefabService] Could not find stat carrier prefab entity for "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(((PrefabGUID)(ref carrier)).GuidHash); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(". Stats will not spawn correctly."); } log2.LogWarning(val); return false; } EntityManager entityManager = Core.EntityManager; if (!TryEnsureScriptSpawn(entityManager, val2)) { Core.Log.LogWarning((object)"[CarrierPrefabService] Could not resolve/add ScriptSpawn dynamically. Immediate stat-buffer fallback will still be attempted, but HUD/stat sync may be less reliable."); } if (!((EntityManager)(ref entityManager)).HasBuffer<ModifyUnitStatBuff_DOTS>(val2)) { ((EntityManager)(ref entityManager)).AddBuffer<ModifyUnitStatBuff_DOTS>(val2); } else { ((EntityManager)(ref entityManager)).GetBuffer<ModifyUnitStatBuff_DOTS>(val2, false).Clear(); } _preparedCarrierGuid = ((PrefabGUID)(ref carrier)).GuidHash; Core.LogDebugIfEnabled($"[CarrierPrefabService] Prepared stat carrier prefab {((PrefabGUID)(ref carrier)).GuidHash} with dynamic ScriptSpawn + ModifyUnitStatBuff_DOTS."); return true; } catch (Exception e) { Core.LogException(e, "PrepareConfiguredStatCarrierPrefab"); return false; } } private static bool TryEnsureScriptSpawn(EntityManager em, Entity prefabEntity) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) Type val = ResolveScriptSpawnIl2CppType(); if (val == (Type)null) { return false; } ComponentType val2 = default(ComponentType); ((ComponentType)(ref val2))..ctor(val, (AccessMode)0); if (!((EntityManager)(ref em)).HasComponent(prefabEntity, val2)) { ((EntityManager)(ref em)).AddComponent(prefabEntity, val2); } return true; } private static Type? ResolveScriptSpawnIl2CppType() { if (_scriptSpawnIl2CppType != (Type)null) { return _scriptSpawnIl2CppType; } if ((object)_scriptSpawnManagedType == null) { _scriptSpawnManagedType = AccessTools.TypeByName("ProjectM.Scripting.ScriptSpawn") ?? AccessTools.TypeByName("ProjectM.Gameplay.Scripting.ScriptSpawn") ?? AccessTools.TypeByName("ProjectM.ScriptSpawn") ?? AccessTools.TypeByName("ScriptSpawn"); } if (_scriptSpawnManagedType == null) { return null; } MethodInfo methodInfo = typeof(Il2CppType).GetMethods(BindingFlags.Static | BindingFlags.Public).FirstOrDefault((MethodInfo method) => method.Name == "Of" && method.IsGenericMethodDefinition && method.GetParameters().Length == 0); if (methodInfo == null) { return null; } object? obj = methodInfo.MakeGenericMethod(_scriptSpawnManagedType).Invoke(null, null); _scriptSpawnIl2CppType = (Type?)((obj is Type) ? obj : null); return _scriptSpawnIl2CppType; } } internal static class ConfigMigrationService { internal static bool TryMigrate(ShopConfigRoot cfg, out ShopConfigRoot migrated) { migrated = cfg ?? ConfigService.DefaultConfigForMigration(); bool flag = false; if (migrated.ConfigVersion < 2) { flag |= FixMovementSpeed(migrated); flag |= ClampOldOvertunedDefaults(migrated); } if (migrated.ConfigVersion < 4) { flag |= DisableVisibleTimerResetLoop(migrated); flag |= EnsureDefaultBloodBuffs(migrated); } if (migrated.ConfigVersion < 6) { flag |= MigrateDefaultStatCarrier(migrated); } if (migrated.ConfigVersion < 7) { flag |= FixDefaultStatNotes(migrated); } if (migrated.ConfigVersion < 8) { flag |= ApplyConfigVersion8RuntimeDefaults(migrated); } if (migrated.ConfigVersion < 11) { flag |= ApplyConfigVersion11CompatibilityDefaults(migrated); } if (migrated.ConfigVersion < 12) { flag |= ApplyConfigVersion12RenewableTimedBuffDefaults(migrated); } if (migrated.ConfigVersion < 13) { flag |= ApplyConfigVersion13AllStatsAndBloodSlots(migrated); } if (migrated.ConfigVersion < 14) { flag |= ApplyConfigVersion14ShortKeys(migrated); } if (migrated.ConfigVersion < 14) { migrated.ConfigVersion = 14; flag = true; } return flag; } private static bool MigrateDefaultStatCarrier(ShopConfigRoot cfg) { if (cfg.StatCarrierBuffPrefab != -1469378405) { return false; } cfg.StatCarrierBuffPrefab = -809648681; return true; } private static bool FixMovementSpeed(ShopConfigRoot cfg) { if (cfg.Stats == null || !cfg.Stats.TryGetValue("movement_speed", out StatShopEntry value) || value == null) { return false; } if (!StatDefinitionService.IsDangerousModifierCombo(value)) { return false; } value.ModificationType = "MultiplyBaseAdd"; value.Notes = "Permanently increases Movement Speed by 0.05 per purchase. Uses MultiplyBaseAdd for HUD/stat compatibility."; return true; } private static bool ClampOldOvertunedDefaults(ShopConfigRoot cfg) { if (cfg.Stats == null) { return false; } return false | SetIfOldValue(cfg, "primary_attack_speed", 0.05f, 0.02f) | SetIfOldValue(cfg, "physical_resistance", 0.05f, 0.02f) | SetIfOldValue(cfg, "spell_resistance", 0.05f, 0.02f) | SetIfOldValue(cfg, "healing_received", 0.05f, 0.03f) | SetIfOldValue(cfg, "damage_reduction", 0.02f, 0.01f) | SetIfOldValue(cfg, "spell_cooldown_recovery_rate", 0.05f, 0.02f) | SetIfOldValue(cfg, "weapon_cooldown_recovery_rate", 0.05f, 0.02f) | SetIfOldValue(cfg, "ability_attack_speed", 0.05f, 0.02f) | SetIfOldValue(cfg, "corruption_damage_reduction", 0.05f, 0.02f) | SetIfOldValue(cfg, "physical_power", 1f, 2f) | SetIfOldValue(cfg, "primary_life_leech", 0.02f, 0.03f) | SetIfOldValue(cfg, "physical_critical_strike_damage", 0.05f, 0.1f) | SetIfOldValue(cfg, "spell_critical_strike_damage", 0.05f, 0.1f) | SetIfOldValue(cfg, "reduced_blood_drain", 0.05f, 0.1f); } private static bool SetIfOldValue(ShopConfigRoot cfg, string key, float oldValue, float newValue) { if (!cfg.Stats.TryGetValue(key, out StatShopEntry value) || value == null) { return false; } if (Math.Abs(value.ValuePerPurchase - oldValue) > 0.0001f) { return false; } value.ValuePerPurchase = newValue; return true; } private static bool DisableVisibleTimerResetLoop(ShopConfigRoot cfg) { if (cfg.Buffs == null) { return false; } bool result = false; foreach (var (_, perkShopEntry2) in cfg.Buffs) { if (perkShopEntry2 != null && (perkShopEntry2.KeepVisibleTimerFrozen || perkShopEntry2.VisibleTimerSeconds != 0 || perkShopEntry2.DurationSeconds != -1)) { perkShopEntry2.KeepVisibleTimerFrozen = false; perkShopEntry2.VisibleTimerSeconds = 0; perkShopEntry2.DurationSeconds = -1; perkShopEntry2.MutateAppliedBuffLifetime = true; perkShopEntry2.PersistThroughDeath = true; result = true; } } return result; } private static bool EnsureDefaultBloodBuffs(ShopConfigRoot cfg) { if (cfg.Buffs == null) { Dictionary<string, PerkShopEntry> dictionary2 = (cfg.Buffs = new Dictionary<string, PerkShopEntry>(StringComparer.OrdinalIgnoreCase)); } bool result = false; foreach (var (key, perkShopEntry2) in ConfigService.DefaultBuffEntriesForMigration()) { if (perkShopEntry2 != null && string.Equals(perkShopEntry2.Category, "blood_buff", StringComparison.OrdinalIgnoreCase) && !cfg.Buffs.ContainsKey(key)) { cfg.Buffs[key] = perkShopEntry2; result = true; } } return result; } private static bool FixDefaultStatNotes(ShopConfigRoot cfg) { if (cfg.Stats == null) { return false; } bool result = false; foreach (var (key, statShopEntry2) in cfg.Stats) { if (statShopEntry2 != null && StatDefinitionService.TryGetDefaultStatNote(key, statShopEntry2.ValuePerPurchase, statShopEntry2.UnitStat, out string note) && !string.Equals(statShopEntry2.Notes, note, StringComparison.Ordinal) && (string.IsNullOrWhiteSpace(statShopEntry2.Notes) || statShopEntry2.Notes.StartsWith("Permanently increases ", StringComparison.OrdinalIgnoreCase) || string.Equals(statShopEntry2.Notes, "Permanent stat purchase.", StringComparison.OrdinalIgnoreCase))) { statShopEntry2.Notes = note; result = true; } } return result; } private static bool ApplyConfigVersion8RuntimeDefaults(ShopConfigRoot cfg) { bool result = false; if (cfg.ConfigFileCheckIntervalSeconds <= 0f) { cfg.ConfigFileCheckIntervalSeconds = 5f; result = true; } if (cfg.PlayerCacheSaveDebounceSeconds <= 0f) { cfg.PlayerCacheSaveDebounceSeconds = 30f; result = true; } _ = cfg.AutoDetectConfigChanges; return result; } private static bool ApplyConfigVersion11CompatibilityDefaults(ShopConfigRoot cfg) { bool result = false; if (cfg.UseClientAttributeStatAliases) { cfg.UseClientAttributeStatAliases = false; result = true; } _ = cfg.EnableExperimentalBloodBuffs; return result; } private static bool ApplyConfigVersion12RenewableTimedBuffDefaults(ShopConfigRoot cfg) { bool result = false; if (!cfg.UseRenewableTimedBuffs) { cfg.UseRenewableTimedBuffs = true; result = true; } if (cfg.RenewableTimedBuffSeconds < 60) { cfg.RenewableTimedBuffSeconds = 7200; result = true; } if (cfg.RenewableTimedBuffCategories == null) { List<string> list2 = (cfg.RenewableTimedBuffCategories = new List<string>()); } string[] array = new string[3] { "potion", "elixir", "blood_buff" }; foreach (string text in array) { if (!cfg.RenewableTimedBuffCategories.Contains<string>(text, StringComparer.OrdinalIgnoreCase)) { cfg.RenewableTimedBuffCategories.Add(text); result = true; } } if (!cfg.EnableExperimentalBloodBuffs) { cfg.EnableExperimentalBloodBuffs = true; result = true; } if (cfg.Buffs != null) { foreach (var (_, perkShopEntry2) in cfg.Buffs) { if (perkShopEntry2 != null && (string.Equals(perkShopEntry2.Category, "potion", StringComparison.OrdinalIgnoreCase) || string.Equals(perkShopEntry2.Category, "elixir", StringComparison.OrdinalIgnoreCase) || string.Equals(perkShopEntry2.Category, "blood_buff", StringComparison.OrdinalIgnoreCase))) { if (perkShopEntry2.DurationSeconds != cfg.RenewableTimedBuffSeconds) { perkShopEntry2.DurationSeconds = cfg.RenewableTimedBuffSeconds; result = true; } if (perkShopEntry2.KeepVisibleTimerFrozen || perkShopEntry2.VisibleTimerSeconds != 0) { perkShopEntry2.KeepVisibleTimerFrozen = false; perkShopEntry2.VisibleTimerSeconds = 0; result = true; } if (perkShopEntry2.PersistThroughDeath) { perkShopEntry2.PersistThroughDeath = false; result = true; } if (!perkShopEntry2.MutateAppliedBuffLifetime) { perkShopEntry2.MutateAppliedBuffLifetime = true; result = true; } } } } return result; } private static bool ApplyConfigVersion13AllStatsAndBloodSlots(ShopConfigRoot cfg) { bool result = false; if (!cfg.EnableClientUnsupportedStats) { cfg.EnableClientUnsupportedStats = true; result = true; } if (cfg.Stats != null) { foreach (var (_, statShopEntry2) in cfg.Stats) { if (statShopEntry2 != null && !statShopEntry2.Enabled) { statShopEntry2.Enabled = true; result = true; } } } if (cfg.Categories == null) { Dictionary<string, BuffCategoryDefinition> dictionary2 = (cfg.Categories = new Dictionary<string, BuffCategoryDefinition>(StringComparer.OrdinalIgnoreCase)); } if (!cfg.Categories.TryGetValue("blood_buff", out BuffCategoryDefinition value) || value == null) { cfg.Categories["blood_buff"] = new BuffCategoryDefinition { DisplayName = "Blood Buff", Documentation = "Renewable 2-hour blood-buff effects. Default limit: five blood packages.", MaxOwnedSlots = 5, SlotFreeCost = 250 }; result = true; } else { if (value.MaxOwnedSlots.GetValueOrDefault() != 5) { value.MaxOwnedSlots = 5; result = true; } if (string.IsNullOrWhiteSpace(value.Documentation) || value.Documentation.Contains("one blood package", StringComparison.OrdinalIgnoreCase) || value.Documentation.Contains("Recommended limit", StringComparison.OrdinalIgnoreCase)) { value.Documentation = "Renewable 2-hour blood-buff effects. Default limit: five blood packages."; result = true; } } return result; } private static bool ApplyConfigVersion14ShortKeys(ShopConfigRoot cfg) { bool flag = false; if (cfg.Stats != null) { foreach (KeyValuePair<string, string> statKeyAlias in KeyAliasService.StatKeyAliases) { flag |= RenameStatKey(cfg, statKeyAlias.Key, statKeyAlias.Value); } } if (cfg.Buffs != null) { foreach (KeyValuePair<string, string> buffKeyAlias in KeyAliasService.BuffKeyAliases) { flag |= RenameBuffKey(cfg, buffKeyAlias.Key, buffKeyAlias.Value); } string[] array = new string[6] { "draculaT1", "draculaT2", "draculaT3", "draculaT4", "draculaT5", "generalT5" }; foreach (string key in array) { if (cfg.Buffs.TryGetValue(key, out PerkShopEntry value) && value != null && value.Enabled) { value.Enabled = false; flag = true; } } } return flag; } private static bool RenameStatKey(ShopConfigRoot cfg, string oldKey, string newKey) { if (cfg.Stats == null || !cfg.Stats.TryGetValue(oldKey, out StatShopEntry value) || value == null) { return false; } if (!cfg.Stats.ContainsKey(newKey)) { cfg.Stats[newKey] = value; } cfg.Stats.Remove(oldKey); return true; } private static bool RenameBuffKey(ShopConfigRoot cfg, string oldKey, string newKey) { if (cfg.Buffs == null || !cfg.Buffs.TryGetValue(oldKey, out PerkShopEntry value) || value == null) { return false; } if (!cfg.Buffs.ContainsKey(newKey)) { cfg.Buffs[newKey] = value; } cfg.Buffs.Remove(oldKey); return true; } } internal static class ConfigService { private static readonly object Lock = new object(); internal static readonly string ConfigDir = Path.Combine(Paths.ConfigPath, "PerkShop"); private static readonly string ConfigFile = Path.Combine(ConfigDir, "perkconfig.json"); private static DateTime _lastWrite = DateTime.MinValue; private static DateTime _nextConfigFileCheckUtc = DateTime.MinValue; private static ShopConfigRoot _root = DefaultConfig(); internal static readonly JsonSerializerOptions JsonOptions = new JsonSerializerOptions { AllowTrailingCommas = true, PropertyNameCaseInsensitive = true, ReadCommentHandling = JsonCommentHandling.Skip, WriteIndented = true }; public static ShopConfigRoot Shop => GetShop(); public static bool DebugLoggingEnabled { get { try { return Shop.EnableDebugLogging; } catch { return false; } } } public static void Initialize() { Load(force: true); } public static void Reload() { Load(force: true); } public static bool UpdateWhitelist(string shopType, ulong platformId, bool add, string displayName = "") { if (platformId == 0L) { return false; } lock (Lock) { Load(force: true); _root = Normalize(_root); bool num = string.Equals(shopType, "stat", StringComparison.OrdinalIgnoreCase); List<ulong> list = (num ? _root.StatWhitelistPlatformIds : _root.BuffWhitelistPlatformIds); Dictionary<ulong, string> dictionary = (num ? _root.StatWhitelistNames : _root.BuffWhitelistNames); bool flag = list.Contains(platformId); if (add) { if (!flag) { list.Add(platformId); list.Sort(); } if (!string.IsNullOrWhiteSpace(displayName)) { dictionary[platformId] = displayName.Trim(); } File.WriteAllText(ConfigFile, JsonSerializer.Serialize(_root, JsonOptions)); _lastWrite = File.GetLastWriteTime(ConfigFile); return !flag; } if (!flag) { return false; } list.Remove(platformId); dictionary.Remove(platformId); File.WriteAllText(ConfigFile, JsonSerializer.Serialize(_root, JsonOptions)); _lastWrite = File.GetLastWriteTime(ConfigFile); return true; } } private static ShopConfigRoot GetShop() { Load(force: false); lock (Lock) { return _root; } } private static void Load(bool force) { //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_0184: Expected O, but got Unknown lock (Lock) { try { Directory.CreateDirectory(ConfigDir); if (!File.Exists(ConfigFile)) { _root = Normalize(DefaultConfig()); StatDefinitionService.RebuildCache(_root); File.WriteAllText(ConfigFile, JsonSerializer.Serialize(_root, JsonOptions)); _lastWrite = File.GetLastWriteTime(ConfigFile); _nextConfigFileCheckUtc = DateTime.UtcNow.AddSeconds(Math.Max(1f, _root.ConfigFileCheckIntervalSeconds)); return; } if (!force) { if (!_root.AutoDetectConfigChanges || DateTime.UtcNow < _nextConfigFileCheckUtc) { return; } _nextConfigFileCheckUtc = DateTime.UtcNow.AddSeconds(Math.Max(1f, _root.ConfigFileCheckIntervalSeconds)); } DateTime lastWriteTime = File.GetLastWriteTime(ConfigFile); if (!force && lastWriteTime <= _lastWrite) { return; } _root = JsonSerializer.Deserialize<ShopConfigRoot>(File.ReadAllText(ConfigFile), JsonOptions) ?? DefaultConfig(); if (ConfigMigrationService.TryMigrate(_root, out ShopConfigRoot migrated)) { _root = Normalize(migrated); StatDefinitionService.RebuildCache(_root); File.WriteAllText(ConfigFile, JsonSerializer.Serialize(_root, JsonOptions)); lastWriteTime = File.GetLastWriteTime(ConfigFile); ManualLogSource log = Core.Log; bool flag = default(bool); BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(53, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[ConfigService] Migrated perkconfig.json to version "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(_root.ConfigVersion); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("."); } log.LogInfo(val); } else { _root = Normalize(_root); StatDefinitionService.RebuildCache(_root); } _lastWrite = lastWriteTime; _nextConfigFileCheckUtc = DateTime.UtcNow.AddSeconds(Math.Max(1f, _root.ConfigFileCheckIntervalSeconds)); } catch (Exception e) { Core.LogException(e, "Load"); _root = Normalize(DefaultConfig()); StatDefinitionService.RebuildCache(_root); _nextConfigFileCheckUtc = DateTime.UtcNow.AddSeconds(Math.Max(1f, _root.ConfigFileCheckIntervalSeconds)); } } } private static ShopConfigRoot Normalize(ShopConfigRoot cfg) { if (cfg == null) { cfg = DefaultConfig(); } if (cfg.ConfigVersion <= 0) { cfg.ConfigVersion = 14; } cfg.CurrencyName = (string.IsNullOrWhiteSpace(cfg.CurrencyName) ? "Currency" : cfg.CurrencyName.Trim()); if (cfg.CurrencyPrefab == 0) { cfg.CurrencyPrefab = 576389135; } if (cfg.ReapplyCheckIntervalSeconds < 3) { cfg.ReapplyCheckIntervalSeconds = 3; } if (cfg.ReapplyMaxUsersPerCycle < 0) { cfg.ReapplyMaxUsersPerCycle = 0; } if (cfg.CarrierFinalizeCheckIntervalSeconds < 0.05f) { cfg.CarrierFinalizeCheckIntervalSeconds = 0.05f; } if (cfg.OwnershipSaveDebounceSeconds < 0f) { cfg.OwnershipSaveDebounceSeconds = 0f; } if (cfg.ConfigFileCheckIntervalSeconds < 1f) { cfg.ConfigFileCheckIntervalSeconds = 5f; } if (cfg.PlayerCacheSaveDebounceSeconds < 0f) { cfg.PlayerCacheSaveDebounceSeconds = 0f; } if (cfg.RenewableTimedBuffSeconds < 60) { cfg.RenewableTimedBuffSeconds = 7200; } ShopConfigRoot shopConfigRoot = cfg; if (shopConfigRoot.RenewableTimedBuffCategories == null) { ShopConfigRoot shopConfigRoot2 = shopConfigRoot; List<string> obj = new List<string> { "potion", "elixir", "blood_buff" }; List<string> list = obj; shopConfigRoot2.RenewableTimedBuffCategories = obj; } cfg.RenewableTimedBuffCategories = (from category in cfg.RenewableTimedBuffCategories where !string.IsNullOrWhiteSpace(category) select category.Trim()).Distinct<string>(StringComparer.OrdinalIgnoreCase).ToList(); if (cfg.RenewableTimedBuffCategories.Count == 0) { cfg.RenewableTimedBuffCategories.AddRange(new string[3] { "potion", "elixir", "blood_buff" }); } if (cfg.ForcePermanentBuffs) { cfg.AllowBuffEntityMutation = true; } shopConfigRoot = cfg; if (shopConfigRoot.BuffWhitelistPlatformIds == null) { List<ulong> list3 = (shopConfigRoot.BuffWhitelistPlatformIds = new List<ulong>()); } shopConfigRoot = cfg; if (shopConfigRoot.StatWhitelistPlatformIds == null) { List<ulong> list3 = (shopConfigRoot.StatWhitelistPlatformIds = new List<ulong>()); } shopConfigRoot = cfg; if (shopConfigRoot.BuffWhitelistNames == null) { Dictionary<ulong, string> dictionary2 = (shopConfigRoot.BuffWhitelistNames = new Dictionary<ulong, string>()); } shopConfigRoot = cfg; if (shopConfigRoot.StatWhitelistNames == null) { Dictionary<ulong, string> dictionary2 = (shopConfigRoot.StatWhitelistNames = new Dictionary<ulong, string>()); } cfg.BuffWhitelistPlatformIds = (from id in cfg.BuffWhitelistPlatformIds.Where((ulong id) => id != 0).Distinct() orderby id select id).ToList(); cfg.StatWhitelistPlatformIds = (from id in cfg.StatWhitelistPlatformIds.Where((ulong id) => id != 0).Distinct() orderby id select id).ToList(); cfg.BuffWhitelistNames = cfg.BuffWhitelistNames.Where<KeyValuePair<ulong, string>>((KeyValuePair<ulong, string> kv) => kv.Key != 0L && !string.IsNullOrWhiteSpace(kv.Value)).ToDictionary((KeyValuePair<ulong, string> kv) => kv.Key, (KeyValuePair<ulong, string> kv) => kv.Value.Trim()); cfg.StatWhitelistNames = cfg.StatWhitelistNames.Where<KeyValuePair<ulong, string>>((KeyValuePair<ulong, string> kv) => kv.Key != 0L && !string.IsNullOrWhiteSpace(kv.Value)).ToDictionary((KeyValuePair<ulong, string> kv) => kv.Key, (KeyValuePair<ulong, string> kv) => kv.Value.Trim()); if (cfg.MaxOwnedStatTypes < 0) { cfg.MaxOwnedStatTypes = 0; } if (cfg.StatTypeSlotFreeCost < 0) { cfg.StatTypeSlotFreeCost = 0; } cfg.MaxHealthPurchaseBehavior = (string.IsNullOrWhiteSpace(cfg.MaxHealthPurchaseBehavior) ? "ClampOnly" : cfg.MaxHealthPurchaseBehavior.Trim()); if (!string.Equals(cfg.MaxHealthPurchaseBehavior, "ClampOnly", StringComparison.OrdinalIgnoreCase) && !string.Equals(cfg.MaxHealthPurchaseBehavior, "FillToMax", StringComparison.OrdinalIgnoreCase) && !string.Equals(cfg.MaxHealthPurchaseBehavior, "PreserveRatio", StringComparison.OrdinalIgnoreCase)) { cfg.MaxHealthPurchaseBehavior = "ClampOnly"; } shopConfigRoot = cfg; if (shopConfigRoot.Stats == null) { Dictionary<string, StatShopEntry> dictionary5 = (shopConfigRoot.Stats = new Dictionary<string, StatShopEntry>(StringComparer.OrdinalIgnoreCase)); } Dictionary<string, StatShopEntry> dictionary6 = new Dictionary<string, StatShopEntry>(StringComparer.OrdinalIgnoreCase); string key; foreach (KeyValuePair<string, StatShopEntry> stat in cfg.Stats) { stat.Deconstruct(out key, out var value); string text = key; StatShopEntry statShopEntry = value; if (!string.IsNullOrWhiteSpace(text) && statShopEntry != null) { statShopEntry.DisplayName = (string.IsNullOrWhiteSpace(statShopEntry.DisplayName) ? text.Trim() : statShopEntry.DisplayName.Trim()); statShopEntry.UnitStat = (string.IsNullOrWhiteSpace(statShopEntry.UnitStat) ? "PhysicalPower" : statShopEntry.UnitStat.Trim()); statShopEntry.ModificationType = (string.IsNullOrWhiteSpace(statShopEntry.ModificationType) ? "Add" : statShopEntry.ModificationType.Trim()); statShopEntry.AttributeCapType = (string.IsNullOrWhiteSpace(statShopEntry.AttributeCapType) ? "Uncapped" : statShopEntry.AttributeCapType.Trim()); if (statShopEntry.Cost <= 0) { statShopEntry.Cost = 1; } if (statShopEntry.ValuePerPurchase == 0f) { statShopEntry.ValuePerPurchase = 1f; } if (statShopEntry.MaxPurchases < 0) { statShopEntry.MaxPurchases = 0; } statShopEntry.Notes = (string.IsNullOrWhiteSpace(statShopEntry.Notes) ? "Permanent stat purchase." : statShopEntry.Notes.Trim()); StatDefinitionService.NormalizeKnownStatEntry(text.Trim(), statShopEntry); dictionary6[text.Trim()] = statShopEntry; } } cfg.Stats = dictionary6; shopConfigRoot = cfg; if (shopConfigRoot.Categories == null) { Dictionary<string, BuffCategoryDefinition> dictionary8 = (shopConfigRoot.Categories = new Dictionary<string, BuffCategoryDefinition>(StringComparer.OrdinalIgnoreCase)); } Dictionary<string, BuffCategoryDefinition> dictionary9 = new Dictionary<string, BuffCategoryDefinition>(StringComparer.OrdinalIgnoreCase); foreach (KeyValuePair<string, BuffCategoryDefinition> category in cfg.Categories) { category.Deconstruct(out key, out var value2); string text2 = key; BuffCategoryDefinition buffCategoryDefinition = value2; if (!string.IsNullOrWhiteSpace(text2) && buffCategoryDefinition != null) { buffCategoryDefinition.DisplayName = (string.IsNullOrWhiteSpace(buffCategoryDefinition.DisplayName) ? text2.Trim() : buffCategoryDefinition.DisplayName.Trim()); buffCategoryDefinition.Documentation = (string.IsNullOrWhiteSpace(buffCategoryDefinition.Documentation) ? string.Empty : buffCategoryDefinition.Documentation.Trim()); if (buffCategoryDefinition.MaxOwnedSlots.HasValue && buffCategoryDefinition.MaxOwnedSlots.Value <= 0) { buffCategoryDefinition.MaxOwnedSlots = null; } if (buffCategoryDefinition.SlotFreeCost.HasValue && buffCategoryDefinition.SlotFreeCost.Value <= 0) { buffCategoryDefinition.SlotFreeCost = null; } dictionary9[text2.Trim()] = buffCategoryDefinition; } } EnsureCategory(dictionary9, "blood_buff", "Blood Buff", "Renewable 2-hour blood-buff effects. Default limit: five blood packages.", 5, 250); EnsureCategory(dictionary9, "set_bonus", "Set Bonus", "Armor set style bonuses or set-derived effects.", null, null); EnsureCategory(dictionary9, "potion", "Potion", "Potion and brew-style buffs. Safer for multi-slot ownership.", 3, 100); EnsureCategory(dictionary9, "elixir", "Elixir", "Elixir-style buffs. Bloodcraft compatibility default: one owned elixir slot.", 1, 500); EnsureCategory(dictionary9, "misc", "Misc", "Fallback category for uncategorized buffs.", null, null); if (dictionary9.TryGetValue("blood_buff", out var value3) && value3.MaxOwnedSlots.GetValueOrDefault() == 1) { value3.MaxOwnedSlots = 5; value3.Documentation = "Renewable 2-hour blood-buff effects. Default limit: five blood packages."; } cfg.Categories = dictionary9; shopConfigRoot = cfg; if (shopConfigRoot.Buffs == null) { Dictionary<string, PerkShopEntry> dictionary11 = (shopConfigRoot.Buffs = new Dictionary<string, PerkShopEntry>(StringComparer.OrdinalIgnoreCase)); } Dictionary<string, PerkShopEntry> dictionary12 = new Dictionary<string, PerkShopEntry>(StringComparer.OrdinalIgnoreCase); foreach (KeyValuePair<string, PerkShopEntry> buff in cfg.Buffs) { buff.Deconstruct(out key, out var value4); string text3 = key; PerkShopEntry perkShopEntry = value4; if (string.IsNullOrWhiteSpace(text3) || perkShopEntry == null) { continue; } perkShopEntry.DisplayName = (string.IsNullOrWhiteSpace(perkShopEntry.DisplayName) ? text3.Trim() : perkShopEntry.DisplayName.Trim()); perkShopEntry.Category = (string.IsNullOrWhiteSpace(perkShopEntry.Category) ? "misc" : perkShopEntry.Category.Trim()); if (!cfg.Categories.ContainsKey(perkShopEntry.Category)) { perkShopEntry.Category = "misc"; } if (perkShopEntry.Cost <= 0) { perkShopEntry.Cost = 1; } if (cfg.ForcePermanentBuffs) { perkShopEntry.PersistentPurchase = true; perkShopEntry.KeepVisibleTimerFrozen = false; perkShopEntry.VisibleTimerSeconds = 0; perkShopEntry.MutateAppliedBuffLifetime = true; if (IsRenewableTimedCategory(cfg, perkShopEntry.Category)) { perkShopEntry.DurationSeconds = cfg.RenewableTimedBuffSeconds; perkShopEntry.PersistThroughDeath = cfg.RenewableTimedBuffsPersistThroughDeath; } else { perkShopEntry.DurationSeconds = -1; perkShopEntry.PersistThroughDeath = true; } } else { if (perkShopEntry.DurationSeconds < -1) { perkShopEntry.DurationSeconds = -1; } if (perkShopEntry.VisibleTimerSeconds < 0) { perkShopEntry.VisibleTimerSeconds = 0; } if (!perkShopEntry.MutateAppliedBuffLifetime) { perkShopEntry.PersistThroughDeath = false; } } dictionary12[text3.Trim()] = perkShopEntry; } cfg.Buffs = dictionary12; return cfg; } internal static bool IsRenewableTimedCategory(ShopConfigRoot cfg, string category) { string category2 = category; if (cfg == null || !cfg.UseRenewableTimedBuffs || string.IsNullOrWhiteSpace(category2)) { return false; } if (cfg.RenewableTimedBuffCategories != null) { return cfg.RenewableTimedBuffCategories.Any((string c) => string.Equals(c, category2, StringComparison.OrdinalIgnoreCase)); } return false; } internal static int ResolveBuffDuration(ShopConfigRoot cfg, PerkShopEntry entry) { if (cfg != null && entry != null && IsRenewableTimedCategory(cfg, entry.Category)) { return Math.Max(60, cfg.RenewableTimedBuffSeconds); } if (cfg == null || !cfg.ForcePermanentBuffs || (entry.KeepVisibleTimerFrozen && entry.VisibleTimerSeconds > 0)) { return entry.DurationSeconds; } return -1; } internal static bool ResolveBuffPersistThroughDeath(ShopConfigRoot cfg, PerkShopEntry entry) { if (cfg != null && entry != null && IsRenewableTimedCategory(cfg, entry.Category)) { return cfg.RenewableTimedBuffsPersistThroughDeath; } if (cfg == null || !cfg.ForcePermanentBuffs) { return entry.PersistThroughDeath; } return true; } internal static bool PreserveVanillaBuffCleanup(ShopConfigRoot cfg, PerkShopEntry entry) { if (cfg != null && entry != null) { return IsRenewableTimedCategory(cfg, entry.Category); } return false; } private static void EnsureCategory(Dictionary<string, BuffCategoryDefinition> categories, string key, string displayName, string documentation, int? maxOwnedSlots, int? slotFreeCost) { if (!categories.TryGetValue(key, out BuffCategoryDefinition value) || value == null) { categories[key] = new BuffCategoryDefinition { DisplayName = displayName, Documentation = documentation, MaxOwnedSlots = maxOwnedSlots, SlotFreeCost = slotFreeCost }; return; } value.DisplayName = (string.IsNullOrWhiteSpace(value.DisplayName) ? displayName : value.DisplayName.Trim()); value.Documentation = (string.IsNullOrWhiteSpace(value.Documentation) ? documentation : value.Documentation.Trim()); if (value.MaxOwnedSlots.HasValue && value.MaxOwnedSlots.Value <= 0) { value.MaxOwnedSlots = null; } if (value.SlotFreeCost.HasValue && value.SlotFreeCost.Value <= 0) { value.SlotFreeCost = null; } } internal static ShopConfigRoot DefaultConfigForMigration() { return DefaultConfig(); } private static ShopConfigRoot DefaultConfig() { return new ShopConfigRoot { ConfigVersion = 14, Enabled = true, EnableDebugLogging = false, CurrencyPrefab = 576389135, CurrencyName = "Greater Stygian Shards", BlockPurchasesInCombat = true, BlockRemovalsInCombat = true, SaveOwnership = true, ReapplyOwnedBuffsOnLogin = false, ReapplyOwnedBuffsWhenMissing = false, ReapplyCheckIntervalSeconds = 60, ReapplyMaxUsersPerCycle = 5, CarrierFinalizeCheckIntervalSeconds = 0.25f, OwnershipSaveDebounceSeconds = 2f, AutoDetectConfigChanges = false, ConfigFileCheckIntervalSeconds = 5f, PlayerCacheSaveDebounceSeconds = 30f, ForcePermanentBuffs = true, AllowBuffEntityMutation = true, UseRenewableTimedBuffs = true, RenewableTimedBuffSeconds = 7200, RenewableTimedBuffCategories = new List<string> { "potion", "elixir", "blood_buff" }, RenewableTimedBuffsPersistThroughDeath = false, EnableStatShop = true, EnableClientUnsupportedStats = true, UseClientAttributeStatAliases = false, EnableExperimentalBloodBuffs = true, StatCarrierBuffPrefab = -809648681, EnableStatTypeSlots = true, MaxOwnedStatTypes = 4, StatTypeSlotFreeCost = 500, MaxHealthPurchaseBehavior = "ClampOnly", EnableBuffWhitelist = false, BuffWhitelistPlatformIds = new List<ulong>(), BuffWhitelistNames = new Dictionary<ulong, string>(), EnableStatWhitelist = false, StatWhitelistPlatformIds = new List<ulong>(), StatWhitelistNames = new Dictionary<ulong, string>(), Stats = new Dictionary<string, StatShopEntry>(StringComparer.OrdinalIgnoreCase) { ["MH"] = new StatShopEntry { DisplayName = "Max Health", UnitStat = "MaxHealth", ModificationType = "Add", AttributeCapType = "Uncapped", ValuePerPurchase = 25f, Cost = 500, MaxPurchases = 10, Notes = "Permanently increases Max Health by 25 per purchase." }, ["PP"] = new StatShopEntry { DisplayName = "Physical Power", UnitStat = "PhysicalPower", ModificationType = "Add", AttributeCapType = "Uncapped", ValuePerPurchase = 2f, Cost = 500, MaxPurchases = 10, Notes = "Permanently increases Physical Power by 2 per purchase." }, ["SP"] = new StatShopEntry { DisplayName = "Spell Power", UnitStat = "SpellPower", ModificationType = "Add", AttributeCapType = "Uncapped", ValuePerPurchase = 1f, Cost = 500, MaxPurchases = 10, Notes = "Permanently increases Spell Power by 1 per purchase." }, ["MS"] = new StatShopEntry { DisplayName = "Movement Speed", UnitStat = "MovementSpeed", ModificationType = "MultiplyBaseAdd", AttributeCapType = "Uncapped", ValuePerPurchase = 0.05f, Cost = 500, MaxPurchases = 5, Notes = "Permanently increases Movement Speed by 0.05 per purchase. Uses MultiplyBaseAdd for HUD/stat compatibility." }, ["AS"] = new StatShopEntry { DisplayName = "Primary Attack Speed", UnitStat = "PrimaryAttackSpeed", ModificationType = "Add", AttributeCapType = "Uncapped", ValuePerPurchase = 0.02f, Cost = 500, MaxPurchases = 5, Notes = "Permanently increases Primary Attack Speed by 0.02 per purchase." }, ["phll"] = new StatShopEntry { DisplayName = "Physical Life Leech", UnitStat = "PhysicalLifeLeech", ModificationType = "Add", AttributeCapType = "Uncapped", ValuePerPurchase = 0.02f, Cost = 500, MaxPurchases = 5, Notes = "Permanently increases Physical Life Leech by 0.02 per purchase." }, ["sll"] = new StatShopEntry { DisplayName = "Spell Life Leech", UnitStat = "SpellLifeLeech", ModificationType = "Add", AttributeCapType = "Uncapped", ValuePerPurchase = 0.02f, Cost = 500, MaxPurchases = 5, Notes = "Permanently increases Spell Life Leech by 0.02 per purchase." }, ["prll"] = new StatShopEntry { DisplayName = "Primary Life Leech", UnitStat = "PrimaryLifeLeech", ModificationType = "Add", AttributeCapType = "Uncapped", ValuePerPurchase = 0.03f, Cost = 500, MaxPurchases = 5, Notes = "Permanently increases Primary Life Leech by 0.03 per purchase." }, ["PCC"] = new StatShopEntry { DisplayName = "Physical Critical Strike Chance", UnitStat = "PhysicalCriticalStrikeChance", ModificationType = "Add", AttributeCapType = "Uncapped", ValuePerPurchase = 0.02f, Cost = 500, MaxPurchases = 5, Notes = "Permanently increases Physical Critical Strike Chance by 0.02 per purchase." }, ["PCD"] = new StatShopEntry { DisplayName = "Physical Critical Strike Damage", UnitStat = "PhysicalCriticalStrikeDamage", ModificationType = "Add", AttributeCapType = "Uncapped", ValuePerPurchase = 0.1f, Cost = 500, MaxPurchases = 5, Notes = "Permanently increases Physical Critical Strike Damage by 0.1 per purchase." }, ["SCC"] = new StatShopEntry { DisplayName = "Spell Critical Strike Chance", UnitStat = "SpellCriticalStrikeChance", ModificationType = "Add", AttributeCapType = "Uncapped", ValuePerPurchase = 0.02f, Cost = 500, MaxPurchases = 5, Notes = "Permanently increases Spell Critical Strike Chance by 0.02 per purchase." }, ["SCD"] = new StatShopEntry { DisplayName = "Spell Critical Strike Damage", UnitStat = "SpellCriticalStrikeDamage", ModificationType = "Add", AttributeCapType = "Uncapped", ValuePerPurchase = 0.1f, Cost = 500, MaxPurchases = 5, Notes = "Permanently increases Spell Critical Strike Damage by 0.1 per purchase." }, ["PR"] = new StatShopEntry { DisplayName = "Physical Resistance", UnitStat = "PhysicalResistance", ModificationType = "Add", AttributeCapType = "Uncapped", ValuePerPurchase = 0.02f, Cost = 500, MaxPurchases = 5, Notes = "Permanently increases Physical Resistance by 0.02 per purchase." }, ["SR"] = new StatShopEntry { DisplayName = "Spell Resistance", UnitStat = "SpellResistance", ModificationType = "Add", AttributeCapType = "Uncapped", ValuePerPurchase = 0.02f, Cost = 500, MaxPurchases = 5, Notes = "Permanently increases Spell Resistance by 0.02 per purchase." }, ["HR"] = new StatShopEntry { DisplayName = "Healing Received", UnitStat = "HealingReceived", ModificationType = "Add", AttributeCapType = "Uncapped", ValuePerPurchase = 0.03f, Cost = 500, MaxPurchases = 5, Notes = "Permanently increases Healing Received by 0.03 per purchase." }, ["DR"] = new StatShopEntry { DisplayName = "Damage Reduction", UnitStat = "DamageReduction", ModificationType = "Add", AttributeCapType = "Uncapped", ValuePerPurchase = 0.01f, Cost = 750, MaxPurchases = 5, Notes = "Permanently increases Damage Reduction by 0.01 per purchase." }, ["RY"] = new StatShopEntry { DisplayName = "Resource Yield", UnitStat = "ResourceYield", ModificationType = "Add", AttributeCapType = "Uncapped", ValuePerPurchase = 0.05f, Cost = 500, MaxPurchases = 5, Notes = "Permanently increases Resource Yield by 0.05 per purchase." }, ["RBD"] = new StatShopEntry { DisplayName = "Reduced Blood Drain", UnitStat = "ReducedBloodDrain", ModificationType = "Add", AttributeCapType = "Uncapped", ValuePerPurchase = 0.1f, Cost = 500, MaxPurchases = 5, Notes = "Permanently increases Reduced Blood Drain by 0.1 per purchase." }, ["SCR"] = new StatShopEntry { DisplayName = "Spell Cooldown Recovery Rate", UnitStat = "SpellCooldownRecoveryRate", ModificationType = "Add", AttributeCapType = "Uncapped", ValuePerPurchase = 0.02f, Cost = 500, MaxPurchases = 5, Notes = "Permanently increases Spell Cooldown Recovery Rate by 0.02 per purchase." }, ["WCR"] = new StatShopEntry { DisplayName = "Weapon Cooldown Recovery Rate", UnitStat = "WeaponCooldownRecoveryRate", ModificationType = "Add", AttributeCapType = "Uncapped", ValuePerPurchase = 0.02f, Cost = 500, MaxPurchases = 5, Notes = "Permanently increases Weapon Cooldown Recovery Rate by 0.02 per purchase." }, ["UCR"] = new StatShopEntry { DisplayName = "Ultimate Cooldown Recovery Rate", UnitStat = "UltimateCooldownRecoveryRate", ModificationType = "Add", AttributeCapType = "Uncapped", ValuePerPurchase = 0.04f, Cost = 500, MaxPurchases = 5, Notes = "Permanently increases Ultimate Cooldown Recovery Rate by 0.04 per purchase." }, ["MD"] = new StatShopEntry { DisplayName = "Minion Damage", UnitStat = "MinionDamage", ModificationType = "Add", AttributeCapType = "Uncapped", ValuePerPurchase = 0.05f, Cost = 500, MaxPurchases = 5, Notes = "Permanently increases Minion Damage by 0.05 per purchase." }, ["AAS"] = new StatShopEntry { DisplayName = "Ability Attack Speed", UnitStat = "AbilityAttackSpeed", ModificationType = "Add", AttributeCapType = "Uncapped", ValuePerPurchase = 0.02f, Cost = 500, MaxPurchases = 5, Notes = "Permanently increases Ability Attack Speed by 0.02 per purchase." }, ["CDR"] = new StatShopEntry { DisplayName = "Corruption Damage Reduction", UnitStat = "CorruptionDamageReduction", ModificationType = "Add", AttributeCapType = "Uncapped", ValuePerPurchase = 0.02f, Cost = 500, MaxPurchases = 5, Notes = "Permanently increases Corruption Damage Reduction by 0.02 per purchase." } }, Categories = new Dictionary<string, BuffCategoryDefinition>(StringComparer.OrdinalIgnoreCase) { ["blood_buff"] = new BuffCategoryDefinition { DisplayName = "Blood Buff", Documentation = "Renewable 2-hour blood-buff effects. Default limit: five blood packages.", MaxOwnedSlots = 5, SlotFreeCost = 250 }, ["set_bonus"] = new BuffCategoryDefinition { DisplayName = "Set Bonus", Documentation = "Armor set style bonuses or set-derived effects.", MaxOwnedSlots = null, SlotFreeCost = null }, ["potion"] = new BuffCategoryDefinition { DisplayName = "Potion", Documentation = "Potion and brew-style buffs. Safer for multi-slot ownership.", MaxOwnedSlots = 3, SlotFreeCost = 100 }, ["elixir"] = new BuffCategoryDefinition { DisplayName = "Elixir", Documentation = "Elixir-style buffs. Bloodcraft compatibility default: one owned elixir slot.", MaxOwnedSlots = 1, SlotFreeCost = 500 }, ["misc"] = new BuffCategoryDefinition { DisplayName = "Misc", Documentation = "Fallback category for uncategorized buffs.", MaxOwnedSlots = null, SlotFreeCost = null } }, Buffs = DefaultBuffEntries() }; } internal static Dictionary<string, PerkShopEntry> DefaultBuffEntriesForMigration() { return DefaultBuffEntries(); } private static Dictionary<string, PerkShopEntry> DefaultBuffEntries() { Dictionary<string, PerkShopEntry> obj = new Dictionary<string, PerkShopEntry>(StringComparer.OrdinalIgnoreCase) { ["sun_immunity"] = new PerkShopEntry { DisplayName = "Sun Immunity", Category = "misc", BuffPrefab = 32681348, Cost = 100, PersistentPurchase = true, DurationSeconds = -1, KeepVisibleTimerFrozen = false, VisibleTimerSeconds = 0, PersistThroughDeath = true, MutateAppliedBuffLifetime = false, PreventDuplicate = true, Notes = "Grants complete immunity to sun exposure." }, ["potion_of_rage"] = new PerkShopEntry { DisplayName = "Potion of Rage", Category = "potion", BuffPrefab = -1591883586, Cost = 400, PersistentPurchase = true, DurationSeconds = 7200, KeepVisibleTimerFrozen = false, VisibleTimerSeconds = 0, PersistThroughDeath = false, MutateAppliedBuffLifetime = false, PreventDuplicate = true, Notes = "Applies AB_Consumable_PhysicalPowerPotion_T02_Buff as a renewable 2-hour owned buff." }, ["elixir_of_the_crow"] = new PerkShopEntry { DisplayName = "Elixir of the Crow", Category = "elixir", BuffPrefab = -262239794, Cost = 750, PersistentPurchase = true, DurationSeconds = -1, KeepVisibleTimerFrozen = false, VisibleTimerSeconds = 0, PersistThroughDeath = true, MutateAppliedBuffLifetime = false, PreventDuplicate = true, Notes = "Applies AB_Elixir_Crow_T01_Buff as a renewable 2-hour owned buff." }, ["empty_layout"] = new PerkShopEntry { Enabled = false, DisplayName = "Empty Buff Layout", Category = "misc", BuffPrefab = 32681348, Cost = 100, PersistentPurchase = true, Durati
PerkShop.dll
Decompiled 23 minutes ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text.Json; using BepInEx; using BepInEx.Core.Logging.Interpolation; using BepInEx.Logging; using BepInEx.Unity.IL2CPP; using HarmonyLib; using Il2CppInterop.Runtime; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppSystem; using Il2CppSystem.Collections.Generic; using Microsoft.CodeAnalysis; using PerkShop.Models; using PerkShop.Services; using PerkShop.Utilities; using ProjectM; using ProjectM.Network; using ProjectM.Scripting; using ProjectM.Shared; using Stunlock.Core; using Stunlock.Network; using Unity.Collections; using Unity.Entities; using Unity.Mathematics; using UnityEngine; using VampireCommandFramework; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("yenko2000")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Chat-command V Rising shop that lets players buy configurable buffs.")] [assembly: AssemblyFileVersion("0.1.2.0")] [assembly: AssemblyInformationalVersion("0.1.2")] [assembly: AssemblyProduct("PerkShop")] [assembly: AssemblyTitle("PerkShop")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/yenko2000/PerkShop")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.1.2.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace PerkShop { internal static class Core { private static bool _initialized; private static World? _server; private static EntityManager _entityManager; private static ServerScriptMapper? _serverScriptMapper; private static DebugEventsSystem? _debugEventsSystem; private static SystemService? _systemService; public static World Server { get { object obj = _server; if (obj == null) { obj = GetWorld("Server") ?? throw new Exception("Server world not found."); _server = (World?)obj; } return (World)obj; } } public static EntityManager EntityManager { get { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) if (!(_entityManager == default(EntityManager))) { return _entityManager; } return _entityManager = Server.EntityManager; } } public static ServerScriptMapper ServerScriptMapper => _serverScriptMapper ?? (_serverScriptMapper = Server.GetExistingSystemManaged<ServerScriptMapper>()); public static ServerGameManager ServerGameManager => ServerScriptMapper.GetServerGameManager(); public static DebugEventsSystem DebugEventsSystem => _debugEventsSystem ?? (_debugEventsSystem = Server.GetExistingSystemManaged<DebugEventsSystem>()); public static SystemService Systems => _systemService ?? (_systemService = new SystemService(Server)); public static ManualLogSource Log => Plugin.PluginLog; internal static void InitializeAfterLoaded() { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) if (!_initialized) { _server = GetWorld("Server") ?? throw new Exception("Server world not found."); _entityManager = _server.EntityManager; _serverScriptMapper = _server.GetExistingSystemManaged<ServerScriptMapper>(); _debugEventsSystem = _server.GetExistingSystemManaged<DebugEventsSystem>(); _systemService = new SystemService(_server); QueryService.Initialize(_systemService); CarrierPrefabService.PrepareConfiguredStatCarrierPrefab(); _initialized = true; Log.LogInfo((object)"Core initialized."); } } internal static World? GetWorld(string name) { Enumerator<World> enumerator = World.s_AllWorlds.GetEnumerator(); while (enumerator.MoveNext()) { World current = enumerator.Current; if (current != null && current.Name == name) { return current; } } return null; } public static void LogDebugIfEnabled(string message) { try { if (ConfigService.DebugLoggingEnabled) { Log.LogInfo((object)message); } } catch { } } public static void LogException(Exception e, [CallerMemberName] string? caller = null) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Expected O, but got Unknown ManualLogSource log = Log; bool flag = default(bool); BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(4, 3, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("["); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(caller); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("] "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(e.Message); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("\n"); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(e.StackTrace); } log.LogError(val); } } internal static class MyPluginInfo { public const string PLUGIN_GUID = "yenko2000.PerkShop"; public const string PLUGIN_NAME = "PerkShop"; public const string PLUGIN_VERSION = "0.1.2"; } [BepInPlugin("yenko2000.PerkShop", "PerkShop", "0.1.2")] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class Plugin : BasePlugin { internal static ManualLogSource PluginLog { get; private set; } internal static Harmony Harmony { get; private set; } public override void Load() { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Expected O, but got Unknown //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Expected O, but got Unknown if (!(Application.productName != "VRisingServer")) { PluginLog = ((BasePlugin)this).Log; ConfigService.Initialize(); OwnershipService.Initialize(); PlayerCacheService.Initialize(); Harmony = new Harmony("yenko2000.PerkShop"); Harmony.PatchAll(Assembly.GetExecutingAssembly()); CommandRegistry.RegisterAll(); ManualLogSource log = ((BasePlugin)this).Log; bool flag = default(bool); BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(9, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>("PerkShop"); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>("0.1.2"); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" loaded."); } log.LogInfo(val); } } public override bool Unload() { OwnershipService.FlushPendingSaves(force: true); PlayerCacheService.FlushPendingSaves(force: true); CommandRegistry.UnregisterAssembly(); Harmony harmony = Harmony; if (harmony != null) { harmony.UnpatchSelf(); } return true; } internal static bool HasLoaded() { World world = Core.GetWorld("Server"); if (world == null) { return false; } PrefabCollectionSystem existingSystemManaged = world.GetExistingSystemManaged<PrefabCollectionSystem>(); if (existingSystemManaged == null) { return false; } return existingSystemManaged.SpawnableNameToPrefabGuidDictionary.Count > 0; } } public static class ECSExtensions { public unsafe static T Read<T>(this Entity entity) where T : struct { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) ComponentType val = default(ComponentType); ((ComponentType)(ref val))..ctor(Il2CppType.Of<T>(), (AccessMode)0); EntityManager entityManager = Core.EntityManager; return Marshal.PtrToStructure<T>(new IntPtr(((EntityManager)(ref entityManager)).GetComponentDataRawRO(entity, val.TypeIndex))); } } } namespace PerkShop.Utilities { internal static class AdminPlayerLookup { public static bool TryFindUser(string playerRef, out Entity userEntity, out User user, out string error) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_020d: Unknown result type (might be due to invalid IL or missing references) //IL_0212: Unknown result type (might be due to invalid IL or missing references) //IL_0220: Unknown result type (might be due to invalid IL or missing references) //IL_0225: Unknown result type (might be due to invalid IL or missing references) //IL_022b: Unknown result type (might be due to invalid IL or missing references) userEntity = Entity.Null; user = default(User); error = string.Empty; if (string.IsNullOrWhiteSpace(playerRef)) { error = "Usage: specify an online player name or PlatformId."; return false; } EntityManager entityManager = Core.EntityManager; EntityQuery val = ((EntityManager)(ref entityManager)).CreateEntityQuery((ComponentType[])(object)new ComponentType[1] { ComponentType.ReadOnly<User>() }); NativeArray<Entity> val2 = ((EntityQuery)(ref val)).ToEntityArray(AllocatorHandle.op_Implicit((Allocator)2)); try { Enumerator<Entity> enumerator; if (ulong.TryParse(playerRef.Trim(), out var result)) { enumerator = val2.GetEnumerator(); while (enumerator.MoveNext()) { Entity current = enumerator.Current; User componentData = ((EntityManager)(ref entityManager)).GetComponentData<User>(current); if (componentData.PlatformId == result) { userEntity = current; user = componentData; PlayerCacheService.Remember(componentData); return true; } } error = $"No online player found with PlatformId {result}."; return false; } string text = playerRef.Trim(); List<(Entity, User)> list = new List<(Entity, User)>(); List<(Entity, User)> list2 = new List<(Entity, User)>(); enumerator = val2.GetEnumerator(); while (enumerator.MoveNext()) { Entity current2 = enumerator.Current; User componentData2 = ((EntityManager)(ref entityManager)).GetComponentData<User>(current2); string text2 = ((object)(FixedString64Bytes)(ref componentData2.CharacterName)).ToString(); if (string.Equals(text2, text, StringComparison.OrdinalIgnoreCase)) { list.Add((current2, componentData2)); } else if (text2.IndexOf(text, StringComparison.OrdinalIgnoreCase) >= 0) { list2.Add((current2, componentData2)); } } List<(Entity, User)> list3 = ((list.Count > 0) ? list : list2); if (list3.Count == 0) { error = "No online player found matching '" + text + "'."; return false; } if (list3.Count > 1) { error = "Multiple players match that query: " + string.Join(", ", list3.Select<(Entity, User), string>(((Entity entity, User user) x) => ((object)(FixedString64Bytes)(ref x.user.CharacterName)).ToString())); return false; } userEntity = list3[0].Item1; user = list3[0].Item2; PlayerCacheService.Remember(user); return true; } finally { if (val2.IsCreated) { val2.Dispose(); } } } } internal static class InventoryHelper { public static int Count(Entity characterEntity, PrefabGUID itemPrefab) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) EntityManager entityManager = Core.EntityManager; Entity val = default(Entity); if (!InventoryUtilities.TryGetInventoryEntity<EntityManager>(entityManager, characterEntity, ref val, 0)) { return 0; } if (!((EntityManager)(ref entityManager)).HasComponent<InventoryBuffer>(val)) { return 0; } DynamicBuffer<InventoryBuffer> buffer = ((EntityManager)(ref entityManager)).GetBuffer<InventoryBuffer>(val, false); int num = 0; for (int i = 0; i < buffer.Length; i++) { InventoryBuffer val2 = buffer[i]; if (((PrefabGUID)(ref val2.ItemType)).GuidHash == ((PrefabGUID)(ref itemPrefab)).GuidHash) { num += buffer[i].Amount; } } return num; } public static bool TryRemove(Entity characterEntity, PrefabGUID itemPrefab, int amount) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) EntityManager entityManager = Core.EntityManager; Entity val = default(Entity); if (!InventoryUtilities.TryGetInventoryEntity<EntityManager>(entityManager, characterEntity, ref val, 0)) { return false; } if (!((EntityManager)(ref entityManager)).HasComponent<InventoryBuffer>(val)) { return false; } DynamicBuffer<InventoryBuffer> buffer = ((EntityManager)(ref entityManager)).GetBuffer<InventoryBuffer>(val, false); int num = amount; int num2 = buffer.Length - 1; while (num2 >= 0 && num > 0) { InventoryBuffer val2 = buffer[num2]; if (((PrefabGUID)(ref val2.ItemType)).GuidHash == ((PrefabGUID)(ref itemPrefab)).GuidHash && val2.Amount > 0) { int num3 = math.min(val2.Amount, num); val2.Amount -= num3; num -= num3; if (val2.Amount <= 0) { val2.ItemType = new PrefabGUID(0); val2.Amount = 0; } buffer[num2] = val2; } num2--; } return num == 0; } } internal static class PlayerStateHelper { private static readonly PrefabGUID[] CombatBuffs = (PrefabGUID[])(object)new PrefabGUID[3] { new PrefabGUID(581443919), new PrefabGUID(697095869), new PrefabGUID(698151145) }; public static bool Exists(Entity entity) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) if (entity != Entity.Null) { EntityManager entityManager = Core.EntityManager; return ((EntityManager)(ref entityManager)).Exists(entity); } return false; } public static bool IsInCombat(Entity characterEntity) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) EntityManager entityManager = Core.EntityManager; if (!Exists(characterEntity)) { return false; } PrefabGUID[] combatBuffs = CombatBuffs; foreach (PrefabGUID val in combatBuffs) { if (BuffUtility.HasBuff<EntityManager>(entityManager, characterEntity, PrefabIdentifier.op_Implicit(val))) { return true; } } return false; } } } namespace PerkShop.Services { internal static class AccessService { public static bool CanAccessPerkShop(Entity userEntity, Action<string> reply) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) ShopConfigRoot shop = ConfigService.Shop; if (!shop.EnableBuffWhitelist) { return true; } if (!TryGetPlatformId(userEntity, out var platformId)) { reply("User component not ready."); return false; } if (shop.BuffWhitelistPlatformIds.Contains(platformId)) { return true; } reply("You are not whitelisted for the perk shop."); return false; } public static bool CanAccessStatShop(Entity userEntity, Action<string> reply) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) ShopConfigRoot shop = ConfigService.Shop; if (!shop.EnableStatWhitelist) { return true; } if (!TryGetPlatformId(userEntity, out var platformId)) { reply("User component not ready."); return false; } if (shop.StatWhitelistPlatformIds.Contains(platformId)) { return true; } reply("You are not whitelisted for the stat shop."); return false; } private static bool TryGetPlatformId(Entity userEntity, out ulong platformId) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) platformId = 0uL; if (!(userEntity == Entity.Null)) { EntityManager entityManager = Core.EntityManager; if (((EntityManager)(ref entityManager)).Exists(userEntity)) { entityManager = Core.EntityManager; if (((EntityManager)(ref entityManager)).HasComponent<User>(userEntity)) { entityManager = Core.EntityManager; User componentData = ((EntityManager)(ref entityManager)).GetComponentData<User>(userEntity); platformId = componentData.PlatformId; return platformId != 0; } } } return false; } } internal static class BuffService { private sealed class PendingPermanentBuffInfo { public DateTime CreatedAtUtc { get; init; } = DateTime.UtcNow; public int DurationSeconds { get; init; } public bool PersistThroughDeath { get; init; } public bool PreserveVanillaCleanup { get; init; } } private static readonly Dictionary<Entity, PendingPermanentBuffInfo> PendingPermanentBuffs = new Dictionary<Entity, PendingPermanentBuffInfo>(); private static readonly TimeSpan PendingBuffTimeout = TimeSpan.FromSeconds(30.0); private static readonly HashSet<int> PreparedPermanentBuffPrefabs = new HashSet<int>(); public static int PendingPermanentBuffCount => PendingPermanentBuffs.Count; public static bool IsRenewableTimedEntry(ShopConfigRoot config, PerkShopEntry entry) { return ConfigService.IsRenewableTimedCategory(config, entry.Category); } public static int ResolveDurationSeconds(ShopConfigRoot config, PerkShopEntry entry) { return ConfigService.ResolveBuffDuration(config, entry); } public static bool ResolvePersistThroughDeath(ShopConfigRoot config, PerkShopEntry entry) { return ConfigService.ResolveBuffPersistThroughDeath(config, entry); } public static bool PreserveVanillaCleanup(ShopConfigRoot config, PerkShopEntry entry) { return ConfigService.PreserveVanillaBuffCleanup(config, entry); } public static void ResetRuntimeStateAfterConfigReload() { PendingPermanentBuffs.Clear(); PreparedPermanentBuffPrefabs.Clear(); } public static void ProcessPendingPermanentBuffsDuringSpawn(EntityQuery spawnQuery) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) if (PendingPermanentBuffs.Count == 0) { return; } NativeArray<Entity> val = ((EntityQuery)(ref spawnQuery)).ToEntityArray(AllocatorHandle.op_Implicit((Allocator)2)); try { for (int i = 0; i < val.Length; i++) { Entity val2 = val[i]; if (PendingPermanentBuffs.TryGetValue(val2, out PendingPermanentBuffInfo value)) { TrySetLifetime(val2, value.DurationSeconds, value.PersistThroughDeath, value.PreserveVanillaCleanup); PendingPermanentBuffs.Remove(val2); } } } finally { if (val.IsCreated) { val.Dispose(); } } } private static void MarkPendingLifetimeMutation(Entity buffEntity, int durationSeconds, bool persistThroughDeath, bool preserveVanillaCleanup) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) if (!(buffEntity == Entity.Null)) { PendingPermanentBuffs[buffEntity] = new PendingPermanentBuffInfo { DurationSeconds = durationSeconds, PersistThroughDeath = persistThroughDeath, PreserveVanillaCleanup = preserveVanillaCleanup }; } } public static void ProcessPendingPermanentBuffs() { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Expected O, but got Unknown //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) if (PendingPermanentBuffs.Count == 0) { return; } EntityManager entityManager = Core.EntityManager; KeyValuePair<Entity, PendingPermanentBuffInfo>[] array = PendingPermanentBuffs.ToArray(); bool flag = default(bool); for (int i = 0; i < array.Length; i++) { KeyValuePair<Entity, PendingPermanentBuffInfo> keyValuePair = array[i]; Entity key = keyValuePair.Key; PendingPermanentBuffInfo value = keyValuePair.Value; if (key == Entity.Null || !((EntityManager)(ref entityManager)).Exists(key)) { PendingPermanentBuffs.Remove(key); } else if (((EntityManager)(ref entityManager)).HasComponent<SpawnTag>(key)) { if (DateTime.UtcNow - value.CreatedAtUtc > PendingBuffTimeout) { PendingPermanentBuffs.Remove(key); ManualLogSource log = Core.Log; BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(67, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[BuffService] Timed out waiting to finalize permanent buff entity:"); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(key.Index); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(":"); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(key.Version); } log.LogWarning(val); } } else { TrySetLifetime(key, value.DurationSeconds, value.PersistThroughDeath, value.PreserveVanillaCleanup); PendingPermanentBuffs.Remove(key); } } } private static void PreparePermanentBuffPrefab(PrefabGUID buffPrefab, int durationSeconds, bool persistThroughDeath) { //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Expected O, but got Unknown //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) if (durationSeconds >= 0 || PreparedPermanentBuffPrefabs.Contains(((PrefabGUID)(ref buffPrefab)).GuidHash)) { return; } try { Entity val = default(Entity); if (!Core.Systems.PrefabCollectionSystem._PrefabGuidToEntityMap.TryGetValue(buffPrefab, ref val)) { return; } EntityManager entityManager = Core.EntityManager; if (!(val == Entity.Null) && ((EntityManager)(ref entityManager)).Exists(val)) { if (((EntityManager)(ref entityManager)).HasComponent<LifeTime>(val)) { ((EntityManager)(ref entityManager)).RemoveComponent<LifeTime>(val); } if (((EntityManager)(ref entityManager)).HasComponent<RemoveBuffOnGameplayEvent>(val)) { ((EntityManager)(ref entityManager)).RemoveComponent<RemoveBuffOnGameplayEvent>(val); } if (((EntityManager)(ref entityManager)).HasComponent<RemoveBuffOnGameplayEventEntry>(val)) { ((EntityManager)(ref entityManager)).RemoveComponent<RemoveBuffOnGameplayEventEntry>(val); } if (persistThroughDeath && !((EntityManager)(ref entityManager)).HasComponent<Buff_Persists_Through_Death>(val)) { ((EntityManager)(ref entityManager)).AddComponent<Buff_Persists_Through_Death>(val); } PreparedPermanentBuffPrefabs.Add(((PrefabGUID)(ref buffPrefab)).GuidHash); Core.LogDebugIfEnabled($"[BuffService] Prepared permanent buff prefab {((PrefabGUID)(ref buffPrefab)).GuidHash} with no LifeTime."); } } catch (Exception ex) { ManualLogSource log = Core.Log; bool flag = default(bool); BepInExWarningLogInterpolatedStringHandler val2 = new BepInExWarningLogInterpolatedStringHandler(56, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("[BuffService] Failed to prepare permanent buff prefab "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<int>(((PrefabGUID)(ref buffPrefab)).GuidHash); ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(": "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(ex.Message); } log.LogWarning(val2); } } public static bool HasBuff(Entity characterEntity, PrefabGUID buffPrefab) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) Entity val = default(Entity); return BuffUtility.TryGetBuff<EntityManager>(Core.EntityManager, characterEntity, PrefabIdentifier.op_Implicit(buffPrefab), ref val); } public static bool ApplyPurchasedBuff(Entity userEntity, Entity characterEntity, PrefabGUID buffPrefab, bool preventDuplicate, bool allowMutation, bool mutateLifetime, int durationSeconds, bool persistThroughDeath, bool keepVisibleTimerFrozen = false, int visibleTimerSeconds = 0, bool preserveVanillaCleanup = false) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) Entity buffEntity = default(Entity); if (BuffUtility.TryGetBuff<EntityManager>(Core.EntityManager, characterEntity, PrefabIdentifier.op_Implicit(buffPrefab), ref buffEntity)) { if (allowMutation && mutateLifetime) { MarkPendingLifetimeMutation(buffEntity, durationSeconds, persistThroughDeath, preserveVanillaCleanup); } return true; } if (preventDuplicate && HasBuff(characterEntity, buffPrefab)) { return false; } FromCharacter val = default(FromCharacter); val.User = userEntity; val.Character = characterEntity; FromCharacter val2 = val; ApplyBuffDebugEvent val3 = default(ApplyBuffDebugEvent); val3.BuffPrefabGUID = buffPrefab; ApplyBuffDebugEvent val4 = val3; Core.DebugEventsSystem.ApplyBuff(val2, val4); Entity buffEntity2 = default(Entity); if (!BuffUtility.TryGetBuff<EntityManager>(Core.EntityManager, characterEntity, PrefabIdentifier.op_Implicit(buffPrefab), ref buffEntity2)) { return false; } if (allowMutation && mutateLifetime) { MarkPendingLifetimeMutation(buffEntity2, durationSeconds, persistThroughDeath, preserveVanillaCleanup); } return true; } public static bool RemoveBuff(Entity characterEntity, PrefabGUID buffPrefab) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) Entity val = default(Entity); if (!BuffUtility.TryGetBuff<EntityManager>(Core.EntityManager, characterEntity, PrefabIdentifier.op_Implicit(buffPrefab), ref val)) { return false; } PendingPermanentBuffs.Remove(val); DestroyUtility.Destroy(Core.EntityManager, val, (DestroyDebugReason)13, (string)null, 0); EntityManager entityManager = Core.EntityManager; if (((EntityManager)(ref entityManager)).Exists(val)) { entityManager = Core.EntityManager; if (!((EntityManager)(ref entityManager)).HasComponent<DestroyTag>(val)) { entityManager = Core.EntityManager; ((EntityManager)(ref entityManager)).AddComponent<DestroyTag>(val); } } return true; } public static void MakeBuffPermanent(Entity buffEntity, bool persistThroughDeath = true) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) TrySetLifetime(buffEntity, -1, persistThroughDeath, preserveVanillaCleanup: false); } public static int RemoveConfiguredCategoryBuffs(Entity characterEntity, string category) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) string category2 = category; if (characterEntity == Entity.Null || string.IsNullOrWhiteSpace(category2)) { return 0; } HashSet<int> hashSet = (from entry in ConfigService.Shop.Buffs.Values where entry != null && entry.BuffPrefab != 0 && string.Equals(entry.Category, category2, StringComparison.OrdinalIgnoreCase) select entry.BuffPrefab).ToHashSet(); if (hashSet.Count == 0) { return 0; } int num = 0; foreach (int item in hashSet) { if (RemoveBuff(characterEntity, new PrefabGUID(item))) { num++; } } return num + RemoveLiveBuffsByPrefabSet(characterEntity, hashSet); } private static int RemoveLiveBuffsByPrefabSet(Entity characterEntity, HashSet<int> prefabSet) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Expected O, but got Unknown //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) if (characterEntity == Entity.Null || prefabSet.Count == 0) { return 0; } NativeArray<Entity> val = default(NativeArray<Entity>); try { EntityManager entityManager = Core.EntityManager; EntityQuery val2 = ((EntityManager)(ref entityManager)).CreateEntityQuery((ComponentType[])(object)new ComponentType[2] { ComponentType.ReadOnly<Buff>(), ComponentType.ReadOnly<PrefabGUID>() }); val = ((EntityQuery)(ref val2)).ToEntityArray(AllocatorHandle.op_Implicit((Allocator)2)); int num = 0; for (int i = 0; i < val.Length; i++) { Entity val3 = val[i]; if (!((EntityManager)(ref entityManager)).Exists(val3) || !((EntityManager)(ref entityManager)).HasComponent<Buff>(val3) || !((EntityManager)(ref entityManager)).HasComponent<PrefabGUID>(val3) || ((EntityManager)(ref entityManager)).GetComponentData<Buff>(val3).Target != characterEntity) { continue; } PrefabGUID componentData = ((EntityManager)(ref entityManager)).GetComponentData<PrefabGUID>(val3); if (prefabSet.Contains(((PrefabGUID)(ref componentData)).GuidHash)) { PendingPermanentBuffs.Remove(val3); DestroyUtility.Destroy(entityManager, val3, (DestroyDebugReason)13, (string)null, 0); if (((EntityManager)(ref entityManager)).Exists(val3) && !((EntityManager)(ref entityManager)).HasComponent<DestroyTag>(val3)) { ((EntityManager)(ref entityManager)).AddComponent<DestroyTag>(val3); } num++; } } return num; } catch (Exception ex) { ManualLogSource log = Core.Log; bool flag = default(bool); BepInExWarningLogInterpolatedStringHandler val4 = new BepInExWarningLogInterpolatedStringHandler(63, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val4).AppendLiteral("[BuffService] Failed scanning live category buffs for removal: "); ((BepInExLogInterpolatedStringHandler)val4).AppendFormatted<string>(ex.Message); } log.LogWarning(val4); return 0; } finally { if (val.IsCreated) { val.Dispose(); } } } public static bool ForceOwnedBuffLifetime(Entity characterEntity, PrefabGUID buffPrefab, int durationSeconds, bool persistThroughDeath, bool keepVisibleTimerFrozen = false, int visibleTimerSeconds = 0, bool preserveVanillaCleanup = false) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) Entity buffEntity = default(Entity); if (!BuffUtility.TryGetBuff<EntityManager>(Core.EntityManager, characterEntity, PrefabIdentifier.op_Implicit(buffPrefab), ref buffEntity)) { return false; } MarkPendingLifetimeMutation(buffEntity, durationSeconds, persistThroughDeath, preserveVanillaCleanup); return true; } private static void TrySetLifetime(Entity buffEntity, int durationSeconds, bool persistThroughDeath, bool preserveVanillaCleanup) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) EntityManager entityManager = Core.EntityManager; if (buffEntity == Entity.Null || !((EntityManager)(ref entityManager)).Exists(buffEntity)) { return; } if (persistThroughDeath && !((EntityManager)(ref entityManager)).HasComponent<Buff_Persists_Through_Death>(buffEntity)) { ((EntityManager)(ref entityManager)).AddComponent<Buff_Persists_Through_Death>(buffEntity); } if (!preserveVanillaCleanup) { if (((EntityManager)(ref entityManager)).HasComponent<RemoveBuffOnGameplayEvent>(buffEntity)) { ((EntityManager)(ref entityManager)).RemoveComponent<RemoveBuffOnGameplayEvent>(buffEntity); } if (((EntityManager)(ref entityManager)).HasComponent<RemoveBuffOnGameplayEventEntry>(buffEntity)) { ((EntityManager)(ref entityManager)).RemoveComponent<RemoveBuffOnGameplayEventEntry>(buffEntity); } } if (durationSeconds < 0) { if (((EntityManager)(ref entityManager)).HasComponent<LifeTime>(buffEntity)) { ((EntityManager)(ref entityManager)).RemoveComponent<LifeTime>(buffEntity); } } else if (durationSeconds > 0) { if (!((EntityManager)(ref entityManager)).HasComponent<LifeTime>(buffEntity)) { ((EntityManager)(ref entityManager)).AddComponent<LifeTime>(buffEntity); } ((EntityManager)(ref entityManager)).SetComponentData<LifeTime>(buffEntity, new LifeTime { Duration = durationSeconds, EndAction = (LifeTimeEndAction)2 }); } } } internal static class CarrierPrefabService { private const string ScriptSpawnTypeName = "ProjectM.Scripting.ScriptSpawn"; private static int _preparedCarrierGuid; private static Type? _scriptSpawnManagedType; private static Type? _scriptSpawnIl2CppType; internal static void Reset() { _preparedCarrierGuid = 0; } internal static bool PrepareConfiguredStatCarrierPrefab() { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Expected O, but got Unknown //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) try { if (!StatService.IsCarrierConfigured(out PrefabGUID carrier, out string error)) { ManualLogSource log = Core.Log; bool flag = default(bool); BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(23, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[CarrierPrefabService] "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(error); } log.LogWarning(val); return false; } if (_preparedCarrierGuid == ((PrefabGUID)(ref carrier)).GuidHash) { return true; } Entity val2 = default(Entity); if (!Core.Systems.PrefabCollectionSystem._PrefabGuidToEntityMap.TryGetValue(carrier, ref val2)) { ManualLogSource log2 = Core.Log; bool flag2 = default(bool); BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(102, 1, ref flag2); if (flag2) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[CarrierPrefabService] Could not find stat carrier prefab entity for "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(((PrefabGUID)(ref carrier)).GuidHash); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(". Stats will not spawn correctly."); } log2.LogWarning(val); return false; } EntityManager entityManager = Core.EntityManager; if (!TryEnsureScriptSpawn(entityManager, val2)) { Core.Log.LogWarning((object)"[CarrierPrefabService] Could not resolve/add ScriptSpawn dynamically. Immediate stat-buffer fallback will still be attempted, but HUD/stat sync may be less reliable."); } if (!((EntityManager)(ref entityManager)).HasBuffer<ModifyUnitStatBuff_DOTS>(val2)) { ((EntityManager)(ref entityManager)).AddBuffer<ModifyUnitStatBuff_DOTS>(val2); } else { ((EntityManager)(ref entityManager)).GetBuffer<ModifyUnitStatBuff_DOTS>(val2, false).Clear(); } _preparedCarrierGuid = ((PrefabGUID)(ref carrier)).GuidHash; Core.LogDebugIfEnabled($"[CarrierPrefabService] Prepared stat carrier prefab {((PrefabGUID)(ref carrier)).GuidHash} with dynamic ScriptSpawn + ModifyUnitStatBuff_DOTS."); return true; } catch (Exception e) { Core.LogException(e, "PrepareConfiguredStatCarrierPrefab"); return false; } } private static bool TryEnsureScriptSpawn(EntityManager em, Entity prefabEntity) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) Type val = ResolveScriptSpawnIl2CppType(); if (val == (Type)null) { return false; } ComponentType val2 = default(ComponentType); ((ComponentType)(ref val2))..ctor(val, (AccessMode)0); if (!((EntityManager)(ref em)).HasComponent(prefabEntity, val2)) { ((EntityManager)(ref em)).AddComponent(prefabEntity, val2); } return true; } private static Type? ResolveScriptSpawnIl2CppType() { if (_scriptSpawnIl2CppType != (Type)null) { return _scriptSpawnIl2CppType; } if ((object)_scriptSpawnManagedType == null) { _scriptSpawnManagedType = AccessTools.TypeByName("ProjectM.Scripting.ScriptSpawn") ?? AccessTools.TypeByName("ProjectM.Gameplay.Scripting.ScriptSpawn") ?? AccessTools.TypeByName("ProjectM.ScriptSpawn") ?? AccessTools.TypeByName("ScriptSpawn"); } if (_scriptSpawnManagedType == null) { return null; } MethodInfo methodInfo = typeof(Il2CppType).GetMethods(BindingFlags.Static | BindingFlags.Public).FirstOrDefault((MethodInfo method) => method.Name == "Of" && method.IsGenericMethodDefinition && method.GetParameters().Length == 0); if (methodInfo == null) { return null; } object? obj = methodInfo.MakeGenericMethod(_scriptSpawnManagedType).Invoke(null, null); _scriptSpawnIl2CppType = (Type?)((obj is Type) ? obj : null); return _scriptSpawnIl2CppType; } } internal static class ConfigMigrationService { internal static bool TryMigrate(ShopConfigRoot cfg, out ShopConfigRoot migrated) { migrated = cfg ?? ConfigService.DefaultConfigForMigration(); bool flag = false; if (migrated.ConfigVersion < 2) { flag |= FixMovementSpeed(migrated); flag |= ClampOldOvertunedDefaults(migrated); } if (migrated.ConfigVersion < 4) { flag |= DisableVisibleTimerResetLoop(migrated); flag |= EnsureDefaultBloodBuffs(migrated); } if (migrated.ConfigVersion < 6) { flag |= MigrateDefaultStatCarrier(migrated); } if (migrated.ConfigVersion < 7) { flag |= FixDefaultStatNotes(migrated); } if (migrated.ConfigVersion < 8) { flag |= ApplyConfigVersion8RuntimeDefaults(migrated); } if (migrated.ConfigVersion < 11) { flag |= ApplyConfigVersion11CompatibilityDefaults(migrated); } if (migrated.ConfigVersion < 12) { flag |= ApplyConfigVersion12RenewableTimedBuffDefaults(migrated); } if (migrated.ConfigVersion < 13) { flag |= ApplyConfigVersion13AllStatsAndBloodSlots(migrated); } if (migrated.ConfigVersion < 14) { flag |= ApplyConfigVersion14ShortKeys(migrated); } if (migrated.ConfigVersion < 14) { migrated.ConfigVersion = 14; flag = true; } return flag; } private static bool MigrateDefaultStatCarrier(ShopConfigRoot cfg) { if (cfg.StatCarrierBuffPrefab != -1469378405) { return false; } cfg.StatCarrierBuffPrefab = -809648681; return true; } private static bool FixMovementSpeed(ShopConfigRoot cfg) { if (cfg.Stats == null || !cfg.Stats.TryGetValue("movement_speed", out StatShopEntry value) || value == null) { return false; } if (!StatDefinitionService.IsDangerousModifierCombo(value)) { return false; } value.ModificationType = "MultiplyBaseAdd"; value.Notes = "Permanently increases Movement Speed by 0.05 per purchase. Uses MultiplyBaseAdd for HUD/stat compatibility."; return true; } private static bool ClampOldOvertunedDefaults(ShopConfigRoot cfg) { if (cfg.Stats == null) { return false; } return false | SetIfOldValue(cfg, "primary_attack_speed", 0.05f, 0.02f) | SetIfOldValue(cfg, "physical_resistance", 0.05f, 0.02f) | SetIfOldValue(cfg, "spell_resistance", 0.05f, 0.02f) | SetIfOldValue(cfg, "healing_received", 0.05f, 0.03f) | SetIfOldValue(cfg, "damage_reduction", 0.02f, 0.01f) | SetIfOldValue(cfg, "spell_cooldown_recovery_rate", 0.05f, 0.02f) | SetIfOldValue(cfg, "weapon_cooldown_recovery_rate", 0.05f, 0.02f) | SetIfOldValue(cfg, "ability_attack_speed", 0.05f, 0.02f) | SetIfOldValue(cfg, "corruption_damage_reduction", 0.05f, 0.02f) | SetIfOldValue(cfg, "physical_power", 1f, 2f) | SetIfOldValue(cfg, "primary_life_leech", 0.02f, 0.03f) | SetIfOldValue(cfg, "physical_critical_strike_damage", 0.05f, 0.1f) | SetIfOldValue(cfg, "spell_critical_strike_damage", 0.05f, 0.1f) | SetIfOldValue(cfg, "reduced_blood_drain", 0.05f, 0.1f); } private static bool SetIfOldValue(ShopConfigRoot cfg, string key, float oldValue, float newValue) { if (!cfg.Stats.TryGetValue(key, out StatShopEntry value) || value == null) { return false; } if (Math.Abs(value.ValuePerPurchase - oldValue) > 0.0001f) { return false; } value.ValuePerPurchase = newValue; return true; } private static bool DisableVisibleTimerResetLoop(ShopConfigRoot cfg) { if (cfg.Buffs == null) { return false; } bool result = false; foreach (var (_, perkShopEntry2) in cfg.Buffs) { if (perkShopEntry2 != null && (perkShopEntry2.KeepVisibleTimerFrozen || perkShopEntry2.VisibleTimerSeconds != 0 || perkShopEntry2.DurationSeconds != -1)) { perkShopEntry2.KeepVisibleTimerFrozen = false; perkShopEntry2.VisibleTimerSeconds = 0; perkShopEntry2.DurationSeconds = -1; perkShopEntry2.MutateAppliedBuffLifetime = true; perkShopEntry2.PersistThroughDeath = true; result = true; } } return result; } private static bool EnsureDefaultBloodBuffs(ShopConfigRoot cfg) { if (cfg.Buffs == null) { Dictionary<string, PerkShopEntry> dictionary2 = (cfg.Buffs = new Dictionary<string, PerkShopEntry>(StringComparer.OrdinalIgnoreCase)); } bool result = false; foreach (var (key, perkShopEntry2) in ConfigService.DefaultBuffEntriesForMigration()) { if (perkShopEntry2 != null && string.Equals(perkShopEntry2.Category, "blood_buff", StringComparison.OrdinalIgnoreCase) && !cfg.Buffs.ContainsKey(key)) { cfg.Buffs[key] = perkShopEntry2; result = true; } } return result; } private static bool FixDefaultStatNotes(ShopConfigRoot cfg) { if (cfg.Stats == null) { return false; } bool result = false; foreach (var (key, statShopEntry2) in cfg.Stats) { if (statShopEntry2 != null && StatDefinitionService.TryGetDefaultStatNote(key, statShopEntry2.ValuePerPurchase, statShopEntry2.UnitStat, out string note) && !string.Equals(statShopEntry2.Notes, note, StringComparison.Ordinal) && (string.IsNullOrWhiteSpace(statShopEntry2.Notes) || statShopEntry2.Notes.StartsWith("Permanently increases ", StringComparison.OrdinalIgnoreCase) || string.Equals(statShopEntry2.Notes, "Permanent stat purchase.", StringComparison.OrdinalIgnoreCase))) { statShopEntry2.Notes = note; result = true; } } return result; } private static bool ApplyConfigVersion8RuntimeDefaults(ShopConfigRoot cfg) { bool result = false; if (cfg.ConfigFileCheckIntervalSeconds <= 0f) { cfg.ConfigFileCheckIntervalSeconds = 5f; result = true; } if (cfg.PlayerCacheSaveDebounceSeconds <= 0f) { cfg.PlayerCacheSaveDebounceSeconds = 30f; result = true; } _ = cfg.AutoDetectConfigChanges; return result; } private static bool ApplyConfigVersion11CompatibilityDefaults(ShopConfigRoot cfg) { bool result = false; if (cfg.UseClientAttributeStatAliases) { cfg.UseClientAttributeStatAliases = false; result = true; } _ = cfg.EnableExperimentalBloodBuffs; return result; } private static bool ApplyConfigVersion12RenewableTimedBuffDefaults(ShopConfigRoot cfg) { bool result = false; if (!cfg.UseRenewableTimedBuffs) { cfg.UseRenewableTimedBuffs = true; result = true; } if (cfg.RenewableTimedBuffSeconds < 60) { cfg.RenewableTimedBuffSeconds = 7200; result = true; } if (cfg.RenewableTimedBuffCategories == null) { List<string> list2 = (cfg.RenewableTimedBuffCategories = new List<string>()); } string[] array = new string[3] { "potion", "elixir", "blood_buff" }; foreach (string text in array) { if (!cfg.RenewableTimedBuffCategories.Contains<string>(text, StringComparer.OrdinalIgnoreCase)) { cfg.RenewableTimedBuffCategories.Add(text); result = true; } } if (!cfg.EnableExperimentalBloodBuffs) { cfg.EnableExperimentalBloodBuffs = true; result = true; } if (cfg.Buffs != null) { foreach (var (_, perkShopEntry2) in cfg.Buffs) { if (perkShopEntry2 != null && (string.Equals(perkShopEntry2.Category, "potion", StringComparison.OrdinalIgnoreCase) || string.Equals(perkShopEntry2.Category, "elixir", StringComparison.OrdinalIgnoreCase) || string.Equals(perkShopEntry2.Category, "blood_buff", StringComparison.OrdinalIgnoreCase))) { if (perkShopEntry2.DurationSeconds != cfg.RenewableTimedBuffSeconds) { perkShopEntry2.DurationSeconds = cfg.RenewableTimedBuffSeconds; result = true; } if (perkShopEntry2.KeepVisibleTimerFrozen || perkShopEntry2.VisibleTimerSeconds != 0) { perkShopEntry2.KeepVisibleTimerFrozen = false; perkShopEntry2.VisibleTimerSeconds = 0; result = true; } if (perkShopEntry2.PersistThroughDeath) { perkShopEntry2.PersistThroughDeath = false; result = true; } if (!perkShopEntry2.MutateAppliedBuffLifetime) { perkShopEntry2.MutateAppliedBuffLifetime = true; result = true; } } } } return result; } private static bool ApplyConfigVersion13AllStatsAndBloodSlots(ShopConfigRoot cfg) { bool result = false; if (!cfg.EnableClientUnsupportedStats) { cfg.EnableClientUnsupportedStats = true; result = true; } if (cfg.Stats != null) { foreach (var (_, statShopEntry2) in cfg.Stats) { if (statShopEntry2 != null && !statShopEntry2.Enabled) { statShopEntry2.Enabled = true; result = true; } } } if (cfg.Categories == null) { Dictionary<string, BuffCategoryDefinition> dictionary2 = (cfg.Categories = new Dictionary<string, BuffCategoryDefinition>(StringComparer.OrdinalIgnoreCase)); } if (!cfg.Categories.TryGetValue("blood_buff", out BuffCategoryDefinition value) || value == null) { cfg.Categories["blood_buff"] = new BuffCategoryDefinition { DisplayName = "Blood Buff", Documentation = "Renewable 2-hour blood-buff effects. Default limit: five blood packages.", MaxOwnedSlots = 5, SlotFreeCost = 250 }; result = true; } else { if (value.MaxOwnedSlots.GetValueOrDefault() != 5) { value.MaxOwnedSlots = 5; result = true; } if (string.IsNullOrWhiteSpace(value.Documentation) || value.Documentation.Contains("one blood package", StringComparison.OrdinalIgnoreCase) || value.Documentation.Contains("Recommended limit", StringComparison.OrdinalIgnoreCase)) { value.Documentation = "Renewable 2-hour blood-buff effects. Default limit: five blood packages."; result = true; } } return result; } private static bool ApplyConfigVersion14ShortKeys(ShopConfigRoot cfg) { bool flag = false; if (cfg.Stats != null) { foreach (KeyValuePair<string, string> statKeyAlias in KeyAliasService.StatKeyAliases) { flag |= RenameStatKey(cfg, statKeyAlias.Key, statKeyAlias.Value); } } if (cfg.Buffs != null) { foreach (KeyValuePair<string, string> buffKeyAlias in KeyAliasService.BuffKeyAliases) { flag |= RenameBuffKey(cfg, buffKeyAlias.Key, buffKeyAlias.Value); } string[] array = new string[6] { "draculaT1", "draculaT2", "draculaT3", "draculaT4", "draculaT5", "generalT5" }; foreach (string key in array) { if (cfg.Buffs.TryGetValue(key, out PerkShopEntry value) && value != null && value.Enabled) { value.Enabled = false; flag = true; } } } return flag; } private static bool RenameStatKey(ShopConfigRoot cfg, string oldKey, string newKey) { if (cfg.Stats == null || !cfg.Stats.TryGetValue(oldKey, out StatShopEntry value) || value == null) { return false; } if (!cfg.Stats.ContainsKey(newKey)) { cfg.Stats[newKey] = value; } cfg.Stats.Remove(oldKey); return true; } private static bool RenameBuffKey(ShopConfigRoot cfg, string oldKey, string newKey) { if (cfg.Buffs == null || !cfg.Buffs.TryGetValue(oldKey, out PerkShopEntry value) || value == null) { return false; } if (!cfg.Buffs.ContainsKey(newKey)) { cfg.Buffs[newKey] = value; } cfg.Buffs.Remove(oldKey); return true; } } internal static class ConfigService { private static readonly object Lock = new object(); internal static readonly string ConfigDir = Path.Combine(Paths.ConfigPath, "PerkShop"); private static readonly string ConfigFile = Path.Combine(ConfigDir, "perkconfig.json"); private static DateTime _lastWrite = DateTime.MinValue; private static DateTime _nextConfigFileCheckUtc = DateTime.MinValue; private static ShopConfigRoot _root = DefaultConfig(); internal static readonly JsonSerializerOptions JsonOptions = new JsonSerializerOptions { AllowTrailingCommas = true, PropertyNameCaseInsensitive = true, ReadCommentHandling = JsonCommentHandling.Skip, WriteIndented = true }; public static ShopConfigRoot Shop => GetShop(); public static bool DebugLoggingEnabled { get { try { return Shop.EnableDebugLogging; } catch { return false; } } } public static void Initialize() { Load(force: true); } public static void Reload() { Load(force: true); } public static bool UpdateWhitelist(string shopType, ulong platformId, bool add, string displayName = "") { if (platformId == 0L) { return false; } lock (Lock) { Load(force: true); _root = Normalize(_root); bool num = string.Equals(shopType, "stat", StringComparison.OrdinalIgnoreCase); List<ulong> list = (num ? _root.StatWhitelistPlatformIds : _root.BuffWhitelistPlatformIds); Dictionary<ulong, string> dictionary = (num ? _root.StatWhitelistNames : _root.BuffWhitelistNames); bool flag = list.Contains(platformId); if (add) { if (!flag) { list.Add(platformId); list.Sort(); } if (!string.IsNullOrWhiteSpace(displayName)) { dictionary[platformId] = displayName.Trim(); } File.WriteAllText(ConfigFile, JsonSerializer.Serialize(_root, JsonOptions)); _lastWrite = File.GetLastWriteTime(ConfigFile); return !flag; } if (!flag) { return false; } list.Remove(platformId); dictionary.Remove(platformId); File.WriteAllText(ConfigFile, JsonSerializer.Serialize(_root, JsonOptions)); _lastWrite = File.GetLastWriteTime(ConfigFile); return true; } } private static ShopConfigRoot GetShop() { Load(force: false); lock (Lock) { return _root; } } private static void Load(bool force) { //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_0184: Expected O, but got Unknown lock (Lock) { try { Directory.CreateDirectory(ConfigDir); if (!File.Exists(ConfigFile)) { _root = Normalize(DefaultConfig()); StatDefinitionService.RebuildCache(_root); File.WriteAllText(ConfigFile, JsonSerializer.Serialize(_root, JsonOptions)); _lastWrite = File.GetLastWriteTime(ConfigFile); _nextConfigFileCheckUtc = DateTime.UtcNow.AddSeconds(Math.Max(1f, _root.ConfigFileCheckIntervalSeconds)); return; } if (!force) { if (!_root.AutoDetectConfigChanges || DateTime.UtcNow < _nextConfigFileCheckUtc) { return; } _nextConfigFileCheckUtc = DateTime.UtcNow.AddSeconds(Math.Max(1f, _root.ConfigFileCheckIntervalSeconds)); } DateTime lastWriteTime = File.GetLastWriteTime(ConfigFile); if (!force && lastWriteTime <= _lastWrite) { return; } _root = JsonSerializer.Deserialize<ShopConfigRoot>(File.ReadAllText(ConfigFile), JsonOptions) ?? DefaultConfig(); if (ConfigMigrationService.TryMigrate(_root, out ShopConfigRoot migrated)) { _root = Normalize(migrated); StatDefinitionService.RebuildCache(_root); File.WriteAllText(ConfigFile, JsonSerializer.Serialize(_root, JsonOptions)); lastWriteTime = File.GetLastWriteTime(ConfigFile); ManualLogSource log = Core.Log; bool flag = default(bool); BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(53, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[ConfigService] Migrated perkconfig.json to version "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(_root.ConfigVersion); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("."); } log.LogInfo(val); } else { _root = Normalize(_root); StatDefinitionService.RebuildCache(_root); } _lastWrite = lastWriteTime; _nextConfigFileCheckUtc = DateTime.UtcNow.AddSeconds(Math.Max(1f, _root.ConfigFileCheckIntervalSeconds)); } catch (Exception e) { Core.LogException(e, "Load"); _root = Normalize(DefaultConfig()); StatDefinitionService.RebuildCache(_root); _nextConfigFileCheckUtc = DateTime.UtcNow.AddSeconds(Math.Max(1f, _root.ConfigFileCheckIntervalSeconds)); } } } private static ShopConfigRoot Normalize(ShopConfigRoot cfg) { if (cfg == null) { cfg = DefaultConfig(); } if (cfg.ConfigVersion <= 0) { cfg.ConfigVersion = 14; } cfg.CurrencyName = (string.IsNullOrWhiteSpace(cfg.CurrencyName) ? "Currency" : cfg.CurrencyName.Trim()); if (cfg.CurrencyPrefab == 0) { cfg.CurrencyPrefab = 576389135; } if (cfg.ReapplyCheckIntervalSeconds < 3) { cfg.ReapplyCheckIntervalSeconds = 3; } if (cfg.ReapplyMaxUsersPerCycle < 0) { cfg.ReapplyMaxUsersPerCycle = 0; } if (cfg.CarrierFinalizeCheckIntervalSeconds < 0.05f) { cfg.CarrierFinalizeCheckIntervalSeconds = 0.05f; } if (cfg.OwnershipSaveDebounceSeconds < 0f) { cfg.OwnershipSaveDebounceSeconds = 0f; } if (cfg.ConfigFileCheckIntervalSeconds < 1f) { cfg.ConfigFileCheckIntervalSeconds = 5f; } if (cfg.PlayerCacheSaveDebounceSeconds < 0f) { cfg.PlayerCacheSaveDebounceSeconds = 0f; } if (cfg.RenewableTimedBuffSeconds < 60) { cfg.RenewableTimedBuffSeconds = 7200; } ShopConfigRoot shopConfigRoot = cfg; if (shopConfigRoot.RenewableTimedBuffCategories == null) { ShopConfigRoot shopConfigRoot2 = shopConfigRoot; List<string> obj = new List<string> { "potion", "elixir", "blood_buff" }; List<string> list = obj; shopConfigRoot2.RenewableTimedBuffCategories = obj; } cfg.RenewableTimedBuffCategories = (from category in cfg.RenewableTimedBuffCategories where !string.IsNullOrWhiteSpace(category) select category.Trim()).Distinct<string>(StringComparer.OrdinalIgnoreCase).ToList(); if (cfg.RenewableTimedBuffCategories.Count == 0) { cfg.RenewableTimedBuffCategories.AddRange(new string[3] { "potion", "elixir", "blood_buff" }); } if (cfg.ForcePermanentBuffs) { cfg.AllowBuffEntityMutation = true; } shopConfigRoot = cfg; if (shopConfigRoot.BuffWhitelistPlatformIds == null) { List<ulong> list3 = (shopConfigRoot.BuffWhitelistPlatformIds = new List<ulong>()); } shopConfigRoot = cfg; if (shopConfigRoot.StatWhitelistPlatformIds == null) { List<ulong> list3 = (shopConfigRoot.StatWhitelistPlatformIds = new List<ulong>()); } shopConfigRoot = cfg; if (shopConfigRoot.BuffWhitelistNames == null) { Dictionary<ulong, string> dictionary2 = (shopConfigRoot.BuffWhitelistNames = new Dictionary<ulong, string>()); } shopConfigRoot = cfg; if (shopConfigRoot.StatWhitelistNames == null) { Dictionary<ulong, string> dictionary2 = (shopConfigRoot.StatWhitelistNames = new Dictionary<ulong, string>()); } cfg.BuffWhitelistPlatformIds = (from id in cfg.BuffWhitelistPlatformIds.Where((ulong id) => id != 0).Distinct() orderby id select id).ToList(); cfg.StatWhitelistPlatformIds = (from id in cfg.StatWhitelistPlatformIds.Where((ulong id) => id != 0).Distinct() orderby id select id).ToList(); cfg.BuffWhitelistNames = cfg.BuffWhitelistNames.Where<KeyValuePair<ulong, string>>((KeyValuePair<ulong, string> kv) => kv.Key != 0L && !string.IsNullOrWhiteSpace(kv.Value)).ToDictionary((KeyValuePair<ulong, string> kv) => kv.Key, (KeyValuePair<ulong, string> kv) => kv.Value.Trim()); cfg.StatWhitelistNames = cfg.StatWhitelistNames.Where<KeyValuePair<ulong, string>>((KeyValuePair<ulong, string> kv) => kv.Key != 0L && !string.IsNullOrWhiteSpace(kv.Value)).ToDictionary((KeyValuePair<ulong, string> kv) => kv.Key, (KeyValuePair<ulong, string> kv) => kv.Value.Trim()); if (cfg.MaxOwnedStatTypes < 0) { cfg.MaxOwnedStatTypes = 0; } if (cfg.StatTypeSlotFreeCost < 0) { cfg.StatTypeSlotFreeCost = 0; } cfg.MaxHealthPurchaseBehavior = (string.IsNullOrWhiteSpace(cfg.MaxHealthPurchaseBehavior) ? "ClampOnly" : cfg.MaxHealthPurchaseBehavior.Trim()); if (!string.Equals(cfg.MaxHealthPurchaseBehavior, "ClampOnly", StringComparison.OrdinalIgnoreCase) && !string.Equals(cfg.MaxHealthPurchaseBehavior, "FillToMax", StringComparison.OrdinalIgnoreCase) && !string.Equals(cfg.MaxHealthPurchaseBehavior, "PreserveRatio", StringComparison.OrdinalIgnoreCase)) { cfg.MaxHealthPurchaseBehavior = "ClampOnly"; } shopConfigRoot = cfg; if (shopConfigRoot.Stats == null) { Dictionary<string, StatShopEntry> dictionary5 = (shopConfigRoot.Stats = new Dictionary<string, StatShopEntry>(StringComparer.OrdinalIgnoreCase)); } Dictionary<string, StatShopEntry> dictionary6 = new Dictionary<string, StatShopEntry>(StringComparer.OrdinalIgnoreCase); string key; foreach (KeyValuePair<string, StatShopEntry> stat in cfg.Stats) { stat.Deconstruct(out key, out var value); string text = key; StatShopEntry statShopEntry = value; if (!string.IsNullOrWhiteSpace(text) && statShopEntry != null) { statShopEntry.DisplayName = (string.IsNullOrWhiteSpace(statShopEntry.DisplayName) ? text.Trim() : statShopEntry.DisplayName.Trim()); statShopEntry.UnitStat = (string.IsNullOrWhiteSpace(statShopEntry.UnitStat) ? "PhysicalPower" : statShopEntry.UnitStat.Trim()); statShopEntry.ModificationType = (string.IsNullOrWhiteSpace(statShopEntry.ModificationType) ? "Add" : statShopEntry.ModificationType.Trim()); statShopEntry.AttributeCapType = (string.IsNullOrWhiteSpace(statShopEntry.AttributeCapType) ? "Uncapped" : statShopEntry.AttributeCapType.Trim()); if (statShopEntry.Cost <= 0) { statShopEntry.Cost = 1; } if (statShopEntry.ValuePerPurchase == 0f) { statShopEntry.ValuePerPurchase = 1f; } if (statShopEntry.MaxPurchases < 0) { statShopEntry.MaxPurchases = 0; } statShopEntry.Notes = (string.IsNullOrWhiteSpace(statShopEntry.Notes) ? "Permanent stat purchase." : statShopEntry.Notes.Trim()); StatDefinitionService.NormalizeKnownStatEntry(text.Trim(), statShopEntry); dictionary6[text.Trim()] = statShopEntry; } } cfg.Stats = dictionary6; shopConfigRoot = cfg; if (shopConfigRoot.Categories == null) { Dictionary<string, BuffCategoryDefinition> dictionary8 = (shopConfigRoot.Categories = new Dictionary<string, BuffCategoryDefinition>(StringComparer.OrdinalIgnoreCase)); } Dictionary<string, BuffCategoryDefinition> dictionary9 = new Dictionary<string, BuffCategoryDefinition>(StringComparer.OrdinalIgnoreCase); foreach (KeyValuePair<string, BuffCategoryDefinition> category in cfg.Categories) { category.Deconstruct(out key, out var value2); string text2 = key; BuffCategoryDefinition buffCategoryDefinition = value2; if (!string.IsNullOrWhiteSpace(text2) && buffCategoryDefinition != null) { buffCategoryDefinition.DisplayName = (string.IsNullOrWhiteSpace(buffCategoryDefinition.DisplayName) ? text2.Trim() : buffCategoryDefinition.DisplayName.Trim()); buffCategoryDefinition.Documentation = (string.IsNullOrWhiteSpace(buffCategoryDefinition.Documentation) ? string.Empty : buffCategoryDefinition.Documentation.Trim()); if (buffCategoryDefinition.MaxOwnedSlots.HasValue && buffCategoryDefinition.MaxOwnedSlots.Value <= 0) { buffCategoryDefinition.MaxOwnedSlots = null; } if (buffCategoryDefinition.SlotFreeCost.HasValue && buffCategoryDefinition.SlotFreeCost.Value <= 0) { buffCategoryDefinition.SlotFreeCost = null; } dictionary9[text2.Trim()] = buffCategoryDefinition; } } EnsureCategory(dictionary9, "blood_buff", "Blood Buff", "Renewable 2-hour blood-buff effects. Default limit: five blood packages.", 5, 250); EnsureCategory(dictionary9, "set_bonus", "Set Bonus", "Armor set style bonuses or set-derived effects.", null, null); EnsureCategory(dictionary9, "potion", "Potion", "Potion and brew-style buffs. Safer for multi-slot ownership.", 3, 100); EnsureCategory(dictionary9, "elixir", "Elixir", "Elixir-style buffs. Bloodcraft compatibility default: one owned elixir slot.", 1, 500); EnsureCategory(dictionary9, "misc", "Misc", "Fallback category for uncategorized buffs.", null, null); if (dictionary9.TryGetValue("blood_buff", out var value3) && value3.MaxOwnedSlots.GetValueOrDefault() == 1) { value3.MaxOwnedSlots = 5; value3.Documentation = "Renewable 2-hour blood-buff effects. Default limit: five blood packages."; } cfg.Categories = dictionary9; shopConfigRoot = cfg; if (shopConfigRoot.Buffs == null) { Dictionary<string, PerkShopEntry> dictionary11 = (shopConfigRoot.Buffs = new Dictionary<string, PerkShopEntry>(StringComparer.OrdinalIgnoreCase)); } Dictionary<string, PerkShopEntry> dictionary12 = new Dictionary<string, PerkShopEntry>(StringComparer.OrdinalIgnoreCase); foreach (KeyValuePair<string, PerkShopEntry> buff in cfg.Buffs) { buff.Deconstruct(out key, out var value4); string text3 = key; PerkShopEntry perkShopEntry = value4; if (string.IsNullOrWhiteSpace(text3) || perkShopEntry == null) { continue; } perkShopEntry.DisplayName = (string.IsNullOrWhiteSpace(perkShopEntry.DisplayName) ? text3.Trim() : perkShopEntry.DisplayName.Trim()); perkShopEntry.Category = (string.IsNullOrWhiteSpace(perkShopEntry.Category) ? "misc" : perkShopEntry.Category.Trim()); if (!cfg.Categories.ContainsKey(perkShopEntry.Category)) { perkShopEntry.Category = "misc"; } if (perkShopEntry.Cost <= 0) { perkShopEntry.Cost = 1; } if (cfg.ForcePermanentBuffs) { perkShopEntry.PersistentPurchase = true; perkShopEntry.KeepVisibleTimerFrozen = false; perkShopEntry.VisibleTimerSeconds = 0; perkShopEntry.MutateAppliedBuffLifetime = true; if (IsRenewableTimedCategory(cfg, perkShopEntry.Category)) { perkShopEntry.DurationSeconds = cfg.RenewableTimedBuffSeconds; perkShopEntry.PersistThroughDeath = cfg.RenewableTimedBuffsPersistThroughDeath; } else { perkShopEntry.DurationSeconds = -1; perkShopEntry.PersistThroughDeath = true; } } else { if (perkShopEntry.DurationSeconds < -1) { perkShopEntry.DurationSeconds = -1; } if (perkShopEntry.VisibleTimerSeconds < 0) { perkShopEntry.VisibleTimerSeconds = 0; } if (!perkShopEntry.MutateAppliedBuffLifetime) { perkShopEntry.PersistThroughDeath = false; } } dictionary12[text3.Trim()] = perkShopEntry; } cfg.Buffs = dictionary12; return cfg; } internal static bool IsRenewableTimedCategory(ShopConfigRoot cfg, string category) { string category2 = category; if (cfg == null || !cfg.UseRenewableTimedBuffs || string.IsNullOrWhiteSpace(category2)) { return false; } if (cfg.RenewableTimedBuffCategories != null) { return cfg.RenewableTimedBuffCategories.Any((string c) => string.Equals(c, category2, StringComparison.OrdinalIgnoreCase)); } return false; } internal static int ResolveBuffDuration(ShopConfigRoot cfg, PerkShopEntry entry) { if (cfg != null && entry != null && IsRenewableTimedCategory(cfg, entry.Category)) { return Math.Max(60, cfg.RenewableTimedBuffSeconds); } if (cfg == null || !cfg.ForcePermanentBuffs || (entry.KeepVisibleTimerFrozen && entry.VisibleTimerSeconds > 0)) { return entry.DurationSeconds; } return -1; } internal static bool ResolveBuffPersistThroughDeath(ShopConfigRoot cfg, PerkShopEntry entry) { if (cfg != null && entry != null && IsRenewableTimedCategory(cfg, entry.Category)) { return cfg.RenewableTimedBuffsPersistThroughDeath; } if (cfg == null || !cfg.ForcePermanentBuffs) { return entry.PersistThroughDeath; } return true; } internal static bool PreserveVanillaBuffCleanup(ShopConfigRoot cfg, PerkShopEntry entry) { if (cfg != null && entry != null) { return IsRenewableTimedCategory(cfg, entry.Category); } return false; } private static void EnsureCategory(Dictionary<string, BuffCategoryDefinition> categories, string key, string displayName, string documentation, int? maxOwnedSlots, int? slotFreeCost) { if (!categories.TryGetValue(key, out BuffCategoryDefinition value) || value == null) { categories[key] = new BuffCategoryDefinition { DisplayName = displayName, Documentation = documentation, MaxOwnedSlots = maxOwnedSlots, SlotFreeCost = slotFreeCost }; return; } value.DisplayName = (string.IsNullOrWhiteSpace(value.DisplayName) ? displayName : value.DisplayName.Trim()); value.Documentation = (string.IsNullOrWhiteSpace(value.Documentation) ? documentation : value.Documentation.Trim()); if (value.MaxOwnedSlots.HasValue && value.MaxOwnedSlots.Value <= 0) { value.MaxOwnedSlots = null; } if (value.SlotFreeCost.HasValue && value.SlotFreeCost.Value <= 0) { value.SlotFreeCost = null; } } internal static ShopConfigRoot DefaultConfigForMigration() { return DefaultConfig(); } private static ShopConfigRoot DefaultConfig() { return new ShopConfigRoot { ConfigVersion = 14, Enabled = true, EnableDebugLogging = false, CurrencyPrefab = 576389135, CurrencyName = "Greater Stygian Shards", BlockPurchasesInCombat = true, BlockRemovalsInCombat = true, SaveOwnership = true, ReapplyOwnedBuffsOnLogin = false, ReapplyOwnedBuffsWhenMissing = false, ReapplyCheckIntervalSeconds = 60, ReapplyMaxUsersPerCycle = 5, CarrierFinalizeCheckIntervalSeconds = 0.25f, OwnershipSaveDebounceSeconds = 2f, AutoDetectConfigChanges = false, ConfigFileCheckIntervalSeconds = 5f, PlayerCacheSaveDebounceSeconds = 30f, ForcePermanentBuffs = true, AllowBuffEntityMutation = true, UseRenewableTimedBuffs = true, RenewableTimedBuffSeconds = 7200, RenewableTimedBuffCategories = new List<string> { "potion", "elixir", "blood_buff" }, RenewableTimedBuffsPersistThroughDeath = false, EnableStatShop = true, EnableClientUnsupportedStats = true, UseClientAttributeStatAliases = false, EnableExperimentalBloodBuffs = true, StatCarrierBuffPrefab = -809648681, EnableStatTypeSlots = true, MaxOwnedStatTypes = 4, StatTypeSlotFreeCost = 500, MaxHealthPurchaseBehavior = "ClampOnly", EnableBuffWhitelist = false, BuffWhitelistPlatformIds = new List<ulong>(), BuffWhitelistNames = new Dictionary<ulong, string>(), EnableStatWhitelist = false, StatWhitelistPlatformIds = new List<ulong>(), StatWhitelistNames = new Dictionary<ulong, string>(), Stats = new Dictionary<string, StatShopEntry>(StringComparer.OrdinalIgnoreCase) { ["MH"] = new StatShopEntry { DisplayName = "Max Health", UnitStat = "MaxHealth", ModificationType = "Add", AttributeCapType = "Uncapped", ValuePerPurchase = 25f, Cost = 500, MaxPurchases = 10, Notes = "Permanently increases Max Health by 25 per purchase." }, ["PP"] = new StatShopEntry { DisplayName = "Physical Power", UnitStat = "PhysicalPower", ModificationType = "Add", AttributeCapType = "Uncapped", ValuePerPurchase = 2f, Cost = 500, MaxPurchases = 10, Notes = "Permanently increases Physical Power by 2 per purchase." }, ["SP"] = new StatShopEntry { DisplayName = "Spell Power", UnitStat = "SpellPower", ModificationType = "Add", AttributeCapType = "Uncapped", ValuePerPurchase = 1f, Cost = 500, MaxPurchases = 10, Notes = "Permanently increases Spell Power by 1 per purchase." }, ["MS"] = new StatShopEntry { DisplayName = "Movement Speed", UnitStat = "MovementSpeed", ModificationType = "MultiplyBaseAdd", AttributeCapType = "Uncapped", ValuePerPurchase = 0.05f, Cost = 500, MaxPurchases = 5, Notes = "Permanently increases Movement Speed by 0.05 per purchase. Uses MultiplyBaseAdd for HUD/stat compatibility." }, ["AS"] = new StatShopEntry { DisplayName = "Primary Attack Speed", UnitStat = "PrimaryAttackSpeed", ModificationType = "Add", AttributeCapType = "Uncapped", ValuePerPurchase = 0.02f, Cost = 500, MaxPurchases = 5, Notes = "Permanently increases Primary Attack Speed by 0.02 per purchase." }, ["phll"] = new StatShopEntry { DisplayName = "Physical Life Leech", UnitStat = "PhysicalLifeLeech", ModificationType = "Add", AttributeCapType = "Uncapped", ValuePerPurchase = 0.02f, Cost = 500, MaxPurchases = 5, Notes = "Permanently increases Physical Life Leech by 0.02 per purchase." }, ["sll"] = new StatShopEntry { DisplayName = "Spell Life Leech", UnitStat = "SpellLifeLeech", ModificationType = "Add", AttributeCapType = "Uncapped", ValuePerPurchase = 0.02f, Cost = 500, MaxPurchases = 5, Notes = "Permanently increases Spell Life Leech by 0.02 per purchase." }, ["prll"] = new StatShopEntry { DisplayName = "Primary Life Leech", UnitStat = "PrimaryLifeLeech", ModificationType = "Add", AttributeCapType = "Uncapped", ValuePerPurchase = 0.03f, Cost = 500, MaxPurchases = 5, Notes = "Permanently increases Primary Life Leech by 0.03 per purchase." }, ["PCC"] = new StatShopEntry { DisplayName = "Physical Critical Strike Chance", UnitStat = "PhysicalCriticalStrikeChance", ModificationType = "Add", AttributeCapType = "Uncapped", ValuePerPurchase = 0.02f, Cost = 500, MaxPurchases = 5, Notes = "Permanently increases Physical Critical Strike Chance by 0.02 per purchase." }, ["PCD"] = new StatShopEntry { DisplayName = "Physical Critical Strike Damage", UnitStat = "PhysicalCriticalStrikeDamage", ModificationType = "Add", AttributeCapType = "Uncapped", ValuePerPurchase = 0.1f, Cost = 500, MaxPurchases = 5, Notes = "Permanently increases Physical Critical Strike Damage by 0.1 per purchase." }, ["SCC"] = new StatShopEntry { DisplayName = "Spell Critical Strike Chance", UnitStat = "SpellCriticalStrikeChance", ModificationType = "Add", AttributeCapType = "Uncapped", ValuePerPurchase = 0.02f, Cost = 500, MaxPurchases = 5, Notes = "Permanently increases Spell Critical Strike Chance by 0.02 per purchase." }, ["SCD"] = new StatShopEntry { DisplayName = "Spell Critical Strike Damage", UnitStat = "SpellCriticalStrikeDamage", ModificationType = "Add", AttributeCapType = "Uncapped", ValuePerPurchase = 0.1f, Cost = 500, MaxPurchases = 5, Notes = "Permanently increases Spell Critical Strike Damage by 0.1 per purchase." }, ["PR"] = new StatShopEntry { DisplayName = "Physical Resistance", UnitStat = "PhysicalResistance", ModificationType = "Add", AttributeCapType = "Uncapped", ValuePerPurchase = 0.02f, Cost = 500, MaxPurchases = 5, Notes = "Permanently increases Physical Resistance by 0.02 per purchase." }, ["SR"] = new StatShopEntry { DisplayName = "Spell Resistance", UnitStat = "SpellResistance", ModificationType = "Add", AttributeCapType = "Uncapped", ValuePerPurchase = 0.02f, Cost = 500, MaxPurchases = 5, Notes = "Permanently increases Spell Resistance by 0.02 per purchase." }, ["HR"] = new StatShopEntry { DisplayName = "Healing Received", UnitStat = "HealingReceived", ModificationType = "Add", AttributeCapType = "Uncapped", ValuePerPurchase = 0.03f, Cost = 500, MaxPurchases = 5, Notes = "Permanently increases Healing Received by 0.03 per purchase." }, ["DR"] = new StatShopEntry { DisplayName = "Damage Reduction", UnitStat = "DamageReduction", ModificationType = "Add", AttributeCapType = "Uncapped", ValuePerPurchase = 0.01f, Cost = 750, MaxPurchases = 5, Notes = "Permanently increases Damage Reduction by 0.01 per purchase." }, ["RY"] = new StatShopEntry { DisplayName = "Resource Yield", UnitStat = "ResourceYield", ModificationType = "Add", AttributeCapType = "Uncapped", ValuePerPurchase = 0.05f, Cost = 500, MaxPurchases = 5, Notes = "Permanently increases Resource Yield by 0.05 per purchase." }, ["RBD"] = new StatShopEntry { DisplayName = "Reduced Blood Drain", UnitStat = "ReducedBloodDrain", ModificationType = "Add", AttributeCapType = "Uncapped", ValuePerPurchase = 0.1f, Cost = 500, MaxPurchases = 5, Notes = "Permanently increases Reduced Blood Drain by 0.1 per purchase." }, ["SCR"] = new StatShopEntry { DisplayName = "Spell Cooldown Recovery Rate", UnitStat = "SpellCooldownRecoveryRate", ModificationType = "Add", AttributeCapType = "Uncapped", ValuePerPurchase = 0.02f, Cost = 500, MaxPurchases = 5, Notes = "Permanently increases Spell Cooldown Recovery Rate by 0.02 per purchase." }, ["WCR"] = new StatShopEntry { DisplayName = "Weapon Cooldown Recovery Rate", UnitStat = "WeaponCooldownRecoveryRate", ModificationType = "Add", AttributeCapType = "Uncapped", ValuePerPurchase = 0.02f, Cost = 500, MaxPurchases = 5, Notes = "Permanently increases Weapon Cooldown Recovery Rate by 0.02 per purchase." }, ["UCR"] = new StatShopEntry { DisplayName = "Ultimate Cooldown Recovery Rate", UnitStat = "UltimateCooldownRecoveryRate", ModificationType = "Add", AttributeCapType = "Uncapped", ValuePerPurchase = 0.04f, Cost = 500, MaxPurchases = 5, Notes = "Permanently increases Ultimate Cooldown Recovery Rate by 0.04 per purchase." }, ["MD"] = new StatShopEntry { DisplayName = "Minion Damage", UnitStat = "MinionDamage", ModificationType = "Add", AttributeCapType = "Uncapped", ValuePerPurchase = 0.05f, Cost = 500, MaxPurchases = 5, Notes = "Permanently increases Minion Damage by 0.05 per purchase." }, ["AAS"] = new StatShopEntry { DisplayName = "Ability Attack Speed", UnitStat = "AbilityAttackSpeed", ModificationType = "Add", AttributeCapType = "Uncapped", ValuePerPurchase = 0.02f, Cost = 500, MaxPurchases = 5, Notes = "Permanently increases Ability Attack Speed by 0.02 per purchase." }, ["CDR"] = new StatShopEntry { DisplayName = "Corruption Damage Reduction", UnitStat = "CorruptionDamageReduction", ModificationType = "Add", AttributeCapType = "Uncapped", ValuePerPurchase = 0.02f, Cost = 500, MaxPurchases = 5, Notes = "Permanently increases Corruption Damage Reduction by 0.02 per purchase." } }, Categories = new Dictionary<string, BuffCategoryDefinition>(StringComparer.OrdinalIgnoreCase) { ["blood_buff"] = new BuffCategoryDefinition { DisplayName = "Blood Buff", Documentation = "Renewable 2-hour blood-buff effects. Default limit: five blood packages.", MaxOwnedSlots = 5, SlotFreeCost = 250 }, ["set_bonus"] = new BuffCategoryDefinition { DisplayName = "Set Bonus", Documentation = "Armor set style bonuses or set-derived effects.", MaxOwnedSlots = null, SlotFreeCost = null }, ["potion"] = new BuffCategoryDefinition { DisplayName = "Potion", Documentation = "Potion and brew-style buffs. Safer for multi-slot ownership.", MaxOwnedSlots = 3, SlotFreeCost = 100 }, ["elixir"] = new BuffCategoryDefinition { DisplayName = "Elixir", Documentation = "Elixir-style buffs. Bloodcraft compatibility default: one owned elixir slot.", MaxOwnedSlots = 1, SlotFreeCost = 500 }, ["misc"] = new BuffCategoryDefinition { DisplayName = "Misc", Documentation = "Fallback category for uncategorized buffs.", MaxOwnedSlots = null, SlotFreeCost = null } }, Buffs = DefaultBuffEntries() }; } internal static Dictionary<string, PerkShopEntry> DefaultBuffEntriesForMigration() { return DefaultBuffEntries(); } private static Dictionary<string, PerkShopEntry> DefaultBuffEntries() { Dictionary<string, PerkShopEntry> obj = new Dictionary<string, PerkShopEntry>(StringComparer.OrdinalIgnoreCase) { ["sun_immunity"] = new PerkShopEntry { DisplayName = "Sun Immunity", Category = "misc", BuffPrefab = 32681348, Cost = 100, PersistentPurchase = true, DurationSeconds = -1, KeepVisibleTimerFrozen = false, VisibleTimerSeconds = 0, PersistThroughDeath = true, MutateAppliedBuffLifetime = false, PreventDuplicate = true, Notes = "Grants complete immunity to sun exposure." }, ["potion_of_rage"] = new PerkShopEntry { DisplayName = "Potion of Rage", Category = "potion", BuffPrefab = -1591883586, Cost = 400, PersistentPurchase = true, DurationSeconds = 7200, KeepVisibleTimerFrozen = false, VisibleTimerSeconds = 0, PersistThroughDeath = false, MutateAppliedBuffLifetime = false, PreventDuplicate = true, Notes = "Applies AB_Consumable_PhysicalPowerPotion_T02_Buff as a renewable 2-hour owned buff." }, ["elixir_of_the_crow"] = new PerkShopEntry { DisplayName = "Elixir of the Crow", Category = "elixir", BuffPrefab = -262239794, Cost = 750, PersistentPurchase = true, DurationSeconds = -1, KeepVisibleTimerFrozen = false, VisibleTimerSeconds = 0, PersistThroughDeath = true, MutateAppliedBuffLifetime = false, PreventDuplicate = true, Notes = "Applies AB_Elixir_Crow_T01_Buff as a renewable 2-hour owned buff." }, ["empty_layout"] = new PerkShopEntry { Enabled = false, DisplayName = "Empty Buff Layout", Category = "misc", BuffPrefab = 32681348, Cost = 100, PersistentPurchase = true, Durati