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 Runecarved v1.0.0
IzzemisMod.dll
Decompiled a week ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Diagnostics; using System.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.Permissions; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using Jotunn; using Jotunn.Configs; using Jotunn.Entities; using Jotunn.Managers; using Jotunn.Utils; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("Runecarved")] [assembly: AssemblyDescription("Rarity tiers, runes, and per-item modifiers for Valheim equipment.")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Izzemi")] [assembly: AssemblyProduct("Runecarved")] [assembly: AssemblyCopyright("Copyright © 2026 Izzemi")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("e3243d22-4307-4008-ba36-9f326008cde5")] [assembly: AssemblyFileVersion("1.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] namespace Runecarved; internal static class ArmourModifiers { private static IEnumerable<ItemData> Pieces(Player player) { yield return ((Humanoid)player).m_helmetItem; yield return ((Humanoid)player).m_chestItem; yield return ((Humanoid)player).m_legItem; yield return ((Humanoid)player).m_shoulderItem; } public static float Sum(Player player, ModifierEffectType effectType) { float num = 0f; if ((Object)(object)player == (Object)null) { return num; } foreach (ItemData item in Pieces(player)) { if (item != null && ModifierCatalog.TryGetModifierValue(item, effectType, out var _, out var value)) { num += value; } } return num; } public static float SumSkill(Player player, SkillType skill) { //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) float num = 0f; if ((Object)(object)player == (Object)null) { return num; } foreach (ItemData item in Pieces(player)) { if (item == null) { continue; } foreach (ModifierDefinition item2 in ModifierCatalog.All) { if (item2.EffectType == ModifierEffectType.SkillLevel && item2.Skill == skill && item2.IsAllowedOn(item) && ItemModifierDataStore.TryGetModifierValue(item, item2.Id, out var value)) { num += value; } } } return num; } } [HarmonyPatch(typeof(SEMan), "ModifyHealthRegen")] internal static class HealthRegenModifierPatch { private static readonly FieldRef<SEMan, Character> Owner = AccessTools.FieldRefAccess<SEMan, Character>("m_character"); private static void Postfix(SEMan __instance, ref float regenMultiplier) { Character obj = Owner.Invoke(__instance); regenMultiplier += ArmourModifiers.Sum((Player)(object)((obj is Player) ? obj : null), ModifierEffectType.HealthRegen); } } [HarmonyPatch(typeof(SEMan), "ModifyStaminaRegen")] internal static class StaminaRegenModifierPatch { private static readonly FieldRef<SEMan, Character> Owner = AccessTools.FieldRefAccess<SEMan, Character>("m_character"); private static void Postfix(SEMan __instance, ref float staminaMultiplier) { Character obj = Owner.Invoke(__instance); staminaMultiplier += ArmourModifiers.Sum((Player)(object)((obj is Player) ? obj : null), ModifierEffectType.StaminaRegen); } } [HarmonyPatch(typeof(Player), "GetTotalFoodValue")] internal static class MaxHealthModifierPatch { private static void Postfix(Player __instance, ref float hp) { hp += ArmourModifiers.Sum(__instance, ModifierEffectType.MaxHealth); } } [HarmonyPatch(typeof(Player), "GetEquipmentDodgeStaminaModifier")] internal static class DodgeStaminaModifierPatch { private static void Postfix(Player __instance, ref float __result) { __result += ArmourModifiers.Sum(__instance, ModifierEffectType.DodgeStaminaCost); } } [HarmonyPatch(typeof(Skills), "GetSkillLevel")] internal static class SkillLevelModifierPatch { private static readonly FieldRef<Skills, Player> Owner = AccessTools.FieldRefAccess<Skills, Player>("m_player"); private static void Postfix(Skills __instance, SkillType skillType, ref float __result) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Invalid comparison between Unknown and I4 //IL_0019: Unknown result type (might be due to invalid IL or missing references) if ((int)skillType > 0) { __result += ArmourModifiers.SumSkill(Owner.Invoke(__instance), skillType); } } } [HarmonyPatch(typeof(Player), "OnDamaged")] internal static class DamagedArmourModifierPatch { private static bool _thornsActive; private static void Postfix(Player __instance, HitData hit) { //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Expected O, but got Unknown if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer || hit.GetTotalDamage() <= 0f) { return; } float num = ArmourModifiers.Sum(__instance, ModifierEffectType.AdrenalineOnHit); if (num > 0f) { ((Character)__instance).AddAdrenaline(num); } Character attacker = hit.GetAttacker(); float num2 = ArmourModifiers.Sum(__instance, ModifierEffectType.Thorns); if (_thornsActive || num2 <= 0f || (Object)(object)attacker == (Object)null || (Object)(object)attacker == (Object)(object)__instance) { return; } float num3 = num2 * ((Character)__instance).GetBodyArmor(); if (num3 <= 0f) { return; } _thornsActive = true; try { HitData val = new HitData { m_point = attacker.GetCenterPoint() }; Vector3 val2 = ((Component)attacker).transform.position - ((Component)__instance).transform.position; val.m_dir = ((Vector3)(ref val2)).normalized; val.m_hitType = (HitType)2; HitData val3 = val; val3.m_damage.m_pierce = num3; val3.SetAttacker((Character)(object)__instance); attacker.Damage(val3); } finally { _thornsActive = false; } } } internal static class BloodMagicModifierHelpers { public static float GetBaseHealthCost(float healthCost, float healthCostPercentage, Humanoid character, ItemData weapon) { //IL_005b: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)character == (Object)null || weapon?.m_shared == null) { return 0f; } float num = healthCost + ((Character)character).GetHealth() * healthCostPercentage / 100f; if (num <= 0f) { return 0f; } float skillFactor = ((Character)character).GetSkillFactor(weapon.m_shared.m_skillType); return Math.Max(0f, num - num * 0.33f * skillFactor); } public static float GetStaminaConversion(ItemData weapon, float baseHealthCost) { ModifierDefinition definition; float value; return (weapon != null && ModifierCatalog.TryGetModifierValue(weapon, ModifierEffectType.BloodHealthToStamina, out definition, out value)) ? (baseHealthCost * value) : 0f; } public static bool TryGetLowHealthBonus(ItemData weapon, Character character, out float bonus) { bonus = 0f; ModifierDefinition definition; return weapon != null && (Object)(object)character != (Object)null && character.GetHealth() < character.GetMaxHealth() * 0.5f && ModifierCatalog.TryGetModifierValue(weapon, ModifierEffectType.LowHealthEffect, out definition, out bonus); } } [HarmonyPatch(typeof(Attack), "GetAttackHealth")] internal static class BloodMagicHealthCostModifierPatch { private static void Postfix(Attack __instance, ref float __result, float ___m_attackHealth, float ___m_attackHealthPercentage) { ItemData weapon = __instance.m_weapon; if (weapon != null) { float num = 0f; if (ModifierCatalog.TryGetModifierValue(weapon, ModifierEffectType.BloodHealthCost, out var definition, out var value)) { num += value; } if (ModifierCatalog.TryGetModifierValue(weapon, ModifierEffectType.BloodHealthToStamina, out definition, out var value2)) { num += value2; } float baseHealthCost = BloodMagicModifierHelpers.GetBaseHealthCost(___m_attackHealth, ___m_attackHealthPercentage, __instance.m_character, weapon); __result = Math.Max(0f, baseHealthCost * (1f - num)); } } } [HarmonyPatch(typeof(Attack), "GetAttackStamina")] internal static class BloodMagicStaminaConversionPatch { private static void Postfix(Attack __instance, ref float __result, float ___m_attackHealth, float ___m_attackHealthPercentage) { ItemData weapon = __instance.m_weapon; if (weapon != null) { float baseHealthCost = BloodMagicModifierHelpers.GetBaseHealthCost(___m_attackHealth, ___m_attackHealthPercentage, __instance.m_character, weapon); __result += BloodMagicModifierHelpers.GetStaminaConversion(weapon, baseHealthCost); } } } [HarmonyPatch(typeof(Projectile), "Setup")] internal static class BloodMagicLowHealthDamagePatch { private static void Postfix(ItemData item, Character owner, ref DamageTypes ___m_damage) { if (BloodMagicModifierHelpers.TryGetLowHealthBonus(item, owner, out var bonus)) { ((DamageTypes)(ref ___m_damage)).Modify(1f + bonus); } } } [HarmonyPatch(typeof(SE_Shield), "Setup")] internal static class BloodMagicLowHealthShieldPatch { private static readonly FieldRef<SE_Shield, float> TotalAbsorbDamage = AccessTools.FieldRefAccess<SE_Shield, float>("m_totalAbsorbDamage"); private static void Postfix(SE_Shield __instance, Character character) { Humanoid val = (Humanoid)(object)((character is Humanoid) ? character : null); if (val != null && BloodMagicModifierHelpers.TryGetLowHealthBonus(val.GetCurrentWeapon(), character, out var bonus)) { TotalAbsorbDamage.Invoke(__instance) *= 1f + bonus; } } } [HarmonyPatch(typeof(ItemData), "GetDamage", new Type[] { typeof(int), typeof(float) })] internal static class ItemModifierDamagePatch { private static void Postfix(ItemData __instance, ref DamageTypes __result) { if (__instance == null) { return; } foreach (ModifierDefinition item in ModifierCatalog.All) { if (item.DamageChannel != ModifierDamageChannel.None && item.IsAllowedOn(__instance) && ItemModifierDataStore.TryGetModifierValue(__instance, item.Id, out var value)) { item.ApplyDamage(ref __result, value); } } } } [Serializable] public sealed class ModifierRoll { public string Id; public float Value; } [Serializable] public sealed class ItemModifierData { public int Version = 1; public string RarityId; public ModifierRoll[] Modifiers = new ModifierRoll[0]; public bool HelTouched; } internal static class ItemModifierDataStore { private const string CustomDataPrefix = "com.izzemi.myfirstmod.variant"; private const string LegacyCustomDataKey = "com.izzemi.myfirstmod.variant"; private const string VersionKey = "com.izzemi.myfirstmod.variant.version"; private const string RarityKey = "com.izzemi.myfirstmod.variant.rarity"; private const string HelKey = "com.izzemi.myfirstmod.variant.hel"; private const string ModifierKeyPrefix = "com.izzemi.myfirstmod.variant.modifier."; public static bool TryGetRarityId(ItemData item, out string rarityId) { if (item == null) { throw new ArgumentNullException("item"); } rarityId = null; if (item.m_customData == null) { return false; } if (item.m_customData.TryGetValue("com.izzemi.myfirstmod.variant.rarity", out rarityId)) { return !string.IsNullOrWhiteSpace(rarityId); } if (!TryLoad(item, out var data) || string.IsNullOrWhiteSpace(data.RarityId)) { return false; } rarityId = data.RarityId; return true; } public static void Clear(ItemData item) { if (item == null) { throw new ArgumentNullException("item"); } if (item.m_customData == null) { return; } List<string> list = new List<string>(); foreach (string key in item.m_customData.Keys) { if (key == "com.izzemi.myfirstmod.variant" || key.StartsWith("com.izzemi.myfirstmod.variant.", StringComparison.Ordinal)) { list.Add(key); } } foreach (string item2 in list) { item.m_customData.Remove(item2); } } public static bool TryGetModifierValue(ItemData item, string modifierId, out float value) { if (item == null) { throw new ArgumentNullException("item"); } value = 0f; if (item.m_customData == null || !item.m_customData.TryGetValue("com.izzemi.myfirstmod.variant.modifier." + modifierId, out var value2)) { return false; } if (float.TryParse(value2, NumberStyles.Float, CultureInfo.InvariantCulture, out value)) { return true; } Logger.LogError((object)("Invalid saved value '" + value2 + "' for variant modifier '" + modifierId + "'.")); return false; } public static void Save(ItemData item, ItemModifierData data) { if (item == null) { throw new ArgumentNullException("item"); } if (data == null) { throw new ArgumentNullException("data"); } if (item.m_customData == null) { item.m_customData = new Dictionary<string, string>(); } List<string> list = new List<string>(); foreach (string key in item.m_customData.Keys) { if (key == "com.izzemi.myfirstmod.variant" || key.StartsWith("com.izzemi.myfirstmod.variant.", StringComparison.Ordinal)) { list.Add(key); } } foreach (string item2 in list) { item.m_customData.Remove(item2); } item.m_customData["com.izzemi.myfirstmod.variant.version"] = data.Version.ToString(CultureInfo.InvariantCulture); item.m_customData["com.izzemi.myfirstmod.variant.rarity"] = data.RarityId ?? string.Empty; if (data.HelTouched) { item.m_customData["com.izzemi.myfirstmod.variant.hel"] = "1"; } if (data.Modifiers == null) { return; } ModifierRoll[] modifiers = data.Modifiers; foreach (ModifierRoll modifierRoll in modifiers) { if (modifierRoll != null && !string.IsNullOrEmpty(modifierRoll.Id)) { item.m_customData["com.izzemi.myfirstmod.variant.modifier." + modifierRoll.Id] = modifierRoll.Value.ToString("R", CultureInfo.InvariantCulture); } } } public static bool TryLoad(ItemData item, out ItemModifierData data) { if (item == null) { throw new ArgumentNullException("item"); } data = null; if (item.m_customData == null) { return false; } if (item.m_customData.TryGetValue("com.izzemi.myfirstmod.variant", out var value)) { data = JsonUtility.FromJson<ItemModifierData>(value); } if (data == null && !item.m_customData.ContainsKey("com.izzemi.myfirstmod.variant.version") && !item.m_customData.ContainsKey("com.izzemi.myfirstmod.variant.rarity")) { return false; } data = data ?? new ItemModifierData(); if (item.m_customData.TryGetValue("com.izzemi.myfirstmod.variant.version", out var value2) && int.TryParse(value2, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) { data.Version = result; } if (item.m_customData.TryGetValue("com.izzemi.myfirstmod.variant.rarity", out var value3)) { data.RarityId = value3; } data.HelTouched = item.m_customData.ContainsKey("com.izzemi.myfirstmod.variant.hel"); List<ModifierRoll> list = new List<ModifierRoll>(); List<string> list2 = new List<string>(); foreach (KeyValuePair<string, string> customDatum in item.m_customData) { if (customDatum.Key.StartsWith("com.izzemi.myfirstmod.variant.modifier.", StringComparison.Ordinal)) { string text = customDatum.Key.Substring("com.izzemi.myfirstmod.variant.modifier.".Length); if (!ModifierCatalog.TryGetById(text, out var _)) { list2.Add(customDatum.Key); continue; } if (float.TryParse(customDatum.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result2)) { list.Add(new ModifierRoll { Id = text, Value = result2 }); continue; } Logger.LogError((object)("Invalid saved value '" + customDatum.Value + "' for variant modifier '" + text + "'.")); list2.Add(customDatum.Key); } } foreach (string item2 in list2) { item.m_customData.Remove(item2); } data.Modifiers = list.ToArray(); return true; } } internal static class MagicWeaponInfo { private static readonly Dictionary<string, bool> AoeCache = new Dictionary<string, bool>(); public static bool FiresProjectile(ItemData item) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Invalid comparison between Unknown and I4 Attack val = item?.m_shared?.m_attack; return val != null && (int)val.m_attackType == 2 && (Object)(object)val.m_attackProjectile != (Object)null; } public static bool HasAoe(ItemData item) { if (item?.m_shared == null) { return false; } string name = item.m_shared.m_name; if (AoeCache.TryGetValue(name, out var value)) { return value; } bool flag = AttackHasAoe(item.m_shared.m_attack) || AttackHasAoe(item.m_shared.m_secondaryAttack); AoeCache[name] = flag; return flag; } private static bool AttackHasAoe(Attack attack) { GameObject val = attack?.m_attackProjectile; if ((Object)(object)val == (Object)null) { return false; } if ((Object)(object)val.GetComponentInChildren<Aoe>(true) != (Object)null) { return true; } Projectile[] componentsInChildren = val.GetComponentsInChildren<Projectile>(true); foreach (Projectile val2 in componentsInChildren) { if (val2.m_aoe > 0f || ((Object)(object)val2.m_spawnOnHit != (Object)null && (Object)(object)val2.m_spawnOnHit.GetComponentInChildren<Aoe>(true) != (Object)null)) { return true; } } return false; } } internal static class StaffShieldSource { private const string StaffPrefabName = "StaffShield"; private static SE_Shield _shield; private static SkillType _skill = (SkillType)10; private static bool _resolved; public static bool TryGet(out SE_Shield shield, out SkillType skill) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected I4, but got Unknown if (!_resolved && (Object)(object)ObjectDB.instance != (Object)null) { _resolved = true; Resolve(); } shield = _shield; skill = (SkillType)(int)_skill; return (Object)(object)_shield != (Object)null; } public static bool IsStaffOfProtection(ItemData item) { return (Object)(object)item?.m_dropPrefab != (Object)null && string.Equals(((Object)item.m_dropPrefab).name, "StaffShield", StringComparison.Ordinal); } private static void Resolve() { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Invalid comparison between Unknown and I4 //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("StaffShield"); ItemDrop val = (((Object)(object)itemPrefab != (Object)null) ? itemPrefab.GetComponent<ItemDrop>() : null); if ((Object)(object)val != (Object)null) { SharedData shared = val.m_itemData.m_shared; if ((int)shared.m_skillType > 0) { _skill = shared.m_skillType; } StatusEffect attackStatusEffect = shared.m_attackStatusEffect; _shield = (SE_Shield)(((object)((attackStatusEffect is SE_Shield) ? attackStatusEffect : null)) ?? ((object)/*isinst with value type is only supported in some contexts*/)); if ((Object)(object)_shield == (Object)null) { GameObject val2 = shared.m_attack?.m_attackProjectile; _shield = (((Object)(object)val2 != (Object)null) ? FindInProjectile(val2) : null); } } if ((Object)(object)_shield == (Object)null) { foreach (StatusEffect statusEffect in ObjectDB.instance.m_StatusEffects) { SE_Shield val3 = (SE_Shield)(object)((statusEffect is SE_Shield) ? statusEffect : null); if (val3 != null && ((Object)val3).name.IndexOf("shield", StringComparison.OrdinalIgnoreCase) >= 0) { _shield = val3; Logger.LogWarning((object)("Staff of Protection shield not found; using '" + ((Object)val3).name + "'.")); break; } } } if ((Object)(object)_shield == (Object)null) { Logger.LogError((object)"No shield status effect found for the shield modifier."); } } private static SE_Shield FindInProjectile(GameObject projectile) { Aoe[] componentsInChildren = projectile.GetComponentsInChildren<Aoe>(true); foreach (Aoe val in componentsInChildren) { SE_Shield val2 = Lookup(val.m_statusEffect) ?? Lookup(val.m_statusEffectIfPlayer); if ((Object)(object)val2 != (Object)null) { return val2; } } Projectile[] componentsInChildren2 = projectile.GetComponentsInChildren<Projectile>(true); foreach (Projectile val3 in componentsInChildren2) { SE_Shield val4 = Lookup(val3.m_statusEffect); if ((Object)(object)val4 == (Object)null && (Object)(object)val3.m_spawnOnHit != (Object)null) { val4 = FindInProjectile(val3.m_spawnOnHit); } if ((Object)(object)val4 != (Object)null) { return val4; } } return null; } private static SE_Shield Lookup(string name) { return (SE_Shield)(string.IsNullOrEmpty(name) ? null : /*isinst with value type is only supported in some contexts*/); } } [HarmonyPatch(typeof(Humanoid), "StartAttack")] internal static class ShieldOnUseModifierPatch { private static void Postfix(Humanoid __instance, bool __result) { //IL_0073: Unknown result type (might be due to invalid IL or missing references) if (__result && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer)) { ItemData currentWeapon = __instance.GetCurrentWeapon(); if (currentWeapon != null && ModifierCatalog.TryGetModifierValue(currentWeapon, ModifierEffectType.ShieldOnUse, out var _, out var value) && Random.value < value && StaffShieldSource.TryGet(out var shield, out var skill)) { ((Character)__instance).GetSEMan().AddStatusEffect((StatusEffect)(object)shield, true, currentWeapon.m_quality, ((Character)__instance).GetSkillLevel(skill), (short)(-1)); } } } } [HarmonyPatch(typeof(Attack), "OnAttackTrigger")] internal static class ShieldAllyHealModifierPatch { private static void Postfix(Attack __instance, Humanoid ___m_character) { //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)___m_character == (Object)null || (Object)(object)___m_character != (Object)(object)Player.m_localPlayer) { return; } ItemData weapon = __instance.GetWeapon(); if (weapon == null || !StaffShieldSource.IsStaffOfProtection(weapon) || !ModifierCatalog.TryGetModifierValue(weapon, ModifierEffectType.ShieldAllyHeal, out var _, out var value)) { return; } Vector3 position = ((Component)___m_character).transform.position; List<Player> list = new List<Player>(); Player.GetPlayersInRange(position, 10f, list); foreach (Player item in list) { if ((Object)(object)item != (Object)(object)___m_character && !((Character)item).IsDead()) { ((Character)item).Heal(value, true); } } foreach (Character allCharacter in Character.GetAllCharacters()) { if (!(allCharacter is Player) && allCharacter.IsTamed() && !allCharacter.IsDead() && Vector3.Distance(((Component)allCharacter).transform.position, position) <= 10f) { allCharacter.Heal(value, true); } } } } [HarmonyPatch(typeof(Humanoid), "StartAttack")] internal static class SummonRootsModifierPatch { private static ItemData _staff; private static bool _resolved; private static void Postfix(Humanoid __instance, bool __result) { //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: 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_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) if (!__result || (Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return; } ItemData currentWeapon = __instance.GetCurrentWeapon(); if (currentWeapon == null || !ModifierCatalog.TryGetModifierValue(currentWeapon, ModifierEffectType.SummonRoots, out var _, out var value) || Random.value >= value) { return; } ItemData staff = GetStaff(); Attack val = staff?.m_shared.m_attack; if (val != null && !((Object)(object)val.m_attackProjectile == (Object)null)) { Vector3 aimDir = __instance.GetAimDir(((Character)__instance).GetEyePoint()); Vector3 val2 = ((Character)__instance).GetEyePoint() + aimDir * 0.5f; GameObject val3 = Object.Instantiate<GameObject>(val.m_attackProjectile, val2, Quaternion.LookRotation(aimDir)); IProjectile component = val3.GetComponent<IProjectile>(); if (component != null) { component.Setup((Character)(object)__instance, aimDir * val.m_projectileVel, -1f, (HitData)null, staff, (ItemData)null); } } } private static ItemData GetStaff() { if (!_resolved && (Object)(object)ObjectDB.instance != (Object)null) { _resolved = true; GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("StaffGreenRoots"); _staff = (((Object)(object)itemPrefab != (Object)null) ? itemPrefab.GetComponent<ItemDrop>() : null)?.m_itemData; if (_staff == null) { Logger.LogError((object)"StaffGreenRoots not found; the summon roots modifier does nothing."); } } return _staff; } } [HarmonyPatch(typeof(Aoe), "Setup")] internal static class AoeRadiusModifierPatch { private static void Postfix(Aoe __instance, ItemData item) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_005c: 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_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) if (item != null && ModifierCatalog.TryGetModifierValue(item, ModifierEffectType.AoeRadius, out var definition, out var value)) { float num = definition.ApplyScalar(1f, value); if ((Object)(object)__instance.m_useCollider != (Object)null) { BoxCollider useCollider = __instance.m_useCollider; useCollider.size *= num; BoxCollider useCollider2 = __instance.m_useCollider; useCollider2.center *= num; } else { __instance.m_radius *= num; } Transform transform = ((Component)__instance).transform; transform.localScale *= num; } } } [HarmonyPatch(typeof(Projectile), "Setup")] internal static class ProjectileAoeModifierPatch { private static void Postfix(Projectile __instance, ItemData item) { if (item != null && __instance.m_aoe > 0f && ModifierCatalog.TryGetModifierValue(item, ModifierEffectType.AoeRadius, out var definition, out var value)) { __instance.m_aoe *= definition.ApplyScalar(1f, value); } } } [HarmonyPatch(typeof(Player), "GetEquipmentEitrRegenModifier")] internal static class HeldEitrRegenModifierPatch { private static void Postfix(Player __instance, ref float __result) { AddHeld(((Humanoid)__instance).m_rightItem, ref __result); if (((Humanoid)__instance).m_leftItem != ((Humanoid)__instance).m_rightItem) { AddHeld(((Humanoid)__instance).m_leftItem, ref __result); } } private static void AddHeld(ItemData item, ref float result) { if (item != null && ModifierCatalog.TryGetModifierValue(item, ModifierEffectType.EitrRegen, out var _, out var value)) { result += value; } } } [HarmonyPatch(typeof(Humanoid), "BlockAttack")] internal static class ParryRewardModifierPatch { private static void Postfix(Humanoid __instance, HitData hit, Character attacker, bool __result) { //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Expected O, but got Unknown if (!__result || (Object)(object)attacker == (Object)null) { return; } ItemData currentBlocker = __instance.GetCurrentBlocker(); if (currentBlocker != null && !(currentBlocker.m_shared.m_timedBlockBonus <= 1f) && __instance.m_blockTimer != -1f && !(__instance.m_blockTimer >= 0.25f)) { if (ModifierCatalog.TryGetModifierValue(currentBlocker, ModifierEffectType.ParryHealth, out var definition, out var value) && value > 0f) { ((Character)__instance).Heal(value, true); } if (ModifierCatalog.TryGetModifierValue(currentBlocker, ModifierEffectType.ParryStamina, out definition, out var value2) && value2 > 0f) { ((Character)__instance).AddStamina(value2); } if (!hit.m_ranged && ModifierCatalog.TryGetModifierValue(currentBlocker, ModifierEffectType.ParryDamage, out definition, out var value3) && value3 > 0f) { float blunt = currentBlocker.GetBlockPower(((Character)__instance).GetSkillFactor((SkillType)6)) * value3; HitData val = new HitData { m_point = attacker.GetCenterPoint() }; Vector3 val2 = ((Component)attacker).transform.position - ((Component)__instance).transform.position; val.m_dir = ((Vector3)(ref val2)).normalized; val.m_hitType = (HitType)((!((Character)__instance).IsPlayer()) ? 1 : 2); HitData val3 = val; val3.m_damage.m_blunt = blunt; val3.SetAttacker((Character)(object)__instance); attacker.Damage(val3); } } } } [HarmonyPatch(typeof(Projectile), "Setup")] internal static class ProjectileSpeedModifierPatch { private static void Prefix(ref Vector3 velocity, ItemData item) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) if (item != null && ModifierCatalog.TryGetModifierValue(item, ModifierEffectType.ProjectileSpeed, out var definition, out var value)) { velocity = definition.ApplyScalar(1f, value) * velocity; } } } [HarmonyPatch(typeof(Attack), "UseAmmo")] internal static class AmmoSaveModifierPatch { private static bool Prefix(Attack __instance, ref ItemData ammoItem, ref ItemData ___m_ammoItem, Humanoid ___m_character, ref bool __result) { //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Invalid comparison between Unknown and I4 ItemData weapon = __instance.m_weapon; if (weapon == null || string.IsNullOrWhiteSpace(weapon.m_shared.m_ammoType) || !ModifierCatalog.TryGetModifierValue(weapon, ModifierEffectType.AmmoSaveChance, out var _, out var value) || Random.value >= value) { return true; } Inventory inventory = ___m_character.GetInventory(); ItemData val = ___m_character.GetAmmoItem(); if (val != null && (!inventory.ContainsItem(val) || val.m_shared.m_ammoType != weapon.m_shared.m_ammoType)) { val = null; } if (val == null) { val = inventory.GetAmmoItem(weapon.m_shared.m_ammoType, (string)null); } if (val == null || (int)val.m_shared.m_itemType == 2) { return true; } ammoItem = val; ___m_ammoItem = val; __result = true; return false; } } [HarmonyPatch(typeof(Attack), "FireProjectileBurst")] internal static class ExtraProjectileModifierPatch { private const float SideAngle = 10f; private static readonly float[] ExtraAngles = new float[2] { -10f, 10f }; private static readonly FieldRef<Attack, bool> PerBurstUsage = AccessTools.FieldRefAccess<Attack, bool>("m_perBurstResourceUsage"); private static readonly Action<Attack> FireBurst = (Action<Attack>)Delegate.CreateDelegate(typeof(Action<Attack>), AccessTools.Method(typeof(Attack), "FireProjectileBurst", (Type[])null, (Type[])null)); internal static bool Firing; internal static float AngleOffset; private static void Postfix(Attack __instance) { ItemData weapon = __instance.m_weapon; if (Firing || weapon == null || PerBurstUsage.Invoke(__instance) || (!ModifierCatalog.TryGetModifierValue(weapon, ModifierEffectType.ExtraProjectileChance, out var definition, out var value) && !ModifierCatalog.TryGetModifierValue(weapon, ModifierEffectType.MagicExtraProjectile, out definition, out value)) || Random.value >= value) { return; } Firing = true; try { float[] extraAngles = ExtraAngles; foreach (float angleOffset in extraAngles) { AngleOffset = angleOffset; FireBurst(__instance); } } finally { AngleOffset = 0f; Firing = false; } } } [HarmonyPatch(typeof(Attack), "GetProjectileSpawnPoint")] internal static class ExtraProjectileAimPatch { private static void Postfix(ref Vector3 aimDir) { //IL_001b: 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_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) if (ExtraProjectileModifierPatch.AngleOffset != 0f) { aimDir = Quaternion.AngleAxis(ExtraProjectileModifierPatch.AngleOffset, Vector3.up) * aimDir; } } } [HarmonyPatch(typeof(Attack), "GetAttackStamina")] internal static class WeaponStaminaModifierPatch { private static void Postfix(Attack __instance, ref float __result) { ItemData weapon = __instance.m_weapon; if (weapon != null && ModifierCatalog.TryGetModifierValue(weapon, ModifierEffectType.AttackStaminaCost, out var definition, out var value)) { __result = definition.ApplyScalar(__result, value); } } } [HarmonyPatch(typeof(Attack), "GetAttackEitr", new Type[] { typeof(Character), typeof(ItemData) })] internal static class WeaponEitrModifierPatch { private static void Postfix(ItemData weapon, ref float __result) { if (weapon != null && ModifierCatalog.TryGetModifierValue(weapon, ModifierEffectType.AttackEitrCost, out var definition, out var value)) { __result = Math.Max(0f, definition.ApplyScalar(__result, value)); } } } [HarmonyPatch(typeof(ItemData), "GetDrawStaminaDrain")] internal static class BowDrawStaminaModifierPatch { private static void Postfix(ItemData __instance, ref float __result) { if (ModifierCatalog.TryGetModifierValue(__instance, ModifierEffectType.BowDrawStaminaCost, out var definition, out var value)) { __result = Math.Max(0f, definition.ApplyScalar(__result, value)); } } } [HarmonyPatch(typeof(ItemData), "GetWeaponLoadingTime")] internal static class CrossbowReloadModifierPatch { private static void Postfix(ItemData __instance, ref float __result) { if (ModifierCatalog.TryGetModifierValue(__instance, ModifierEffectType.ReloadTime, out var definition, out var value)) { __result = Math.Max(0f, definition.ApplyScalar(__result, value)); } } } [HarmonyPatch(typeof(Player), "GetEquipmentMovementModifier")] internal static class MovementPenaltyModifierPatch { private static void Postfix(Player __instance, ref float __result) { List<ItemData> list = new List<ItemData>(); AddEquippedItem(list, ((Humanoid)__instance).GetCurrentWeapon()); AddEquippedItem(list, ((Humanoid)__instance).m_leftItem); AddEquippedItem(list, ((Humanoid)__instance).m_rightItem); AddEquippedItem(list, ((Humanoid)__instance).m_helmetItem); AddEquippedItem(list, ((Humanoid)__instance).m_chestItem); AddEquippedItem(list, ((Humanoid)__instance).m_legItem); AddEquippedItem(list, ((Humanoid)__instance).m_shoulderItem); foreach (ItemData item in list) { if (item.m_shared.m_movementModifier < 0f && ModifierCatalog.TryGetModifierValue(item, ModifierEffectType.MovementPenalty, out var definition, out var value)) { __result += Math.Min(value, 0f - item.m_shared.m_movementModifier); } if (ModifierCatalog.TryGetModifierValue(item, ModifierEffectType.MovementSpeedBonus, out definition, out var value2)) { __result += value2; } } } private static void AddEquippedItem(List<ItemData> items, ItemData item) { if (item == null) { return; } foreach (ItemData item2 in items) { if (item2 == item) { return; } } items.Add(item); } } [HarmonyPatch(typeof(ItemData), "GetBaseBlockPower", new Type[] { typeof(int) })] internal static class BlockArmourModifierPatch { private static void Postfix(ItemData __instance, ref float __result) { if (ModifierCatalog.TryGetModifierValue(__instance, ModifierEffectType.BlockPower, out var definition, out var value)) { __result = definition.ApplyScalar(__result, value); } } } [HarmonyPatch(typeof(ItemData), "GetArmor", new Type[] { typeof(int), typeof(float) })] internal static class ArmourModifierPatch { private static void Postfix(ItemData __instance, ref float __result) { if (ModifierCatalog.TryGetModifierValue(__instance, ModifierEffectType.ArmourFlat, out var definition, out var value)) { __result = definition.ApplyScalar(__result, value); } if (ModifierCatalog.TryGetModifierValue(__instance, ModifierEffectType.ArmourPercentage, out var definition2, out var value2)) { __result = (float)Math.Ceiling(definition2.ApplyScalar(__result, value2)); } } } [HarmonyPatch(typeof(Humanoid), "BlockAttack")] internal static class ParryBonusModifierPatch { private struct State { public ItemData Item; public float OriginalTimedBlockBonus; public float OriginalBlockAdrenaline; public float OriginalPerfectBlockAdrenaline; public float OriginalBlockStaminaDrain; public float OriginalPerfectBlockStaminaDrain; public bool ModifiedTimedBlockBonus; public bool ModifiedAdrenaline; public bool ModifiedStamina; } private static void Prefix(Humanoid __instance, out State __state) { __state = default(State); ItemData currentBlocker = __instance.GetCurrentBlocker(); if (currentBlocker != null) { if (ModifierCatalog.TryGetModifierValue(currentBlocker, ModifierEffectType.ParryBonus, out var definition, out var value)) { __state.Item = currentBlocker; __state.OriginalTimedBlockBonus = currentBlocker.m_shared.m_timedBlockBonus; SharedData shared = currentBlocker.m_shared; shared.m_timedBlockBonus += value; __state.ModifiedTimedBlockBonus = true; } if (__instance.m_blockTimer != -1f && __instance.m_blockTimer < 0.25f && currentBlocker.m_shared.m_timedBlockBonus > 1f && ModifierCatalog.TryGetModifierValue(currentBlocker, ModifierEffectType.ParryAdrenaline, out definition, out var value2)) { __state.Item = currentBlocker; __state.OriginalBlockAdrenaline = currentBlocker.m_shared.m_blockAdrenaline; __state.OriginalPerfectBlockAdrenaline = currentBlocker.m_shared.m_perfectBlockAdrenaline; SharedData shared2 = currentBlocker.m_shared; shared2.m_blockAdrenaline += value2; SharedData shared3 = currentBlocker.m_shared; shared3.m_perfectBlockAdrenaline += value2; __state.ModifiedAdrenaline = true; } if (ModifierCatalog.TryGetModifierValue(currentBlocker, ModifierEffectType.BlockStaminaCost, out var definition2, out var value3)) { __state.OriginalBlockStaminaDrain = __instance.m_blockStaminaDrain; __state.OriginalPerfectBlockStaminaDrain = __instance.m_perfectBlockStaminaDrain; __instance.m_blockStaminaDrain = definition2.ApplyScalar(__instance.m_blockStaminaDrain, value3); __instance.m_perfectBlockStaminaDrain = definition2.ApplyScalar(__instance.m_perfectBlockStaminaDrain, value3); __state.ModifiedStamina = true; } } } private static Exception Finalizer(Humanoid __instance, State __state, Exception __exception) { if (__state.ModifiedTimedBlockBonus) { __state.Item.m_shared.m_timedBlockBonus = __state.OriginalTimedBlockBonus; } if (__state.ModifiedAdrenaline) { __state.Item.m_shared.m_blockAdrenaline = __state.OriginalBlockAdrenaline; __state.Item.m_shared.m_perfectBlockAdrenaline = __state.OriginalPerfectBlockAdrenaline; } if (__state.ModifiedStamina) { __instance.m_blockStaminaDrain = __state.OriginalBlockStaminaDrain; __instance.m_perfectBlockStaminaDrain = __state.OriginalPerfectBlockStaminaDrain; } return __exception; } } [HarmonyPatch(typeof(Character), "ApplyDamage", new Type[] { typeof(HitData), typeof(bool), typeof(bool), typeof(DamageModifier) })] internal static class ArmourElementalResistancePatch { private static void Prefix(Character __instance, HitData hit) { Player val = (Player)(object)((__instance is Player) ? __instance : null); if (val != null && hit != null) { ItemData[] armour = (ItemData[])(object)new ItemData[4] { ((Humanoid)val).m_helmetItem, ((Humanoid)val).m_chestItem, ((Humanoid)val).m_legItem, ((Humanoid)val).m_shoulderItem }; hit.m_damage.m_fire *= 1f - GetResistance(armour, ModifierEffectType.FireResistance); hit.m_damage.m_frost *= 1f - GetResistance(armour, ModifierEffectType.FrostResistance); hit.m_damage.m_lightning *= 1f - GetResistance(armour, ModifierEffectType.LightningResistance); hit.m_damage.m_poison *= 1f - GetResistance(armour, ModifierEffectType.PoisonResistance); } } private static float GetResistance(ItemData[] armour, ModifierEffectType effectType) { float num = 0f; foreach (ItemData item in armour) { if (ModifierCatalog.TryGetModifierValue(item, effectType, out var _, out var value)) { num = Math.Max(num, value); } } return Math.Max(0f, Math.Min(1f, num)); } } [HarmonyPatch(typeof(Attack), "DoMeleeAttack")] internal static class MeleeWeaponModifierPatch { private struct State { public ItemData Weapon; public float OriginalBackstabBonus; } private static void Prefix(Attack __instance, out State __state) { __state = default(State); ItemData weapon = __instance.m_weapon; if (weapon != null && ModifierCatalog.TryGetModifierValue(weapon, ModifierEffectType.BackstabBonus, out var _, out var value)) { __state.Weapon = weapon; __state.OriginalBackstabBonus = weapon.m_shared.m_backstabBonus; SharedData shared = weapon.m_shared; shared.m_backstabBonus += value; } } private static Exception Finalizer(State __state, Exception __exception) { if (__state.Weapon != null) { __state.Weapon.m_shared.m_backstabBonus = __state.OriginalBackstabBonus; } return __exception; } private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { MethodInfo addAdrenaline = AccessTools.Method(typeof(Character), "AddAdrenaline", new Type[1] { typeof(float) }, (Type[])null); MethodInfo addHitAdrenaline = AccessTools.Method(typeof(MeleeWeaponModifierPatch), "AddHitAdrenaline", (Type[])null, (Type[])null); bool patched = false; foreach (CodeInstruction instruction in instructions) { if (!patched && (instruction.opcode == OpCodes.Call || instruction.opcode == OpCodes.Callvirt) && object.Equals(instruction.operand, addAdrenaline)) { yield return new CodeInstruction(OpCodes.Ldarg_0, (object)null); yield return new CodeInstruction(OpCodes.Call, (object)addHitAdrenaline); patched = true; } else { yield return instruction; } } if (!patched) { throw new InvalidOperationException("Could not find the melee hit adrenaline call to apply weapon modifiers."); } } private static void AddHitAdrenaline(Character character, float amount, Attack attack) { ItemData weapon = attack.m_weapon; if (weapon != null && ModifierCatalog.TryGetModifierValue(weapon, ModifierEffectType.AdrenalineGain, out var _, out var value)) { amount += value; } character.AddAdrenaline(amount); } } [HarmonyPatch(typeof(Projectile), "OnHit", new Type[] { typeof(Collider), typeof(Vector3), typeof(bool), typeof(Vector3) })] internal static class ProjectileWeaponModifierPatch { private struct State { public float OriginalBackstabBonus; public float OriginalAdrenaline; public bool ModifiedBackstabBonus; public bool ModifiedAdrenaline; } private static void Prefix(Projectile __instance, out State __state) { __state = default(State); ItemData weapon = __instance.m_weapon; if (weapon != null) { if (ModifierCatalog.TryGetModifierValue(weapon, ModifierEffectType.BackstabBonus, out var definition, out var value)) { __state.OriginalBackstabBonus = __instance.m_backstabBonus; __instance.m_backstabBonus += value; __state.ModifiedBackstabBonus = true; } if (ModifierCatalog.TryGetModifierValue(weapon, ModifierEffectType.AdrenalineGain, out definition, out var value2)) { __state.OriginalAdrenaline = __instance.m_adrenaline; __instance.m_adrenaline += value2; __state.ModifiedAdrenaline = true; } } } private static Exception Finalizer(Projectile __instance, State __state, Exception __exception) { if (__state.ModifiedBackstabBonus) { __instance.m_backstabBonus = __state.OriginalBackstabBonus; } if (__state.ModifiedAdrenaline) { __instance.m_adrenaline = __state.OriginalAdrenaline; } return __exception; } } internal static class SummonWeaponInfo { public static bool HasSummonAbility(ItemData item) { return HasSummonAbility(item?.m_shared?.m_attack) || HasSummonAbility(item?.m_shared?.m_secondaryAttack) || IsKnownSummonWeapon(item); } private static bool IsKnownSummonWeapon(ItemData item) { string text = (((Object)(object)item?.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : string.Empty); string text2 = item?.m_shared?.m_name ?? string.Empty; return text.IndexOf("StaffSkeleton", StringComparison.OrdinalIgnoreCase) >= 0 || text2.IndexOf("staffskeleton", StringComparison.OrdinalIgnoreCase) >= 0 || text2.IndexOf("dead raiser", StringComparison.OrdinalIgnoreCase) >= 0; } private static bool HasSummonAbility(Attack attack) { return GetSpawnAbilities(attack).Length != 0; } internal static SpawnAbility[] GetSpawnAbilities(Attack attack) { GameObject val = attack?.m_attackProjectile; if ((Object)(object)val == (Object)null) { return Array.Empty<SpawnAbility>(); } List<SpawnAbility> list = new List<SpawnAbility>(val.GetComponentsInChildren<SpawnAbility>(true)); Projectile[] componentsInChildren = val.GetComponentsInChildren<Projectile>(true); foreach (Projectile val2 in componentsInChildren) { if ((Object)(object)val2.m_spawnOnHit != (Object)null) { list.AddRange(val2.m_spawnOnHit.GetComponentsInChildren<SpawnAbility>(true)); } } return list.ToArray(); } } [HarmonyPatch(typeof(SpawnAbility), "Setup")] internal static class SummonModifierSetupPatch { private static void Prefix(SpawnAbility __instance, ItemData item) { if (item == null) { return; } if (ModifierCatalog.TryGetModifierValue(item, ModifierEffectType.SummonMaxCount, out var definition, out var value)) { int num = Mathf.RoundToInt(value); if (__instance.m_maxSpawned > 0) { __instance.m_maxSpawned += num; } bool setMaxInstancesFromWeaponLevel = __instance.m_setMaxInstancesFromWeaponLevel; __instance.m_setMaxInstancesFromWeaponLevel = false; if (__instance.m_levelUpSettings != null) { foreach (LevelUpSettings levelUpSetting in __instance.m_levelUpSettings) { int num2 = (setMaxInstancesFromWeaponLevel ? item.m_quality : levelUpSetting.m_maxSpawns); levelUpSetting.m_maxSpawns = num2 + num; } } } if (ModifierCatalog.TryGetModifierValue(item, ModifierEffectType.ExtraSummonChance, out definition, out var value2) && Random.value < value2) { __instance.m_minToSpawn++; __instance.m_maxToSpawn++; } if (__instance.m_levelUpSettings == null || !ModifierCatalog.TryGetModifierValue(item, ModifierEffectType.ExtraSummonStars, out definition, out var value3) || !(Random.value < value3)) { return; } int num3 = Random.Range(1, 3); foreach (LevelUpSettings levelUpSetting2 in __instance.m_levelUpSettings) { levelUpSetting2.m_setLevel = Mathf.Min(3, levelUpSetting2.m_setLevel + num3); } } } [HarmonyPatch(typeof(InventoryGrid), "CreateItemTooltip")] internal static class ItemModifierTooltipPatch { private static bool Prefix(InventoryGrid __instance, ItemData item, UITooltip tooltip) { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0305: Unknown result type (might be due to invalid IL or missing references) //IL_030b: Unknown result type (might be due to invalid IL or missing references) string text = RuneEffects.DescribeValheimRune(item); if (text != null) { tooltip.Set(Localization.instance.Localize(item.m_shared.m_name), item.GetTooltip(-1).TrimEnd(Array.Empty<char>()) + "\n\n" + text, __instance.m_tooltipAnchor, default(Vector2)); return false; } if (item == null || !ItemModifierDataStore.TryLoad(item, out var data)) { return true; } string text2 = Localization.instance.Localize(item.m_shared.m_name); if (ItemRarityCatalog.TryGet(data.RarityId, out var definition)) { text2 = text2 + " <color=" + definition.TooltipColor + ">(" + definition.DisplayName + ")</color>"; } List<string> list = new List<string>(); if (data.Modifiers != null) { int num = definition?.MaxModifiers ?? int.MaxValue; int num2 = -1; ModifierRoll[] modifiers = data.Modifiers; foreach (ModifierRoll modifierRoll in modifiers) { num2++; if (modifierRoll == null || string.IsNullOrWhiteSpace(modifierRoll.Id)) { continue; } if (ModifierCatalog.TryGetById(modifierRoll.Id, out var definition2)) { string text3 = definition2.FormatTooltipLine(modifierRoll.Value, num2 >= num); if (definition2.EffectType == ModifierEffectType.ParryBonus) { float value = item.m_shared.m_timedBlockBonus + modifierRoll.Value; text3 = text3 + " (total: " + FormatMultiplier(value) + ")"; } else if (definition2.EffectType == ModifierEffectType.BackstabBonus) { float value2 = item.m_shared.m_backstabBonus + modifierRoll.Value; text3 = text3 + " (total: " + FormatMultiplier(value2) + ")"; } list.Add(text3); } else { string text4 = CultureInfo.InvariantCulture.TextInfo.ToTitleCase(modifierRoll.Id.Replace('_', ' ').ToLowerInvariant()); string text5 = modifierRoll.Value.ToString("+0.##;-0.##;0", CultureInfo.InvariantCulture); list.Add("<color=#ADD8E6>" + text4 + ":</color> <color=green>" + text5 + "</color>"); } } } string text6 = item.GetTooltip(-1); if (data.HelTouched) { list.Add("<color=#9B30FF>Touched by Hel</color>"); } if (list.Count > 0) { text6 = text6.TrimEnd(Array.Empty<char>()) + "\n\n" + string.Join("\n", list); } tooltip.Set(text2, text6, __instance.m_tooltipAnchor, default(Vector2)); return false; } private static string FormatMultiplier(float value) { return value.ToString("0.##", CultureInfo.InvariantCulture) + "x"; } } [HarmonyPatch(typeof(InventoryGui), "DoCrafting", new Type[] { typeof(Player) })] internal static class ItemModifierUpgradePatch { private struct State { public bool Active; public Player Player; public ItemData SourceItem; public ItemModifierData ModifierData; public List<ItemData> ExistingItems; public string SharedName; public int Quality; public int Variant; } private static void Prefix(InventoryGui __instance, Player player, out State __state) { __state = default(State); ItemData craftUpgradeItem = __instance.m_craftUpgradeItem; if (craftUpgradeItem != null && !((Object)(object)player == (Object)null) && ItemModifierDataStore.TryLoad(craftUpgradeItem, out var data)) { __state.Active = true; __state.Player = player; __state.SourceItem = craftUpgradeItem; __state.ModifierData = Clone(data); __state.ExistingItems = new List<ItemData>(((Humanoid)player).GetInventory().GetAllItems()); __state.SharedName = craftUpgradeItem.m_shared.m_name; __state.Quality = craftUpgradeItem.m_quality; __state.Variant = craftUpgradeItem.m_variant; } } private static void Postfix(State __state) { if (!__state.Active || ContainsReference(((Humanoid)__state.Player).GetInventory().GetAllItems(), __state.SourceItem)) { return; } foreach (ItemData allItem in ((Humanoid)__state.Player).GetInventory().GetAllItems()) { if (!ContainsReference(__state.ExistingItems, allItem) && allItem.m_shared.m_name == __state.SharedName && allItem.m_quality != __state.Quality && allItem.m_variant == __state.Variant) { ItemModifierDataStore.Save(allItem, __state.ModifierData); break; } } } private static bool ContainsReference(List<ItemData> items, ItemData target) { foreach (ItemData item in items) { if (item == target) { return true; } } return false; } private static ItemModifierData Clone(ItemModifierData data) { List<ModifierRoll> list = new List<ModifierRoll>(); if (data.Modifiers != null) { ModifierRoll[] modifiers = data.Modifiers; foreach (ModifierRoll modifierRoll in modifiers) { if (modifierRoll != null) { list.Add(new ModifierRoll { Id = modifierRoll.Id, Value = modifierRoll.Value }); } } } return new ItemModifierData { Version = data.Version, RarityId = data.RarityId, HelTouched = data.HelTouched, Modifiers = list.ToArray() }; } } internal sealed class ItemRarityDefinition { public string Id { get; } public string DisplayName { get; } public string TooltipColor { get; } public Color GlowColor { get; } public int Rank { get; } public int MaxModifiers { get; } public ItemRarityDefinition(string id, string displayName, string tooltipColor, Color glowColor, int rank, int maxModifiers) { //IL_001e: 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) Id = id; DisplayName = displayName; TooltipColor = tooltipColor; GlowColor = glowColor; Rank = rank; MaxModifiers = maxModifiers; } } internal static class ItemRarityCatalog { private static readonly ItemRarityDefinition[] Definitions = new ItemRarityDefinition[3] { new ItemRarityDefinition("magic", "Magic", "green", new Color(0.2f, 1f, 0.35f), 1, 1), new ItemRarityDefinition("rare", "Rare", "blue", new Color(0.25f, 0.55f, 1f), 2, 2), new ItemRarityDefinition("epic", "Epic", "purple", new Color(0.75f, 0.3f, 1f), 3, 4) }; public static IReadOnlyList<ItemRarityDefinition> All => Definitions; public static List<string> GetCommandOptions() { List<string> list = new List<string>(Definitions.Length + 1) { "0" }; ItemRarityDefinition[] definitions = Definitions; foreach (ItemRarityDefinition itemRarityDefinition in definitions) { list.Add(itemRarityDefinition.Id); } return list; } public static bool TryGet(string id, out ItemRarityDefinition definition) { ItemRarityDefinition[] definitions = Definitions; foreach (ItemRarityDefinition itemRarityDefinition in definitions) { if (string.Equals(itemRarityDefinition.Id, id, StringComparison.OrdinalIgnoreCase)) { definition = itemRarityDefinition; return true; } } definition = null; return false; } } [HarmonyPatch(typeof(ItemDrop), "Start")] internal static class WorldDropRarityGlowPatch { private static void Postfix(ItemDrop __instance) { ItemRarityVisuals.SetWorldDropGlow(__instance); } } [HarmonyPatch(typeof(InventoryGrid), "UpdateInventory", new Type[] { typeof(Inventory), typeof(Player), typeof(ItemData) })] internal static class InventoryIconRarityGlowPatch { private static void Postfix(InventoryGrid __instance, Inventory ___m_inventory, List<InventoryElement> ___m_elements) { ItemRarityVisuals.ApplyInventoryIconRarity(__instance, ___m_inventory, ___m_elements); } } [HarmonyPatch(typeof(Humanoid), "EquipItem")] internal static class EquipRarityGlowPatch { private static void Postfix(Humanoid __instance) { ItemRarityVisuals.RefreshEquippedGlow(__instance); } } [HarmonyPatch(typeof(Humanoid), "UnequipItem")] internal static class UnequipRarityGlowPatch { private static void Postfix(Humanoid __instance) { ItemRarityVisuals.RefreshEquippedGlow(__instance); } } internal static class ItemRarityVisuals { private const string GlowObjectName = "RunecarvedRarityGlow"; private const float DropGlowRange = 4.5f; private const float DropGlowIntensity = 1.5f; private const float EquippedGlowRange = 4f; private const float EquippedGlowIntensity = 0.9f; public static bool TryGetRarity(ItemData item, out ItemRarityDefinition rarity) { rarity = null; string rarityId; return ItemModifierDataStore.TryGetRarityId(item, out rarityId) && ItemRarityCatalog.TryGet(rarityId, out rarity); } public static void SetWorldDropGlow(ItemDrop itemDrop) { //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)itemDrop == (Object)null)) { if (TryGetRarity(itemDrop.m_itemData, out var rarity)) { SetGlow(((Component)itemDrop).transform, rarity.GlowColor, new Vector3(0f, 0.25f, 0f), 4.5f, 1.5f); } else { SetGlow(((Component)itemDrop).transform, null, Vector3.zero, 0f, 0f); } } } public static void RefreshEquippedGlow(Humanoid humanoid) { //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)humanoid == (Object)null) { return; } ItemRarityDefinition itemRarityDefinition = null; Inventory inventory = humanoid.GetInventory(); if (inventory != null) { foreach (ItemData allItem in inventory.GetAllItems()) { if (allItem.m_equipped && TryGetRarity(allItem, out var rarity) && (itemRarityDefinition == null || rarity.Rank > itemRarityDefinition.Rank)) { itemRarityDefinition = rarity; } } } SetGlow(((Component)humanoid).transform, itemRarityDefinition?.GlowColor, new Vector3(0f, 1f, 0f), 4f, 0.9f); } public static void ApplyInventoryIconRarity(InventoryGrid grid, Inventory inventory, List<InventoryElement> elements) { //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)grid == (Object)null || inventory == null || elements == null) { return; } foreach (InventoryElement element in elements) { Outline component = ((Component)element.m_icon).GetComponent<Outline>(); if ((Object)(object)component != (Object)null) { ((Behaviour)component).enabled = false; } } int width = inventory.GetWidth(); foreach (ItemData allItem in inventory.GetAllItems()) { if (!TryGetRarity(allItem, out var rarity)) { continue; } int num = allItem.m_gridPos.y * width + allItem.m_gridPos.x; if (num >= 0 && num < elements.Count) { Image icon = elements[num].m_icon; Outline val = ((Component)icon).GetComponent<Outline>(); if ((Object)(object)val == (Object)null) { val = ((Component)icon).gameObject.AddComponent<Outline>(); } Color glowColor = rarity.GlowColor; glowColor.a = 1f; ((Shadow)val).effectColor = glowColor; ((Shadow)val).effectDistance = new Vector2(3f, 3f); ((Shadow)val).useGraphicAlpha = true; ((Behaviour)val).enabled = true; } } } private static void SetGlow(Transform owner, Color? color, Vector3 localPosition, float range, float intensity) { //IL_0083: 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_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Expected O, but got Unknown Transform val = owner.Find("RunecarvedRarityGlow"); if (!color.HasValue) { if ((Object)(object)val != (Object)null) { ((Component)val).gameObject.SetActive(false); } return; } if ((Object)(object)val == (Object)null) { GameObject val2 = new GameObject("RunecarvedRarityGlow"); val = val2.transform; val.SetParent(owner, false); Light val3 = val2.AddComponent<Light>(); val3.type = (LightType)2; val3.shadows = (LightShadows)0; } val.localPosition = localPosition; ((Component)val).gameObject.SetActive(true); Light component = ((Component)val).GetComponent<Light>(); component.color = color.Value; component.range = range; component.intensity = intensity; } } internal static class ModCommands { private sealed class SetRarityCommand : ConsoleCommand { public override string Name => "set_rarity"; public override bool IsCheat => true; public override string Help => "Sets rarity on an equipped item. Usage: set_rarity <rarity> [slot] (use 0 to clear rarity and modifiers)."; public override List<string> CommandOptionList() { return ItemRarityCatalog.GetCommandOptions(); } public override void Run(string[] args) { if (args.Length < 1 || string.IsNullOrWhiteSpace(args[0])) { Console.instance.Print(((ConsoleCommand)this).Help); return; } if (args.Length > 2) { Console.instance.Print(((ConsoleCommand)this).Help); return; } string text = args[0].Trim().ToLowerInvariant(); ItemRarityDefinition definition = null; if (text != "0" && !ItemRarityCatalog.TryGet(text, out definition)) { Console.instance.Print("Invalid rarity '" + args[0] + "'. Valid rarities are magic, rare, and epic; use 0 to remove rarity and modifiers."); } else { if (!TryGetEquippedItem(args, 1, out var item)) { return; } if (text == "0") { ItemModifierDataStore.Clear(item); ItemRarityVisuals.RefreshEquippedGlow((Humanoid)(object)Player.m_localPlayer); Console.instance.Print("Cleared rarity and modifiers from the equipped item."); return; } if (!ItemModifierDataStore.TryLoad(item, out var data)) { data = new ItemModifierData(); } int modifierCount = GetModifierCount(data.Modifiers); if (modifierCount > definition.MaxModifiers) { Console.instance.Print($"Cannot set rarity to '{text}': the item has {modifierCount} modifiers, but {definition.DisplayName} allows at most {definition.MaxModifiers}. Remove modifiers first."); } else { data.RarityId = text; ItemModifierDataStore.Save(item, data); ItemRarityVisuals.RefreshEquippedGlow((Humanoid)(object)Player.m_localPlayer); Console.instance.Print("Set equipped item rarity to '" + data.RarityId + "'."); } } } } private sealed class ModifierCommand : ConsoleCommand { public override string Name => "modifier"; public override bool IsCheat => true; public override string Help => "Adds a modifier to an equipped item at its max value. Usage: modifier <modifier> [value] [slot]. Value uses tooltip units (25 = 25% for percentage modifiers) and may exceed the normal range."; public override List<string> CommandOptionList() { return ModifierCatalog.GetCommandOptions(); } public override void Run(string[] args) { if (args.Length < 1 || string.IsNullOrWhiteSpace(args[0]) || args.Length > 3) { Console.instance.Print(((ConsoleCommand)this).Help); } else { if (!TryGetModifierDefinition(args[0], out var definition)) { return; } float? num = null; string text = null; for (int i = 1; i < args.Length; i++) { if (!num.HasValue && float.TryParse(args[i], NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { num = definition.FromDisplayValue(result); continue; } if (text == null) { text = args[i]; continue; } Console.instance.Print(((ConsoleCommand)this).Help); return; } string id = definition.Id; string commandName = definition.CommandName; if (!TryGetEquippedItem(text, out var item)) { return; } if (!definition.IsAllowedOn(item) && ModifierCatalog.TryGetItemCategory(item, out var category)) { if (category == ModifierItemCategory.Shield && ModifierCatalog.IsTowerShield(item) && ModifierDefinition.IsParryOnlyEffect(definition.EffectType)) { Console.instance.Print("Parry modifiers are not available for tower shields."); return; } Console.instance.Print("Modifier '" + commandName + "' is not compatible with " + ModifierCatalog.GetCategoryDisplayName(category) + " items."); return; } if (!ItemModifierDataStore.TryLoad(item, out var data)) { data = new ItemModifierData(); } ModifierRoll[] array = data.Modifiers ?? new ModifierRoll[0]; ModifierRoll[] array2 = array; foreach (ModifierRoll modifierRoll in array2) { if (modifierRoll != null && modifierRoll.Id == id) { Console.instance.Print("Equipped item already has modifier '" + commandName + "'."); return; } } if (!ItemRarityCatalog.TryGet(data.RarityId, out var definition2)) { Console.instance.Print("Set a rarity before adding modifiers. Magic allows 1, Rare allows 2, and Epic allows 4 modifiers."); return; } int modifierCount = GetModifierCount(array); if (modifierCount >= definition2.MaxModifiers) { Console.instance.Print($"{definition2.DisplayName} items can have at most {definition2.MaxModifiers} modifier(s)."); return; } ModifierRoll[] array3 = new ModifierRoll[array.Length + 1]; array.CopyTo(array3, 0); array3[^1] = new ModifierRoll { Id = id, Value = (num ?? definition.StrongestValue) }; data.Modifiers = array3; ItemModifierDataStore.Save(item, data); Console.instance.Print("Added '" + commandName + "' to the equipped item (" + definition.FormatTooltipLine(array3[^1].Value) + ")."); } } } private sealed class RemoveModifierCommand : ConsoleCommand { public override string Name => "remove_modifier"; public override bool IsCheat => true; public override string Help => "Removes a modifier from an equipped item. Usage: remove_modifier <modifier> [slot]."; public override List<string> CommandOptionList() { return ModifierCatalog.GetCommandOptions(); } public override void Run(string[] args) { if (args.Length < 1 || string.IsNullOrWhiteSpace(args[0]) || args.Length > 2) { Console.instance.Print(((ConsoleCommand)this).Help); } else { if (!TryGetModifierDefinition(args[0], out var definition)) { return; } string id = definition.Id; string commandName = definition.CommandName; if (!TryGetEquippedItem(args, 1, out var item)) { return; } if (!ItemModifierDataStore.TryLoad(item, out var data) || data.Modifiers == null || data.Modifiers.Length == 0) { Console.instance.Print("Equipped item does not have modifier '" + commandName + "'."); return; } int num = 0; ModifierRoll[] modifiers = data.Modifiers; foreach (ModifierRoll modifierRoll in modifiers) { if (modifierRoll == null || modifierRoll.Id != id) { num++; } } if (num == data.Modifiers.Length) { Console.instance.Print("Equipped item does not have modifier '" + commandName + "'."); return; } ModifierRoll[] array = new ModifierRoll[num]; int num2 = 0; ModifierRoll[] modifiers2 = data.Modifiers; foreach (ModifierRoll modifierRoll2 in modifiers2) { if (modifierRoll2 == null || modifierRoll2.Id != id) { array[num2++] = modifierRoll2; } } data.Modifiers = array; ItemModifierDataStore.Save(item, data); Console.instance.Print("Removed '" + commandName + "' from the equipped item."); } } } private sealed class DumpItemDataCommand : ConsoleCommand { public override string Name => "dump_item_data"; public override string Help => "Prints the local player's inventory item data."; public override void Run(string[] args) { //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { Console.instance.Print("No local player is available."); return; } List<ItemData> allItems = ((Humanoid)localPlayer).GetInventory().GetAllItems(); Console.instance.Print($"Inventory contains {allItems.Count} item entries."); for (int i = 0; i < allItems.Count; i++) { ItemData val = allItems[i]; SharedData shared = val.m_shared; Console instance = Console.instance; string[] array = new string[5]; object arg = i; GameObject dropPrefab = val.m_dropPrefab; array[0] = string.Format("Item {0}: prefab={1}, ", arg, ((dropPrefab != null) ? ((Object)dropPrefab).name : null) ?? "<none>"); array[1] = $"name={shared.m_name}, quality={val.m_quality}, stack={val.m_stack}, "; array[2] = $"durability={val.m_durability}, worldLevel={val.m_worldLevel}, "; array[3] = $"variant={val.m_variant}, equipped={val.m_equipped}, "; array[4] = $"pickedUp={val.m_pickedUp}, crafter={val.m_crafterName} ({val.m_crafterID})"; instance.Print(string.Concat(array)); Console.instance.Print(" Base damage: " + ((DamageTypes)(ref shared.m_damages)).GetTooltipString()); DamageTypes damage = val.GetDamage(); Console.instance.Print(" Calculated damage: " + ((DamageTypes)(ref damage)).GetTooltipString()); if (ItemModifierDataStore.TryLoad(val, out var data)) { Console.instance.Print($" Item data: rarity={data.RarityId}, version={data.Version}"); if (data.Modifiers == null || data.Modifiers.Length == 0) { Console.instance.Print(" Modifiers: <none>"); } else { Console.instance.Print(" Modifiers:"); ModifierRoll[] modifiers = data.Modifiers; foreach (ModifierRoll modifierRoll in modifiers) { if (modifierRoll != null) { Console.instance.Print($" {modifierRoll.Id} = {modifierRoll.Value}"); } } } } else { Console.instance.Print(" Item data: <not found>"); } if (val.m_customData == null || val.m_customData.Count == 0) { Console.instance.Print(" Custom data: <empty>"); continue; } foreach (KeyValuePair<string, string> customDatum in val.m_customData) { Console.instance.Print(" Custom data [" + customDatum.Key + "] = " + customDatum.Value); } } } } public static void Register() { CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new SetRarityCommand()); CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new ModifierCommand()); CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new RemoveModifierCommand()); CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new DumpItemDataCommand()); } private static bool IsWeaponCategory(ModifierItemCategory category) { return category == ModifierItemCategory.Weapon || category == ModifierItemCategory.Bow || category == ModifierItemCategory.Crossbow || category == ModifierItemCategory.MagicWeapon; } private static void AddIfSupported(List<ItemData> items, ItemData item) { if (!ModifierCatalog.TryGetItemCategory(item, out var _)) { return; } foreach (ItemData item2 in items) { if (item2 == item) { return; } } items.Add(item); } private static List<ItemData> GetEquippedArmourItems(Player player) { List<ItemData> list = new List<ItemData>(); AddIfSupported(list, ((Humanoid)player).m_helmetItem); AddIfSupported(list, ((Humanoid)player).m_chestItem); AddIfSupported(list, ((Humanoid)player).m_legItem); AddIfSupported(list, ((Humanoid)player).m_shoulderItem); return list; } private static List<ItemData> GetSupportedEquippedItems(Player player) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Invalid comparison between Unknown and I4 List<ItemData> list = new List<ItemData>(); AddIfSupported(list, ((Humanoid)player).GetCurrentWeapon()); if (((Humanoid)player).m_leftItem != null && (int)((Humanoid)player).m_leftItem.m_shared.m_itemType == 5) { AddIfSupported(list, ((Humanoid)player).m_leftItem); } foreach (ItemData equippedArmourItem in GetEquippedArmourItems(player)) { AddIfSupported(list, equippedArmourItem); } return list; } private static bool TryGetOneItem(List<ItemData> items, string slot, out ItemData item) { item = null; if (items.Count == 1) { item = items[0]; return true; } if (items.Count == 0) { Console.instance.Print("No equipped " + slot + " was found."); } else { Console.instance.Print("More than one " + slot + " is equipped. Specify a specific slot."); } return false; } private static bool TryGetEquippedItem(string slotArgument, out ItemData item) { item = null; if ((Object)(object)Player.m_localPlayer == (Object)null) { Console.instance.Print("No local player is available."); return false; } Player localPlayer = Player.m_localPlayer; if (string.IsNullOrWhiteSpace(slotArgument)) { List<ItemData> supportedEquippedItems = GetSupportedEquippedItems(localPlayer); if (supportedEquippedItems.Count == 1) { item = supportedEquippedItems[0]; return true; } Console.instance.Print((supportedEquippedItems.Count == 0) ? "No supported weapon, shield, or armour is equipped." : "Multiple supported items are equipped. Specify weapon, shield, armour, helmet, chest, legs, or cape."); return false; } string text = slotArgument.Trim().ToLowerInvariant(); switch (text) { case "weapon": { item = ((Humanoid)localPlayer).GetCurrentWeapon(); if (ModifierCatalog.TryGetItemCategory(item, out var category2) && IsWeaponCategory(category2)) { return true; } Console.instance.Print("No supported weapon is equipped in the weapon slot."); return false; } case "bow": case "crossbow": case "magic": case "magic_weapon": { item = ((Humanoid)localPlayer).GetCurrentWeapon(); if (ModifierCatalog.TryGetItemCategory(item, out var category) && ((text == "bow" && category == ModifierItemCategory.Bow) || (text == "crossbow" && category == ModifierItemCategory.Crossbow) || ((text == "magic" || text == "magic_weapon") && category == ModifierItemCategory.MagicWeapon))) { return true; } Console.instance.Print("No equipped " + text.Replace('_', ' ') + " was found."); return false; } case "shield": { List<ItemData> list = new List<ItemData>(); AddIfShield(list, ((Humanoid)localPlayer).m_leftItem); AddIfShield(list, ((Humanoid)localPlayer).m_rightItem); return TryGetOneItem(list, "shield", out item); } case "armour": return TryGetOneItem(GetEquippedArmourItems(localPlayer), "armour piece", out item); case "helmet": item = ((Humanoid)localPlayer).m_helmetItem; break; case "chest": item = ((Humanoid)localPlayer).m_chestItem; break; case "legs": item = ((Humanoid)localPlayer).m_legItem; break; case "cape": item = ((Humanoid)localPlayer).m_shoulderItem; break; default: Console.instance.Print("Invalid slot. Use weapon, shield, armour, helmet, chest, legs, cape, bow, crossbow, or magic."); return false; } if (ModifierCatalog.TryGetItemCategory(item, out var category3) && category3 == ModifierItemCategory.Armour) { return true; } Console.instance.Print("No equipped " + text + " was found."); return false; } private static void AddIfShield(List<ItemData> shields, ItemData item) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Invalid comparison between Unknown and I4 if (item != null && (int)item.m_shared.m_itemType == 5) { AddIfSupported(shields, item); } } private static bool TryGetEquippedItem(string[] args, int requiredArgumentCount, out ItemData item) { item = null; if (args.Length < requiredArgumentCount || args.Length > requiredArgumentCount + 1) { return false; } return TryGetEquippedItem((args.Length == requiredArgumentCount) ? null : args[requiredArgumentCount], out item); } private static bool TryGetModifierDefinition(string commandName, out ModifierDefinition definition) { if (ModifierCatalog.TryGetByCommandName(commandName.Trim(), out definition)) { return true; } Console.instance.Print("Unknown modifier. Available modifiers: " + string.Join(", ", ModifierCatalog.GetCommandOptions()) + "."); return false; } private static int GetModifierCount(ModifierRoll[] modifiers) { if (modifiers == null) { return 0; } int num = 0; foreach (ModifierRoll modifierRoll in modifiers) { if (modifierRoll != null && !string.IsNullOrWhiteSpace(modifierRoll.Id)) { num++; } } return num; } } [Flags] internal enum ModifierItemCategory { None = 0, Weapon = 1, Bow = 2, Crossbow = 4, MagicWeapon = 8, Shield = 0x10, Armour = 0x20 } internal enum ModifierDamageChannel { None, All, Slash, Pierce, Blunt, Chop, Pickaxe, Fire, Frost, Lightning, Poison, Spirit } internal enum ModifierValueType { Percentage, PercentagePoints, Multiplier, Flat } internal enum ModifierEffectType { None, AttackStaminaCost, MovementPenalty, BlockPower, ParryBonus, BackstabBonus, AdrenalineGain, BowDrawStaminaCost, ReloadTime, BlockStaminaCost, MovementSpeedBonus, ArmourFlat, ArmourPercentage, FireResistance, FrostResistance, LightningResistance, PoisonResistance, ParryAdrenaline, ProjectileSpeed, AmmoSaveChance, ExtraProjectileChance, ParryDamage, ParryHealth, ParryStamina, HealthRegen, StaminaRegen, MaxHealth, AdrenalineOnHit, DodgeStaminaCost, Thorns, SkillLevel, AttackEitrCost, ShieldOnUse, AoeRadius, EitrRegen, MagicExtraProjectile, SummonRoots, BloodHealthCost, BloodHealthToStamina, LowHealthEffect, ShieldAllyHeal, SummonMaxCount, ExtraSummonChance, ExtraSummonStars } internal sealed class ModifierDefinition { public string Id { get; } public string CommandName { get; } public string DisplayName { get; } public float BaseValue { get; } public float WeakestValue { get; } public float StrongestValue { get; } public float Step { get; } public ModifierDamageChannel DamageChannel { get; } public ModifierValueType ValueType { get; } public ModifierEffectType EffectType { get; } public ModifierItemCategory AllowedItemCategories { get; } public SkillType Skill { get; } public ModifierDefinition(string id, string commandName, string displayName, float testValue, ModifierDamageChannel damageChannel, ModifierValueType valueType, ModifierEffectType effectType = ModifierEffectType.None, ModifierItemCategory allowedItemCategories = ModifierItemCategory.Weapon, SkillType skill = (SkillType)0) { //IL_009f: 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) Id = id; CommandName = commandName; DisplayName = displayName; BaseValue = testValue; if (ModifierCatalog.RollRanges.TryGetValue(id, out var value)) { WeakestValue = value[0]; StrongestValue = value[1]; Step = value[2]; } else { WeakestValue = testValue * 0.5f; StrongestValue = testValue * 1.5f; Step = 0f; } DamageChannel = damageChannel; ValueType = valueType; EffectType = effectType; AllowedItemCategories = allowedItemCategories; Skill = skill; } public float FromDisplayValue(float displayValue) { return (ValueType == ModifierValueType.Percentage || ValueType == ModifierValueType.PercentagePoints) ? (displayValue / 100f) : displayValue; } public float Roll() { if (Step <= 0f) { return StrongestValue; } int num = (int)Math.Round(Math.Abs(StrongestValue - WeakestValue) / Step); int num2 = Random.Range(0, num + 1); float num3 = ((StrongestValue >= WeakestValue) ? 1f : (-1f)); return (float)Math.Round(WeakestValue + num3 * (float)num2 * Step, 4); } public bool IsAllowedOn(ItemData item) { //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Invalid comparison between Unknown and I4 //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Invalid comparison between Unknown and I4 if (!ModifierCatalog.TryGetItemCategory(item, out var category) || (AllowedItemCategories & category) == 0) { return false; } if (EffectType == ModifierEffectType.MovementPenalty && item.m_shared.m_movementModifier >= 0f) { return false; } if (EffectType == ModifierEffectType.AoeRadius && !MagicWeaponInfo.HasAoe(item)) { return false; } if ((EffectType == ModifierEffectType.MagicExtraProjectile || EffectType == ModifierEffectType.SummonRoots) && (int)item.m_shared.m_skillType != 9) { return false; } if ((EffectType == ModifierEffectType.BloodHealthCost || EffectType == ModifierEffectType.BloodHealthToStamina || EffectType == ModifierEffectType.LowHealthEffect) && (int)item.m_shared.m_skillType != 10) { return false; } if (EffectType == ModifierEffectType.ShieldAllyHeal && !StaffShieldSource.IsStaffOfProtection(item)) { return false; } if ((EffectType == ModifierEffectType.SummonMaxCount || EffectType == ModifierEffectType.ExtraSummonChance || EffectType == ModifierEffectType.ExtraSummonStars) && !SummonWeaponInfo.HasSummonAbility(item)) { return false; } if (EffectType == ModifierEffectType.MagicExtraProjectile && !MagicWeaponInfo.FiresProjectile(item)) { return false; } return !IsParryOnlyEffect(EffectType) || category != ModifierItemCategory.Shield || !ModifierCatalog.IsTowerShield(item); } public static bool IsParryOnlyEffect(ModifierEffectType effectType) { return effectType == ModifierEffectType.ParryBonus || effectType == ModifierEffectType.ParryAdrenaline || effectType == ModifierEffectType.ParryDamage || effectType == ModifierEffectType.ParryHealth || effectType == ModifierEffectType.ParryStamina; } public void ApplyDamage(ref DamageTypes damage, float value) { switch (DamageChannel) { case ModifierDamageChannel.All: ApplyValue(ref damage.m_blunt, value); ApplyValue(ref damage.m_slash, value); ApplyValue(ref damage.m_pierce, value); ApplyValue(ref damage.m_chop, value); ApplyValue(ref damage.m_pickaxe, value); ApplyValue(ref damage.m_fire, value); ApplyValue(ref damage.m_frost, value); ApplyValue(ref damage.m_lightning, value); ApplyValue(ref damage.m_poison, value); ApplyValue(ref damage.m_spirit, value); break; case ModifierDamageChannel.Slash: ApplyValue(ref damage.m_slash, value); break; case ModifierDamageChannel.Pierce: ApplyValue(ref damage.m_pierce, value); break; case ModifierDamageChannel.Blunt: ApplyValue(ref damage.m_blunt, value); break; case ModifierDamageChannel.Chop: ApplyValue(ref damage.m_chop, value); break; case ModifierDamageChannel.Pickaxe: ApplyValue(ref damage.m_pickaxe, value); break; case ModifierDamageChannel.Fire: ApplyValue(ref damage.m_fire, value); break; case ModifierDamageChannel.Frost: ApplyValue(ref damage.m_frost, value); break; case ModifierDamageChannel.Lightning: ApplyValue(ref damage.m_lightning, value); break; case ModifierDamageChannel.Poison: ApplyValue(ref damage.m_poison, value); break; case ModifierDamageChannel.Spirit: ApplyValue(ref damage.m_spirit, value); break; } } private void ApplyValue(ref float damage, float value) { damage = ApplyScalar(damage, value); } public float ApplyScalar(float currentValue, float modifierValue) { return (ValueType == ModifierValueType.Percentage) ? (currentValue * (1f + modifierValue)) : (currentValue + modifierValue); } public bool IsOverLimit(float value) { return (StrongestValue >= WeakestValue) ? (value > StrongestValue + 0.0001f) : (value < StrongestValue - 0.0001f); } public string FormatTooltipLine(float value, bool wholeLinePurple = false) { string text; if (ValueType == ModifierValueType.Percentage || ValueType == ModifierValueType.PercentagePoints) { text = (value * 100f).ToString("+0.##;-0.##;0", CultureInfo.InvariantCulture) + "%"; } else if (ValueType == ModifierValueType.Multiplier) { text = value.ToString("+0.##;-0.##;0", CultureInfo.InvariantCulture) + "x"; if (EffectType == ModifierEffectType.ParryDamage) { text += " block armor"; } } else { text = value.ToString("+0.##;-0.##;0", CultureInfo.InvariantCulture); } if (wholeLinePurple) { return "<color=#9B30FF>" + DisplayName + ": " + text + "</color>"; } string text2 = (IsOverLimit(value) ? "#9B30FF" : "green"); return "<color=#ADD8E6>" + DisplayName + ":</color> <color=" + text2 + ">" + text + "</color>"; } } internal static class ModifierCatalog { internal static readonly Dictionary<string, float[]> RollRanges = new Dictionary<string, float[]> { { "base_damage", new float[3] { 0.1f, 0.3f, 0.01f } }, { "fire_damage", new float[3] { 5f, 15f, 1f } }, { "frost_damage", new float[3] { 5f, 15f, 1f } }, { "lightning_damage", new float[3] { 5f, 15f, 1f } }, { "poison_damage", new float[3] { 5f, 15f, 1f } }, { "spirit_damage", new float[3] { 5f, 15f, 1f } }, { "stamina_cost", new float[3] { -0.05f, -0.15f, 0.01f } }, { "eitr_cost", new float[3] { -0.1f, -0.2f, 0.01f } }, { "shield_on_use", new float[3] { 0.1f, 0.2f, 0.01f } }, { "aoe_radius", new float[3] { 0.25f, 0.5f, 0.01f } }, { "eitr_regen", new float[3] { 0.15f, 0.3f, 0.01f } }, { "magic_extra_projectile", new float[3] { 0.1f, 0.25f, 0.01f } }, { "summon_roots", new float[3] { 0.1f, 0.2f, 0.01f } }, { "blood_health_cost", new float[3] { 0.1f, 0.3f, 0.01f } }, { "blood_health_to_stamina", new float[3] { 0.1f, 0.3f, 0.01f } }, { "low_health_effect", new float[3] { 0.25f, 0.5f, 0.01f } }, { "shield_ally_heal", new float[3] { 20f, 40f, 1f } }, { "summon_max_count", new float[3] { 1f, 2f, 1f } }, { "extra_summon_chance", new float[3] { 0.15f, 0.3f, 0.01f } }, { "extra_summon_stars", new float[3] { 0.25f, 0.5f, 0.01f } }, { "bow_draw_stamina", new float[3] { -1f, -3f, 1f } }, { "crossbow_reload", new float[3] { -0.25f, -0.25f, 0f } }, { "projectile_speed", new float[3] { 0.25f, 1f, 0.05f } }, { "ammo_save_chance", new float[3] { 0.25f, 0.5f, 0.01f } }, { "extra_projectile_chance", new float[3] { 0.25f, 0.5f, 0.01f } }, { "movement_penalty", new float[3] { 0.5f, 1f, 0.05f } }, { "block_armour", new float[3] { 0.1f, 0.3f, 0.01f } }, { "parry_bonus", new float[3] { 0.25f, 0.75f, 0.05f } }, { "backstab_bonus", new float[3] { 0.5f, 1.5f, 0.5f } }, { "adrenaline_gain", new float[3] { 1f, 2f, 1f } }, { "shield_block_stamina", new float[3] { -0.5f, -0.5f, 0f } }, { "shield_movespeed", new float[3] { 0.03f, 0.07f, 0.01f } }, { "armour_flat", new float[3] { 2f, 6f, 1f } }, { "armour_percent", new float[3] { 0.1f, 0.3f, 0.01f } }, { "armour_movespeed", new float[3] { 0.02f, 0.04f, 0.01f } }, { "fire_resistance", new float[3] { 0.25f, 0.25f, 0f } }, { "frost_resistance", new float[3] { 0.25f, 0.25f, 0f } }, { "lightning_resistance", new float[3] { 0.25f, 0.25f, 0f } }, { "poison_resistance", new float[3] { 0.25f, 0.25f, 0f } }, { "parry_adrenaline", new float[3] { 3f, 7f, 1f } }, { "parry_damage", new float[3] { 1f, 2f, 0.1f } }, { "parry_health", new float[3] { 5f, 10f, 1f } }, { "parry_stamina", new float[3] { 10f, 20f, 1f } }, { "health_regen", new float[3] { 0.05f, 0.15f, 0.01f } }, { "stamina_regen", new float[3] { 0.05f, 0.15f, 0.01f } }, { "max_health", new float[3] { 2f, 5f, 1f } }, { "adrenaline_on_hit", new float[3] { 1f, 4f, 1f } }, { "dodge_stamina", new float[3] { -0.05f, -0.15f, 0.01f } }, { "thorns", new float[3] { 0.2f, 0.4f, 0.01f } }, { "skill_axes", new float[3] { 10f, 20f, 1f } }, { "skill_blood_magic", new float[3] { 10f, 20f, 1f } }, { "skill_bows", new float[3] { 10f, 20f, 1f } }, { "skill_clubs", new float[3] { 10f, 20f, 1f } }, { "skill_crossbows", new float[3] { 10f, 20f, 1f } }, { "skill_elemental_magic", new float[3] { 10f, 20f, 1f } }, { "skill_fists", new float[3] { 10f, 20f, 1f } }, { "skill_knives", new float[3] { 10f, 20f, 1f } }, { "skill_polearms", new float[3] { 10f, 20f, 1f } }, { "skill_spears", new float[3] { 10f, 20f, 1f } }, { "skill_swords", new float[3] { 10f, 20f, 1f } } }; internal static readonly Dictionary<string, float> Weights = new Dictionary<string, float> { { "base_damage", 5f }, { "fire_damage", 1f }, { "frost_damage", 1f }, { "lightning_damage", 1f }, { "poison_damage", 1f }, { "spirit_damage", 1f }, { "stamina_cost", 1f }, { "eitr_cost", 1f }, { "shield_on_use", 1f }, { "aoe_radius", 1f }, { "eitr_regen", 1f }, { "magic_extra_projectile", 1f }, { "summon_roots", 1f }, { "blood_health_cost", 1f }, { "blood_health_to_stamina", 1f }, { "low_health_effect", 1f }, { "shield_ally_heal", 1f }, { "summon_max_count", 1f }, { "extra_summon_chance", 1f }, { "extra_summon_stars", 1f }, { "backstab_bonus", 2f }, { "adrenaline_gain", 1f }, { "bow_draw_stamina", 2f }, { "crossbow_reload", 1f }, { "projectile_speed", 2f }, { "ammo_save_chance", 2f }, { "extra_projectile_chance", 1f }, { "movement_penalty", 2f }, { "block_armour", 1f }, { "parry_bonus", 2f }, { "shield_block_stamina", 4f }, { "shield_movespeed", 2f }, { "parry_adrenaline", 1f }, { "parry_damage", 1f }, { "parry_health", 1f }, { "parry_stamina", 1f }, { "armour_flat", 1f }, { "armour_percent", 3f }, { "armour_movespeed", 2f }, { "fire_resistance", 1f }, { "frost_resistance", 1f }, { "lightning_resistance", 1f }, { "poison_resistance", 1f }, { "health_regen", 1f }, { "stamina_regen", 1f }, { "max_health", 1f }, { "adrenaline_on_hit", 1f }, { "dodge_stamina", 1f }, { "thorns", 2f }, { "skill_axes", 0.2f }, { "skill_blood_magic", 0.2f }, { "skill_bows", 0.2f }, { "skill_clubs", 0.2f }, { "skill_crossbows", 0.2f }, { "skill_elemental_magic", 0.2f }, { "skill_fists", 0.2f }, { "skill_knives", 0.2f }, { "skill_polearms", 0.2f }, { "skill_spears", 0.2f }, { "skill_swords", 0.2f } }; private static readonly ModifierDefinition[] Definitions = new ModifierDefinition[60] { new ModifierDefinition("base_damage", "base_damage", "Base damage", 0.2f, ModifierDamageChannel.All, ModifierValueType.Percentage, ModifierEffectType.None, ModifierItemCategory.Weapon | ModifierItemCategory.Bow | ModifierItemCategory.Crossbow | ModifierItemCategory.MagicWeapon, (SkillType)0), new ModifierDefinition("fire_damage", "fire_damage", "Fire damage", 10f, ModifierDamageChannel.Fire, ModifierValueType.Flat, ModifierEffectType.None, ModifierItemCategory.Weapon | ModifierItemCategory.Bow | ModifierItemCategory.Crossbow, (SkillType)0), new ModifierDefinition("frost_damage", "frost_damage", "Frost damage", 10f, ModifierDamageChannel.Frost, ModifierValueType.Flat, ModifierEffectType.None, ModifierItemCategory.Weapon | ModifierItemCategory.Bow | ModifierItemCategory.Crossbow, (SkillType)0), new ModifierDefinition("lightning_damage", "lightning_damage", "Lightning damage", 10f, ModifierDamageChannel.Lightning, ModifierValueType.Flat, ModifierEffectType.None, ModifierItemCategory.Weapon | ModifierItemCategory.Bow | ModifierItemCategory.Crossbow, (SkillType)0), new ModifierDefinition("poison_damage", "poison_damage", "Poison damage", 10f, ModifierDamageChannel.Poison, ModifierValueType.Flat, ModifierEffectType.None, ModifierItemCategory.Weapon | ModifierItemCategory.Bow | ModifierItemCategory.Crossbow, (SkillType)0), new ModifierDefinition("spirit_damage", "spirit_damage", "Spirit damage", 10f, ModifierDamageChannel.Spirit, ModifierValueType.Flat, ModifierEffectType.None, ModifierItemCategory.Weapon | ModifierItemCategory.Bow | ModifierItemCategory.Crossbow, (SkillType)0), new ModifierDefinition("stamina_cost", "stamina_cost", "Weapon stamina cost", -0.1f, ModifierDamageChannel.None, ModifierValueType.Percentage, ModifierEffectType.AttackStaminaCost, ModifierItemCategory.Weapon, (SkillType)0), new ModifierDefinition("eitr_cost", "eitr_cost", "Weapon eitr cost", -0.15f, ModifierDamageChannel.None, ModifierValueType.Percentage, ModifierEffectType.AttackEitrCost, ModifierItemCategory.MagicWeapon, (SkillType)0), new ModifierDefinition("shield_on_use", "shield_on_use", "Chance to gain a shield on use", 0.15f, ModifierDamageChannel.None, ModifierValueType.PercentagePoints, ModifierEffectType.ShieldOnUse, ModifierItemCategory.MagicWeapon, (SkillType)0), new ModifierDefinition("aoe_radius", "aoe_radius", "Area of effect radius", 0.35f, ModifierDamageChannel.None, ModifierValueType.Percentage, ModifierEffectType.AoeRadius, ModifierItemCategory.MagicWeapon, (SkillType)0), new ModifierDefinition("eitr_regen", "eitr_regen", "Eitr regeneration while held", 0.2f, ModifierDamageChannel.None, ModifierValueType.Percentage, ModifierEffectType.EitrRegen, ModifierItemCategory.MagicWeapon, (SkillType)0), new ModifierDefinition("magic_extra_projectile", "magic_extra_projectile", "Chance for additional projectiles", 0.15f, ModifierDamageChannel.None, ModifierValueType.PercentagePoints, ModifierEffectType.MagicExtraProjectile, ModifierItemCategory.MagicWeapon, (SkillType)0), new ModifierDefinition("summon_roots", "summon_roots", "Chance to summon roots on use", 0.15f, ModifierDamageChannel.None, ModifierValueType.PercentagePoints, ModifierEffectType.SummonRoots, ModifierItemCategory.MagicWeapon, (SkillType)0), new ModifierDefinition("blood_health_cost", "blood_health_cost", "Blood magic health cost", 0.2f, ModifierDamageChannel.None, ModifierValueType.Percentage, ModifierEffectType.BloodHealthCost, ModifierItemCategory.MagicWeapon, (SkillType)0), new ModifierDefinition("blood_health_to_stamina", "blood_health_to_stamina", "Convert health cost to stamina cost", 0.2f, ModifierDamageChannel.None, ModifierValueType.Percentage, ModifierEffectType.BloodHealthToStamina, ModifierItemCategory.MagicWeapon, (SkillType)0), new ModifierDefinition("low_health_effect", "low_health_effect", "Effect while below 50% health", 0.35f, ModifierDamageChannel.None, ModifierValueType.Percentage, ModifierEffectType.LowHealthEffect, ModifierItemCategory.MagicWeapon, (SkillType)0), new ModifierDefinition("shield_ally_heal", "shield_ally_heal", "Heal nearby allies when shield is granted", 30f, ModifierDamageChannel.None, ModifierValueType.Flat, ModifierEffectType.ShieldAllyHeal, ModifierItemCategory.MagicWeapon, (SkillType)0), new ModifierDefinition("summon_max_count", "summon_max_count", "Maximum summons", 1f, ModifierDamageChannel.None, ModifierValueType.Flat, ModifierEffectType.SummonMaxCount, ModifierItemCategory.Weapon | ModifierItemCategory.MagicWeapon, (SkillType)0), new ModifierDefinition("extra_summon_chance", "extra_summon_chance", "Chance to spawn an extra summon", 0.225f, ModifierDamageChannel.None, ModifierValueType.PercentagePoints, ModifierEffectType.ExtraSummonChance, ModifierItemCategory.Weapon | ModifierItemCategory.MagicWeapon, (SkillType)0), new ModifierDefinition("extra_summon_stars", "extra_summon_stars", "Chance for extra summon stars (max 2)", 0.375f, ModifierDamageChannel.None, ModifierValueType.PercentagePoints, ModifierEffectType.ExtraSummonStars, ModifierItemCategory.Weapon | ModifierItemCategory.MagicWeapon, (SkillType)0), new ModifierDefinition("bow_draw_stamina", "bow_draw_stamina", "Bow draw stamina cost", -2f, ModifierDamageChannel.None, ModifierValueType.Flat, ModifierEffectType.BowDrawStaminaCost, ModifierItemCategory.Bow, (SkillType)0), new ModifierDefinition("crossbow_reload", "crossbow_reload", "Crossbow reload time", -0.25f, ModifierDamageChannel.None, ModifierValueType.Percentage, ModifierEffectType.ReloadTime, ModifierItemCategory.Crossbow, (SkillType)0), new ModifierDefinition("projectile_speed", "projectile_speed", "Projectile speed", 0.6f, ModifierDamageChannel.None, ModifierValueType.Percentage, ModifierEffectType.ProjectileSpeed, ModifierItemCategory.Bow | ModifierItemCategory.Crossbow, (SkillType)0), new ModifierDefinition("ammo_save_chance", "ammo_save_chance", "Chance to not consume ammo", 0.35f, ModifierDamageChannel.None, ModifierValueType.PercentagePoints, ModifierEffectType.AmmoSaveChance, ModifierItemCategory.Bow | ModifierItemCategory.Crossbow, (SkillType)0), new ModifierDefinition("extra_projectile_chance", "extra_projectile_chance", "Chance to fire 2 extra projectiles", 0.4f, ModifierDamageChannel.None, ModifierValueType.PercentagePoints, ModifierEffectType.ExtraProjectileChance, ModifierItemCategory.Bow | ModifierItemCategory.Crossbow, (SkillType)0), new ModifierDefinition("movement_penalty", "movement_penalty", "Movement penalty reduction", 1f, ModifierDamageChannel.None, ModifierValueType.PercentagePoints, ModifierEffectType.MovementPenalty, ModifierItemCategory.Weapon | ModifierItemCategory.MagicWeapon | ModifierItemCategory.Shield | ModifierItemCategory.Armour, (SkillType)0), new ModifierDefinition("block_armour", "block_armour", "Block armour", 0.2f, ModifierDamageChannel.None, ModifierValueType.Percentage, ModifierEffectType.BlockPower, ModifierItemCategory.Weapon | ModifierItemCategory.MagicWeapon | ModifierItemCategory.Shield, (SkillType)0), new ModifierDefinition("parry_bonus", "parry_bonus", "Parry bonus", 0.5f, ModifierDamageChannel.None, ModifierValueType.Multiplier, ModifierEffectType.ParryBonus, ModifierItemCategory.Weapon | ModifierItemCategory.Shield, (SkillT