Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of PraetorisClient v0.1.121
PraetorisClient.dll
Decompiled a day ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Linq.Expressions; using System.Net.Security; using System.Net.Sockets; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Authentication; using System.Security.Cryptography; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using Common; using EpicLoot; using EpicLoot.Abilities; using EpicLoot.Adventure; using EpicLoot.Config; using EpicLoot.MagicItemEffects; using EpicLoot.ShardStones; using EpicLoot.src.Magic.MagicItemEffects.Helpers; using EpicLootAPI; using EpicLootLeslieAlphaTest.src; using EpicLootLeslieAlphaTest.src.MagicEffectss; using EpicLootLeslieAlphaTest.src.StatusEffects; using EpicLootLeslieAlphaTest.src.StatusEffects.VFX; using EpicLootLeslieAlphaTest.src.Utilities; using GUIFramework; using HarmonyLib; using JetBrains.Annotations; using Jotunn; using Jotunn.Configs; using Jotunn.Entities; using Jotunn.Managers; using Jotunn.Utils; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using PraetorisClient.BodyBounties; using PraetorisClient.CreatureOwnership; using PraetorisClient.EpicLootFeature; using PraetorisClient.GuardStoneFeature; using PraetorisClient.Maintenance; using PraetorisClient.NetworkWardFeature; using PraetorisClient.ServerChestFeature; using PraetorisClient.ServerGuideFeature; using PraetorisClient.ShipPasswordFeature; using PraetorisClient.SurtlingBoats; using PraetorisClient.Tombstones; using SoftReferenceableAssets; using Splatform; using TMPro; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.Networking; using UnityEngine.UI; using ValheimRcon; using ValheimRcon.Commands; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("PraetorisClient")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("PraetorisClient")] [assembly: AssemblyCopyright("Copyright © 2023")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("3A303D15-4E60-4110-8EDF-EDF38560FBE2")] [assembly: AssemblyFileVersion("0.1.121")] [assembly: TargetFramework(".NETFramework,Version=v4.8.1", FrameworkDisplayName = ".NET Framework 4.8.1")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.1.121.0")] [module: UnverifiableCode] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } } namespace EpicLootLeslieAlphaTest.src { public static class MagicEffects { public static void Init() { MagicItemEffectDefinition magicItemEffectDefinition = new MagicItemEffectDefinition("AncestralSlam", "Ancestral Slam", "Summons ancestral spirit to mirror your attacks for each enemy hit."); magicItemEffectDefinition.Requirements.AllowedItemTypes.Add("TwoHandedWeapon"); magicItemEffectDefinition.Requirements.AllowedSkillTypes.Add((SkillType)3); Helpers.Add(magicItemEffectDefinition.Requirements.AllowedRarities, ItemRarity.Epic, ItemRarity.Legendary, ItemRarity.Mythic, ItemRarity.Ancient); magicItemEffectDefinition.SelectionWeight = 1f; magicItemEffectDefinition.Register(); MagicItemEffectDefinition magicItemEffectDefinition2 = new MagicItemEffectDefinition("GlancingBlows", "Glancing Blows", "Increases your block value by 50%. Take addtional damage equal to 50% of all blocked or parried hits. This additional damage is only mitigate by player armor and is not reduced from player resistances."); Helpers.Add(magicItemEffectDefinition2.Requirements.AllowedItemTypes, "Bucklers", "RoundShields", "TowerShields", "OneHandedWeapon", "TwoHandedWeapon"); magicItemEffectDefinition2.Requirements.ItemHasBlockPower = true; Helpers.Add(magicItemEffectDefinition2.Requirements.AllowedRarities, ItemRarity.Magic, ItemRarity.Rare, ItemRarity.Epic, ItemRarity.Legendary, ItemRarity.Mythic, ItemRarity.Ancient); magicItemEffectDefinition2.ValuesPerRarity.Magic.Set(1f, 1f); magicItemEffectDefinition2.ValuesPerRarity.Rare = new ValueDef(1f, 1f, 1f); magicItemEffectDefinition2.ValuesPerRarity.Epic = new ValueDef(1f, 1f, 1f); magicItemEffectDefinition2.ValuesPerRarity.Legendary = new ValueDef(1f, 1f, 1f); magicItemEffectDefinition2.ValuesPerRarity.Mythic = new ValueDef(1f, 1f, 1f); magicItemEffectDefinition2.ValuesPerRarity.Ancient = new ValueDef(1f, 1f, 1f); magicItemEffectDefinition2.SelectionWeight = 5f; magicItemEffectDefinition2.Register(); MagicItemEffectDefinition magicItemEffectDefinition3 = new MagicItemEffectDefinition("Retaliation", "Retaliation +{0}", "On block gain a stack of Relatliation. On Attack consumes a stack of Relataliation to increase the attack speed of your next attack string. All stacks of Retaliation expire after 5 seconds. Consuming or gaining a stack of Retaliation resets the expiration timer."); Helpers.Add(magicItemEffectDefinition3.Requirements.AllowedItemTypes, "Bucklers", "RoundShields", "TowerShields", "OneHandedWeapon", "TwoHandedWeapon"); magicItemEffectDefinition3.Requirements.ItemHasBlockPower = true; Helpers.Add(magicItemEffectDefinition3.Requirements.AllowedRarities, ItemRarity.Magic, ItemRarity.Rare, ItemRarity.Epic, ItemRarity.Legendary, ItemRarity.Mythic, ItemRarity.Ancient); magicItemEffectDefinition3.ValuesPerRarity.Magic.Set(1f, 1f); magicItemEffectDefinition3.ValuesPerRarity.Rare = new ValueDef(2f, 2f, 1f); magicItemEffectDefinition3.ValuesPerRarity.Epic = new ValueDef(3f, 3f, 1f); magicItemEffectDefinition3.ValuesPerRarity.Legendary = new ValueDef(4f, 4f, 1f); magicItemEffectDefinition3.ValuesPerRarity.Mythic = new ValueDef(5f, 5f, 1f); magicItemEffectDefinition3.ValuesPerRarity.Ancient = new ValueDef(6f, 6f, 1f); magicItemEffectDefinition3.SelectionWeight = 2f; magicItemEffectDefinition3.Register(); MagicItemEffectDefinition magicItemEffectDefinition4 = new MagicItemEffectDefinition("Infusion", "Infusion +{0}%", "Cleanses certain status effects and adds <b><color=yellow>X</color></b>% of weapon damage as their respective type. Certain cleansed status effects grant addittional bonuses."); Helpers.Add(magicItemEffectDefinition4.Requirements.AllowedItemTypes, "OneHandedWeapon", "TwoHandedWeapon"); List<SkillType> allowedSkillTypes = magicItemEffectDefinition4.Requirements.AllowedSkillTypes; SkillType[] array = new SkillType[8]; RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/); Helpers.Add(allowedSkillTypes, (SkillType[])(object)array); Helpers.Add(magicItemEffectDefinition4.Requirements.AllowedRarities, ItemRarity.Magic, ItemRarity.Rare, ItemRarity.Epic, ItemRarity.Legendary, ItemRarity.Mythic, ItemRarity.Ancient); magicItemEffectDefinition4.ValuesPerRarity.Magic.Set(2f, 4f); magicItemEffectDefinition4.ValuesPerRarity.Rare = new ValueDef(3f, 6f, 1f); magicItemEffectDefinition4.ValuesPerRarity.Epic = new ValueDef(4f, 8f, 1f); magicItemEffectDefinition4.ValuesPerRarity.Legendary = new ValueDef(5f, 10f, 1f); magicItemEffectDefinition4.ValuesPerRarity.Mythic = new ValueDef(6f, 12f, 1f); magicItemEffectDefinition4.ValuesPerRarity.Ancient = new ValueDef(7f, 14f, 1f); magicItemEffectDefinition4.SelectionWeight = 2f; magicItemEffectDefinition4.Ability = "Infusion"; AbilityProxyDefinition abilityProxyDefinition = new AbilityProxyDefinition("Infusion", AbilityActivationMode.Activated, typeof(Infusion.InfusionAbilityProxy)); abilityProxyDefinition.Ability.Cooldown = 120f; PrefabManager.OnPrefabsRegistered += delegate { EpicLoot.RegisterAsset("InfusionIcon", ObjectDB.instance.GetStatusEffect(StringExtensionMethods.GetStableHashCode("SetEffect_MageArmor"))?.m_icon); }; abilityProxyDefinition.Ability.IconAsset = "InfusionIcon"; magicItemEffectDefinition4.Register(); MagicItemEffectDefinition magicItemEffectDefinition5 = new MagicItemEffectDefinition("Whirlwind", "Whirlwind", "Spin"); magicItemEffectDefinition5.Requirements.AllowedItemTypes.Add("TwoHandedWeapon"); magicItemEffectDefinition5.Requirements.AllowedSkillTypes.Add((SkillType)4); Helpers.Add(magicItemEffectDefinition5.Requirements.AllowedRarities, ItemRarity.Epic, ItemRarity.Legendary, ItemRarity.Mythic, ItemRarity.Ancient); magicItemEffectDefinition5.SelectionWeight = 1f; magicItemEffectDefinition5.Register(); MagicItemEffectDefinition magicItemEffectDefinition6 = new MagicItemEffectDefinition("Onslaught", "Onslaught +{0}%", "Grants Onslaught on last hit of an attack string and when hitting a staggered enemy. While in last hit of attack string during Onslaught you continue attacking using the last hit animation of your attack string. Each consecutive Onslaught hit deals {0}% increased damage per hit. Onslaught expires if an enemy has not been struck with an attack in last hit animation for 2 seconds."); Helpers.Add(magicItemEffectDefinition6.Requirements.AllowedItemTypes, "OneHandedWeapon", "TwoHandedWeapon"); magicItemEffectDefinition6.Requirements.AllowedSkillTypes.Add((SkillType)3); Helpers.Add(magicItemEffectDefinition6.Requirements.AllowedRarities, ItemRarity.Magic, ItemRarity.Rare, ItemRarity.Epic, ItemRarity.Legendary, ItemRarity.Mythic, ItemRarity.Ancient); magicItemEffectDefinition6.ValuesPerRarity.Magic.Set(0.5f, 1f, 0.1f); magicItemEffectDefinition6.ValuesPerRarity.Rare = new ValueDef(0.5f, 1.5f, 0.1f); magicItemEffectDefinition6.ValuesPerRarity.Epic = new ValueDef(1f, 2f, 0.1f); magicItemEffectDefinition6.ValuesPerRarity.Legendary = new ValueDef(1.5f, 2.5f, 0.1f); magicItemEffectDefinition6.ValuesPerRarity.Mythic = new ValueDef(2f, 3f, 0.1f); magicItemEffectDefinition6.ValuesPerRarity.Ancient = new ValueDef(2f, 3f, 0.1f); magicItemEffectDefinition6.SelectionWeight = 1f; magicItemEffectDefinition6.Register(); } } } namespace EpicLootLeslieAlphaTest.src.Utilities { public static class Help { public static void CopyFieldsFrom<T, V>(this T target, V source) where T : Humanoid where V : Humanoid { Dictionary<string, FieldInfo> dictionary = typeof(T).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).ToDictionary((FieldInfo f) => f.Name); FieldInfo[] fields = typeof(V).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { if (dictionary.TryGetValue(fieldInfo.Name, out var value) && value.FieldType.IsAssignableFrom(fieldInfo.FieldType)) { value.SetValue(target, fieldInfo.GetValue(source)); } } } public static void Add<T>(this List<T> list, params T[] values) { list.AddRange(values); } public static void Remove<T>(this GameObject prefab) where T : Component { T val = default(T); if (prefab.TryGetComponent<T>(ref val)) { Object.DestroyImmediate((Object)(object)val); } } } public class HumanoidFactory { public static GameObject playerAncestor; public static void Create() { //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_0218: Unknown result type (might be due to invalid IL or missing references) //IL_027d: Unknown result type (might be due to invalid IL or missing references) //IL_0256: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)playerAncestor != (Object)null) { return; } GameObject playerPrefab = Game.instance.m_playerPrefab; bool activeSelf = playerPrefab.activeSelf; playerPrefab.SetActive(false); try { Player component = playerPrefab.GetComponent<Player>(); playerAncestor = Object.Instantiate<GameObject>(playerPrefab); playerAncestor.Remove<AbilityController>(); playerAncestor.Remove<AdventureComponent>(); playerAncestor.Remove<Player>(); playerAncestor.Remove<PlayerController>(); playerAncestor.Remove<Talker>(); playerAncestor.Remove<Skills>(); playerAncestor.Remove<ZSyncTransform>(); ((Object)playerAncestor).name = "playerAncestorHashString"; playerAncestor.GetComponent<ZNetView>().m_persistent = false; playerAncestor.GetComponent<ZNetView>().m_type = (ObjectType)0; Humanoid obj = playerAncestor.AddComponent<Humanoid>(); obj.CopyFieldsFrom<Humanoid, Player>(component); ((Character)obj).m_animator = playerAncestor.GetComponentInChildren<Animator>(); ((Character)obj).m_zanim = playerAncestor.GetComponent<ZSyncAnimation>(); ((Character)obj).m_body = playerAncestor.GetComponent<Rigidbody>(); ((Character)obj).m_collider = playerAncestor.GetComponent<CapsuleCollider>(); obj.m_visEquipment = playerAncestor.GetComponent<VisEquipment>(); ((Character)obj).m_turnSpeed = 0f; ((Character)obj).m_animEvent = playerAncestor.GetComponentInChildren<CharacterAnimEvent>(); ((Character)obj).m_eye = Utils.FindChild(playerAncestor.transform, "EyePos", (IterativeSearchType)0); if ((Object)(object)playerAncestor.GetComponent<ZSyncAnimation>() != (Object)null) { playerAncestor.GetComponent<ZSyncAnimation>().m_animator = playerAncestor.GetComponent<Animator>(); playerAncestor.GetComponent<ZSyncAnimation>().m_nview = playerAncestor.GetComponent<ZNetView>(); } Rigidbody component2 = playerAncestor.GetComponent<Rigidbody>(); if ((Object)(object)component2 != (Object)null) { component2.isKinematic = false; component2.useGravity = false; component2.constraints = (RigidbodyConstraints)126; } Shader shader = Shader.Find("Sprites/Default"); Renderer[] componentsInChildren = playerAncestor.GetComponentsInChildren<Renderer>(true); foreach (Renderer val in componentsInChildren) { Material[] materials = val.materials; if (materials.Length != 0) { materials[0].shader = shader; materials[0].mainTexture = null; materials[0].color = new Color(0.3f, 0.5f, 0.9f, 0.03f); if (materials.Length > 1) { materials[1].shader = shader; materials[1].mainTexture = null; materials[1].color = new Color(0.3f, 0.5f, 0.9f, 0f); } materials[0].SetColor("_SkinColor", new Color(0.3f, 0.5f, 0.9f, 0.03f)); val.materials = materials; } } componentsInChildren = playerAncestor.GetComponentsInChildren<Renderer>(true); foreach (Renderer val2 in componentsInChildren) { SkinnedMeshRenderer val3 = (SkinnedMeshRenderer)(object)((val2 is SkinnedMeshRenderer) ? val2 : null); if (val3 != null && (Object)(object)val3.sharedMesh != (Object)null) { _ = ((Object)val3.sharedMesh).name; } else if (val2 is MeshRenderer) { MeshFilter component3 = ((Component)val2).GetComponent<MeshFilter>(); if (component3 != null && (Object)(object)component3.sharedMesh != (Object)null) { _ = ((Object)component3.sharedMesh).name; } } } Collider[] componentsInChildren2 = playerAncestor.GetComponentsInChildren<Collider>(); for (int i = 0; i < componentsInChildren2.Length; i++) { componentsInChildren2[i].enabled = false; } ZNetScene.instance.m_namedPrefabs[StringExtensionMethods.GetStableHashCode("playerAncestorHashString")] = playerAncestor; playerAncestor.SetActive(false); } finally { playerPrefab.SetActive(activeSelf); } } } public static class WA { public static bool IsHeavyWeapon(ItemData item) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Invalid comparison between Unknown and I4 //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Invalid comparison between Unknown and I4 //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Invalid comparison between Unknown and I4 //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Invalid comparison between Unknown and I4 AnimationState animationState = item.m_shared.m_animationState; if ((int)animationState != 8 && (int)animationState != 2 && (int)animationState != 7) { return (int)animationState == 13; } return true; } public static bool IsRangedWeapon(ItemData item) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Invalid comparison between Unknown and I4 //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Invalid comparison between Unknown and I4 //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Invalid comparison between Unknown and I4 //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Invalid comparison between Unknown and I4 AnimationState animationState = item.m_shared.m_animationState; if ((int)animationState != 3 && (int)animationState != 10 && (int)animationState != 12) { return (int)animationState == 14; } return true; } public static bool IsTwoHanded(ItemData item) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Invalid comparison between Unknown and I4 //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Invalid comparison between Unknown and I4 //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Invalid comparison between Unknown and I4 //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Invalid comparison between Unknown and I4 //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Invalid comparison between Unknown and I4 //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Invalid comparison between Unknown and I4 //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Invalid comparison between Unknown and I4 AnimationState animationState = item.m_shared.m_animationState; if ((int)animationState != 8 && (int)animationState != 2 && (int)animationState != 7 && (int)animationState != 13 && (int)animationState != 15 && (int)animationState != 11) { return (int)animationState == 0; } return true; } } } namespace EpicLootLeslieAlphaTest.src.StatusEffects { public static class SERegistry { public static void RegisterStatusEffects() { Register<SE_Retaliation>("SE_Retaliation"); Register<SE_Onslaught>("SE_Onslaught"); Register<SE_FireInfused>("SE_FireInfused"); Register<SE_FrostInfused>("SE_FrostInfused"); Register<SE_LightningInfused>("SE_LightningInfused"); Register<SE_PoisonInfused>("SE_PoisonInfused"); Register<SE_WindInfused>("SE_WindInfused"); } private static void Register<T>(string effectName) where T : StatusEffect { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected O, but got Unknown T val = ScriptableObject.CreateInstance<T>(); ((Object)(object)val).name = effectName; ((StatusEffect)val).m_name = effectName; ItemManager.Instance.AddStatusEffect(new CustomStatusEffect((StatusEffect)(object)val, false)); } } public class SE_FireInfused : SE_Stats { public const string EffectName = "SE_FireInfused"; private GameObject m_vfxInstance; private GameObject m_vfxInstance2; private ItemData weapon; private float infusedFireDamage; public float Efficacy { get; set; } public override void Setup(Character character) { //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) ((SE_Stats)this).Setup(character); if (!((Object)(object)Player.m_localPlayer == (Object)null)) { Efficacy = Infusion.Efficacy; GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("SwordDyrnwyn"); object icon; if (itemPrefab == null) { icon = null; } else { ItemDrop component = itemPrefab.GetComponent<ItemDrop>(); icon = ((component != null) ? component.m_itemData.m_shared.m_icons[0] : null); } ((StatusEffect)this).m_icon = (Sprite)icon; ((StatusEffect)this).m_name = "Fire Infused"; ((StatusEffect)this).m_ttl = 120f; Player localPlayer = Player.m_localPlayer; weapon = ((localPlayer != null) ? ((Humanoid)localPlayer).GetCurrentWeapon() : null); if (weapon != null && !WA.IsRangedWeapon(weapon)) { DamageTypes damage = weapon.GetDamage(); float totalDamage = ((DamageTypes)(ref damage)).GetTotalDamage(); float num = (WA.IsHeavyWeapon(weapon) ? 2f : 1f); Logger.LogWarning((object)string.Format("[FireInfused] weapon base damage:{0} efficacy:{1} weapon multiplier:{2} added:{3} + {4} * {5}", totalDamage, Efficacy, num, totalDamage * Player.m_localPlayer.GetTotalActiveMagicEffectValue("Infusion", 0.01f), Player.m_localPlayer.GetTotalActiveMagicEffectValue("Infusion", 0.01f) * Efficacy, num)); infusedFireDamage += totalDamage * Player.m_localPlayer.GetTotalActiveMagicEffectValue("Infusion", 0.01f) + Efficacy * Player.m_localPlayer.GetTotalActiveMagicEffectValue("Infusion", 0.1f) * num; weapon.m_shared.m_damages.m_fire += infusedFireDamage; Logger.LogWarning((object)$"Added {infusedFireDamage} fire damage"); } } } public override void Stop() { ((StatusEffect)this).Stop(); if (weapon != null) { weapon.m_shared.m_damages.m_fire -= infusedFireDamage; } if ((Object)(object)m_vfxInstance != (Object)null) { Object.Destroy((Object)(object)m_vfxInstance); } if ((Object)(object)m_vfxInstance2 != (Object)null) { Object.Destroy((Object)(object)m_vfxInstance2); } } public override void UpdateStatusEffect(float dt) { ((SE_Stats)this).UpdateStatusEffect(dt); if (!((Object)(object)Player.m_localPlayer == (Object)null)) { if (((Humanoid)Player.m_localPlayer).GetCurrentWeapon() == weapon && (Object)(object)m_vfxInstance == (Object)null) { CreateVFX(); } else if (((Humanoid)Player.m_localPlayer).GetCurrentWeapon() != weapon && (Object)(object)m_vfxInstance != (Object)null) { DestroyVFX(); } } } private void CreateVFX() { VisEquipment visEquipment = ((Humanoid)Player.m_localPlayer).m_visEquipment; if (!WA.IsTwoHanded(weapon)) { m_vfxInstance = Object.Instantiate<GameObject>(InfusionVFX.FireInfusionVFX, visEquipment.m_rightHand); m_vfxInstance.SetActive(true); return; } m_vfxInstance = Object.Instantiate<GameObject>(InfusionVFX.FireInfusionVFX, visEquipment.m_rightHand); m_vfxInstance2 = Object.Instantiate<GameObject>(InfusionVFX.FireInfusionVFX, visEquipment.m_leftHand); m_vfxInstance.SetActive(true); m_vfxInstance2.SetActive(true); } private void DestroyVFX() { if ((Object)(object)m_vfxInstance != (Object)null) { Object.Destroy((Object)(object)m_vfxInstance); m_vfxInstance = null; } if ((Object)(object)m_vfxInstance2 != (Object)null) { Object.Destroy((Object)(object)m_vfxInstance2); m_vfxInstance2 = null; } } } public class SE_FrostInfused : SE_Stats { public const string EffectName = "SE_FrostInfused"; private GameObject m_vfxInstance; private GameObject m_vfxInstance2; private ItemData weapon; private float infusedFrostDamage; public float Efficacy { get; set; } public override void Setup(Character character) { //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) ((SE_Stats)this).Setup(character); if (!((Object)(object)Player.m_localPlayer == (Object)null)) { Efficacy = Infusion.Efficacy; GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("SwordMistwalker"); object icon; if (itemPrefab == null) { icon = null; } else { ItemDrop component = itemPrefab.GetComponent<ItemDrop>(); icon = ((component != null) ? component.m_itemData.m_shared.m_icons[0] : null); } ((StatusEffect)this).m_icon = (Sprite)icon; ((StatusEffect)this).m_name = "Frost Infused"; ((StatusEffect)this).m_ttl = 120f; Player localPlayer = Player.m_localPlayer; weapon = ((localPlayer != null) ? ((Humanoid)localPlayer).GetCurrentWeapon() : null); if (weapon != null && !WA.IsRangedWeapon(weapon)) { DamageTypes damage = weapon.GetDamage(); float totalDamage = ((DamageTypes)(ref damage)).GetTotalDamage(); float num = (WA.IsHeavyWeapon(weapon) ? 2f : 1f); Logger.LogWarning((object)string.Format("[FrostInfused] weapon base damage:{0} efficacy:{1} weapon multiplier:{2} added:{3} + {4} * {5}", totalDamage, Efficacy, num, totalDamage * Player.m_localPlayer.GetTotalActiveMagicEffectValue("Infusion", 0.01f), Player.m_localPlayer.GetTotalActiveMagicEffectValue("Infusion", 0.01f) * Efficacy, num)); infusedFrostDamage += totalDamage * Player.m_localPlayer.GetTotalActiveMagicEffectValue("Infusion", 0.01f) + Efficacy * Player.m_localPlayer.GetTotalActiveMagicEffectValue("Infusion", 0.1f) * num; weapon.m_shared.m_damages.m_frost += infusedFrostDamage; Logger.LogWarning((object)$"Added {infusedFrostDamage} frost damage"); } } } public override void Stop() { ((StatusEffect)this).Stop(); if (weapon != null) { weapon.m_shared.m_damages.m_frost -= infusedFrostDamage; } if ((Object)(object)m_vfxInstance != (Object)null) { Object.Destroy((Object)(object)m_vfxInstance); } if ((Object)(object)m_vfxInstance2 != (Object)null) { Object.Destroy((Object)(object)m_vfxInstance2); } } public override void UpdateStatusEffect(float dt) { ((SE_Stats)this).UpdateStatusEffect(dt); if (!((Object)(object)Player.m_localPlayer == (Object)null)) { if (((Humanoid)Player.m_localPlayer).GetCurrentWeapon() == weapon && (Object)(object)m_vfxInstance == (Object)null) { CreateVFX(); } else if (((Humanoid)Player.m_localPlayer).GetCurrentWeapon() != weapon && (Object)(object)m_vfxInstance != (Object)null) { DestroyVFX(); } } } private void CreateVFX() { VisEquipment visEquipment = ((Humanoid)Player.m_localPlayer).m_visEquipment; if (!WA.IsTwoHanded(weapon)) { m_vfxInstance = Object.Instantiate<GameObject>(InfusionVFX.FrostInfusionVFX, visEquipment.m_rightHand); m_vfxInstance.SetActive(true); return; } m_vfxInstance = Object.Instantiate<GameObject>(InfusionVFX.FrostInfusionVFX, visEquipment.m_rightHand); m_vfxInstance2 = Object.Instantiate<GameObject>(InfusionVFX.FrostInfusionVFX, visEquipment.m_leftHand); m_vfxInstance.SetActive(true); m_vfxInstance2.SetActive(true); } private void DestroyVFX() { if ((Object)(object)m_vfxInstance != (Object)null) { Object.Destroy((Object)(object)m_vfxInstance); m_vfxInstance = null; } if ((Object)(object)m_vfxInstance2 != (Object)null) { Object.Destroy((Object)(object)m_vfxInstance2); m_vfxInstance2 = null; } } } public class SE_LightningInfused : SE_Stats { public const string EffectName = "SE_LightningInfused"; private GameObject m_vfxInstance; private GameObject m_vfxInstance2; private ItemData weapon; private float infusedLightningDamage; public float Efficacy { get; set; } public override void Setup(Character character) { //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) ((SE_Stats)this).Setup(character); if (!((Object)(object)Player.m_localPlayer == (Object)null)) { Efficacy = Infusion.Efficacy; GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("SwordNiedhoggLightning"); object icon; if (itemPrefab == null) { icon = null; } else { ItemDrop component = itemPrefab.GetComponent<ItemDrop>(); icon = ((component != null) ? component.m_itemData.m_shared.m_icons[0] : null); } ((StatusEffect)this).m_icon = (Sprite)icon; ((StatusEffect)this).m_name = "Lightning Infused"; ((StatusEffect)this).m_ttl = 120f; Player localPlayer = Player.m_localPlayer; weapon = ((localPlayer != null) ? ((Humanoid)localPlayer).GetCurrentWeapon() : null); if (weapon != null && !WA.IsRangedWeapon(weapon)) { DamageTypes damage = weapon.GetDamage(); float totalDamage = ((DamageTypes)(ref damage)).GetTotalDamage(); float num = (WA.IsHeavyWeapon(weapon) ? 2f : 1f); Logger.LogWarning((object)string.Format("[LightningInfused] weapon base damage:{0} efficacy:{1} weapon multiplier:{2} added:{3} + {4} * {5}", totalDamage, Efficacy, num, totalDamage * Player.m_localPlayer.GetTotalActiveMagicEffectValue("Infusion", 0.01f), Player.m_localPlayer.GetTotalActiveMagicEffectValue("Infusion", 0.01f) * Efficacy, num)); infusedLightningDamage += totalDamage * Player.m_localPlayer.GetTotalActiveMagicEffectValue("Infusion", 0.01f) + Efficacy * Player.m_localPlayer.GetTotalActiveMagicEffectValue("Infusion", 0.1f) * num; weapon.m_shared.m_damages.m_lightning += infusedLightningDamage; Logger.LogWarning((object)$"Added {infusedLightningDamage} lightning damage"); } } } public override void Stop() { ((StatusEffect)this).Stop(); if (weapon != null) { weapon.m_shared.m_damages.m_lightning -= infusedLightningDamage; } if ((Object)(object)m_vfxInstance != (Object)null) { Object.Destroy((Object)(object)m_vfxInstance); } if ((Object)(object)m_vfxInstance2 != (Object)null) { Object.Destroy((Object)(object)m_vfxInstance2); } } public override void UpdateStatusEffect(float dt) { ((SE_Stats)this).UpdateStatusEffect(dt); if (!((Object)(object)Player.m_localPlayer == (Object)null)) { if (((Humanoid)Player.m_localPlayer).GetCurrentWeapon() == weapon && (Object)(object)m_vfxInstance == (Object)null) { CreateVFX(); } else if (((Humanoid)Player.m_localPlayer).GetCurrentWeapon() != weapon && (Object)(object)m_vfxInstance != (Object)null) { DestroyVFX(); } } } private void CreateVFX() { VisEquipment visEquipment = ((Humanoid)Player.m_localPlayer).m_visEquipment; if (!WA.IsTwoHanded(weapon)) { m_vfxInstance = Object.Instantiate<GameObject>(InfusionVFX.LightningInfusionVFX, visEquipment.m_rightHand); m_vfxInstance.SetActive(true); return; } m_vfxInstance = Object.Instantiate<GameObject>(InfusionVFX.LightningInfusionVFX, visEquipment.m_rightHand); m_vfxInstance2 = Object.Instantiate<GameObject>(InfusionVFX.LightningInfusionVFX, visEquipment.m_leftHand); m_vfxInstance.SetActive(true); m_vfxInstance2.SetActive(true); } private void DestroyVFX() { if ((Object)(object)m_vfxInstance != (Object)null) { Object.Destroy((Object)(object)m_vfxInstance); m_vfxInstance = null; } if ((Object)(object)m_vfxInstance2 != (Object)null) { Object.Destroy((Object)(object)m_vfxInstance2); m_vfxInstance2 = null; } } } public class SE_Onslaught : SE_Stats { public const string EffectName = "SE_Onslaught"; public int m_stacks; public int m_maxStacks = 9999; private float m_onslaughtHits; private const float m_expire = 2f; public override void Setup(Character character) { ((SE_Stats)this).Setup(character); if ((Object)(object)character.m_animator != (Object)null) { GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("MaceIron"); object icon; if (itemPrefab == null) { icon = null; } else { ItemDrop component = itemPrefab.GetComponent<ItemDrop>(); icon = ((component != null) ? component.m_itemData.m_shared.m_icons[0] : null); } ((StatusEffect)this).m_icon = (Sprite)icon; ((StatusEffect)this).m_name = "Onslaught Hits"; ((StatusEffect)this).m_flashIcon = true; m_stacks++; } } public void AddStack() { ((StatusEffect)this).m_ttl = 2f; if (m_stacks < m_maxStacks) { m_stacks++; ((StatusEffect)this).m_time = 0f; } } public void ConsumeStack() { if (m_stacks > 0) { m_stacks--; ((StatusEffect)this).m_time = 0f; } } public override void UpdateStatusEffect(float dt) { ((SE_Stats)this).UpdateStatusEffect(dt); Player localPlayer = Player.m_localPlayer; if (!WA.IsRangedWeapon((localPlayer != null) ? ((Humanoid)localPlayer).GetCurrentWeapon() : null)) { if (m_stacks <= 0 && !((StatusEffect)this).m_character.InAttack()) { ((StatusEffect)this).m_character.GetSEMan().RemoveStatusEffect((StatusEffect)(object)this, false); } if (!((Character)Player.m_localPlayer).InAttack() && ((StatusEffect)this).m_time > 1.5f) { m_onslaughtHits = 0f; } } } public override void ModifyAttack(SkillType skill, ref HitData hitData) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) ((SE_Stats)this).ModifyAttack(skill, ref hitData); Logger.LogWarning((object)$"stacks{m_stacks}"); Character character = ((StatusEffect)this).m_character; int? num = ((Humanoid)(((character is Humanoid) ? character : null)?)).m_currentAttack?.m_currentAttackCainLevel; if (m_stacks > 0 && !(num < 2)) { m_onslaughtHits += 1f; Logger.LogWarning((object)$"{m_onslaughtHits}"); float num2 = 1f + m_onslaughtHits * Player.m_localPlayer.GetTotalActiveMagicEffectValue("Onslaught", 0.01f); hitData.ApplyModifier(num2); } } public override string GetIconText() { return $"{m_onslaughtHits}"; } } public class SE_PoisonInfused : SE_Stats { public const string EffectName = "SE_PoisonInfused"; private GameObject m_vfxInstance; private GameObject m_vfxInstance2; private ItemData weapon; private float infusedPoisonDamage; public float Efficacy { get; set; } public override void Setup(Character character) { //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) ((SE_Stats)this).Setup(character); if (!((Object)(object)Player.m_localPlayer == (Object)null)) { Efficacy = Infusion.Efficacy; GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("SwordNiedhoggNature"); object icon; if (itemPrefab == null) { icon = null; } else { ItemDrop component = itemPrefab.GetComponent<ItemDrop>(); icon = ((component != null) ? component.m_itemData.m_shared.m_icons[0] : null); } ((StatusEffect)this).m_icon = (Sprite)icon; ((StatusEffect)this).m_name = "Poison Infused"; ((StatusEffect)this).m_ttl = 120f; Player localPlayer = Player.m_localPlayer; weapon = ((localPlayer != null) ? ((Humanoid)localPlayer).GetCurrentWeapon() : null); if (weapon != null && !WA.IsRangedWeapon(weapon)) { DamageTypes damage = weapon.GetDamage(); float totalDamage = ((DamageTypes)(ref damage)).GetTotalDamage(); float num = (WA.IsHeavyWeapon(weapon) ? 2f : 1f); Logger.LogWarning((object)string.Format("[PoisonInfused] weapon base damage:{0} efficacy:{1} weapon multiplier:{2} added:{3} + {4} * {5}", totalDamage, Efficacy, num, totalDamage * Player.m_localPlayer.GetTotalActiveMagicEffectValue("Infusion", 0.01f), Player.m_localPlayer.GetTotalActiveMagicEffectValue("Infusion", 0.01f) * Efficacy, num)); infusedPoisonDamage += totalDamage * Player.m_localPlayer.GetTotalActiveMagicEffectValue("Infusion", 0.01f) + Efficacy * Player.m_localPlayer.GetTotalActiveMagicEffectValue("Infusion", 0.1f) * num; weapon.m_shared.m_damages.m_poison += infusedPoisonDamage; Logger.LogWarning((object)$"Added {infusedPoisonDamage} poison damage"); } } } public override void Stop() { ((StatusEffect)this).Stop(); if (weapon != null) { weapon.m_shared.m_damages.m_poison -= infusedPoisonDamage; } if ((Object)(object)m_vfxInstance != (Object)null) { Object.Destroy((Object)(object)m_vfxInstance); } if ((Object)(object)m_vfxInstance2 != (Object)null) { Object.Destroy((Object)(object)m_vfxInstance2); } } public override void UpdateStatusEffect(float dt) { ((SE_Stats)this).UpdateStatusEffect(dt); if (!((Object)(object)Player.m_localPlayer == (Object)null)) { if (((Humanoid)Player.m_localPlayer).GetCurrentWeapon() == weapon && (Object)(object)m_vfxInstance == (Object)null) { CreateVFX(); } else if (((Humanoid)Player.m_localPlayer).GetCurrentWeapon() != weapon && (Object)(object)m_vfxInstance != (Object)null) { DestroyVFX(); } } } private void CreateVFX() { VisEquipment visEquipment = ((Humanoid)Player.m_localPlayer).m_visEquipment; if (!WA.IsTwoHanded(weapon)) { m_vfxInstance = Object.Instantiate<GameObject>(InfusionVFX.PoisonInfusionVFX, visEquipment.m_rightHand); m_vfxInstance.SetActive(true); return; } m_vfxInstance = Object.Instantiate<GameObject>(InfusionVFX.PoisonInfusionVFX, visEquipment.m_rightHand); m_vfxInstance2 = Object.Instantiate<GameObject>(InfusionVFX.PoisonInfusionVFX, visEquipment.m_leftHand); m_vfxInstance.SetActive(true); m_vfxInstance2.SetActive(true); } private void DestroyVFX() { if ((Object)(object)m_vfxInstance != (Object)null) { Object.Destroy((Object)(object)m_vfxInstance); m_vfxInstance = null; } if ((Object)(object)m_vfxInstance2 != (Object)null) { Object.Destroy((Object)(object)m_vfxInstance2); m_vfxInstance2 = null; } } } public class SE_Retaliation : SE_Stats { public const string EffectName = "SE_Retaliation"; public int m_stacks; public int m_maxStacks = 1; private const float m_expire = 5f; private float baseSpeed = 1f; private bool addedRetaliationSpeed; private bool amAttacking; private static readonly Dictionary<string, float> _animSpeeds = new Dictionary<string, float> { { "atgeir_attack", 1.2f }, { "atgeir_secondary", 2f }, { "knife_stab", 1.2f }, { "knife_secondary", 1.1f }, { "battleaxe_attack", 1.6f }, { "swing_axe", 1.2f }, { "swing_longsword", 1.2f }, { "mace_secondary", 1.35f }, { "spear_poke", 1.2f }, { "sword_secondary", 0.5f }, { "dualaxes", 1.1f }, { "dualaxes_secondary", 1.1f }, { "dual_knives", 1.1f }, { "dual_knives_secondary", 1.2f }, { "greatsword", 1.2f }, { "greatsword_secondary", 1.2f }, { "swing_sledge", 1.4f }, { "unarmed_attack", 2.2f } }; public override void Setup(Character character) { ((SE_Stats)this).Setup(character); if ((Object)(object)character.m_animator != (Object)null) { m_maxStacks = Mathf.RoundToInt(Player.m_localPlayer.GetTotalActiveMagicEffectValue("Retaliation")); baseSpeed = character.m_animator.speed; character.m_animator.speed = ((Retaliation.AttackType != null && _animSpeeds.TryGetValue(Retaliation.AttackType, out var value)) ? value : 1.2f); GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("ShieldBanded"); object icon; if (itemPrefab == null) { icon = null; } else { ItemDrop component = itemPrefab.GetComponent<ItemDrop>(); icon = ((component != null) ? component.m_itemData.m_shared.m_icons[3] : null); } ((StatusEffect)this).m_icon = (Sprite)icon; ((StatusEffect)this).m_name = "Retaliation"; ((StatusEffect)this).m_flashIcon = true; } } public void AddStack() { ((StatusEffect)this).m_ttl = 5f; if (m_stacks < m_maxStacks) { m_stacks++; ((StatusEffect)this).m_time = 0f; } } public void ConsumeStack() { if (m_stacks > 0) { m_stacks--; ((StatusEffect)this).m_time = 0f; } } public override void UpdateStatusEffect(float dt) { ((SE_Stats)this).UpdateStatusEffect(dt); Player localPlayer = Player.m_localPlayer; if (!WA.IsRangedWeapon((localPlayer != null) ? ((Humanoid)localPlayer).GetCurrentWeapon() : null)) { if (!((StatusEffect)this).m_character.InAttack() && amAttacking) { ConsumeStack(); } amAttacking = ((StatusEffect)this).m_character.InAttack(); if ((Object)(object)((StatusEffect)this).m_character.m_animator != (Object)null && ((StatusEffect)this).m_character.m_animator.speed >= 5f && !addedRetaliationSpeed) { Animator animator = ((StatusEffect)this).m_character.m_animator; animator.speed += ((Retaliation.AttackType != null && _animSpeeds.TryGetValue(Retaliation.AttackType, out var value)) ? value : 1.2f); addedRetaliationSpeed = true; } if ((Object)(object)((StatusEffect)this).m_character.m_animator != (Object)null && ((StatusEffect)this).m_character.m_animator.speed < 5f) { ((StatusEffect)this).m_character.m_animator.speed = ((Retaliation.AttackType != null && _animSpeeds.TryGetValue(Retaliation.AttackType, out var value2)) ? value2 : 1.2f); } if (m_stacks > 0 && !((StatusEffect)this).m_character.InAttack()) { ((StatusEffect)this).m_character.m_animator.speed = baseSpeed; } if (m_stacks <= 0 && !((StatusEffect)this).m_character.InAttack()) { ((StatusEffect)this).m_character.GetSEMan().RemoveStatusEffect((StatusEffect)(object)this, false); } } } public override void Stop() { ((StatusEffect)this).Stop(); if ((Object)(object)((StatusEffect)this).m_character?.m_animator != (Object)null) { ((StatusEffect)this).m_character.m_animator.speed = baseSpeed; } } public override string GetIconText() { return $"{m_stacks}"; } } public class SE_WindInfused : SE_Stats { public const string EffectName = "SE_WindInfused"; private GameObject m_vfxInstance; private GameObject m_vfxInstance2; private ItemData weapon; private float infusedWindValue; private float baseSpeed = 1f; public override void Setup(Character character) { ((SE_Stats)this).Setup(character); if (!((Object)(object)Player.m_localPlayer == (Object)null)) { weapon = ((Humanoid)Player.m_localPlayer).GetCurrentWeapon(); baseSpeed = ((StatusEffect)this).m_character.m_animator.speed; ((StatusEffect)this).m_name = "Wind Infused"; ((StatusEffect)this).m_ttl = 120f; } } public override void Stop() { ((StatusEffect)this).Stop(); ((StatusEffect)this).m_character.m_animator.speed = baseSpeed; if ((Object)(object)m_vfxInstance != (Object)null) { Object.Destroy((Object)(object)m_vfxInstance); } if ((Object)(object)m_vfxInstance2 != (Object)null) { Object.Destroy((Object)(object)m_vfxInstance2); } } public override void UpdateStatusEffect(float dt) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) ((SE_Stats)this).UpdateStatusEffect(dt); if (!((Object)(object)Player.m_localPlayer == (Object)null)) { _ = ((Humanoid)Player.m_localPlayer).m_visEquipment.m_rightItemInstance; _ = weapon.m_shared.m_animationState; _ = ((Humanoid)Player.m_localPlayer).m_visEquipment.m_rightHand; _ = ((Humanoid)Player.m_localPlayer).m_visEquipment.m_leftHand; if (((Character)Player.m_localPlayer).InAttack() && ((Humanoid)Player.m_localPlayer).GetCurrentWeapon() == weapon && baseSpeed <= 3f * baseSpeed) { Animator animator = ((StatusEffect)this).m_character.m_animator; float num = (animator.speed += Player.m_localPlayer.GetTotalActiveMagicEffectValue("Infusion", 0.01f)); } if (((Humanoid)Player.m_localPlayer).GetCurrentWeapon() == weapon && (Object)(object)m_vfxInstance == (Object)null) { CreateVFX(); } else if (((Humanoid)Player.m_localPlayer).GetCurrentWeapon() != weapon && (Object)(object)m_vfxInstance != (Object)null) { DestroyVFX(); } } } private void CreateVFX() { VisEquipment visEquipment = ((Humanoid)Player.m_localPlayer).m_visEquipment; if (!WA.IsTwoHanded(weapon)) { m_vfxInstance = Object.Instantiate<GameObject>(InfusionVFX.WindInfusionVFX, visEquipment.m_rightHand); m_vfxInstance.SetActive(true); PlayAll(m_vfxInstance); return; } m_vfxInstance = Object.Instantiate<GameObject>(InfusionVFX.WindInfusionVFX, visEquipment.m_rightHand); m_vfxInstance2 = Object.Instantiate<GameObject>(InfusionVFX.WindInfusionVFX, visEquipment.m_leftHand); m_vfxInstance.SetActive(true); m_vfxInstance2.SetActive(true); PlayAll(m_vfxInstance); PlayAll(m_vfxInstance2); } private void PlayAll(GameObject vfx) { //IL_0047: 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_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) Light componentInChildren = vfx.GetComponentInChildren<Light>(); if ((Object)(object)componentInChildren != (Object)null) { ((Component)componentInChildren).gameObject.SetActive(false); } ParticleSystem[] componentsInChildren = vfx.GetComponentsInChildren<ParticleSystem>(); foreach (ParticleSystem val in componentsInChildren) { if ((Object)(object)((Component)val).transform == (Object)(object)vfx.transform) { val.Stop(); continue; } MainModule main = val.main; ((MainModule)(ref main)).loop = true; ((MainModule)(ref main)).duration = 2f; ((MainModule)(ref main)).startColor = MinMaxGradient.op_Implicit(new Color(0.7607843f, 1f, 0.57254905f, 1f)); val.Play(); } } private void DestroyVFX() { if ((Object)(object)m_vfxInstance != (Object)null) { Object.Destroy((Object)(object)m_vfxInstance); m_vfxInstance = null; } if ((Object)(object)m_vfxInstance2 != (Object)null) { Object.Destroy((Object)(object)m_vfxInstance2); m_vfxInstance2 = null; } } } } namespace EpicLootLeslieAlphaTest.src.StatusEffects.VFX { public static class InfusionVFX { public static GameObject FireInfusionVFX; public static GameObject FrostInfusionVFX; public static GameObject LightningInfusionVFX; public static GameObject PoisonInfusionVFX; public static GameObject WindInfusionVFX; public static void Init() { FireInfusionVFX = BuildVFX(ZNetScene.instance.GetPrefab("Torch"), 0.1f); FrostInfusionVFX = BuildVFX(ZNetScene.instance.GetPrefab("vfx_Freezing"), 0.3f); LightningInfusionVFX = BuildVFX(ZNetScene.instance.GetPrefab("fx_Lightning"), 0.2f); PoisonInfusionVFX = BuildVFX(ZNetScene.instance.GetPrefab("vfx_Poison"), 0.2f); WindInfusionVFX = BuildVFX(ZNetScene.instance.GetPrefab("vfx_MeadSwimmer"), 0.08f); } private static GameObject BuildVFX(GameObject source, float scale) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown //IL_0081: 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_00a1: 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) if ((Object)(object)source == (Object)null) { return null; } GameObject val = new GameObject("InfusionVFX_" + ((Object)source).name); val.SetActive(false); Object.DontDestroyOnLoad((Object)(object)val); if ((Object)(object)val.GetComponent<TimedDestruction>() != (Object)null) { Object.Destroy((Object)(object)val.GetComponent<TimedDestruction>()); } ParticleSystem[] componentsInChildren = source.GetComponentsInChildren<ParticleSystem>(true); foreach (ParticleSystem val2 in componentsInChildren) { if (!((Object)(object)((Component)val2).transform == (Object)(object)source.transform)) { GameObject obj = Object.Instantiate<GameObject>(((Component)val2).gameObject, val.transform); obj.transform.localPosition = Vector3.zero; obj.transform.localRotation = Quaternion.identity; Transform transform = obj.transform; transform.localScale *= scale; } } return val; } } } namespace EpicLootLeslieAlphaTest.src.MagicEffectss { public class AncestralSlam { [HarmonyPatch(typeof(Attack), "OnAttackTrigger")] public class Attack_Trigger { public static void Prefix(Attack __instance) { if (__instance.m_character is Player) { _lastAttackCainLevel = __instance.m_currentAttackCainLevel; _lastAttackAnim = __instance.m_attackAnimation; _lastAttackRange = __instance.m_attackRange; _lastAttack = __instance; } } } [HarmonyPatch(typeof(Character), "Damage")] public class Character_Damage_SpawnClone_Patch { public static void Postfix(Character __instance, HitData hit) { //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Invalid comparison between Unknown and I4 //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Invalid comparison between Unknown and I4 //IL_00dc: 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_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Invalid comparison between Unknown and I4 //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Invalid comparison between Unknown and I4 //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Invalid comparison between Unknown and I4 //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Invalid comparison between Unknown and I4 //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Invalid comparison between Unknown and I4 //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Invalid comparison between Unknown and I4 //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Invalid comparison between Unknown and I4 //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Invalid comparison between Unknown and I4 //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Invalid comparison between Unknown and I4 if (((Humanoid)Player.m_localPlayer).GetCurrentWeapon() == null) { return; } ItemData currentWeapon = ((Humanoid)Player.m_localPlayer).GetCurrentWeapon(); if (currentWeapon == null || !currentWeapon.HasMagicEffect("AncestralSlam")) { return; } Character attacker = hit.GetAttacker(); if (!((Object)(object)attacker == (Object)null) && !((Object)(object)attacker != (Object)(object)Player.m_localPlayer) && !ancestralClonesHASH.Contains(((Component)attacker).gameObject)) { SkillType skill = hit.m_skill; if ((int)skill == 1 || (int)skill == 3 || (int)skill == 2 || (int)skill == 11 || (int)skill == 7 || (int)skill == 4 || (int)skill == 5 || (int)skill == 8 || (int)skill == 14 || (int)skill == 9 || (int)skill == 10) { Vector3 position = ((Component)__instance).transform.position; Vector3 val = position - ((Component)Player.m_localPlayer).transform.position; Vector3 normalized = ((Vector3)(ref val)).normalized; SpawnAncestralSlam(rotation: (normalized != Vector3.zero) ? Quaternion.LookRotation(normalized) : ((Component)Player.m_localPlayer).transform.rotation, position: position - normalized * 1.5f, weapon: currentWeapon); } } } } [HarmonyPatch(typeof(Character), "Damage")] public class Character_Clone_Damage_Patch { public static void Prefix(Character __instance, HitData hit) { //IL_005a: 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_0085: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Player.m_localPlayer == (Object)null) { return; } ItemData currentWeapon = ((Humanoid)Player.m_localPlayer).GetCurrentWeapon(); if (currentWeapon == null || !currentWeapon.HasMagicEffect("AncestralSlam")) { return; } Character attacker = hit.GetAttacker(); if (!((Object)(object)attacker == (Object)null) && ancestralClonesHASH.Contains(((Component)attacker).gameObject)) { float skillFactor = ((Character)Player.m_localPlayer).GetSkillFactor(currentWeapon.m_shared.m_skillType); float num = Random.Range(0.96f, 1.04f); DamageTypes damage = ((DamageTypes)(ref currentWeapon.m_shared.m_damages)).Clone(); if (_lastAttackCainLevel >= 2) { ((DamageTypes)(ref damage)).Modify((skillFactor * 0.75f + 0.25f) * 2f * num); } else { ((DamageTypes)(ref damage)).Modify((skillFactor * 0.75f + 0.25f) * num); } hit.m_damage = damage; } } } [HarmonyPatch(typeof(EnemyHud), "ShowHud")] internal class EnemyHud_ShowHud_Patch { private static bool Prefix(Character c) { return !ancestralClonesHASH.Contains(((Component)c).gameObject); } } private const float damageModifier = 1f; private static int _lastAttackCainLevel = 0; private static string _lastAttackAnim = ""; private static Attack _lastAttack = null; private static float _lastAttackRange = 2f; public static readonly HashSet<GameObject> ancestralClonesHASH = new HashSet<GameObject>(); internal static void SpawnAncestralSlam(Vector3 position, Quaternion rotation, ItemData weapon) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0064: 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_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)HumanoidFactory.playerAncestor == (Object)null)) { GameObject val = Object.Instantiate<GameObject>(HumanoidFactory.playerAncestor, position, rotation); ancestralClonesHASH.Add(val); val.SetActive(true); bool flag = weapon.m_shared.m_secondaryAttack.m_attackAnimation == _lastAttackAnim; Humanoid component = val.GetComponent<Humanoid>(); component.m_rightItem = weapon.Clone(); val.GetComponent<Animator>(); ((Character)component).m_lookDir = rotation * Vector3.forward; ((Character)component).StartAttack((Character)null, flag); if (component.m_currentAttack != null) { Attack currentAttack = component.m_currentAttack; currentAttack.m_attackRange *= 1.2f; component.m_currentAttack.m_currentAttackCainLevel = _lastAttackCainLevel; ((Character)component).m_zanim.SetTrigger(_lastAttackAnim + _lastAttackCainLevel); } ((MonoBehaviour)component).StartCoroutine(DelayedDestruction(val.GetComponent<ZNetView>(), val, 2.5f)); } } private static IEnumerator DelayedDestruction(ZNetView zview, GameObject go, float attackTime) { yield return (object)new WaitForSeconds(attackTime + 0.5f); Character component = go.GetComponent<Character>(); if ((Object)(object)component != (Object)null) { EnemyHud instance = EnemyHud.instance; if (instance != null) { instance.RemoveCharacterHud(component); } Character.Instances.Remove((IMonoUpdater)(object)component); } ancestralClonesHASH.Remove(go); if ((Object)(object)zview != (Object)null && zview.IsOwner()) { ZNetScene.instance.Destroy(((Component)zview).gameObject); } } } public static class GlancingBlows { [HarmonyPatch(typeof(Humanoid), "BlockAttack")] public class Block_Attack_GlancingBlowsBlockAttackPrefix_Patch { public static void Prefix(HitData hit) { initialHitValue = hit.GetTotalBlockableDamage(); } } [HarmonyPatch(typeof(Humanoid), "BlockAttack")] public class Block_Attack_GlancingBlowsBlockAttackPostfix_Patch { public static void Postfix(HitData hit, Character attacker) { //IL_0082: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)Player.m_localPlayer) && Player.m_localPlayer.HasActiveMagicEffect("GlancingBlows", out var _)) { float num = initialHitValue * 0.5f; float health = ((Character)Player.m_localPlayer).GetHealth(); float num2 = (float)Math.Round(num - ((Character)Player.m_localPlayer).GetBodyArmor(), 2); if (!((Character)Player.m_localPlayer).IsDead() && num2 > 0f) { ((Character)Player.m_localPlayer).SetHealth(health - num2); DamageText.instance.ShowText((TextType)0, ((Component)Player.m_localPlayer).transform.position, num2.ToString(), true); } } } } [HarmonyPatch(typeof(ItemData), "GetBlockPower", new Type[] { typeof(int), typeof(float) })] public class GlancingBlowsDamage_Patch { public static void Postfix(float skillFactor, ref float __result) { if (Object.op_Implicit((Object)(object)Player.m_localPlayer) && Player.m_localPlayer.HasActiveMagicEffect("GlancingBlows", out var _)) { __result *= 1.5f; } } } private static float effectValue = 1f; private static float initialHitValue = 0f; } public static class Infusion { public enum InfusionElement { Fire, Frost, Lightning, Poison, Wind, Blood } public class InfusionAbilityProxy : Proxy { public static IEnumerator RechargeAnimation(Player player, float duration) { ((Character)player).m_zanim.SetBool("recharge_lightningstaff", true); yield return (object)new WaitForSeconds(duration); ((Character)player).m_zanim.SetBool("recharge_lightningstaff", false); ((Character)player).m_zanim.SetTrigger("recharge_lightningstaff_done"); } public override void Activate() { if (IsOnCooldown() || (Object)(object)Player.m_localPlayer == (Object)null) { return; } SetCooldownEndTime(Proxy.GetTime() + Cooldown); List<StatusEffect> list = new List<StatusEffect>(((Character)Player.m_localPlayer).GetSEMan().GetStatusEffects()); isInfused = false; Dictionary<InfusionElement, float> dictionary = new Dictionary<InfusionElement, float>(); List<StatusEffect> list2 = new List<StatusEffect>(); for (int num = list.Count - 1; num >= 0; num--) { string text = ((Object)list[num]).name.ToLower(); foreach (var (value, key) in elementMap) { if (text.Contains(value)) { float efficacy = GetEfficacy(list[num]); list2.Add(list[num]); if (!dictionary.ContainsKey(key) || efficacy > dictionary[key]) { dictionary[key] = efficacy; } break; } } } ((MonoBehaviour)Player.m_localPlayer).StartCoroutine(DeferRemoveAndApply(list2, dictionary)); static IEnumerator DeferRemoveAndApply(List<StatusEffect> toRemove, Dictionary<InfusionElement, float> bestEfficacy) { foreach (StatusEffect item in toRemove) { ((Character)Player.m_localPlayer).GetSEMan().RemoveStatusEffect(item, false); } yield return null; foreach (KeyValuePair<InfusionElement, float> item2 in bestEfficacy) { Efficacy = item2.Value; ((Character)Player.m_localPlayer).GetSEMan().AddStatusEffect(StringExtensionMethods.GetStableHashCode($"SE_{item2.Key}Infused"), false, 0, 0f, (short)(-1)); isInfused = true; } if (isInfused) { ((MonoBehaviour)Player.m_localPlayer).StartCoroutine(RechargeAnimation(Player.m_localPlayer, 1f)); } if (!isInfused) { ((Character)Player.m_localPlayer).m_zanim.SetTrigger("emote_shrug"); } } } } private static bool isInfused = false; private static readonly List<(string keyword, InfusionElement element)> elementMap = new List<(string, InfusionElement)> { ("fire", InfusionElement.Fire), ("burn", InfusionElement.Fire), ("frost", InfusionElement.Frost), ("freez", InfusionElement.Frost), ("lightning", InfusionElement.Lightning), ("shock", InfusionElement.Lightning), ("poison", InfusionElement.Poison), ("wind", InfusionElement.Wind), ("aero", InfusionElement.Wind), ("moder", InfusionElement.Wind), ("leaf", InfusionElement.Wind), ("petal", InfusionElement.Wind), ("blood", InfusionElement.Blood) }; private static readonly Dictionary<string, Func<StatusEffect, float>> efficacyType = new Dictionary<string, Func<StatusEffect, float>> { { "Burning", (StatusEffect se) => ((SE_Burning)se).m_fireDamageLeft }, { "Frost", (StatusEffect se) => ((StatusEffect)(SE_Frost)se).m_ttl * 10f }, { "Lightning", (StatusEffect se) => (!(se.m_ttl > 0f) || ((Character)Player.m_localPlayer).m_lastHit == null) ? 0f : ((Character)Player.m_localPlayer).m_lastHit.m_damage.m_lightning }, { "Poison", (StatusEffect se) => ((SE_Poison)se).m_damageLeft } }; public static float Efficacy; private static float GetEfficacy(StatusEffect se) { Logger.LogWarning((object)$"GetEfficacy: {((Object)se).name} found {efficacyType.ContainsKey(((Object)se).name)}"); if (efficacyType.TryGetValue(((Object)se).name, out Func<StatusEffect, float> value)) { return value(se); } return 0f; } } public static class Onslaught { [HarmonyPatch(typeof(Character), "Damage")] public class Attack_Trigger_Patch { public static void Prefix(Character __instance, HitData hit) { Character attacker = hit.GetAttacker(); Humanoid val = (Humanoid)(object)((attacker is Humanoid) ? attacker : null); if (!((Object)(object)val == (Object)null) && !((Object)(object)Player.m_localPlayer != (Object)(object)val) && ((Humanoid)Player.m_localPlayer).GetCurrentWeapon().HasMagicEffect("Onslaught") && ((val.m_currentAttack?.m_currentAttackCainLevel ?? (-1)) >= 2 || __instance.IsStaggering())) { SEMan sEMan = ((Character)Player.m_localPlayer).GetSEMan(); SE_Onslaught sE_Onslaught = sEMan.GetStatusEffect(StringExtensionMethods.GetStableHashCode("SE_Onslaught")) as SE_Onslaught; if ((Object)(object)sE_Onslaught != (Object)null) { sE_Onslaught.AddStack(); } else { (sEMan.AddStatusEffect(StringExtensionMethods.GetStableHashCode("SE_Onslaught"), false, 0, 0f, (short)(-1)) as SE_Onslaught).AddStack(); } } } } [HarmonyPatch(typeof(Attack), "Start")] public class Attack_Modify_Chain_Patch { public static void Postfix(Attack __instance, Humanoid character, ref bool __result) { if (!((Object)(object)character != (Object)(object)Player.m_localPlayer)) { SE_Onslaught sE_Onslaught = ((Character)character).GetSEMan().GetStatusEffect(StringExtensionMethods.GetStableHashCode("SE_Onslaught")) as SE_Onslaught; if (!((Object)(object)sE_Onslaught == (Object)null) && sE_Onslaught.m_stacks > 0 && character.GetCurrentWeapon().HasMagicEffect("Onslaught")) { __instance.m_currentAttackCainLevel = 2; __instance.m_zanim.SetTrigger(__instance.m_attackAnimation + "2"); sE_Onslaught.ConsumeStack(); } } } } } public static class PerfectStrike { [HarmonyPatch(typeof(Attack), "Start")] public static class Attack_PerfectStrikeAnimation_Patch { public static void Postfix(Attack __instance, Humanoid character) { if (!((Object)(object)character != (Object)(object)Player.m_localPlayer) && Player.m_localPlayer.HasActiveMagicEffect("PerfectStrike", out var _)) { if (isPerfectStrike) { int lastPerfectChain = ((_lastPerfectChain == 2) ? 1 : 2); __instance.m_currentAttackCainLevel = 2; __instance.m_zanim.SetTrigger(__instance.m_attackAnimation + lastPerfectChain); _lastPerfectChain = lastPerfectChain; _clicked = false; isPerfectStrike = false; _perfectDamagePending = true; } _currentAttack = __instance; _currentAnim = __instance.m_attackAnimation; _currentChain = __instance.m_currentAttackCainLevel; _currentAnimSpeed = __instance.m_speedFactor; _attackStartTime = Time.time; } } } [HarmonyPatch(typeof(PlayerController), "FixedUpdate")] public static class Input_AntiSpam_Patch { public static void Postfix(PlayerController __instance) { //IL_0295: Unknown result type (might be due to invalid IL or missing references) //IL_01e2: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__instance.m_character != (Object)(object)Player.m_localPlayer || !Player.m_localPlayer.HasActiveMagicEffect("PerfectStrike", out var _)) { return; } if (!((Character)Player.m_localPlayer).InAttack()) { _clicked = false; _windowSFXplayed = false; if ((Object)(object)_windowVFXInstance != (Object)null) { Object.Destroy((Object)(object)_windowVFXInstance); _windowVFXInstance = null; } return; } float value = 0f; float num = 0f; float num2 = 0f; bool flag = _currentAnim != null && _attackDeltas.TryGetValue((_currentAnim, _currentChain, "default"), out value); if (flag) { num = value * 0.5f; num2 = value * 0.75f; } float num3 = Time.time - _attackStartTime; bool flag2 = _currentChain == 0 && _currentAnim != null && !_currentAnim.Contains("secondary"); if (flag && !flag2) { bool flag3 = num3 >= num && num3 <= num2; if (flag3 && (Object)(object)_windowVFXInstance == (Object)null) { if ((Object)(object)_cleanVFXPrefab == (Object)null) { GameObject prefab = ZNetScene.instance.GetPrefab("vfx_kiln_addore"); if ((Object)(object)prefab != (Object)null) { bool activeSelf = prefab.activeSelf; prefab.SetActive(false); _cleanVFXPrefab = Object.Instantiate<GameObject>(prefab); prefab.SetActive(activeSelf); Object.DestroyImmediate((Object)(object)_cleanVFXPrefab.GetComponent<ZNetView>()); } } if ((Object)(object)_cleanVFXPrefab != (Object)null) { GameObject rightItemInstance = ((Humanoid)Player.m_localPlayer).m_visEquipment.m_rightItemInstance; Transform val = (((Object)(object)rightItemInstance != (Object)null) ? rightItemInstance.transform : ((Humanoid)Player.m_localPlayer).m_visEquipment.m_rightHand); _windowVFXInstance = Object.Instantiate<GameObject>(_cleanVFXPrefab, val); _windowVFXInstance.SetActive(true); _windowVFXInstance.transform.localPosition = Vector3.zero; } } else if (!flag3 && (Object)(object)_windowVFXInstance != (Object)null) { Object.Destroy((Object)(object)_windowVFXInstance); _windowVFXInstance = null; } } if (ZInput.GetButtonDown("Attack")) { _nowClicked = Time.time; if (!_clicked) { _clicked = true; if (flag) { isPerfectStrike = num3 >= num && num3 <= num2; } } else { _ = isPerfectStrike; } } if (isPerfectStrike && !_windowSFXplayed && !flag2) { GameObject prefab2 = ZNetScene.instance.GetPrefab("sfx_perfectblock"); if ((Object)(object)prefab2 != (Object)null) { Object.Instantiate<GameObject>(prefab2, ((Component)Player.m_localPlayer).transform).transform.localPosition = Vector3.zero; _windowSFXplayed = true; } } if (Time.time - _nowClicked >= _clickSpam) { _clicked = false; } } } [HarmonyPatch(typeof(Character), "Damage")] public static class PerfectStrike_Damage_Patch { public static void Postfix(Character __instance, HitData hit) { if (_perfectDamagePending) { ((DamageTypes)(ref hit.m_damage)).Modify(Player.m_localPlayer.GetTotalActiveMagicEffectValue("PerfectStrike", 0.01f)); _perfectDamagePending = false; _windowSFXplayed = false; } } } private static float _attackStartTime; private static float _clickSpam = 0.25f; private static float _nowClicked; private static bool _clicked = false; private static bool _windowSFXplayed = false; private static int _lastPerfectChain = 0; private static bool isPerfectStrike = false; private static bool _perfectDamagePending = false; private static Attack _currentAttack; private static string _currentAnim; private static int _currentChain; private static float _currentAnimSpeed; private static GameObject _cleanVFXPrefab; private static GameObject _windowVFXInstance; private static readonly Dictionary<(string, int, string), float> _attackDeltas = new Dictionary<(string, int, string), float> { { ("battleaxe_attack", 0, "default"), 0.878f }, { ("battleaxe_attack", 0, "fast"), 0.599f }, { ("battleaxe_attack", 1, "default"), 1.04f }, { ("battleaxe_attack", 1, "fast"), 0.761f }, { ("battleaxe_attack", 2, "default"), 1.304f }, { ("battleaxe_attack", 2, "fast"), 1.078f }, { ("battleaxe_secondary", 0, "default"), 0.843f }, { ("greatsword", 0, "default"), 1.642f }, { ("greatsword", 0, "fast"), 1.258f }, { ("greatsword", 1, "default"), 0.839f }, { ("greatsword", 1, "fast"), 0.579f }, { ("greatsword", 2, "default"), 0.958f }, { ("greatsword", 2, "fast"), 0.682f }, { ("greatsword_secondary", 0, "default"), 2.125f }, { ("greatsword_secondary", 0, "fast"), 1.703f }, { ("swing_longsword", 0, "default"), 1.018f } }; } public static class Retaliation { [HarmonyPatch(typeof(Humanoid), "BlockAttack")] public class Block_Attack_Retaliate_Patch { public static void Postfix(Humanoid __instance, bool __result) { if (Player.m_localPlayer.HasActiveMagicEffect("Retaliation", out var _) && __result && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer)) { SEMan sEMan = ((Character)__instance).GetSEMan(); SE_Retaliation sE_Retaliation = sEMan.GetStatusEffect(StringExtensionMethods.GetStableHashCode("SE_Retaliation")) as SE_Retaliation; if ((Object)(object)sE_Retaliation != (Object)null) { sE_Retaliation.AddStack(); } else { (sEMan.AddStatusEffect(StringExtensionMethods.GetStableHashCode("SE_Retaliation"), false, 0, 0f, (short)(-1)) as SE_Retaliation).AddStack(); } } } } [HarmonyPatch(typeof(Attack), "Start")] public class Humanoid_Attack_Patch { private static void Prefix(Attack __instance, Humanoid character) { if (!((Object)(object)character != (Object)(object)Player.m_localPlayer) && Player.m_localPlayer.HasActiveMagicEffect("Retaliation", out var _)) { AttackType = __instance.m_attackAnimation; } } } public static string AttackType; } public static class Whirlwind { [HarmonyPatch(typeof(CharacterAnimEvent), "CustomFixedUpdate")] [HarmonyPriority(0)] public static class Whirlwind_CharAnim_CFU_Patch { public static void Postfix(CharacterAnimEvent __instance) { //IL_0073: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__instance.m_character == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)__instance.m_character != (Object)(object)Player.m_localPlayer) { return; } InWhirlwind = ((Humanoid)Player.m_localPlayer).GetCurrentWeapon().HasMagicEffect("Whirlwind") && (Object)(object)__instance.m_character != (Object)null && __instance.m_character.InAttack() && __instance.m_character.m_secondaryAttackHold && ((Humanoid)__instance.m_character).m_currentAttack.m_attackAnimation == "atgeir_secondary"; if (InWhirlwind) { if (__instance.m_animator.speed > 1f && __instance.m_animator.speed != 10f) { overFlowAnimSpeed = Mathf.Clamp(__instance.m_animator.speed - 2f, 0f, 0.4f); } __instance.m_animator.speed = 10f; WhirlwindDamage = true; __instance.m_character.m_attack = false; __instance.m_character.m_attackHold = false; } else if (!__instance.m_character.InAttack()) { WhirlwindDamage = false; overFlowAnimSpeed = 0f; } } } [HarmonyPatch(typeof(Character), "Damage")] public static class Whirlwind_Damage_Patch { public static void Prefix(Character __instance, HitData hit) { if (!((Object)(object)hit.GetAttacker() != (Object)(object)Player.m_localPlayer) && InWhirlwind) { ((DamageTypes)(ref hit.m_damage)).Modify(0.4f * (1f + overFlowAnimSpeed)); hit.m_staggerMultiplier = 1f; } } } [HarmonyPatch(typeof(Attack), "GetAttackStamina")] public static class Whirlwind_Stamina_Patch { public static void Postfix(ref float __result) { if (InWhirlwind) { __result *= 0.3f; } } } private static bool InWhirlwind; private static bool WhirlwindDamage; private static float overFlowAnimSpeed; } } namespace EpicLootAPI { [Serializable] [PublicAPI] public enum AbilityActivationMode { Passive, Triggerable, Activated } [Serializable] [PublicAPI] public enum AbilityAction { Custom, StatusEffect } [Serializable] [PublicAPI] public class AbilityDefinition { public string ID = ""; public string IconAsset = ""; public AbilityActivationMode ActivationMode; public float Cooldown; public AbilityAction Action; public List<string> ActionParams = new List<string>(); internal static readonly Method API_AddAbility = new Method("AddAbility"); internal static readonly Method API_UpdateAbility = new Method("UpdateAbility"); internal static List<AbilityDefinition> Abilities = new List<AbilityDefinition>(); [Description("Register a status effect ability which activates on player input")] public AbilityDefinition(string ID, string iconAsset, float cooldown, string statusEffectName) { this.ID = ID; ActivationMode = AbilityActivationMode.Activated; Cooldown = cooldown; Action = AbilityAction.StatusEffect; ActionParams.Add(statusEffectName); IconAsset = iconAsset; Abilities.Add(this); } internal AbilityDefinition(string ID, AbilityActivationMode mode) { this.ID = ID; ActivationMode = mode; } public AbilityDefinition() { } public static void RegisterAll() { foreach (AbilityDefinition item in new List<AbilityDefinition>(Abilities)) { item.Register(); } } public bool Register() { string text = JsonConvert.SerializeObject((object)this); if (!(API_AddAbility.Invoke(text)[0] is string value)) { return false; } RunTimeRegistry.Register(this, value); Abilities.Remove(this); EpicLoot.logger.LogDebug("Registered ability: " + ID); return true; } public bool Update() { if (!RunTimeRegistry.TryGetValue(this, out string value)) { return false; } string text = JsonConvert.SerializeObject((object)this); bool flag = (bool)(API_UpdateAbility.Invoke(value, text)[0] ?? ((object)false)); EpicLoot.logger.LogDebug($"Updated ability {ID}: {flag}"); return flag; } } [PublicAPI] public class AbilityProxyDefinition { public readonly AbilityDefinition Ability; public readonly Dictionary<string, Delegate> Callbacks = new Dictionary<string, Delegate>(); internal static readonly List<AbilityProxyDefinition> ProxyAbilities = new List<AbilityProxyDefinition>(); internal static readonly Method API_RegisterProxyAbility = new Method("RegisterProxyAbility"); internal static readonly Method API_UpdateProxyAbility = new Method("UpdateProxyAbility"); [Description("Register a complex ability behavior using Proxy class")] public AbilityProxyDefinition(string ID, AbilityActivationMode mode, Proxy definition) { Ability = new AbilityDefinition(ID, mode); RegisterCallbacks(definition); ProxyAbilities.Add(this); } [Description("Register a complex ability behavior using Proxy class")] public AbilityProxyDefinition(string ID, AbilityActivationMode mode, Type type) { Ability = new AbilityDefinition(ID, mode); if (!typeof(Proxy).IsAssignableFrom(type)) { EpicLoot.logger.LogError("Ability Proxy " + ID + " Type " + type.Name + " does not implement Proxy class"); return; } try { object obj = Activator.CreateInstance(type); RegisterCallbacks((Proxy)obj); ProxyAbilities.Add(this); } catch (Exception ex) { EpicLoot.logger.LogError("Failed to create instance of " + type.Name + ": " + ex.Message); } } private void RegisterCallbacks(Proxy implementation) { Type type = implementation.GetType(); MethodInfo[] methods = typeof(Proxy).GetMethods(); foreach (MethodInfo methodInfo in methods) { try { Type[] array = (from p in methodInfo.GetParameters() select p.ParameterType).ToArray(); MethodInfo method = type.GetMethod(methodInfo.Name, array); if (method == null) { EpicLoot.logger.LogWarning("Method " + methodInfo.Name + " not found in implementation"); continue; } Type type2 = ((!(methodInfo.ReturnType == typeof(void))) ? Expression.GetFuncType(array.Concat(new Type[1] { methodInfo.ReturnType }).ToArray()) : ((array.Length != 0) ? Expression.GetActionType(array) : typeof(Action))); Delegate value = Delegate.CreateDelegate(type2, implementation, method); Callbacks[methodInfo.Name] = value; } catch (Exception ex) { EpicLoot.logger.LogError("Failed to register callback for method " + methodInfo.Name + ": " + ex.Message); } } } public static void RegisterAll() { foreach (AbilityProxyDefinition item in new List<AbilityProxyDefinition>(ProxyAbilities)) { item.Register(); } } public bool Register() { string text = JsonConvert.SerializeObject((object)Ability); if (!(API_RegisterProxyAbility.Invoke(text, Callbacks)[0] is string value)) { return false; } RunTimeRegistry.Register(this, value); ProxyAbilities.Remove(this); EpicLoot.logger.LogDebug("Registered proxy ability: " + Ability.ID); return true; } public bool Update() { if (!RunTimeRegistry.TryGetValue(this, out string value)) { return false; } string text = JsonConvert.SerializeObject((object)Ability); bool flag = (bool)(API_UpdateProxyAbility.Invoke(value, text, Callbacks)[0] ?? ((object)false)); EpicLoot.logger.LogDebug($"Updated proxy ability: {Ability.ID}, {flag}"); return flag; } } [Description("Functions are converted into delegates when registered to epic loot")] [PublicAPI] public class Proxy { protected Player Player; public string AbilityID = ""; public float Cooldown; public virtual string CooldownEndKey => "EpicLoot." + AbilityID + ".CooldownEnd"; public virtual void Initialize(Player player, string id, float cooldown) { Player = player; AbilityID = id; Cooldown = cooldown; } public virtual void OnUpdate() { } public virtual bool ShouldTrigger() { return false; } public virtual bool IsOnCooldown() { if (HasCooldown()) { return GetTime() < GetCooldownEndTime(); } return false; } public virtual float TimeUntilCooldownEnds() { return Mathf.Max(0f, GetCooldownEndTime() - GetTime()); } public virtual float PercentCooldownComplete() { if (HasCooldown() && IsOnCooldown()) { return 1f - TimeUntilCooldownEnds() / Cooldown; } return 1f; } public virtual bool CanActivate() { return !IsOnCooldown(); } public virtual void TryActivate() { if (CanActivate()) { Activate(); } } public virtual void Activate() { if (HasCooldown()) { float cooldownEndTime = GetTime() + Cooldown; SetCooldownEndTime(cooldownEndTime); } } public virtual void ActivateCustomAction() { } public virtual void ActivateStatusEffectAction() { } public virtual bool HasCooldown() { return Cooldown > 0f; } public virtual void SetCooldownEndTime(float cooldownEndTime) { if (!((Object)(object)Player == (Object)null)) { ((Character)Player).m_nview.GetZDO().Set(CooldownEndKey, cooldownEndTime); } } public virtual float GetCooldownEndTime() { if ((Object)(object)Player == (Object)null) { return 0f; } return ((Character)Player).m_nview.GetZDO().GetFloat(CooldownEndKey, 0f); } public virtual void OnRemoved() { } protected static float GetTime() { return (float)ZNet.instance.GetTimeSeconds(); } } public static class EpicLoot { [PublicAPI] public static class ChangeReason { public const string Enchant = "Enchant"; public const string Augment = "Augment"; public const string Disenchant = "Disenchant"; public const string Rune = "Rune"; public const string Temper = "Temper"; public const string Socket = "Socket"; public const string Unsocket = "Unsocket"; public const string LootRoll = "LootRoll"; public const string Transfer = "Transfer"; public const string AddSocket = "AddSocket"; public const string Unspecified = "Unspecified"; } public static readonly Logger logger = new Logger(); private static readonly Method API_GetTotalActiveMagicEffectValue = new Method("GetTotalActiveMagicEffectValue"); private static readonly Method API_GetTotalActiveMagicEffectValueForWeapon = new Method("GetTotalActiveMagicEffectValueForWeapon"); private static readonly Method API_HasActiveMagicEffect = new Method("HasActiveMagicEffect"); private static readonly Method API_HasActiveMagicEffectOnWeapon = new Method("HasActiveMagicEffectOnWeapon"); private static readonly Method API_GetTotalActiveSetEffectValue = new Method("GetTotalActiveSetEffectValue"); private static readonly Method API_GetAllActiveMagicEffects = new Method("GetAllActiveMagicEffects"); private static readonly Method API_GetAllSetMagicEffects = new Method("GetAllActiveSetMagicEffects"); private static readonly Method API_GetPlayerTotalActiveMagicEffectValue = new Method("GetTotalPlayerActiveMagicEffectValue"); private static readonly Method API_PlayerHasActiveMagicEffect = new Method("PlayerHasActiveMagicEffect"); private static readonly Method API_HasLegendaryItem = new Method("HasLegendaryItem"); private static readonly Method API_HasLegendarySet = new Method("HasLegendarySet"); private static readonly Method API_RegisterAsset = new Method("RegisterAsset"); private static readonly Method API_GetMagicItemJson = new Method("GetMagicItemJson"); private static readonly Method API_RegisterMagicEffectRequirement = new Method("RegisterMagicEffectRequirement", typeof(string), typeof(Func<ItemData, object, string, bool, bool, bool, bool>)); private static readonly Method API_AddMagicItemChangedListener = new Method("AddMagicItemChangedListener", typeof(Action<ItemData, string>)); private static readonly Method API_RemoveMagicItemChangedListener = new Method("RemoveMagicItemChangedListener", typeof(Action<ItemData, string>)); private static readonly Method API_AddLootGeneratedListener = new Method("AddLootGeneratedListener", typeof(Action<ItemData>)); private static readonly Method API_RemoveLootGeneratedListener = new Method("RemoveLootGeneratedListener", typeof(Action<ItemData>)); private static readonly Method API_AddBountyCompletedListener = new Method("AddBountyCompletedListener", typeof(Action<Player, string>)); private static readonly Method API_RemoveBountyCompletedListener = new Method("RemoveBountyCompletedListener", typeof(Action<Player, string>)); private static readonly Method API_TryMakeMagicItem = new Method("TryMakeMagicItem"); private static readonly Method API_RollMagicItemJson = new Method("RollMagicItemJson"); private static readonly Method API_ApplyMagicItemJson = new Method("ApplyMagicItemJson"); private static readonly Method API_GetLuckFactor = new Method("GetLuckFactor"); private static readonly Method API_RollEffectCountForRarity = new Method("RollEffectCountForRarity"); private static readonly Method API_GetLegendaryIDs = new Method("GetLegendaryIDs"); private static readonly Method API_GetLegendaryInfoJson = new Method("GetLegendaryInfoJson"); private static readonly Method API_GetAvailableEffectTypes = new Method("GetAvailableEffectTypes"); private static readonly Method API_AddLootTables = new Method("AddLootTables"); private static readonly Method API_UpdateLootTables = new Method("UpdateLootTables"); private static readonly Method API_RegisterInventoryProvider = new Method("RegisterInventoryProvider", typeof(string), typeof(Func<List<ItemData>>), typeof(Func<string, int>), typeof(Func<string, int, int>), typeof(Func<ItemData, int, int>)); private static readonly Method API_UnregisterInventoryProvider = new Method("UnregisterInventoryProvider"); private static readonly Method API_RegisterEquipmentProvider = new Method("RegisterEquipmentProvider", typeof(string), typeof(Func<Player, List<ItemData>>)); private static readonly Method API_UnregisterEquipmentProvider = new Method("UnregisterEquipmentProvider"); private static readonly Method API_RegisterSacrificeFilter = new Method("RegisterSacrificeFilter", typeof(string), typeof(Func<ItemData, bool>)); private static readonly Method API_UnregisterSacrificeFilter = new Method("UnregisterSacrificeFilter"); private static readonly Method API_InvalidatePlayerEffectCache = new Method("InvalidatePlayerEffectCache"); private static readonly Method API_GetRegisteredProviders = new Method("GetRegisteredProviders"); private static readonly Method API_GetApiVersion = new Method("GetApiVersion"); private static readonly Method API_GetPluginVersion = new Method("GetPluginVersion"); private static readonly Method API_HasEndpoint = new Method("HasEndpoint"); private static readonly Method API_IsMagicItem = new Method("IsMagicItem"); private static readonly Method API_TryGetRarity = new Method("TryGetRarity"); private static readonly Method API_GetRarityCount = new Method("GetRarityCount"); private static readonly Method API_GetRarityColorByIndex = new Method("GetRarityColorByIndex"); private static readonly Method API_GetRarityDisplayNameByIndex = new Method("GetRarityDisplayNameByIndex"); private static readonly Method API_GetItemRarityColor = new Method("GetItemRarityColor"); private static readonly Method API_IsEpicLootItem = new Method("IsEpicLootItem"); private static readonly Method API_IsShardStone = new Method("IsShardStone"); private static readonly Method API_IsRunestone = new Method("IsRunestone"); private static readonly Method API_IsMagicCraftingMaterial = new Method("IsMagicCraftingMaterial"); private static readonly Method API_IsUnidentified = new Method("IsUnidentified"); private static readonly Method API_CanBeMagicItem = new Method("CanBeMagicItem"); private static readonly Method API_ItemHasMagicEffect = new Method("ItemHasMagicEffect"); private static readonly Method API_GetAllMagicEffectTypes = new Method("GetAllMagicEffectTypes"); private static readonly Method API_GetEnchantCostsJson = new Method("GetEnchantCostsJson"); private static readonly Method API_GetSacrificeProductsJson = new Method("GetSacrificeProductsJson"); private static readonly Method API_GetItemDisplayName = new Method("GetItemDisplayName"); private static readonly Method API_GetItemDecoratedName = new Method("GetItemDecoratedName", typeof(ItemData), typeof(string)); private static readonly Method API_ApplyMagicItemBackground = new Method("ApplyMagicItemBackground", typeof(GameObject), typeof(GameObject), typeof(ItemData), typeof(bool)); private static readonly Method API_ApplyMagicItemBackgroundToIcon = new Method("ApplyMagicItemBackgroundToIcon", typeof(GameObject), typeof(ItemData)); [PublicAPI] [Description("Send all your custom conversions, effects, item definitions, etc... to Epic Loot")] public static void RegisterAll() { MaterialConversion.RegisterAll(); MagicItemEffectDefinition.RegisterAll(); AbilityDefinition.RegisterAll(); CustomRecipe.RegisterAll(); Sacrifice.RegisterAll(); TreasureMap.RegisterAll(); SecretStashItem.RegisterAll(); LegendaryInfo.RegisterAll(); LegendarySetInfo.RegisterAll(); BountyTarget.RegisterAll(); AbilityProxyDefinition.RegisterAll(); } [PublicAPI] [Description("Register asset into EpicLoot in order to target them in your definitions")] public static bool RegisterAsset(string name, object asset) { bool flag = (bool)(API_RegisterAsset.Invoke(name, asset)[0] ?? ((object)false)); logger.LogDebug($"Registered asset: {name}, {flag}"); return flag; } [PublicAPI] public static bool RegisterMagicEffectRequirement(string externalRequirement, Func<ItemData, object, string, bool, bool, bool, bool> requirement) { bool flag = (bool)(API_RegisterMagicEffectRequirement.Invoke(externalRequirement, requirement)[0] ?? ((object)false)); logger.LogDebug($"Registered magic effect requirement: {externalRequirement}, {flag}"); return flag; } [PublicAPI] public static bool HasLegendaryItem(this Player player, string legendaryItemID) { bool flag = (bool)(API_HasLegendaryItem.Invoke(player, legendaryItemID)[0] ?? ((object)false)); logger.LogDebug($"Has legendary item: {legendaryItemID}, {flag} "); return flag; } [PublicAPI] public static bool HasLegendarySet(this Player player, string legendarySetID, out int count) { count = 0; object[] array = API_HasLegendarySet.Invoke(player, legendarySetID, count); count = (int)(array[3] ?? ((object)0)); bool flag = (bool)(array[0] ?? ((object)false)); logger.LogDebug($"Has legendary set: {legendarySetID}, {flag}, count: {count}"); return flag; } [PublicAPI] public static bool HasActiveMagicEffectOnWeapon(Player player, ItemData item, string effectType, out float effectValue) { effectValue = 0f; object[] array = API_HasActiveMagicEffectOnWeapon.Invoke(player, item, effectType, effectValue); effectValue = (float)(array[4] ?? ((object)0f)); bool flag = (bool)(array[0] ?? ((object)false)); logger.LogDebug($"Has magic effect on weapon: {effectType}, {flag}, value: {effectValue}"); return flag; } [PublicAPI] public static float GetTotalActiveMagicEffectValue(Player player, ItemData item, string effectType, float scale = 1f) { float num = (float)(API_GetTotalActiveMagicEffectValue.Invoke(player, item, effectType, scale)[0] ?? ((object)0f)); logger.LogDebug($"Total magic effect value: {effectType}, amount: {num}"); return num; } [PublicAPI] public static float GetTotalActiveMagicEffectValueForWeapon(Player player, ItemData item, string effectType, float scale = 1f) { float num = (float)(API_GetTotalActiveMagicEffectValueForWeapon.Invoke(player, item, effectType, scale)[0] ?? ((object)0f)); logger.LogDebug($"Total effect on weapon: {effectType}, amount: {num}"); return num; } [PublicAPI] public static bool HasActiveMagicEffect(Player player, string effectType, ItemData item, out float effectValue) { effectValue = 0f; object[] array = API_HasActiveMagicEffect.Invoke(player, effectType, item, effectValue); effectValue = (float)(array[4] ?? ((object)0f)); bool flag = (bool)(array[0] ?? ((object)false)); logger.LogDebug($"Has active effect: {effectType}, {flag}, value: {effectValue}"); return flag; } [PublicAPI] public static float GetTotalActiveSetEffectValue(Player player, string effectType, float scale = 1f) { float num = (float)(API_GetTotalActiveSetEffectValue.Invoke(player, effectType, scale)[0] ?? ((object)0f)); logger.LogDebug($"Total effect value: {effectType}, amount: {num}"); return num; } [PublicAPI] public static List<MagicItemEffect> GetAllActiveMagicEffects(this Player player, string effectType = null) { List<string> list = (List<string>)(API_GetAllActiveMagicEffects.Invoke(player, effectType)[0] ?? new List<string>()); if (list.Count <= 0) { return new List<MagicItemEffect>(); } List<MagicItemEffect> list2 = new List<MagicItemEffect>(); list2.DeserializeList(list); return list2; } [PublicAPI] public static List<MagicItemEffect> GetAllActiveSetMagicEffects(this Player player, string effectType = null) { List<string> list = (List<string>)(API_GetAllSetMagicEffects.Invoke(player, effectType)[0] ?? new List<string>()); if (list.Count <= 0) { return new List<MagicItemEffect>(); } List<MagicItemEffect> list2 = new List<MagicItemEffect>(); list2.DeserializeList(list); return list2; } private static void DeserializeList<T>(this List<T> output, List<string> input) { foreach (string item in input) { try { T val = JsonConvert.DeserializeObject<T>(item); if (val != null) { output.Add(val); } } catch { logger.LogWarning("Failed to parse " + typeof(T).Name); } } } [PublicAPI] public static float GetTotalActiveMagicEffectValue(this Player player, string effectType, float scale = 1f, ItemData ignoreThisItem = null) { float num = (float)(API_GetPlayerTotalActiveMagicEffectValue.Invoke(player, effectType, scale, ignoreThisItem)[0] ?? ((object)0f)); logger.LogDebug($"Total magic effect: {effectType}, value: {num}"); return num; } [PublicAPI] public static bool HasActiveMagicEffect(this Player player, string effectType, out float effectValue, float scale = 1f, ItemData ignoreThisItem = null) { effectValue = 0f; object[] array = API_PlayerHasActiveMagicEffect.Invoke(player, effectType, effectValue, scale, ignoreThisItem); effectValue = (float)(array[3] ?? ((object)0f)); bool flag = (bool)(array[0] ?? ((object)false)); logger.LogDebug($"Has active magic effect: {effectType}, value: {flag}"); return flag; } [PublicAPI] public static MagicItem GetMagicItem(this ItemData itemData) { string text = (string)(API_GetMagicItemJson.Invoke(itemData)[0] ?? ""); if (string.IsNullOrEmpty(text)) { return null; } return JsonConvert.DeserializeObject<MagicItem>(text); } [PublicAPI] public static bool AddMagicItemChangedListener(Action<ItemData, string> listener) { return (bool)(API_AddMagicItemChangedListener.Invoke(listener)[0] ?? ((object)false)); } [PublicAPI] public static bool RemoveMagicItemChangedListener(Action<ItemData, string> listener) { return (bool)(API_RemoveMagicItemChangedListener.Invoke(listener)[0] ?? ((object)false)); } [PublicAPI] public static bool AddLootGeneratedListener(Action<ItemData> listener) { return (bool)(API_AddLootGeneratedListener.Invoke(listener)[0] ?? ((object)false)); } [PublicAPI] public static bool RemoveLootGeneratedListener(Action<ItemData> listener) { return (bool)(API_RemoveLootGeneratedListener.Invoke(listener)[0] ?? ((object)false)); } [PublicAPI] public static bool AddBountyCompletedListener(Action<Player, string> listener) { return (bool)(API_AddBountyCompletedListener.Invoke(listener)[0] ?? ((object)false)); } [PublicAPI] public static bool RemoveBountyCompletedListener(Action<Player, string> listener) { return (bool)(API_RemoveBountyCompletedListener.Invoke(listener)[0] ?? ((object)false)); } [PublicAPI] public static bool TryMakeMagicItem(this ItemData item, ItemRarity rarity, float luck = 0f, string? legendaryID = null) { bool flag = (bool)(API_TryMakeMagicItem.Invoke(item, (int)rarity, luck, legendaryID ?? "")[0] ?? ((object)false)); logger.LogDebug($"Made magic item: {item?.m_shared?.m_name}, {rarity}, {flag}"); return flag; } [PublicAPI] public static MagicItem? RollMagicItem(ItemRarity rarity, ItemData item, float luck = 0f) { string text = (string)(API_RollMagicItemJson.Invoke((int)rarity, item, luck)[0] ?? ""); if (string.IsNullOrEmpty(text)) { return null; } try { return JsonConvert.DeserializeObject<MagicItem>(text); } catch { logger.LogWarning("Failed to parse rolled magic item"); return null; } } [PublicAPI] public static bool ApplyMagicItem(this ItemData item, MagicItem magicItem) { if (magicItem == null) { return false; } string text = JsonConvert.SerializeObject((object)magicItem); return (bool)(API_ApplyMagicItemJson.Invoke(item, text)[0] ?? ((object)false)); } [PublicAPI] public static float GetLuckFactor(Vector3 position) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) return (float)(API_GetLuckFactor.Invoke(position)[0] ?? ((object)0f)); } [PublicAPI] public static int RollEffectCountForRarity(ItemRarity rarity) { return (int)(API_RollEffectCountForRarity.Invoke((int)rarity)[0] ?? ((object)0)); } [PublicAPI] public static List<string> GetLegendaryIDs(ItemRarity rarity) { return (List<string>)(API_GetLegendaryIDs.Invoke((int)rarity)[0] ?? new List<string>()); } [PublicAPI] public static LegendaryInfo? GetLegendaryInfo(string legendaryID) { string text = (string)(API_GetLegendaryInfoJson.Invoke(legendaryID)[0] ?? ""); if (string.IsNullOrEmpty(text)) { return null; } try { return JsonConvert.DeserializeObject<LegendaryInfo>(text); } catch { logger.LogWarning("Failed to parse legendary info for " + legendaryID); return null; } } [PublicAPI] public static List<string> GetAvailableEffectTypes(this ItemData item, ItemRarity rarity, MagicItem? magicI
ValheimEndgame.dll
Decompiled a day ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using HarmonyLib; using Jotunn; using Jotunn.Configs; using Jotunn.Entities; using Jotunn.Managers; using Jotunn.Utils; using Microsoft.CodeAnalysis; using SoftReferenceableAssets; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.Rendering; using UnityEngine.UI; using YamlDotNet.RepresentationModel; using YamlDotNet.Serialization; using YamlDotNet.Serialization.NamingConventions; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("ValheimEndgame")] [assembly: AssemblyDescription("Configurable endgame dungeon encounters")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("warpalicious")] [assembly: AssemblyProduct("ValheimEndgame")] [assembly: AssemblyCopyright("Copyright © 2026 warpalicious. All rights reserved.")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("40D101FA-4FD5-401D-84E8-AD9A9BCDD7BE")] [assembly: AssemblyFileVersion("1.6.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.6.0.0")] [module: UnverifiableCode] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } } namespace ValheimEndgame { internal sealed class ClearRoomCommand : ConsoleCommand { public override string Name => "ve_clearroom"; public override string Help => "Clear the current encounter room for development: kill its spawned creatures and cancel its remaining spawns."; public override bool IsCheat => true; public override void Run(string[] args) { ClearWave(); } internal static void ClearWave() { Player player = Player.m_localPlayer; if ((Object)(object)player == (Object)null || ((Character)player).IsDead() || ((Character)player).IsTeleporting()) { ((Terminal)Console.instance).AddString("Finish entering the room or respawning first."); return; } RoomRun[] array = RoomRun.Loaded.Where((RoomRun run) => run.State == RoomState.Active && run.ContainsActiveRoom(((Component)player).transform.position) && run.IsParticipant(player.GetPlayerID())).ToArray(); if (array.Length != 1) { ((Terminal)Console.instance).AddString("Stand inside the active room of your encounter."); } else { array[0].ClearRoomForDevelopment(player); } } } internal sealed class EndEncounterCommand : ConsoleCommand { public override string Name => "ve_endencounter"; public override string Help => "Abort your encounter without completion credit and recover belongings. Use inside the room or beside its altar."; public override bool IsCheat => true; public override void Run(string[] args) { Terminal instance = (Terminal)(object)Console.instance; Player player = Player.m_localPlayer; if ((Object)(object)player == (Object)null || ((Character)player).IsDead() || ((Character)player).IsTeleporting()) { instance.AddString("Enter the world and finish respawning or teleporting first."); return; } RoomRun[] array = RoomRun.Loaded.Where((RoomRun run) => (Object)(object)run != (Object)null && run.CanEndEncounter(player)).ToArray(); if (array.Length != 1) { instance.AddString((array.Length == 0) ? "No available encounter here. Stand inside your room or within 6 metres of its altar." : "Multiple encounters match. Move inside the room you want to end."); return; } array[0].EndEncounter(player); instance.AddString("Ending encounter without completion credit. Returning you and recovering belongings beside the Dungeon Master."); } } internal sealed class GiveTabletCommand : ConsoleCommand { public override string Name => "ve_givetablet"; public override string Help => "ve_givetablet <1-15> <theme> [room id]: give yourself one tablet with seven attempts. A room id fixes the tablet to that room."; public override bool IsCheat => true; public override List<string> CommandOptionList() { return AvailableThemes(); } private static List<string> AvailableThemes() { RoomCatalog catalog = Plugin.Instance?.Catalog?.Rooms; if (catalog == null || !RoomAssets.HasLoadedRooms) { return new List<string>(); } return (from id in catalog.EnabledRooms.Where(RoomAssets.HasRoom) select catalog.ThemeFor(id)).Distinct<string>(StringComparer.Ordinal).OrderBy<string, string>((string theme) => theme, StringComparer.Ordinal).ToList(); } private static List<string> ThemeRooms(string theme) { RoomCatalog catalog = Plugin.Instance?.Catalog?.Rooms; if (catalog == null) { return new List<string>(); } return RoomAssets.LoadedRooms.Where((string id) => catalog.ThemeFor(id) == theme).OrderBy<string, string>((string id) => id, StringComparer.Ordinal).ToList(); } private static string? MatchTheme(List<string> themes, string requested) { string text = themes.FirstOrDefault((string value) => value == requested); if (text != null) { return text; } string[] array = themes.Where((string value) => string.Equals(value, requested, StringComparison.OrdinalIgnoreCase)).ToArray(); if (array.Length != 1) { return null; } return array[0]; } [HarmonyPatch(typeof(Terminal), "tabCycle")] [HarmonyPrefix] private static void CompleteTab(Terminal __instance, ref string word, ref List<string> options, bool usePrefix) { CompleteTheme(__instance, ref word, ref options, usePrefix); } [HarmonyPatch(typeof(Terminal), "updateSearch")] [HarmonyPrefix] private static void CompleteSearch(Terminal __instance, ref string word, ref List<string> options, bool usePrefix) { CompleteTheme(__instance, ref word, ref options, usePrefix); } private static void CompleteTheme(Terminal __instance, ref string word, ref List<string> options, bool usePrefix) { if (usePrefix) { return; } string text = ((TMP_InputField)__instance.m_input).text; if (!text.StartsWith("ve_givetablet ", StringComparison.OrdinalIgnoreCase)) { return; } options = new List<string>(); string text2 = text.Substring("ve_givetablet ".Length); int num = text2.IndexOf(' '); if (num >= 0 && int.TryParse(text2.Substring(0, num), NumberStyles.None, CultureInfo.InvariantCulture, out var result) && result >= 1 && result <= 15) { string requested = text2.Substring(num + 1); List<string> list = AvailableThemes(); string text3 = list.FirstOrDefault((string value) => requested.StartsWith(value + " ", StringComparison.OrdinalIgnoreCase)); word = ((text3 == null) ? requested : requested.Substring(text3.Length + 1)); if (text3 == null) { options = list; } else { options = ThemeRooms(text3); } } } public override void Run(string[] args) { Terminal instance = (Terminal)(object)Console.instance; if (args.Length < 2 || !int.TryParse(args[0], NumberStyles.None, CultureInfo.InvariantCulture, out var result) || result < 1 || result > 15) { instance.AddString(((ConsoleCommand)this).Help); return; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { instance.AddString("Enter a world before giving yourself a tablet."); return; } if (Plugin.Instance?.Catalog?.Rooms == null || !RoomAssets.HasLoadedRooms) { instance.AddString("Room configuration and prefabs are not ready."); return; } List<string> list = AvailableThemes(); string text = MatchTheme(list, string.Join(" ", args.Skip(1))); string text2 = null; if (text == null && args.Length > 2) { text = MatchTheme(list, string.Join(" ", args.Skip(1).Take(args.Length - 2))); text2 = args[^1]; if (text != null && !ThemeRooms(text).Contains(text2)) { instance.AddString("Specify a loaded " + text + " room: " + string.Join(", ", ThemeRooms(text))); return; } } if (text == null) { instance.AddString("Specify an available theme: " + string.Join(", ", list)); return; } ItemData val = TabletItem.Create(result); Tablet tablet = Tablet.Read(val); tablet.Theme = text; tablet.Room = text2; tablet.Save(); val.m_cheated = true; Inventory inventory = ((Humanoid)localPlayer).GetInventory(); if (!inventory.CanAddItem(val, 1) || !inventory.AddItem(val)) { instance.AddString("Your inventory has no room for a tablet."); return; } ((Character)localPlayer).ShowPickupMessage(val, 1); instance.AddString($"Added Tier {result} {text2 ?? text} tablet with {tablet.Attempts} attempts."); } } internal sealed class NextWaveCommand : ConsoleCommand { public override string Name => "ve_nextwave"; public override string Help => "Clear the current wave for development. The next wave starts normally; use the exit after a room's final wave."; public override bool IsCheat => true; public override void Run(string[] args) { if (args.Length != 0) { ((Terminal)Console.instance).AddString(((ConsoleCommand)this).Help); } else { ClearRoomCommand.ClearWave(); } } } internal sealed class RewardOddsCommand : ConsoleCommand { public override string Name => "ve_rewardodds"; public override string Help => "ve_rewardodds <1-15> [modifier bonus percent]: show completion reward rolls and rarity probabilities."; public override void Run(string[] args) { double result = 0.0; int num = args.Length; bool flag = ((num < 1 || num > 2) ? true : false); if (flag || !int.TryParse(args[0], out var result2) || result2 < 1 || result2 > 15 || (args.Length == 2 && !double.TryParse(args[1], NumberStyles.Float, CultureInfo.InvariantCulture, out result)) || double.IsNaN(result) || double.IsInfinity(result) || result < 0.0 || result > 10000.0) { ((Terminal)Console.instance).AddString(((ConsoleCommand)this).Help); return; } RewardCatalog rewardCatalog = Plugin.Instance?.Catalog?.Rewards; if (rewardCatalog == null) { ((Terminal)Console.instance).AddString("Enter a world with a valid reward configuration first."); return; } ((Terminal)Console.instance).AddString($"Tier {result2}: {rewardCatalog.Rolls(result2, result)} reward rolls; modifier difficulty {result:0.##}%."); foreach (KeyValuePair<string, double> item in rewardCatalog.EffectiveProbabilities(result2)) { ((Terminal)Console.instance).AddString($"{item.Key}: {item.Value:P2} per roll; {rewardCatalog.AtLeastOne(result2, result, item.Value):P2} for at least one in the completed encounter."); } } } internal sealed class SetPlayerTierCommand : ConsoleCommand { public override string Name => "ve_setplayertier"; public override string Help => "ve_setplayertier <0-15>: admin only. Mark Tiers 1 to N completed for you in this world's quest ledger. 0 resets progression."; public override bool IsCheat => true; public override void Run(string[] args) { Terminal instance = (Terminal)(object)Console.instance; if (args.Length != 1 || !int.TryParse(args[0], NumberStyles.None, CultureInfo.InvariantCulture, out var result) || result < 0 || result > 15) { instance.AddString(((ConsoleCommand)this).Help); return; } if ((Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)QuestService.Instance == (Object)null) { instance.AddString("Enter a world before setting your progress tier."); return; } QuestService.Instance.SetProgress(result); instance.AddString($"Requested progress tier {result}. The Dungeon Master would sell {TabletProgression.Describe(result)}."); } } internal static class ConfigurationLoader { internal static RewardCatalog LoadRewardDefaults() { return RewardCatalog.Load(ReadEmbedded("rewards.yaml")); } internal static EventCatalog Load(string directory, bool useCustomConfiguration) { RoomCatalog rooms = LoadFile<RoomCatalog>("rooms.yaml", RoomCatalog.Load); RewardCatalog rewards = LoadFile<RewardCatalog>("rewards.yaml", RewardCatalog.Load); EventCatalog eventCatalog = LoadFile<EventCatalog>("events.yaml", (string yaml) => EventCatalog.Load(yaml, rooms, rewards)); eventCatalog.Monsters = LoadFile<MonsterCatalog>("creatures.yaml", MonsterCatalog.Load); return eventCatalog; T LoadFile<T>(string file, Func<string, T> parse) { string text = (useCustomConfiguration ? Path.Combine(directory, file) : ("embedded " + file)); try { if (!useCustomConfiguration) { return parse(ReadEmbedded(file)); } if (!File.Exists(text)) { Directory.CreateDirectory(directory); using FileStream stream = new FileStream(text, FileMode.CreateNew, FileAccess.Write); using StreamWriter streamWriter = new StreamWriter(stream); streamWriter.Write(ReadEmbedded(file)); } return parse(File.ReadAllText(text)); } catch (Exception ex) { throw new InvalidOperationException("Invalid configuration in " + text + ": " + ex.GetBaseException().Message, ex); } } } private static string ReadEmbedded(string file) { using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("ValheimEndgame." + file) ?? throw new InvalidOperationException("Missing embedded configuration: " + file); using StreamReader streamReader = new StreamReader(stream); return streamReader.ReadToEnd(); } } public sealed class EventCatalog { public int SchemaVersion { get; set; } public List<EventDefinition> Events { get; set; } = new List<EventDefinition>(); public TabletBalance Tablets { get; set; } = new TabletBalance(); public RewardCatalog? Rewards { get; set; } public RoomCatalog? Rooms { get; set; } public MonsterCatalog? Monsters { get; set; } public bool CanResolve(int tier) { if (Monsters != null && Events.Any((EventDefinition e) => e.Enabled)) { return Monsters.Supports(tier); } return false; } public EventDefinition? Resolve(int tier) { if (!CanResolve(tier)) { return null; } EventDefinition eventDefinition = Events.FirstOrDefault((EventDefinition e) => e.Enabled && e.Tier == tier); EventDefinition eventDefinition2 = Monsters.Build(tier, eventDefinition ?? Events.First((EventDefinition e) => e.Enabled)); eventDefinition2.Rewards = Rewards ?? throw new InvalidOperationException("Missing rewards.yaml configuration."); return eventDefinition2; } public static EventCatalog Load(string yaml, RoomCatalog? rooms = null, RewardCatalog? rewards = null) { EventCatalog eventCatalog = Yaml.ConfigurationReader.Deserialize<EventCatalog>(yaml); if (eventCatalog == null || eventCatalog.SchemaVersion != 6 || eventCatalog.Events == null || eventCatalog.Events.Count == 0) { throw new ArgumentException("Expected schemaVersion: 6 with rewards in rewards.yaml."); } if (eventCatalog.Events.Any((EventDefinition e) => e == null) || eventCatalog.Events.Select((EventDefinition e) => e.Id).Distinct().Count() != eventCatalog.Events.Count || eventCatalog.Events.Select((EventDefinition e) => e.Tier).Distinct().Count() != eventCatalog.Events.Count) { throw new ArgumentException("Event IDs and tiers must be unique."); } foreach (EventDefinition @event in eventCatalog.Events) { @event.Validate(); } if (rewards != null) { eventCatalog.Rewards = rewards; } if (eventCatalog.Rewards == null) { throw new ArgumentException("Missing rewards.yaml configuration."); } eventCatalog.Rewards.Validate(); eventCatalog.Monsters?.Validate(); if (rooms != null) { eventCatalog.Rooms = rooms; } if (eventCatalog.Rooms == null) { throw new ArgumentException("Missing room configuration."); } eventCatalog.Rooms.Validate(); if (eventCatalog.Tablets == null || new float[11] { eventCatalog.Tablets.TotemRadius, eventCatalog.Tablets.MovementSlowPercent, eventCatalog.Tablets.AttackSlowPercent, eventCatalog.Tablets.EitrDrainPercent, eventCatalog.Tablets.HealthDrainPercent, eventCatalog.Tablets.FireDamage, eventCatalog.Tablets.FrostDamage, eventCatalog.Tablets.HazardDamagePerTierPercent, eventCatalog.Tablets.FireballOrbDamage, eventCatalog.Tablets.IceShardOrbDamage, eventCatalog.Tablets.MistileOrbDamage }.Any((float n) => float.IsNaN(n) || float.IsInfinity(n) || n < 0f || n > 500f)) { throw new ArgumentException("Tablet percentages must be finite and between 0 and 500."); } if (eventCatalog.Tablets.TotemRadius <= 0f) { throw new ArgumentException("Totem radius must be positive."); } if (new float[3] { eventCatalog.Tablets.MovementSlowPercent, eventCatalog.Tablets.AttackSlowPercent, eventCatalog.Tablets.ProtectionReductionPercent }.Any((float n) => float.IsNaN(n) || float.IsInfinity(n) || n < 0f || n > 80f)) { throw new ArgumentException($"Totem slows and protection must be between 0 and {80f} percent per totem."); } if (eventCatalog.Tablets.EitrDrainPercent > 100f || eventCatalog.Tablets.HealthDrainPercent > 100f) { throw new ArgumentException("Totem drains must be between 0 and 100 percent of maximum per second per totem."); } return eventCatalog; } } public sealed class EventDefinition { public const int WavesPerRoom = 3; public string Id { get; set; } = ""; public int Tier { get; set; } public bool Enabled { get; set; } public string StartMessage { get; set; } = ""; public string EndMessage { get; set; } = ""; public float SpawnInterval { get; set; } = 2f; public float GroupDelay { get; set; } = 8f; public int MaxAlive { get; set; } = 8; public RewardCatalog? Rewards { get; set; } public List<List<CreatureEntry>> Waves { get; set; } = new List<List<CreatureEntry>>(); public void Validate() { if (string.IsNullOrWhiteSpace(Id) || Tier < 1 || Tier > 15 || !Finite(SpawnInterval) || SpawnInterval < 0.1f || !Finite(GroupDelay) || GroupDelay < 0f || MaxAlive < 1 || MaxAlive > 100 || Waves == null || Waves.Count > 15) { throw new ArgumentException("Invalid encounter ID, tier, pacing, or groups."); } foreach (List<CreatureEntry> wave in Waves) { if (wave == null || wave.Count == 0 || wave.Any((CreatureEntry e) => e == null || string.IsNullOrWhiteSpace(e.Prefab) || !new string[5] { "standard", "magic", "rare", "miniboss", "boss" }.Contains(e.Role) || e.Amount < 1 || e.Amount > 100 || e.Stars < 0 || e.Stars > 10000 || e.MinStars.HasValue != e.MaxStars.HasValue || e.MinStars < 0 || e.MaxStars > 10000 || e.MaxStars < e.MinStars) || wave.Sum((CreatureEntry e) => e.Amount) > 500) { throw new ArgumentException("Invalid prefab, count, or stars."); } foreach (CreatureEntry item in wave) { if (item.Modifiers != null && item.Modifiers.Groups.Any((List<string> g) => g?.Any(string.IsNullOrWhiteSpace) ?? true)) { throw new ArgumentException("Empty SLS modifier name."); } } } } private static bool Finite(float n) { if (!float.IsNaN(n)) { return !float.IsInfinity(n); } return false; } public int ThemeBossStage() { int num = Waves.FindLastIndex((List<CreatureEntry> entries) => entries.Any((CreatureEntry entry) => entry.Role == "boss")); if (num >= 0) { return num / 3; } return -1; } } public sealed class CreatureEntry { public string Prefab { get; set; } = ""; public string Role { get; set; } = "standard"; public int Amount { get; set; } public int Stars { get; set; } public bool WorldStars { get; set; } public int? MinStars { get; set; } public int? MaxStars { get; set; } public CreatureModifiers? Modifiers { get; set; } } public sealed class CreatureModifiers { public List<string> Major { get; set; } = new List<string>(); public List<string> Minor { get; set; } = new List<string>(); public List<string> Boss { get; set; } = new List<string>(); [YamlIgnore] public IEnumerable<List<string>> Groups => new List<string>[3] { Major, Minor, Boss }; } public sealed class MonsterCatalog { public const int MinimumTier = 1; public const int MaximumTier = 15; public const int StarsPerTier = 3; private static readonly string[] Roles = new string[7] { "standard", "magic", "rare", "miniboss", "boss", "hazard", "excluded" }; private readonly Dictionary<string, IReadOnlyList<string>> rosters = new Dictionary<string, IReadOnlyList<string>>(StringComparer.Ordinal); public int SchemaVersion { get; set; } public string Notes { get; set; } = ""; public Dictionary<string, CatalogCreature> Creatures { get; set; } = new Dictionary<string, CatalogCreature>(); public List<string> Minibosses { get; set; } = new List<string>(); public List<string> Bosses { get; set; } = new List<string>(); public List<WavePlan> Layout { get; set; } = new List<WavePlan>(); public static MonsterCatalog Load(string yaml) { MonsterCatalog obj = Yaml.ConfigurationReader.Deserialize<MonsterCatalog>(yaml) ?? throw new ArgumentException("Empty creature catalog."); obj.Validate(); return obj; } public void Validate() { rosters.Clear(); if (SchemaVersion != 2) { throw new ArgumentException("Expected creatures.yaml schemaVersion: 2."); } if (Creatures == null || Minibosses == null || Bosses == null || Layout == null || Layout.Count != 10) { throw new ArgumentException("Invalid creature catalog or ten-wave layout."); } foreach (KeyValuePair<string, CatalogCreature> creature in Creatures) { CatalogCreature value = creature.Value; if (string.IsNullOrWhiteSpace(creature.Key) || value == null || !Roles.Contains(value.Role) || value.Biomes == null || value.Biomes.Any(string.IsNullOrWhiteSpace) || value.WaveLimits == null || (value.Enabled && (value.Role == "excluded" || value.Stars == null))) { throw new ArgumentException("Invalid catalog creature: " + creature.Key); } if (value.Stars != null && (value.Stars.MinStars < 0 || value.Stars.MaxStars < value.Stars.MinStars || value.Stars.MaxStars > 9958)) { throw new ArgumentException("Invalid Tier 1 star range: " + creature.Key); } if (value.WaveLimits.Any((KeyValuePair<int, int> limit) => limit.Key < 1 || limit.Key > 15 || limit.Value < 1 || limit.Value > 100)) { throw new ArgumentException("Invalid wave limit: " + creature.Key); } } List<string>[] array = new List<string>[2] { Minibosses, Bosses }; foreach (List<string> list in array) { if (list.Count == 0 || list.Distinct().Count() != list.Count || list.Any((string name) => string.IsNullOrWhiteSpace(name) || !Creatures.TryGetValue(name, out CatalogCreature value2) || value2.Role == "excluded")) { throw new ArgumentException("Missing, excluded or duplicate creature in special wave roster."); } } foreach (KeyValuePair<string, CatalogCreature> creature2 in Creatures) { if (creature2.Value.Enabled && (creature2.Value.Role == "boss" || creature2.Value.Role == "miniboss") && !Minibosses.Contains(creature2.Key) && !Bosses.Contains(creature2.Key)) { throw new ArgumentException("Enabled creature is absent from special wave rosters: " + creature2.Key); } } for (int num2 = 0; num2 < Layout.Count; num2++) { WavePlan wavePlan = Layout[num2]; object obj; switch (num2) { default: obj = "regular"; break; case 2: case 5: case 8: obj = "miniboss"; break; case 9: obj = "boss"; break; } string text = (string)obj; if (wavePlan == null || wavePlan.Kind != text || wavePlan.Standard < 0 || wavePlan.Magic < 0 || wavePlan.Rare < 0 || wavePlan.Standard > 100 || wavePlan.Magic > 100 || wavePlan.Rare > 100 || wavePlan.Standard + wavePlan.Magic + wavePlan.Rare == 0) { throw new ArgumentException("Invalid wave layout entry " + (num2 + 1)); } } } public IReadOnlyList<string> Candidates(string role) { if (rosters.TryGetValue(role, out IReadOnlyList<string> value)) { return value; } IEnumerable<string> source; if (!(role == "miniboss")) { if (!(role == "boss")) { source = from pair in Creatures where pair.Value.Role == role select pair.Key; } else { IEnumerable<string> bosses = Bosses; source = bosses; } } else { IEnumerable<string> bosses = Minibosses; source = bosses; } IReadOnlyList<string> readOnlyList = Array.AsReadOnly(source.Where((string name) => Creatures[name].Enabled).ToArray()); rosters[role] = readOnlyList; return readOnlyList; } public bool Supports(int tier) { if (tier < 1 || tier > 15) { return false; } return new string[3] { "standard", "magic", "rare" }.Where((string role) => Layout.Any((WavePlan wave) => (!(role == "standard")) ? ((!(role == "magic")) ? (wave.Rare > 0) : (wave.Magic > 0)) : (wave.Standard > 0))).ToArray().Concat(new string[2] { "miniboss", "boss" }) .All((string role) => Candidates(role).Count > 0); } internal CreatureEntry HazardEntry(string kind, int tier) { string text = kind switch { "tentacle" => "staff_greenroots_tentaroot", "root" => "TentaRoot", "brood_creature" => "SeekerBrood", _ => throw new ArgumentException("Unknown hazard creature: " + kind), }; if (!Creatures.TryGetValue(text, out CatalogCreature value) || value.Stars == null) { throw new InvalidOperationException("Missing hazard star range: " + text); } CatalogStarRange catalogStarRange = value.RangeAt(tier); return new CreatureEntry { Prefab = text, Amount = 1, MinStars = catalogStarRange.MinStars, MaxStars = catalogStarRange.MaxStars }; } public EventDefinition Build(int tier, EventDefinition settings) { if (!Supports(tier)) { throw new ArgumentException("Monster catalog unavailable for tier."); } EventDefinition eventDefinition = new EventDefinition { Id = $"all-tier-{tier}", Tier = tier, Enabled = true, StartMessage = $"Tier {tier}. Ten waves.", EndMessage = $"Tier {tier} complete.", SpawnInterval = settings.SpawnInterval, GroupDelay = settings.GroupDelay, MaxAlive = settings.MaxAlive, Rewards = settings.Rewards }; foreach (WavePlan item in Layout) { List<CreatureEntry> list = new List<CreatureEntry>(); if (item.Kind != "regular") { Add(list, Candidates(item.Kind), tier, 1); } foreach (KeyValuePair<string, int> item2 in new Dictionary<string, int> { ["standard"] = item.Standard, ["magic"] = item.Magic, ["rare"] = item.Rare }) { if (item2.Value > 0) { Add(list, Candidates(item2.Key), tier, item2.Value); } } eventDefinition.Waves.Add(list); } eventDefinition.Validate(); return eventDefinition; } private void Add(List<CreatureEntry> wave, IReadOnlyList<string> names, int tier, int amount) { string prefab = names[Random.Range(0, names.Count)]; CatalogCreature catalogCreature = Creatures[prefab]; CatalogStarRange catalogStarRange = catalogCreature.RangeAt(tier); if (catalogCreature.WaveLimits.TryGetValue(tier, out var value)) { amount = Math.Min(amount, value - wave.Where((CreatureEntry entry) => entry.Prefab == prefab).Sum((CreatureEntry entry) => entry.Amount)); } if (amount > 0) { wave.Add(new CreatureEntry { Prefab = prefab, Role = catalogCreature.Role, Amount = amount, MinStars = catalogStarRange.MinStars, MaxStars = catalogStarRange.MaxStars }); } } } public sealed class CatalogCreature { public bool Enabled { get; set; } public string Role { get; set; } = ""; public List<string> Biomes { get; set; } = new List<string>(); public CatalogStarRange? Stars { get; set; } public Dictionary<int, int> WaveLimits { get; set; } = new Dictionary<int, int>(); public string Notes { get; set; } = ""; public CatalogStarRange RangeAt(int tier) { if (Stars == null || tier < 1 || tier > 15) { throw new ArgumentOutOfRangeException("tier", "A Tier 1 baseline and a tier from 1 to 15 are required."); } int num = 3 * (tier - 1); return new CatalogStarRange { MinStars = Stars.MinStars + num, MaxStars = Stars.MaxStars + num }; } } public sealed class CatalogStarRange { public int MinStars { get; set; } public int MaxStars { get; set; } public string Status { get; set; } = ""; } public sealed class WavePlan { public string Kind { get; set; } = "regular"; public int Standard { get; set; } public int Magic { get; set; } public int Rare { get; set; } } public sealed class QuestReward { public string Prefab { get; set; } = ""; public int Quantity { get; set; } = 1; public int Quality { get; set; } = 1; internal ItemData Item() { GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(Prefab); ItemDrop val = (((Object)(object)itemPrefab == (Object)null) ? null : itemPrefab.GetComponent<ItemDrop>()); if ((Object)(object)val == (Object)null || Quality < 1 || Quality > val.m_itemData.m_shared.m_maxQuality || Quantity < 1 || Quantity > 10000) { throw new InvalidDataException($"Invalid reward {Prefab}: quantity={Quantity}, quality={Quality}."); } ItemData obj = val.m_itemData.Clone(); obj.m_dropPrefab = itemPrefab; obj.m_quality = Quality; obj.m_stack = 1; obj.m_durability = obj.GetMaxDurability(); return obj; } } public sealed class TierQuest { public int Tier { get; set; } public string Objective { get; set; } = ""; public List<QuestReward> Rewards { get; set; } = new List<QuestReward>(); [YamlIgnore] public string Error { get; internal set; } = ""; internal void Validate() { if (Tier < 1 || Tier > 15 || string.IsNullOrWhiteSpace(Objective) || Objective.Length > 500 || Rewards == null || Rewards.Count < 1 || Rewards.Count > 32 || Rewards.Any((QuestReward r) => r == null || string.IsNullOrWhiteSpace(r.Prefab) || r.Prefab.Length > 128 || r.Quantity < 1 || r.Quantity > 10000 || r.Quality < 1 || r.Quality > 100)) { throw new InvalidDataException($"Tier {Tier}: expected objective and 1–32 valid item rewards."); } } internal void ValidateItems() { Validate(); int num = 0; foreach (QuestReward reward in Rewards) { ItemData val = reward.Item(); num += (reward.Quantity + val.m_shared.m_maxStackSize - 1) / val.m_shared.m_maxStackSize; } if (num > 128) { throw new InvalidDataException($"Tier {Tier}: reward exceeds 128 stacks."); } } } public sealed class QuestCatalog { internal static readonly ISerializer Serializer = ((BuilderSkeleton<SerializerBuilder>)new SerializerBuilder()).WithNamingConvention(CamelCaseNamingConvention.Instance).Build(); internal static readonly IDeserializer Deserializer = ((BuilderSkeleton<DeserializerBuilder>)new DeserializerBuilder()).WithNamingConvention(CamelCaseNamingConvention.Instance).WithDuplicateKeyChecking().Build(); public int SchemaVersion { get; set; } = 1; public List<TierQuest> Tiers { get; set; } = new List<TierQuest>(); internal TierQuest? Find(int tier) { return Tiers.FirstOrDefault((TierQuest t) => t.Tier == tier); } internal static QuestCatalog Load(string yaml) { QuestCatalog questCatalog = Deserializer.Deserialize<QuestCatalog>(yaml); if (questCatalog == null || questCatalog.SchemaVersion != 1 || questCatalog.Tiers == null || questCatalog.Tiers.Count > 15 || questCatalog.Tiers.Any((TierQuest t) => t == null || t.Tier < 1 || t.Tier > 15) || questCatalog.Tiers.Select((TierQuest t) => t.Tier).Distinct().Count() != questCatalog.Tiers.Count) { throw new InvalidDataException("quests.yaml: expected schemaVersion: 1 and unique tiers from 1 to 15."); } foreach (TierQuest tier in questCatalog.Tiers) { try { tier.Validate(); } catch (Exception ex) { tier.Error = ex.Message; } } return questCatalog; } internal void ValidateItems(bool logErrors = true) { foreach (TierQuest tier in Tiers) { if (tier.Error.Length == 0) { try { tier.ValidateItems(); } catch (Exception ex) { tier.Error = $"Tier {tier.Tier}: {ex.Message}"; } } if (logErrors && tier.Error.Length != 0) { Debug.LogError((object)("[Valheim Endgame] Quest disabled: " + tier.Error)); } } } internal static QuestCatalog Read(string directory) { string path = Path.Combine(directory, "quests.yaml"); if (!File.Exists(path)) { Directory.CreateDirectory(directory); QuestCatalog questCatalog = new QuestCatalog(); for (int i = 1; i <= 15; i++) { questCatalog.Tiers.Add(new TierQuest { Tier = i, Objective = $"Complete a Tier {i} encounter. Stay alive inside the encounter when the final wave ends.", Rewards = new List<QuestReward> { new QuestReward { Prefab = TabletItem.PrefabName(Math.Min(i + 1, 15)), Quantity = ((i != 15) ? 1 : 5) } } }); } using FileStream stream = new FileStream(path, FileMode.CreateNew, FileAccess.Write); using StreamWriter streamWriter = new StreamWriter(stream); streamWriter.WriteLine("# Restart the server/world after editing. Pending claims keep their original rewards."); streamWriter.Write(Serializer.Serialize((object)questCatalog)); } return Load(File.ReadAllText(path)); } } public sealed class RewardCatalog { public int SchemaVersion { get; set; } = 2; public List<RewardItem> Items { get; set; } = new List<RewardItem>(); public List<RewardRarity> Rarities { get; set; } = new List<RewardRarity>(); public RewardRules Rules { get; set; } = new RewardRules(); public Dictionary<string, PopulationModifier> PopulationModifiers { get; set; } = new Dictionary<string, PopulationModifier>(); public static RewardCatalog Load(string yaml) { RewardCatalog obj = Yaml.ConfigurationReader.Deserialize<RewardCatalog>(yaml) ?? throw new ArgumentException("rewards.yaml must contain a reward catalog."); obj.Validate(); Require(obj.PopulationModifiers.Count == 4, "populationModifiers", "requires the four tablet population effects"); return obj; } public void Validate() { Require(SchemaVersion == 2, "schemaVersion", "must be 2; replace the old base/wave roll settings with difficulty budgets"); if (Items == null) { throw new ArgumentException("rewards.yaml items must contain item definitions."); } Require(Items.Count > 0 && Items.All((RewardItem i) => i != null), "items", "must contain item definitions"); Require(Items.Select((RewardItem i) => i.Id).Distinct().Count() == Items.Count, "items", "IDs must be unique"); foreach (RewardItem item in Items) { string text = "items." + item.Id; Require(!string.IsNullOrWhiteSpace(item.Id) && (item.RandomLegendarySetRune || !string.IsNullOrWhiteSpace(item.Prefab)), text, "requires id and prefab, or a random set reward"); if (item.RandomLegendarySetRune) { Require(item.Prefab == "" && item.MinQuantity == 1 && item.MaxQuantity == 1 && item.TabletTier == 0 && item.TabletTheme == "", text, "random set rewards require quantity 1 and no prefab or tablet settings"); } Require(item.MinTier >= 1 && item.MaxTier <= 15 && item.MinTier <= item.MaxTier, text, "tier range must be within 1–15"); Require(item.MinQuantity >= 1 && item.MaxQuantity >= item.MinQuantity && item.MaxQuantity <= 1000, text, "quantity range must be within 1–1000"); Require(item.TabletTier >= 0 && item.TabletTier <= 15, text + ".tabletTier", "must be 0 (encounter tier) or 1–15"); Require(item.TabletTheme != null, text + ".tabletTheme", "must be a theme name or an empty string"); } if (Rarities == null) { throw new ArgumentException("rewards.yaml rarities must contain ordered rarity tables."); } Require(Rarities.Count > 0 && Rarities.All((RewardRarity r) => r != null), "rarities", "must contain ordered rarity tables"); Require(Rarities.Select((RewardRarity r) => r.Id).Distinct().Count() == Rarities.Count, "rarities", "IDs must be unique"); foreach (RewardRarity rarity in Rarities) { string text2 = "rarities." + rarity.Id; Require(!string.IsNullOrWhiteSpace(rarity.Id) && Finite(rarity.Probability) && rarity.Probability >= 0.0 && rarity.Probability <= 1.0, text2, "requires an id and probability between 0 and 1"); if (rarity.Entries == null) { throw new ArgumentException("rewards.yaml " + text2 + ".entries must be a list."); } Require(rarity.Entries.All((RewardWeight e) => e != null), text2 + ".entries", "must be a list"); Require(rarity.Entries.Select((RewardWeight e) => e.Item).Distinct().Count() == rarity.Entries.Count, text2, "item references must be unique"); foreach (RewardWeight entry in rarity.Entries) { Require(Items.Any((RewardItem i) => i.Id == entry.Item) && Finite(entry.Weight) && entry.Weight > 0.0 && entry.Weight <= 1000000.0, text2 + "." + entry.Item, "must reference an item with a positive weight no greater than 1000000"); } } Require(Math.Abs(Rarities.Sum((RewardRarity r) => r.Probability) - 1.0) < 1E-06, "rarities", "overall probabilities must sum to 1"); if (Rules == null) { throw new ArgumentException("rewards.yaml rules are required."); } Require(Finite(Rules.BudgetPerRoll) && Rules.BudgetPerRoll >= 0.01 && Rules.BudgetPerRoll <= 1000000.0 && Rules.MaxRolls >= 1 && Rules.MaxRolls <= 10000, "rules", "budgetPerRoll must be 0.01–1000000 and maxRolls must be 1–10000"); Require(Rules.Tiers != null && Rules.Tiers.Count == 15 && Rules.Tiers.All((RewardTier t) => t != null && Finite(t.Difficulty) && t.Difficulty >= 0.0 && t.Difficulty <= 1000000.0 && Finite(t.BonusPercent) && t.BonusPercent >= 0.0 && t.BonusPercent <= 1000.0), "rules.tiers", "requires 15 finite difficulty and bonus rows"); if (Rules.CategoryDifficulty == null || Rules.ModifierDifficulty == null) { throw new ArgumentException("rewards.yaml rules require categoryDifficulty and modifierDifficulty."); } string[] array = new string[4] { "population", "hazards", "totems", "creatures" }; foreach (string text3 in array) { Require(Rules.CategoryDifficulty.TryGetValue(text3, out var value) && Finite(value) && value >= 0.0 && value <= 500.0, "rules.categoryDifficulty." + text3, "must be between 0 and 500 percent"); } Require(Rules.ModifierDifficulty.All<KeyValuePair<string, double>>((KeyValuePair<string, double> p) => !string.IsNullOrWhiteSpace(p.Key) && Finite(p.Value) && p.Value >= 0.0 && p.Value <= 500.0), "rules.modifierDifficulty", "requires named contributions between 0 and 500 percent"); Require(PopulationModifiers != null, "populationModifiers", "must be a mapping"); if (PopulationModifiers.Count > 0) { string[] array2 = new string[4] { "VE_PopulationStandard", "VE_PopulationSupport", "VE_PopulationElite", "VE_PopulationBosses" }; Require(PopulationModifiers.Count == array2.Length && array2.All((string id) => PopulationModifiers.TryGetValue(id, out PopulationModifier value2) && value2 != null), "populationModifiers", "requires all four population effects"); array = array2; foreach (string text4 in array) { PopulationModifiers[text4].Validate(text4 == "VE_PopulationBosses"); } } for (int num2 = 1; num2 <= 15; num2++) { for (int num3 = 0; num3 < Rarities.Count; num3++) { if (Rarities[num3].Probability > 0.0) { Require(Fallback(num3, num2) >= 0, "rarities." + Rarities[num3].Id, $"tier {num2} needs an eligible entry in this table or a lower rarity table"); } } } } internal List<(RewardItem item, double weight)> Eligible(int rarity, int tier) { return (from e in Rarities[rarity].Entries select (item: Items.Single((RewardItem i) => i.Id == e.Item), weight: e.Weight) into e where e.item.Enabled && tier >= e.item.MinTier && tier <= e.item.MaxTier select e).ToList(); } internal int Fallback(int rarity, int tier) { for (int i = rarity; i < Rarities.Count; i++) { if (Eligible(i, tier).Count > 0) { return i; } } return -1; } public Dictionary<string, double> EffectiveProbabilities(int tier) { Dictionary<string, double> dictionary = Rarities.ToDictionary((RewardRarity r) => r.Id, (RewardRarity _) => 0.0); for (int num = 0; num < Rarities.Count; num++) { if (Rarities[num].Probability > 0.0) { dictionary[Rarities[Fallback(num, tier)].Id] += Rarities[num].Probability; } } return dictionary; } internal RewardRollBudget Budget(int tier, double modifierDifficulty) { if (tier < 1 || tier > 15 || !Finite(modifierDifficulty) || modifierDifficulty < 0.0) { throw new ArgumentException("Reward rolls require tier 1–15 and finite nonnegative modifier difficulty."); } RewardTier rewardTier = Rules.Tiers[tier - 1]; double num = rewardTier.Difficulty * (1.0 + modifierDifficulty / 100.0) * (1.0 + rewardTier.BonusPercent / 100.0); double num2 = Math.Round(num / Rules.BudgetPerRoll, 10, MidpointRounding.AwayFromZero); return new RewardRollBudget(rewardTier.Difficulty, rewardTier.BonusPercent, modifierDifficulty, num, Rules.BudgetPerRoll, Rules.MaxRolls, num2 > (double)Rules.MaxRolls, (int)Math.Min(Rules.MaxRolls, Math.Ceiling(num2))); } internal int Rolls(int tier, double modifierDifficulty) { return Budget(tier, modifierDifficulty).Rolls; } internal List<RewardOdds> Odds(int tier) { Dictionary<string, double> dictionary = EffectiveProbabilities(tier); List<RewardOdds> list = new List<RewardOdds>(); for (int i = 0; i < Rarities.Count; i++) { double probability = dictionary[Rarities[i].Id]; if (!(probability <= 0.0)) { List<(RewardItem, double)> source = Eligible(i, tier); double total = source.Sum<(RewardItem, double)>(((RewardItem item, double weight) c) => c.weight); list.Add(new RewardOdds(Rarities[i].Id, probability, source.Select<(RewardItem, double), (RewardItem, double)>(((RewardItem item, double weight) c) => (item: c.item, probability * c.weight / total)).ToList())); } } return list; } internal double AtLeastOne(int tier, double modifierDifficulty, double probability) { return 1.0 - Math.Pow(1.0 - probability, Rolls(tier, modifierDifficulty)); } private static bool Finite(double n) { if (!double.IsNaN(n)) { return !double.IsInfinity(n); } return false; } private static void Require(bool valid, string path, string correction) { if (!valid) { throw new ArgumentException("rewards.yaml " + path + ": " + correction + "."); } } } internal sealed class RewardRollBudget { internal double TierDifficulty { get; } internal double TierBonusPercent { get; } internal double ModifierPercent { get; } internal double Budget { get; } internal double CostPerRoll { get; } internal int MaxRolls { get; } internal bool Capped { get; } internal int Rolls { get; } internal RewardRollBudget(double tierDifficulty, double tierBonusPercent, double modifierPercent, double budget, double costPerRoll, int maxRolls, bool capped, int rolls) { TierDifficulty = tierDifficulty; TierBonusPercent = tierBonusPercent; ModifierPercent = modifierPercent; Budget = budget; CostPerRoll = costPerRoll; MaxRolls = maxRolls; Capped = capped; Rolls = rolls; } } internal sealed class RewardOdds { internal string Rarity { get; } internal double Probability { get; } internal List<(RewardItem item, double probability)> Items { get; } internal RewardOdds(string rarity, double probability, List<(RewardItem item, double probability)> items) { Rarity = rarity; Probability = probability; Items = items; } } public sealed class RewardItem { public string Id { get; set; } = ""; public string Prefab { get; set; } = ""; public bool RandomLegendarySetRune { get; set; } public bool Enabled { get; set; } = true; public int MinTier { get; set; } = 1; public int MaxTier { get; set; } = 15; public int MinQuantity { get; set; } = 1; public int MaxQuantity { get; set; } = 1; public int TabletTier { get; set; } public string TabletTheme { get; set; } = ""; } public sealed class RewardWeight { public string Item { get; set; } = ""; public double Weight { get; set; } = 1.0; } public sealed class RewardRarity { public string Id { get; set; } = ""; public double Probability { get; set; } public List<RewardWeight> Entries { get; set; } = new List<RewardWeight>(); } public sealed class RewardRules { public double BudgetPerRoll { get; set; } = 10.0; public int MaxRolls { get; set; } = 10000; public List<RewardTier> Tiers { get; set; } = new List<RewardTier>(); public Dictionary<string, double> CategoryDifficulty { get; set; } = new Dictionary<string, double>(); public Dictionary<string, double> ModifierDifficulty { get; set; } = new Dictionary<string, double>(); } public sealed class RewardTier { public double Difficulty { get; set; } public double BonusPercent { get; set; } } public sealed class RoomCatalog { public List<string> EnabledRooms { get; set; } = new List<string>(); public Dictionary<string, string>? Themes { get; set; } public string DefaultBossRoom { get; set; } = ""; public Dictionary<string, string?>? BossRooms { get; set; } public static RoomCatalog Load(string yaml) { RoomCatalog obj = Yaml.ConfigurationReader.Deserialize<RoomCatalog>(yaml) ?? throw new ArgumentException("Missing room configuration."); obj.Validate(); return obj; } public void Validate() { if (EnabledRooms == null || EnabledRooms.Count == 0 || EnabledRooms.Any(string.IsNullOrWhiteSpace) || EnabledRooms.Distinct<string>(StringComparer.Ordinal).Count() != EnabledRooms.Count) { throw new ArgumentException("At least one unique room ID must be enabled in rooms.yaml."); } if (Themes == null || Themes.Any<KeyValuePair<string, string>>((KeyValuePair<string, string> pair) => string.IsNullOrWhiteSpace(pair.Key) || string.IsNullOrWhiteSpace(pair.Value))) { throw new ArgumentException("Every room theme assignment needs a room ID and a theme."); } foreach (string enabledRoom in EnabledRooms) { if (!Themes.ContainsKey(enabledRoom)) { throw new ArgumentException("Assign a theme in rooms.yaml for room: " + enabledRoom); } } if (!Themes.ContainsKey(DefaultBossRoom)) { throw new ArgumentException("defaultBossRoom in rooms.yaml must be a room with a theme."); } if (BossRooms == null) { throw new ArgumentException("Missing bossRooms in rooms.yaml."); } foreach (string item in EnabledRooms.Select((string id) => Themes[id]).Distinct<string>(StringComparer.Ordinal)) { if (!BossRooms.ContainsKey(item)) { throw new ArgumentException("Assign a boss room or null in rooms.yaml for theme: " + item); } } foreach (KeyValuePair<string, string> bossRoom in BossRooms) { if (bossRoom.Value != null && !Themes.ContainsKey(bossRoom.Value)) { throw new ArgumentException("Boss room for theme " + bossRoom.Key + " is not a room with a theme: " + bossRoom.Value); } } } public string? ThemeFor(string id) { if (Themes == null || !Themes.TryGetValue(id, out string value)) { return null; } return value; } public string BossRoomFor(string theme) { if (BossRooms == null || !BossRooms.TryGetValue(theme, out string value) || value == null) { return DefaultBossRoom; } return value; } } internal static class Yaml { internal static readonly ISerializer Writer = new SerializerBuilder().Build(); internal static readonly ISerializer ConfigurationWriter = ((BuilderSkeleton<SerializerBuilder>)new SerializerBuilder()).WithNamingConvention(CamelCaseNamingConvention.Instance).Build(); internal static readonly ISerializer JsonWriter = new SerializerBuilder().JsonCompatible().Build(); internal static readonly IDeserializer Reader = new DeserializerBuilder().Build(); internal static readonly IDeserializer CompatibleReader = new DeserializerBuilder().IgnoreUnmatchedProperties().Build(); internal static readonly IDeserializer ConfigurationReader = ((BuilderSkeleton<DeserializerBuilder>)new DeserializerBuilder()).WithNamingConvention(CamelCaseNamingConvention.Instance).WithDuplicateKeyChecking().Build(); } internal sealed class EncounterController : MonoBehaviour { internal const string PrefabName = "VE_EncounterController"; internal const string AltarKey = "VE_SourceAltar"; internal const string LinkKey = "VE_EncounterController"; internal const string PendingKey = "VE_PendingController"; internal const string ReturnKey = "VE_ReturnPosition"; internal const string ReturnRotationKey = "VE_ReturnRotation"; internal const string RecoveryKey = "VE_RecoveryPosition"; internal const string MapKey = "VE_ExteriorPosition"; internal const string SourceIdentityKey = "VE_AltarIdentity"; internal const string ClosureReadyKey = "VE_ClosureReady"; internal static void Register() { //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Expected O, but got Unknown if (!((Object)(object)PrefabManager.Instance.GetPrefab("VE_EncounterController") != (Object)null)) { GameObject val = PrefabManager.Instance.CreateEmptyPrefab("VE_EncounterController", true); Object.DestroyImmediate((Object)(object)val.GetComponent<Collider>()); Object.DestroyImmediate((Object)(object)val.GetComponent<MeshRenderer>()); Object.DestroyImmediate((Object)(object)val.GetComponent<MeshFilter>()); val.GetComponent<ZNetView>().m_persistent = true; val.AddComponent<EncounterController>(); val.AddComponent<RoomRun>(); PrefabManager.Instance.AddPrefab(new CustomPrefab(val, false)); } } internal static bool IsController(ZDO record) { return record.GetPrefab() == StringExtensionMethods.GetStableHashCode("VE_EncounterController"); } internal static void ReconnectSavedEncounters() { //IL_00d9: 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_01c8: 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_024d: Unknown result type (might be due to invalid IL or missing references) Dictionary<string, ZDO[]> dictionary = (from record in ZDOMan.instance.m_objectsByID.Values where !IsController(record) && record.GetString("VE_AltarIdentity", "").Length != 0 && IsAltar(record) group record by record.GetString("VE_AltarIdentity", "")).ToDictionary((IGrouping<string, ZDO> group) => group.Key, (IGrouping<string, ZDO> group) => group.ToArray()); foreach (ZDO item in dictionary.Values.SelectMany((ZDO[] records) => records)) { item.Set("VE_EncounterController", ZDOID.None); item.Set("VE_PendingController", ZDOID.None); } List<ZDO> list = new List<ZDO>(); EncounterObjects.Reset(ZDOMan.instance); foreach (ZDO value2 in ZDOMan.instance.m_objectsByID.Values) { EncounterObjects.Track(value2); if (IsController(value2)) { list.Add(value2); } } EncounterPlacement.Reset(ZDOMan.instance, list); foreach (IGrouping<string, ZDO> item2 in from record in list group record by record.GetString("VE_AltarIdentity", "")) { ZDO[] array = item2.ToArray(); ZDO[] array2 = array; for (int num = 0; num < array2.Length; num++) { array2[num].Set("VE_SourceAltar", ZDOID.None); } if (array.Length != 1 || !dictionary.TryGetValue(item2.Key, out var value) || value.Length != 1) { Debug.LogWarning((object)"[Valheim Endgame] Saved encounter retained: source altar is missing or ambiguous."); continue; } ZDO val = array[0]; ZDO val2 = value[0]; val.Set("VE_SourceAltar", val2.m_uid); if (val.GetBool("VE_Prepared", false)) { EncounterTravel.FinishStart(val2, val); } else { val2.Set("VE_EncounterController", val.m_uid); } } foreach (ZDO altar in dictionary.Values.SelectMany((ZDO[] records) => records)) { if (altar.GetBool("VE_ClosureReady", false) && !ZDOMan.instance.m_objectsByID.Values.Any((ZDO record) => IsController(record) && record.GetString("VE_AltarIdentity", "") == altar.GetString("VE_AltarIdentity", ""))) { FinishClosure(altar); } } } internal static void FinishClosure(ZDO altar) { //IL_0011: 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) altar.SetOwner(ZNet.GetUID()); altar.Set("VE_EncounterController", ZDOID.None); altar.Set("VE_Room_State", 0); altar.Set("VE_Room_Player", 0L); altar.Set("VE_Room_CleanupStatus", ""); altar.Set("VE_ClosureReady", false); ZDOMan.instance.ForceSendZDO(altar.m_uid); } internal static bool IsAltar(ZDO record) { GameObject prefab = ZNetScene.instance.GetPrefab(record.GetPrefab()); int num = record.GetInt(ZDOVars.s_location, 0); if ((Object)(object)prefab != (Object)null && (Object)(object)prefab.GetComponent<LocationProxy>() != (Object)null) { return num == StringExtensionMethods.GetStableHashCode("MWL_MapRoom1"); } return false; } internal static Transform Marker(Transform parent, string name, Vector3 position, Vector3 rotation = default(Vector3)) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001b: 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) Transform transform = new GameObject(name).transform; transform.SetParent(parent, false); transform.position = position; transform.rotation = Quaternion.Euler(rotation); return transform; } internal static void Bind(RoomRun run, ZDO record) { //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_001f: 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_0021: 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_004b: 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_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)run.ReturnPoint != (Object)null)) { Vector3 vec = record.GetVec3("VE_ReturnPosition", Vector3.zero); if (vec == Vector3.zero) { throw new InvalidOperationException("Encounter return position is missing."); } run.ReturnPoint = Marker(((Component)run).transform, "Return", vec, record.GetVec3("VE_ReturnRotation", Vector3.zero)); run.RecoveryPoint = Marker(((Component)run).transform, "Recovery", record.GetVec3("VE_RecoveryPosition", Vector3.zero)); } } } internal static class EncounterIdentity { internal const string CreatureKey = "VE_EncounterCreature"; private static readonly Dictionary<string, ZDOID> owners = new Dictionary<string, ZDOID>(); private static ZDOMan? manager; internal static RoomRun? LoadedOwner(ZDO record) { string run = record.GetString("VE_Encounter", ""); if (run.Length != 0) { return RoomRun.Loaded.FirstOrDefault((RoomRun room) => room.NetworkId != ZDOID.None && room.RunId == run); } return null; } internal static string AssignCreature(ZDO record) { string text = record.GetString("VE_EncounterCreature", ""); if (text.Length != 0) { return text; } text = Guid.NewGuid().ToString("N"); record.Set("VE_EncounterCreature", text); return text; } internal static ZDO? Owner(string run) { //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrEmpty(run) || ZDOMan.instance == null) { return null; } if (manager != ZDOMan.instance) { owners.Clear(); manager = ZDOMan.instance; } if (owners.TryGetValue(run, out var value)) { ZDO zDO = manager.GetZDO(value); if (zDO != null && zDO.GetString("VE_Room_RunId", "") == run) { return zDO; } owners.Remove(run); } ZDO val = RoomRun.Loaded.FirstOrDefault((RoomRun room) => room.NetworkId != ZDOID.None && room.RunId == run)?.HazardData ?? ((IEnumerable<ZDO>)manager.m_objectsByID.Values).FirstOrDefault((Func<ZDO, bool>)((ZDO record) => record.GetString("VE_Room_RunId", "") == run)); if (val != null) { owners[run] = val.m_uid; } return val; } } internal static class EncounterObjects { private const string Tag = "VE_Encounter"; private static readonly int TagHash = StringExtensionMethods.GetStableHashCode("VE_Encounter"); private static readonly Dictionary<string, HashSet<ZDOID>> runs = new Dictionary<string, HashSet<ZDOID>>(StringComparer.Ordinal); private static readonly Dictionary<ZDOID, (ZDO Record, string Run)> members = new Dictionary<ZDOID, (ZDO, string)>(); private static ZDOMan? world; internal static bool Available => Ready; private static bool Ready { get { if (world != null && world == ZDOMan.instance && (Object)(object)ZNet.instance != (Object)null) { return ZNet.instance.IsServer(); } return false; } } internal static void Clear() { world = null; runs.Clear(); members.Clear(); } internal static void Reset(ZDOMan manager) { Clear(); world = manager; } internal static void Track(ZDO record) { //IL_0015: 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_0097: 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) if (record.SaveClone || !Ready || world.GetZDO(record.m_uid) != record) { return; } string text = record.GetString("VE_Encounter", ""); if (members.TryGetValue(record.m_uid, out (ZDO, string) value)) { if (value.Item2 == text && value.Item1 == record) { return; } Remove(value.Item1); } if (text.Length != 0) { if (!runs.TryGetValue(text, out HashSet<ZDOID> value2)) { value2 = new HashSet<ZDOID>(); runs.Add(text, value2); } value2.Add(record.m_uid); members[record.m_uid] = (record, text); } } private static void Remove(ZDO record) { //IL_0006: 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_0041: Unknown result type (might be due to invalid IL or missing references) if (members.TryGetValue(record.m_uid, out (ZDO, string) value) && value.Item1 == record) { members.Remove(record.m_uid); HashSet<ZDOID> hashSet = runs[value.Item2]; hashSet.Remove(record.m_uid); if (hashSet.Count == 0) { runs.Remove(value.Item2); } } } internal static ZDO[] Records(string run) { //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_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) if (!Ready) { throw new InvalidOperationException("Saved encounter objects have not been indexed for this world."); } if (string.IsNullOrEmpty(run)) { throw new InvalidOperationException("Encounter identity is missing."); } if (!runs.TryGetValue(run, out HashSet<ZDOID> value)) { return Array.Empty<ZDO>(); } List<ZDO> list = new List<ZDO>(); ZDOID[] array = value.ToArray(); foreach (ZDOID val in array) { ZDO zDO = world.GetZDO(val); (ZDO, string) value2; if (zDO != null && zDO.IsValid() && zDO.GetString("VE_Encounter", "") == run) { list.Add(zDO); } else if (members.TryGetValue(val, out value2)) { Remove(value2.Item1); if (zDO != null && zDO.IsValid()) { Track(zDO); } } } return list.ToArray(); } [HarmonyPatch(typeof(ZDO), "Set", new Type[] { typeof(int), typeof(string) })] [HarmonyPostfix] private static void TagChanged(ZDO __instance, int hash) { if (hash == TagHash) { Track(__instance); } } [HarmonyPatch(typeof(ZDO), "Deserialize")] [HarmonyPostfix] private static void Received(ZDO __instance) { Track(__instance); } [HarmonyPatch(typeof(ZDO), "Reset")] [HarmonyPrefix] private static void Released(ZDO __instance) { if (!__instance.SaveClone) { Remove(__instance); } } } public sealed class EncounterMember { public long Id { get; set; } public string Name { get; set; } = ""; public int Deaths { get; set; } public bool Active { get; set; } } internal sealed class EncounterParty { internal const string Key = "VE_Room_Participants"; internal const string CompletionKey = "VE_Room_CompletionPlayers"; internal const string EntryKey = "VE_EncounterActiveRun"; internal const string LocalEntryKey = "warpalicious.ValheimEndgame.ActiveEncounter"; internal const string DeathRunKey = "VE_EncounterDeathRun"; internal const string DeathCountKey = "VE_EncounterDeaths"; private string runId = ""; private string source = ""; private string completionSource = ""; private Dictionary<long, EncounterMember> members = new Dictionary<long, EncounterMember>(); private HashSet<long> eligible = new HashSet<long>(); private bool dirty; internal IEnumerable<EncounterMember> Members => members.Values; internal int TotalDeaths { get; private set; } internal void Refresh(ZDO record, string run) { if (run != runId) { runId = run; source = ""; completionSource = ""; members.Clear(); eligible.Clear(); TotalDeaths = 0; dirty = false; } string text = record.GetString("VE_Room_Participants", ""); if (text != source) { members = Deserialize(text).ToDictionary((EncounterMember member) => member.Id); TotalDeaths = members.Values.Sum((EncounterMember member) => member.Deaths); source = text; dirty = false; } string text2 = record.GetString("VE_Room_CompletionPlayers", ""); if (text2 != completionSource) { eligible = new HashSet<long>(from member in Deserialize(text2) select member.Id); completionSource = text2; } } internal bool HasHistory(long id) { return members.ContainsKey(id); } internal bool IsActive(long id) { if (members.TryGetValue(id, out EncounterMember value)) { return value.Active; } return false; } internal bool IsEligible(long id) { return eligible.Contains(id); } internal int Deaths(long id) { if (!members.TryGetValue(id, out EncounterMember value)) { return 0; } return value.Deaths; } internal void Join(long id, string name) { if (!members.TryGetValue(id, out EncounterMember value)) { if (members.Count >= 64) { throw new InvalidOperationException("Encounter participant limit reached."); } value = new EncounterMember { Id = id }; members.Add(id, value); dirty = true; } if (value.Name != name) { value.Name = name; dirty = true; } SetActive(id, active: true); } internal void SetActive(long id, bool active) { if (members.TryGetValue(id, out EncounterMember value) && value.Active != active) { value.Active = active; dirty = true; } } internal void RecordDeaths(long id, int count) { if (members.TryGetValue(id, out EncounterMember value)) { count = Math.Max(0, Math.Min(1000, count)); if (count > value.Deaths) { TotalDeaths += count - value.Deaths; value.Deaths = count; dirty = true; } } } internal void Save(ZDO record) { if (dirty) { source = Yaml.Writer.Serialize((object)members.Values.ToList()); record.Set("VE_Room_Participants", source); dirty = false; } } internal void CaptureCompletion(ZDO record, Func<long, bool> aliveInside) { List<EncounterMember> list = (from member in members.Values where member.Active && aliveInside(member.Id) select new EncounterMember { Id = member.Id, Name = member.Name }).ToList(); completionSource = Yaml.Writer.Serialize((object)list); record.Set("VE_Room_CompletionPlayers", completionSource); eligible = new HashSet<long>(list.Select((EncounterMember member) => member.Id)); } internal static List<EncounterMember> ReadCompletion(ZDO record) { return Deserialize(record.GetString("VE_Room_CompletionPlayers", "")); } private static List<EncounterMember> Deserialize(string value) { if (value.Length == 0) { return new List<EncounterMember>(); } List<EncounterMember> list = Yaml.CompatibleReader.Deserialize<List<EncounterMember>>(value); if (list == null || list.Count > 64 || list.Any((EncounterMember member) => member == null || member.Id == 0L || member.Name == null || member.Deaths < 0 || member.Deaths > 1000) || list.Select((EncounterMember member) => member.Id).Distinct().Count() != list.Count) { throw new InvalidOperationException("Invalid encounter participants."); } return list; } } internal static class EncounterPlacement { internal const string RadiusKey = "VE_EncounterRadius"; private static ZDOMan? world; private static readonly List<ZDO> occupied = new List<ZDO>(); internal static void Remember(ZDO controller) { if (!occupied.Contains(controller)) { occupied.Add(controller); } } internal static void Reset(ZDOMan manager, IEnumerable<ZDO> controllers) { world = manager; occupied.Clear(); occupied.AddRange(controllers); } private static float WorldRadius() { Assembly assembly = Chainloader.PluginInfos.Values.Select((PluginInfo plugin) => ((object)plugin.Instance).GetType().Assembly).FirstOrDefault((Assembly assembly2) => assembly2.GetType("ExpandWorldSize.Configuration") != null); if (assembly == null) { return 10500f; } if (!(assembly.GetType("ExpandWorldSize.Configuration").GetProperty("WorldTotalRadius", BindingFlags.Static | BindingFlags.Public)?.GetValue(null) is float num) || float.IsNaN(num) || float.IsInfinity(num) || num < 1f) { throw new InvalidOperationException("Cannot read Expand World Size's effective world boundary."); } return num; } internal static void ValidateExisting(ZDO controller) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) float num = controller.GetFloat("VE_EncounterRadius", 0f); if (Utils.DistanceXZ(Vector3.zero, controller.GetPosition()) < WorldRadius() + num || !FitsSaveGrid(controller.GetPosition(), num)) { throw new InvalidOperationException("The world boundary now overlaps this encounter. Recover it at the altar before starting another."); } } internal static Vector3 Allocate(out float reservedRadius) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: 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_0142: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Unknown result type (might be due to invalid IL or missing references) SimulationDistance val = SimulationDistance.GetSimulationDistance(GraphicsSettingIntExtentions.GetRange((GraphicsSettingInt)8192).m_maxValue); int totalSimulationDistance = ((SimulationDistance)(ref val)).TotalSimulationDistance; val = ZNet.instance.GetSyncedSimulationDistance(); int num = Math.Max(totalSimulationDistance, ((SimulationDistance)(ref val)).TotalSimulationDistance); float maximumPlacementRadius = RoomAssets.MaximumPlacementRadius; if (maximumPlacementRadius <= 0f) { throw new InvalidOperationException("Room bounds are unavailable."); } reservedRadius = Mathf.Ceil((maximumPlacementRadius + (float)((num + 1) * 64) + 64f) / 64f) * 64f; float num2 = Mathf.Ceil((WorldRadius() + reservedRadius) / 64f) * 64f; if (world != ZDOMan.instance) { throw new InvalidOperationException("Saved encounter positions have not been read for this world."); } occupied.RemoveAll((ZDO record) => !record.IsValid() || world.GetZDO(record.m_uid) != record); int num3 = 0; int num4 = 0; while (num3 < 4096) { float num5 = num2 + (float)num4 * reservedRadius * 2f; int num6 = Mathf.Max(1, Mathf.FloorToInt((float)Math.PI / Mathf.Asin(reservedRadius / num5))); for (int num7 = 0; num7 < num6; num7++) { if (num3++ >= 4096) { break; } float num8 = (float)num7 * (float)Math.PI * 2f / (float)num6; Vector3 candidate = new Vector3(num5 * Mathf.Cos(num8), 5000f, num5 * Mathf.Sin(num8)); if (FitsSaveGrid(candidate, reservedRadius)) { float radius = reservedRadius; if (occupied.All((ZDO record) => Utils.DistanceXZ(candidate, record.GetPosition()) >= radius + record.GetFloat("VE_EncounterRadius", radius))) { return candidate; } } } num4++; } throw new InvalidOperationException("No separated encounter position fits the world's save grid."); } private static bool FitsSaveGrid(Vector3 position, float radius) { //IL_0007: 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_0071: 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_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) for (int i = 0; i < 4; i++) { int num = Mathf.FloorToInt((position.x + (((i & 1) == 0) ? (0f - radius) : radius) + 32f) / 64f); int num2 = Mathf.FloorToInt((position.z + (((i & 2) == 0) ? (0f - radius) : radius) + 32f) / 64f); if (num < -32768 || num > 32767 || num2 < -32768 || num2 > 32767) { return false; } Vector2s val = ZoneSystem.IndexToSector(ZoneSystem.SectorToIndex(num, num2).Sector); if (val.x != num || val.y != num2) { return false; } } return true; } } internal static class EncounterPopulation { internal static int[] Counts(IReadOnlyList<CreatureEntry> wave, TabletEffects effects) { float value; int[] counts = wave.Select((CreatureEntry entry) => Mathf.CeilToInt((float)entry.Amount * (effects.PopulationByRole.TryGetValue(entry.Role, out value) ? value : 1f))).ToArray(); int[] array = (from index in Enumerable.Range(0, wave.Count) where counts[index] > 0 && (wave[index].Role == "boss" || wave[index].Role == "miniboss") select index).ToArray(); if (array.Length != 0) { for (int num = 0; num < effects.ExtraBossesPerWave; num++) { counts[array[num % array.Length]]++; } } return counts; } } public sealed class EncounterReward { public string Run { get; set; } = ""; public int Tier { get; set; } public int Height { get; set; } public long CompletedAt { get; set; } public string Items { get; set; } = ""; public bool Collected { get; set; } [YamlIgnore] public bool Valid { get { if (Guid.TryParseExact(Run, "N", out var _) && Tier >= 1 && Tier <= 15 && Height >= 8 && Height <= 1250 && CompletedAt > 0) { long completedAt = CompletedAt; DateTime maxValue = DateTime.MaxValue; if (completedAt <= maxValue.Ticks) { return !string.IsNullOrEmpty(Items); } } return false; } } internal static string Save(Inventory inventory) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); inventory.Save(val); return Convert.ToBase64String(val.GetArray()); } internal Inventory Load() { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Expected O, but got Unknown //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Expected O, but got Unknown //IL_0052: Expected O, but got Unknown if (!Valid) { throw new InvalidOperationException("Invalid saved encounter reward."); } Inventory val = new Inventory($"Tier {Tier} encounter rewards", (Sprite)null, 8, Height); val.Load(new ZPackage(Convert.FromBase64String(Items))); Compact(val); return val; } internal static void Compact(Inventory inventory) { //IL_0018: 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) List<ItemData> allItemsInGridOrder = inventory.GetAllItemsInGridOrder(); for (int i = 0; i < allItemsInGridOrder.Count; i++) { allItemsInGridOrder[i].m_gridPos = new Vector2i(i % 8, i / 8); } inventory.SetHeight(Math.Max(1, (allItemsInGridOrder.Count + 7) / 8)); } } internal sealed class QuestService : MonoBehaviour { private CustomRPC rpc; private QuestData data = new QuestData(); private ZNet? network; private long world; private string path = ""; private bool ready; private bool itemsChecked; private string failure = ""; private Coroutine? requestTimeout; private Player? local; private readonly Dictionary<long, float> rateLimits = new Dictionary<long, float>(); internal IEnumerable<EncounterReward> EncounterRewards => from reward in Progress.EncounterRewards.Where((EncounterReward reward) => !EncounterRewardCollection.Collected(world, reward.Run)).Concat(EncounterRewardCollection.Pending(world)) group reward by reward.Run into @group select @group.First() into reward orderby reward.CompletedAt select reward; internal static QuestService? Instance { get; private set; } internal QuestCatalog Catalog { get; private set; } = new QuestCatalog(); internal QuestPlayer Progress { get; private set; } = new QuestPlayer(); internal string Status { get; private set; } = "Loading quests…"; internal bool Pending => requestTimeout != null; internal long World => world; internal static event Action? Changed; private static QuestPlayer PlayerSnapshot(QuestPlayer player) { return new QuestPlayer { Id = player.Id, Account = player.Account, Completed = new HashSet<int>(player.Completed), Claims = player.Claims.ToList(), EncounterRewards = player.EncounterRewards.Select((EncounterReward reward) => new EncounterReward { Run = reward.Run, Tier = reward.Tier, Height = reward.Height, CompletedAt = reward.CompletedAt }).ToList() }; } internal bool RecordEncounterRewards(ZDO record, string run, IEnumerable<EncounterMember> members) { if (!Ready() || !network.IsServer()) { return false; } if (data.RewardedRuns.Contains(run)) { return true; } EventDefinition definition = Yaml.CompatibleReader.Deserialize<EventDefinition>(record.GetString("VE_Room_Definition", "")); TabletEffects effects = Yaml.CompatibleReader.Deserialize<TabletEffects>(record.GetString("VE_Room_Effects", "")); Dictionary<long, EncounterReward> allocations = members.ToDictionary((EncounterMember m) => m.Id, delegate { Inventory val = RewardItems.Roll(definition.Rewards, definition.Tier, effects.LootBonusPercent); return new EncounterReward { Run = run, Tier = definition.Tier, Height = val.GetHeight(), CompletedAt = DateTime.UtcNow.Ticks, Items = EncounterReward.Save(val) }; }); return Commit(delegate(QuestData candidate) { foreach (KeyValuePair<long, EncounterReward> item in allocations) { candidate.Player(item.Key).EncounterRewards.Add(item.Value); } candidate.RewardedRuns.Add(run); }); } internal void ClaimEncounter(string run) { if (!Pending) { if (EncounterRewardCollection.Has(world, run)) { EncounterRewardCollection.Open(world, run); } else { Send("encounter", 0, run); } } } internal void ConfirmEncounter(string run) { if (Progress.EncounterRewards.Any((EncounterReward reward) => reward.Run == run)) { Send("encounterAck", 0, run); } } private void HandleEncounter(QuestPlayer progress, string action, string run, QuestReply reply) { EncounterReward encounterReward = progress.EncounterRewards.FirstOrDefault((EncounterReward r) => r.Run == run); if (encounterReward == null) { reply.Message = "This encounter reward is unavailable or already claimed."; } else if (action == "encounter") { reply.EncounterDelivery = encounterReward; } else if (!Commit(delegate(QuestData candidate) { candidate.Player(progress.Id).EncounterRewards.RemoveAll((EncounterReward r) => r.Run == run); })) { reply.Message = "Could not confirm the reward receipt. Reopen Rewards to retry."; } } private void Awake() { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown //IL_002e: Expected O, but got Unknown Instance = this; rpc = NetworkManager.Instance.AddRPC("TierQuests", new CoroutineHandler(ReceiveRequest), new CoroutineHandler(ReceiveReply)); ItemManager.OnItemsRegistered += ValidateServerItems; PrefabManager.OnPrefabsRegistered += ValidateServerItems; } private bool Ready() { ZNet instance = ZNet.instance; if ((Object)(object)instance == (Object)null || instance.GetWorld() == null) { return false; } long worldUID = instance.GetWorldUID(); if ((Object)(object)network == (Object)(object)instance && world == worldUID) { return ready; } ResetWorld(); network = instance; world = worldUID; path = Path.Combine(Paths.BepInExRootPath, "data", "ValheimEndgame", "quests-" + world + ".yaml"); try { if (instance.IsServer()) { data = QuestData.Load(path); try { Catalog = QuestCatalog.Read(Path.Combine(Paths.ConfigPath, "ValheimEndgame")); } catch (Exception ex) { failure = "Quest configuration unavailable. Check the server log."; Debug.LogError((object)("[Valheim Endgame] " + ex)); } } ready = true; } catch (Exception ex2) { Status = "Quest progress unavailable. Check the server log."; Debug.LogError((object)("[Valheim Endgame] " + ex2)); } QuestService.Changed?.Invoke(); return ready; } [HarmonyPostfix] [HarmonyPatch(typeof(ZNet), "Awake")] private static void EnterWorld() { Instance?.Ready(); } [HarmonyPrefix] [HarmonyPatch(typeof(ZNet), "OnDestroy")] private static void LeaveWorld() { Instance?.ResetWorld(); QuestService.Changed?.Invoke(); } [HarmonyPostfix] [HarmonyPatch(typeof(Game), "SpawnPlayer")] private static void PlayerSpawned(Player __result) { Instance?.RequestForPlayer(__result); } [HarmonyPostfix] [HarmonyPatch(typeof(Player), "SetIntro")] private static void IntroChanged(Player __instance, bool intro) { if (!intro) { Instance?.RequestForPlayer(__instance); } } private void RequestForPlayer(Player player) { if (!((Object)(object)player == (Object)null) && !((Object)(object)player != (Object)(object)Player.m_localPlayer) && !((Character)player).InIntro() && Ready() && !((Object)(object)player == (Object)(object)local)) { local = player; Request(); } } private void ValidateServerItems() { if (Ready() && network.IsServer() && !itemsChecked && (Object)(object)ZNetScene.instance != (Object)null && (Object)(object)ObjectDB.instance != (Object)null) { Catalog.ValidateItems(); itemsChecked = true; } } private void ResetWorld() { CancelRequestTimeout(); network = null; world = 0L; path = ""; ready = false; itemsChecked = false; local = null; data = new QuestData(); Catalog = new QuestCatalog(); Progress = new QuestPlayer(); rateLimits.Clear(); failure = ""; Status = "Loading quests…"; } private void CancelRequestTimeout() { if (requestTimeout != null) { ((MonoBehaviour)this).StopCoroutine(requestTimeout); } requestTimeout = null; } private IEnumerator RequestTimedOut() { yield return (object)new WaitForSecondsRealtime(10f); requestTimeout = null; Status = "Request timed out. Reopen the shop or try again."; QuestService.Changed?.Invoke(); } internal bool RecordCompletion(IEnumerable<EncounterMember> members, int tier) { if (!Ready() || !network.IsServer() || tier < 1 || tier > 15) { return false; } long[] ids = members.Select((EncounterMember m) => m.Id).Distinct().ToArray(); if (ids.All((long id) => data.Players.Any((QuestPlayer p) => p.Id == id && p.Completed.Contains(tier)))) { return true; } return Commit(delegate(QuestData candidate) { long[] array = ids; foreach (long id in array) { candidate.Player(id).Completed.Add(tier); } }); } private bool Commit(Action<QuestData> update) { try { QuestData questData = data.Copy(); update(questData); questData.Save(path); data = questData; return true; } catch (Exception ex) { Debug.LogError((object)("[Valheim Endgame] Quest ledger write failed: " + ex)); return false; } } internal void Request() { Send("status", 0, ""); } internal void Claim(int tier) { if (!Pending) { Send("claim", tier, ""); } } internal void SetProgress(int tier) { Send("setProgress", tier, ""); } private void Send(string action, int tier, string transaction) { //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Expected O, but got Unknown if (Ready() && !((Object)(object)Player.m_localPlayer == (Object)null) && ZRoutedRpc.instance != null) { CancelRequestTimeout(); requestTimeout = ((MonoBehaviour)this).StartCoroutine(RequestTimedOut()); Status = ""; QuestService.Changed?.Invoke(); if (network.IsServer()) { QuestReply reply = Handle(Player.m_localPlayer.GetPlayerID(), "local", admin: true, action, tier, transaction); Apply(reply); return; } ZPackage val = new ZPackage(); val.Write(action); val.Write(tier); val.Write(transaction); rpc.SendPackage(ZRoutedRpc.instance.GetServerPeerID(), val); } } private QuestReply Handle(long id, string account, bool admin, string action, int tier, string transaction) { ValidateServerItems(); QuestReply questReply = new QuestReply { World = world, Catalog = QuestCatalog.Serializer.Serialize((object)Catalog) }; QuestPlayer questPlayer = data.Players.FirstOrDefault((QuestPlayer p) => p.Id == id); if (questPlayer != null && questPlayer.Account.Length > 0 && questPlayer.Account != account) { questReply.Message = "Character identity belongs to a different account."; return questReply; } if ((questPlayer == null || questPlayer.Account.Length == 0) && !Commit(delegate(QuestData candidate) { candidate.Player(id).Account = account; })) { questReply.Message = "Could not save quest progress. Try again."; return questReply; } QuestPlayer questPlayer2 = data.Player(id); if ((action == "encounter" || action == "encounterAck") ? true : false) { HandleEncounter(questPlayer2, action, transaction, questReply); questReply.Player = PlayerSnapshot(data.Player(id)); return questReply; } QuestClaim questClaim = questPlayer2.Claims.FirstOrDefault((QuestClaim c) => c.Tier == tier); switch (action) { case "claim": { TierQuest quest = Catalog.Find(tier); if (!questPlayer2.Completed.Contains(tier)) { questReply.Message = "Complete this tier before claiming its reward."; } else if (questClaim != null && questClaim.Delivered) { questReply.Message = "Reward already claimed."; } else if (questClaim != null) { questReply.Delivery = questClaim; } else if (!itemsChecked || quest == null || quest.Error.Length != 0 || failure.Length != 0) { questReply.Message = "This tier's reward is unavailable. Check the server configuration."; } else if (Commit(delegate(QuestData candidate) { candidate.Player(id).Claims.Add(new QuestClaim { Tier = tier, Transaction = Guid.NewGuid().ToString("N"), Rewards = quest.Rewards }); })) { questReply.Delivery = data.Player(id).Claims.Single((QuestClaim c) => c.Tier == tier); } else { questReply.Message = "Could not save the claim. Try again."; } break; } case "setProgress": if (!admin) { questReply.Message = "Only an admin can set Endgame progress."; } else if (tier < 0 || tier > 15) { questReply.Message = "Progress tier must be 0–15."; } else if (Commit(delegate(QuestData candidate) { QuestPlayer questPlayer3 = candidate.Player(id); questPlayer3.Completed = new HashSet<int>(Enumerable.Range(1, tier)); questPlayer3.Claims.RemoveAll((QuestClaim c) => c.Tier > tier); })) { questReply.Message = $"Progress tier set to {tier}."; } else { questReply.Message = "Could not save progress. Try again."; } break; case "ack": if (questClaim == null || questClaim.Transaction != transaction) { questReply.Message = "Unknown reward receipt."; } else if (!questClaim.Delivered && !Commit(delegate(QuestData candidate) { candidate.Player(id).Claims.Single((QuestClaim c) => c.Tier == tier).Delivered = true; })) { questReply.Message = "Receipt saved on your character. Retry to confirm it with the server."; } break; case "status": questReply.Delivery = questPlayer2.Claims.FirstOrDefault((QuestClaim c) => !c.Delivered); break; default: questReply.Message = "Unknown quest request."; break; } questReply.Player = PlayerSnapshot(data.Player(id)); if (questReply.Message.Length == 0 && failure.Length != 0) { questReply.Message = failure; } return questReply; } private IEnumerator ReceiveRequest(long sender, ZPackage package) { if (!Ready() || !network.IsServer()) { yield break; } ZNetPeer val = ((IEnumerable<ZNetPeer>)network.GetPeers()).FirstOrDefault((Func<ZNetPeer, bool>)((ZNetPeer p) => p.IsReady() && p.m_uid == sender)); if (val == null) { yield break; } ZDO zDO = ZDOMan.instance.GetZDO(val.m_characterID); long num = ((zDO != null) ? zDO.GetLong(ZDOVars.s_playerID, 0L) : 0); if (num == 0L) { yield break; } try { string text = package.ReadString(); int tier = package.ReadInt(); string text2 = package.ReadString(); bool flag = ((text == "ack" || text == "encounterAck") ? true : false); if (flag || !rateLimits.TryGetValue(sender, out var value) || !(Time.unscaledTime - value < 0.2f)) { rateLimits[sender] = Time.unscaledTime; if (text2.Length <= 32) { string hostName = val.m_socket.GetHostName(); QuestReply questReply = Handle(num, hostName, network.IsAdmin(hostName), text, tier, text2); ZPackage val2 = new ZPackage(); val2.Write(QuestCatalog.Serializer.Serialize((object)questReply)); rpc.SendPackage(sender, val2); } } } catch (Exception ex) { Debug.LogWarning((object)("[Valheim Endgame] Quest request rejected: " + ex.Message)); } } private IEnumerator ReceiveReply(long sender, ZPackage package) { if (!Ready() || network.IsServer() || ZRoutedRpc.instance == null || sender != ZRoutedRpc.instance.GetServerPeerID()) { yield break; } try { Apply(QuestCatalog.Deserializer.Deserialize<QuestReply>(package.ReadString())); } catch (Exception ex) { CancelRequestTimeout(); Status = "Invalid quest response."; QuestService.Changed?.Invoke(); Debug.LogWarning((object)("[Valheim Endgame] " + ex)); } } private void Apply(QuestReply reply) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || reply.World != world) { return; } CancelRequestTimeout(); Status = reply.Message; if (reply.Player.Id != localPlayer.GetPlayerID()) { QuestService.Changed?.Invoke(); return; } int num = ((Progress.Id == localPlayer.GetPlayerID()) ? Progress.ProgressTier() : (-1)); Catalog = QuestCatalog.Load(reply.Catalog); Catalog.ValidateItems(logErrors: false); Progress = reply.Player; QuestService.Changed?.Invoke(); if (num >= 0 && Progress.ProgressTier() > num && Status.Length == 0) { ((Character)localPlayer).Message((MessageType)2, $"Progress tier {Progress.ProgressTier()}. Dungeon Master tablets: {TabletProgression.Describe(Progress.ProgressTier())}.", 0, (Sprite)null, false); } if (reply.EncounterDelivery != null) { try { EncounterRewardCollection.Receive(localPlayer, world, reply.EncounterDelivery); EncounterRewardCollection.Open(world, reply.EncounterDelivery.Run); } catch (Exception ex) { Status = ex.Message; QuestService.Changed?.Invoke(); ((Character)localPlayer).Message((MessageType)2, Status, 0, (Sprite)null, false); } } if (reply.Delivery != null) { try { QuestDelivery.Deliver(localPlayer, world, reply.Delivery); Send("ack", reply.Delivery.Tier, reply.Delivery.Transaction); return; } catch (Exception ex2) { Status = ex2.Message; QuestService.Changed?.Invoke(); ((Character)localPlayer).Message((MessageType)2, Status, 0, (Sprite)null, false); return; } } if (Status.Length > 0) { ((Character)localPlayer).Message((MessageType)2, Status, 0, (Sprite)null, false); } } private void OnDestroy() { ItemManager.OnItemsRegistered -= ValidateServerItems; PrefabManager.OnPrefabsRegistered -= ValidateServerItems; CancelRequestTimeout(); if ((Object)(object)Instance == (Object)(object)this) { Instance = null; } } } internal sealed class EncounterRewardCollection : MonoBehaviour { private const string KeyPrefix = "warpalicious.ValheimEndgame.EncounterRewards."; private static EncounterRewardCollection? active; private static InventoryGrid? compactedGrid; private Inventory inventory; private EncounterReward reward; private Player player; private long world; private Container container; private static List<string> savedFinished = new List<string>(); private static List<EncounterReward> Read(Player player, long world) { if (!player.m_customData.TryGetValue("warpalicious.ValheimEndgame.EncounterRewards." + world, out var value)) { return new List<EncounterReward>(); } return QuestCatalog.Deserializer.Deserialize<List<EncounterReward>>(value); } private static void Write(Player player, long world, List<EncounterReward> rewards) { player.m_customData["warpalicious.ValheimEndgame.EncounterRewards." + world] = QuestCatalog.Serializer.Serialize((object)rewards); } internal static bool Has(long world, string run) { if ((Object)(object)Player.m_localPlayer != (Object)null) { return Read(Player.m_localPlayer, world).Any((EncounterReward reward) => reward.Run == run); } return false; } internal static bool Collected(long world, string run) { if ((Object)(object)Player.m_localPlayer != (Object)null) { return Read(Player.m_localPlayer, world).Any((EncounterReward reward) => reward.Run == run && reward.Collected); } return false; } internal static IEnumerable<EncounterReward> Pending(long world) { if (!((Object)(object)Player.m_localPlayer == (Object)null)) { return from reward in Read(Player.m_localPlayer, world) where !reward.Collected select reward; } return Enumerable.Empty<EncounterReward>(); } internal static void Receive(Player player, long world, EncounterReward reward) { List<EncounterReward> list = Read(player, world); if (list.All((EncounterReward existing) => existing.Run != reward.Run)) { reward.Collected = reward.Load().NrOfItems() == 0; list.Add(reward); Write(player, world, list); } } internal static void Open(long world, string run) { //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Expected O, but got Unknown if ((Object)(object)active != (Object)null) { return; } Player localPlayer = Player.m_localPlayer; EncounterReward encounterReward = Read(localPlayer, world).FirstOrDefault((EncounterReward r) => r.Run == run); if (encounterReward == null) { return; } Inventory val = encounterReward.Load(); if (val.NrOfItems() != 0) { StoreGui instance = StoreGui.instance; if (instance != null) { instance.Hide(); } GameObject val2 = new GameObject("Personal encounter rewards"); val2.transform.SetParent(((Component)localPlayer).transform, false); EncounterRewardCollection encounterRewardCollection = val2.AddComponent<EncounterRewardCollection>(); encounterRewardCollection.player = localPlayer; encounterRewardCollection.world = world; encounterRewardCollection.reward = encounterReward; encounterRewardCollection.inventory = val; encounterRewardCollection.container = val2.AddComponent<Container>(); active = encounterRewardCollection; val.m_onChanged = (Action)Delegate.Combine(val.m_onChanged, new Action(encounterRewardCollection.InventoryChanged)); InventoryGui.instance.Show(encounterRewardCollection.container, 1); } } private void InventoryChanged() { EncounterReward.Compact(inventory); UpdateReceipt(); } private void UpdateReceipt() { reward.Items = EncounterReward.Save(inventory); reward.Collected = inventory.NrOfItems() == 0; List<EncounterReward> list = Read(player, world); int num = list.FindIndex((EncounterReward existing) => existing.Run == reward.Run); if (num < 0) { throw new InvalidOperationException("The reward receipt is missing."); } list[num] = reward; Write(player, world, list); } [HarmonyPostfix] [HarmonyPatch(typeof(PlayerProfile), "SavePlayerData")] private static void CharacterSerialized(Player player) { QuestService instance = QuestService.Instance; savedFinished = (((Object)(object)player == (Object)(object)Player.m_localPlayer && (Object)(object)instance != (Object)null) ? (from reward in Read(player, instance.World) where reward.Collected select reward.Run).ToList() : new List<string>()); } [HarmonyPostfix] [HarmonyPatch(typeof(PlayerProfile), "Save")] private static void CharacterSaved(bool __result) { if (__result) { foreach (string item in savedFinished) { QuestService.Instance?.ConfirmEncounter(item); } } savedFinished.Clear(); } private void OnDestroy() { if (inventory != null) { Inventory obj = inventory; obj.m_onChanged = (Action)Delegate.Remove(obj.m_onChanged, new Action(InventoryChanged)); } if ((Object)(object)active == (Object)(object)this) { RestoreGrid(); active = null; } } [HarmonyPrefix] [HarmonyPatch(typeof(Container), "Awake")] private static bool InitializeContainer(Container __instance) { EncounterRewardCollection component = ((Component)__instance).GetComponent<EncounterRewardCollection>(); if ((Object)(object)component == (Object)null) { return true; } __instance.m_inventory = component.inventory; return false; } [HarmonyPrefix] [HarmonyPatch(typeof(Container), "IsOwner")] private static bool IsOwner(Container __instance, ref bool __result) { if ((Object)(object)((Component)__instance).GetComponent<EncounterRewardCollection>() == (Object)null) { return true; } __result = true; return false; } [HarmonyPrefix] [HarmonyPatch(typeof(Container), "SetInUse")] private static bool LocalContainer(Container __instance) { return (Object)(object)((Component)__instance).GetComponent<EncounterRewardCollection>() == (Object)null; } [HarmonyPrefix] [HarmonyPatch(typeof(Container), "StackAll")] private static bool NoStackContainer(Container __instance) { return LocalContainer(__instance); } [HarmonyPrefix] [HarmonyPatch(typeof(Container), "TakeAll")] private static bool TakeAll(Container __instance, Humanoid character, ref bool __result) { if ((Object)(object)((Component)__instance).GetComponent<EncounterRewardCollection>() == (Object)null) { return true; } character.GetInventory().MoveAll(__instance.GetInventory()); __result = true; return false; } private static bool Collecting(InventoryGui gui) { if ((Object)(object)active != (Object)null) { return (Object)(object)gui.m_currentContainer == (Object)(object)active.container; } return false; } [HarmonyPrefix] [HarmonyPatch(typeof(InventoryGui), "UpdateContainer")] private static void RestoreOtherContainerSlots(InventoryGui __instance) { if (!Collecting(__instance)) { RestoreGrid(); } } [HarmonyPostfix] [HarmonyPatch(typeof(InventoryGui), "UpdateContainer")] private static void UpdateRewardSlots(InventoryGui __instance) { //IL_0075: 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) if (Collecting(__instance)) { InventoryGrid containerGrid = __instance.m_containerGrid; if ((Object)(object)compactedGrid != (Object)(object)containerGrid) { RestoreGrid(); } compactedGrid = containerGrid; int num = active.inventory.NrOfItems(); int widt