Please disclose if your mod was created primarily 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 BlacksmithingExpanded v1.1.7
BlacksmithingExpanded.dll
Decompiled a month ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using System.Threading; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using HarmonyLib; using ItemDataManager; using JetBrains.Annotations; using Microsoft.CodeAnalysis; using ServerSync; using SkillManager; using TMPro; using UnityEngine; using YamlDotNet.Core; using YamlDotNet.Core.Events; using YamlDotNet.Core.ObjectPool; using YamlDotNet.Core.Tokens; using YamlDotNet.Helpers; using YamlDotNet.Serialization; using YamlDotNet.Serialization.BufferedDeserialization; using YamlDotNet.Serialization.BufferedDeserialization.TypeDiscriminators; using YamlDotNet.Serialization.Callbacks; using YamlDotNet.Serialization.Converters; using YamlDotNet.Serialization.EventEmitters; using YamlDotNet.Serialization.NamingConventions; using YamlDotNet.Serialization.NodeDeserializers; using YamlDotNet.Serialization.NodeTypeResolvers; using YamlDotNet.Serialization.ObjectFactories; using YamlDotNet.Serialization.ObjectGraphTraversalStrategies; using YamlDotNet.Serialization.ObjectGraphVisitors; using YamlDotNet.Serialization.Schemas; using YamlDotNet.Serialization.TypeInspectors; using YamlDotNet.Serialization.TypeResolvers; using YamlDotNet.Serialization.Utilities; using YamlDotNet.Serialization.ValueDeserializers; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("BlacksmithingExpanded")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("BlacksmithingExpanded")] [assembly: AssemblyCopyright("Copyright © 2025")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("D181CDA7-EF07-4BBC-B975-2B80FC6BBFAE")] [assembly: AssemblyFileVersion("1.0.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")] [module: UnverifiableCode] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } } namespace SkillManager { [PublicAPI] public class Skill { public static class LocalizationCache { private static readonly Dictionary<string, Localization> localizations = new Dictionary<string, Localization>(); internal static void LocalizationPostfix(Localization __instance, string language) { string key = localizations.FirstOrDefault((KeyValuePair<string, Localization> l) => l.Value == __instance).Key; if (key != null) { localizations.Remove(key); } if (!localizations.ContainsKey(language)) { localizations.Add(language, __instance); } } public static Localization ForLanguage(string? language = null) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown if (localizations.TryGetValue(language ?? PlayerPrefs.GetString("language", "English"), out var value)) { return value; } value = new Localization(); if (language != null) { value.SetupLanguage(language); } return value; } } [PublicAPI] public class LocalizeKey { private static readonly List<LocalizeKey> keys = new List<LocalizeKey>(); public readonly string Key; public readonly Dictionary<string, string> Localizations = new Dictionary<string, string>(); public LocalizeKey(string key) { Key = key.Replace("$", ""); keys.Add(this); } public void Alias(string alias) { Localizations.Clear(); if (!alias.Contains("$")) { alias = "$" + alias; } Localizations["alias"] = alias; if (Localization.m_instance != null) { Localization.instance.AddWord(Key, Localization.instance.Localize(alias)); } } public LocalizeKey English(string key) { return addForLang("English", key); } public LocalizeKey Swedish(string key) { return addForLang("Swedish", key); } public LocalizeKey French(string key) { return addForLang("French", key); } public LocalizeKey Italian(string key) { return addForLang("Italian", key); } public LocalizeKey German(string key) { return addForLang("German", key); } public LocalizeKey Spanish(string key) { return addForLang("Spanish", key); } public LocalizeKey Russian(string key) { return addForLang("Russian", key); } public LocalizeKey Romanian(string key) { return addForLang("Romanian", key); } public LocalizeKey Bulgarian(string key) { return addForLang("Bulgarian", key); } public LocalizeKey Macedonian(string key) { return addForLang("Macedonian", key); } public LocalizeKey Finnish(string key) { return addForLang("Finnish", key); } public LocalizeKey Danish(string key) { return addForLang("Danish", key); } public LocalizeKey Norwegian(string key) { return addForLang("Norwegian", key); } public LocalizeKey Icelandic(string key) { return addForLang("Icelandic", key); } public LocalizeKey Turkish(string key) { return addForLang("Turkish", key); } public LocalizeKey Lithuanian(string key) { return addForLang("Lithuanian", key); } public LocalizeKey Czech(string key) { return addForLang("Czech", key); } public LocalizeKey Hungarian(string key) { return addForLang("Hungarian", key); } public LocalizeKey Slovak(string key) { return addForLang("Slovak", key); } public LocalizeKey Polish(string key) { return addForLang("Polish", key); } public LocalizeKey Dutch(string key) { return addForLang("Dutch", key); } public LocalizeKey Portuguese_European(string key) { return addForLang("Portuguese_European", key); } public LocalizeKey Portuguese_Brazilian(string key) { return addForLang("Portuguese_Brazilian", key); } public LocalizeKey Chinese(string key) { return addForLang("Chinese", key); } public LocalizeKey Japanese(string key) { return addForLang("Japanese", key); } public LocalizeKey Korean(string key) { return addForLang("Korean", key); } public LocalizeKey Hindi(string key) { return addForLang("Hindi", key); } public LocalizeKey Thai(string key) { return addForLang("Thai", key); } public LocalizeKey Abenaki(string key) { return addForLang("Abenaki", key); } public LocalizeKey Croatian(string key) { return addForLang("Croatian", key); } public LocalizeKey Georgian(string key) { return addForLang("Georgian", key); } public LocalizeKey Greek(string key) { return addForLang("Greek", key); } public LocalizeKey Serbian(string key) { return addForLang("Serbian", key); } public LocalizeKey Ukrainian(string key) { return addForLang("Ukrainian", key); } private LocalizeKey addForLang(string lang, string value) { Localizations[lang] = value; if (Localization.m_instance != null) { if (Localization.instance.GetSelectedLanguage() == lang) { Localization.instance.AddWord(Key, value); } else if (lang == "English" && !Localization.instance.m_translations.ContainsKey(Key)) { Localization.instance.AddWord(Key, value); } } return this; } [HarmonyPriority(300)] internal static void AddLocalizedKeys(Localization __instance, string language) { foreach (LocalizeKey key in keys) { string value2; if (key.Localizations.TryGetValue(language, out var value) || key.Localizations.TryGetValue("English", out value)) { __instance.AddWord(key.Key, value); } else if (key.Localizations.TryGetValue("alias", out value2)) { __instance.AddWord(key.Key, Localization.instance.Localize(value2)); } } } } private class ConfigurationManagerAttributes { [UsedImplicitly] public string? Category; } [HarmonyPatch(typeof(Skills), "IsSkillValid")] private static class Patch_Skills_IsSkillValid { private static void Postfix(SkillType type, ref bool __result) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) if (!__result && skills.ContainsKey(type)) { __result = true; } } } private static readonly Dictionary<SkillType, Skill> skills; internal static readonly Dictionary<string, Skill> skillByName; private readonly string skillName; private readonly string internalSkillName; private readonly SkillDef skillDef; public readonly LocalizeKey Name; public readonly LocalizeKey Description; private float skillEffectFactor = 1f; private int skillLoss = 5; public bool Configurable = false; private static bool InitializedTerminal; private static Localization? _english; private static BaseUnityPlugin? _plugin; private static bool hasConfigSync; private static object? _configSync; public float SkillGainFactor { get { return skillDef.m_increseStep; } set { skillDef.m_increseStep = value; this.SkillGainFactorChanged?.Invoke(value); } } public float SkillEffectFactor { get { return skillEffectFactor; } set { skillEffectFactor = value; this.SkillEffectFactorChanged?.Invoke(value); } } public int SkillLoss { get { return skillLoss; } set { skillLoss = value; this.SkillLossChanged?.Invoke(value); } } private static Localization english => _english ?? (_english = LocalizationCache.ForLanguage("English")); private static BaseUnityPlugin plugin { get { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Expected O, but got Unknown object obj = _plugin; if (obj == null) { BaseUnityPlugin val = (BaseUnityPlugin)Chainloader.ManagerObject.GetComponent((Type)Assembly.GetExecutingAssembly().DefinedTypes.First((TypeInfo t) => t.IsClass && typeof(BaseUnityPlugin).IsAssignableFrom(t))); _plugin = val; obj = (object)val; } return (BaseUnityPlugin)obj; } } private static object? configSync { get { if (_configSync == null && hasConfigSync) { Type type = Assembly.GetExecutingAssembly().GetType("ServerSync.ConfigSync"); if ((object)type != null) { _configSync = Activator.CreateInstance(type, plugin.Info.Metadata.GUID + " SkillManager"); type.GetField("CurrentVersion").SetValue(_configSync, plugin.Info.Metadata.Version.ToString()); type.GetProperty("IsLocked").SetValue(_configSync, true); } else { hasConfigSync = false; } } return _configSync; } } public event Action<float>? SkillGainFactorChanged; public event Action<float>? SkillEffectFactorChanged; public event Action<float>? SkillLossChanged; public Skill(string englishName, string icon) : this(englishName, loadSprite(icon, 64, 64)) { } public Skill(string englishName, Sprite icon) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Expected O, but got Unknown SkillType val = fromName(englishName); string text = new Regex("[^a-zA-Z]").Replace(englishName, "_"); skills[val] = this; skillByName[englishName] = this; skillDef = new SkillDef { m_description = "$skilldesc_" + text, m_icon = icon, m_increseStep = 1f, m_skill = val }; internalSkillName = text; skillName = englishName; Name = new LocalizeKey("skill_" + ((object)(SkillType)(ref val)).ToString()).English(englishName); Description = new LocalizeKey("skilldesc_" + text); } public static SkillType fromName(string englishName) { return (SkillType)Math.Abs(StringExtensionMethods.GetStableHashCode(englishName)); } static Skill() { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Expected O, but got Unknown //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Expected O, but got Unknown //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Expected O, but got Unknown //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Expected O, but got Unknown //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Expected O, but got Unknown //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Expected O, but got Unknown //IL_01ad: Expected O, but got Unknown //IL_01dc: Unknown result type (might be due to invalid IL or missing references) //IL_01e9: Expected O, but got Unknown //IL_0217: Unknown result type (might be due to invalid IL or missing references) //IL_0234: Unknown result type (might be due to invalid IL or missing references) //IL_023f: Expected O, but got Unknown //IL_023f: Expected O, but got Unknown skills = new Dictionary<SkillType, Skill>(); skillByName = new Dictionary<string, Skill>(); InitializedTerminal = false; hasConfigSync = true; Harmony val = new Harmony("org.bepinex.helpers.skillmanager"); val.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(FejdStartup), "Awake", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(Skill), "Patch_FejdStartup", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(Skills), "GetSkillDef", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(Skill), "Patch_Skills_GetSkillDef", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(Skills), "CheatRaiseSkill", (Type[])null, (Type[])null), new HarmonyMethod(AccessTools.DeclaredMethod(typeof(Skill), "Patch_Skills_CheatRaiseskill", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(Skills), "CheatResetSkill", (Type[])null, (Type[])null), new HarmonyMethod(AccessTools.DeclaredMethod(typeof(Skill), "Patch_Skills_CheatResetSkill", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(Localization), "LoadCSV", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(LocalizeKey), "AddLocalizedKeys", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(Terminal), "InitTerminal", (Type[])null, (Type[])null), new HarmonyMethod(AccessTools.DeclaredMethod(typeof(Skill), "Patch_Terminal_InitTerminal_Prefix", (Type[])null, (Type[])null)), new HarmonyMethod(AccessTools.DeclaredMethod(typeof(Skill), "Patch_Terminal_InitTerminal", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(Localization), "SetupLanguage", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(LocalizationCache), "LocalizationPostfix", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(Skills), "OnDeath", (Type[])null, (Type[])null), new HarmonyMethod(AccessTools.DeclaredMethod(typeof(Skill), "Patch_Skills_OnDeath_Prefix", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(Skill), "Patch_Skills_OnDeath_Finalizer", (Type[])null, (Type[])null)), (HarmonyMethod)null); } private static void Patch_FejdStartup() { //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Expected O, but got Unknown //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Expected O, but got Unknown //IL_01e1: Unknown result type (might be due to invalid IL or missing references) //IL_01eb: Expected O, but got Unknown foreach (Skill skill in skills.Values) { if (skill.Configurable) { string key = skill.Name.Key; string group = new Regex("['[\"\\]]").Replace(english.Localize(key), "").Trim(); string category = Localization.instance.Localize(key).Trim(); ConfigEntry<float> skillGain = config(group, "Skill gain factor", skill.SkillGainFactor, new ConfigDescription("The rate at which you gain experience for the skill.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 5f), new object[1] { new ConfigurationManagerAttributes { Category = category } })); skill.SkillGainFactor = skillGain.Value; skillGain.SettingChanged += delegate { skill.SkillGainFactor = skillGain.Value; }; ConfigEntry<float> skillEffect = config(group, "Skill effect factor", skill.SkillEffectFactor, new ConfigDescription("The power of the skill, based on the default power.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 5f), new object[1] { new ConfigurationManagerAttributes { Category = category } })); skill.SkillEffectFactor = skillEffect.Value; skillEffect.SettingChanged += delegate { skill.SkillEffectFactor = skillEffect.Value; }; ConfigEntry<int> skillLoss = config(group, "Skill loss", skill.skillLoss, new ConfigDescription("How much experience to lose on death.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), new object[1] { new ConfigurationManagerAttributes { Category = category } })); skill.skillLoss = skillLoss.Value; skillLoss.SettingChanged += delegate { skill.skillLoss = skillLoss.Value; }; } } } private static void Patch_Skills_GetSkillDef(ref SkillDef? __result, List<SkillDef> ___m_skills, SkillType type) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) if (__result == null) { SkillDef val = GetSkillDef(type); if (val != null) { ___m_skills.Add(val); __result = val; } } } private static bool Patch_Skills_CheatRaiseskill(Skills __instance, string name, float value, Player ___m_player) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) foreach (SkillType key in skills.Keys) { SkillType current = key; Skill skill = skills[current]; if (string.Equals(skill.internalSkillName, name, StringComparison.CurrentCultureIgnoreCase)) { Skill skill2 = __instance.GetSkill(current); skill2.m_level += value; skill2.m_level = Mathf.Clamp(skill2.m_level, 0f, 100f); ((Character)___m_player).Message((MessageType)1, "Skill increased " + Localization.instance.Localize("$skill_" + ((object)(SkillType)(ref current)).ToString()) + ": " + (int)skill2.m_level, 0, skill2.m_info.m_icon); Console.instance.Print("Skill " + skill.internalSkillName + " = " + skill2.m_level); return false; } } return true; } private static bool Patch_Skills_CheatResetSkill(Skills __instance, string name) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) foreach (SkillType key in skills.Keys) { Skill skill = skills[key]; if (string.Equals(skill.internalSkillName, name, StringComparison.CurrentCultureIgnoreCase)) { __instance.ResetSkill(key); Console.instance.Print("Skill " + skill.internalSkillName + " reset"); return false; } } return true; } private static void Patch_Skills_OnDeath_Prefix(Skills __instance, ref Dictionary<SkillType, Skill>? __state) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) if (__state == null) { __state = new Dictionary<SkillType, Skill>(); } foreach (KeyValuePair<SkillType, Skill> skill in skills) { if (__instance.m_skillData.TryGetValue(skill.Key, out var value)) { __state[skill.Key] = value; if (skill.Value.skillLoss > 0) { Skill obj = value; obj.m_level -= value.m_level * (float)skill.Value.SkillLoss / 100f; value.m_accumulator = 0f; } __instance.m_skillData.Remove(skill.Key); } } } private static void Patch_Skills_OnDeath_Finalizer(Skills __instance, ref Dictionary<SkillType, Skill>? __state) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) if (__state == null) { return; } foreach (KeyValuePair<SkillType, Skill> item in __state) { __instance.m_skillData[item.Key] = item.Value; } __state = null; } private static void Patch_Terminal_InitTerminal_Prefix() { InitializedTerminal = Terminal.m_terminalInitialized; } private static void Patch_Terminal_InitTerminal() { if (!InitializedTerminal) { AddSkill(Terminal.commands["raiseskill"]); AddSkill(Terminal.commands["resetskill"]); } static void AddSkill(ConsoleCommand command) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Expected O, but got Unknown ConsoleOptionsFetcher fetcher = command.m_tabOptionsFetcher; command.m_tabOptionsFetcher = (ConsoleOptionsFetcher)delegate { List<string> list = fetcher.Invoke(); list.AddRange(skills.Values.Select((Skill skill) => skill.internalSkillName)); return list; }; } } private static SkillDef? GetSkillDef(SkillType skillType) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) if (!skills.ContainsKey(skillType)) { return null; } Skill skill = skills[skillType]; return skill.skillDef; } private static byte[] ReadEmbeddedFileBytes(string name) { using MemoryStream memoryStream = new MemoryStream(); Assembly.GetExecutingAssembly().GetManifestResourceStream(Assembly.GetExecutingAssembly().GetName().Name + "." + name).CopyTo(memoryStream); return memoryStream.ToArray(); } private static Texture2D loadTexture(string name) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Expected O, but got Unknown Texture2D val = new Texture2D(0, 0); ImageConversion.LoadImage(val, ReadEmbeddedFileBytes("icons." + name)); return val; } private static Sprite loadSprite(string name, int width, int height) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) return Sprite.Create(loadTexture(name), new Rect(0f, 0f, (float)width, (float)height), Vector2.zero); } private static ConfigEntry<T> config<T>(string group, string name, T value, ConfigDescription description) { ConfigEntry<T> val = plugin.Config.Bind<T>(group, name, value, description); configSync?.GetType().GetMethod("AddConfigEntry").MakeGenericMethod(typeof(T)) .Invoke(configSync, new object[1] { val }); return val; } private static ConfigEntry<T> config<T>(string group, string name, T value, string description) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Expected O, but got Unknown return config(group, name, value, new ConfigDescription(description, (AcceptableValueBase)null, Array.Empty<object>())); } } public static class SkillManagerVersion { public const string Version = "1.7.0"; } [PublicAPI] public static class SkillExtensions { public static float GetSkillFactor(this Character character, string name) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) return character.GetSkillFactor(Skill.fromName(name)) * Skill.skillByName[name].SkillEffectFactor; } public static float GetSkillFactor(this Skills skills, string name) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) return skills.GetSkillFactor(Skill.fromName(name)) * Skill.skillByName[name].SkillEffectFactor; } public static void RaiseSkill(this Character character, string name, float value = 1f) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) character.RaiseSkill(Skill.fromName(name), value); } public static void RaiseSkill(this Skills skill, string name, float value = 1f) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) skill.RaiseSkill(Skill.fromName(name), value); } public static void LowerSkill(this Character character, string name, float factor = 1f) { character.GetSkills().LowerSkill(name, factor); } public static void LowerSkill(this Skills skills, string name, float factor) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) if (factor > 0f && skills.m_skillData.TryGetValue(Skill.fromName(name), out var value)) { Skill obj = value; obj.m_level -= value.m_level * factor; value.m_accumulator = 0f; } } } } namespace BlacksmithingExpanded { [BepInPlugin("org.bepinex.plugins.blacksmithingexpanded", "Blacksmithing Expanded", "1.1.7")] public class BlacksmithingExpanded : BaseUnityPlugin { private struct ItemBaseStats { public float armor; public DamageTypes damages; public float durability; public List<DamageModPair> resistances; public bool isCached; } public class WorkstationInfusion { public int tier; public float timestamp; public float originalSpeed; public float bonusSpeed; public bool wasActive; public bool IsExpired => Time.time - timestamp > cfg_InfusionExpireTime.Value; public float RemainingTime => Mathf.Max(0f, cfg_InfusionExpireTime.Value - (Time.time - timestamp)); } private class ItemFilterConfig { public List<string> Whitelist { get; set; } = new List<string>(); public List<string> Blacklist { get; set; } = new List<string>(); } public static class ItemEligibilityCache { private static readonly Dictionary<string, bool> eligibilityCache = new Dictionary<string, bool>(); private static readonly Dictionary<string, bool> allowedCache = new Dictionary<string, bool>(); private const int MAX_CACHE_SIZE = 200; public static bool IsEligibleForBlacksmithingBonuses(ItemData item) { if (item?.m_shared == null) { return false; } string name = item.m_shared.m_name; if (eligibilityCache.TryGetValue(name, out var value)) { return value; } bool flag = CalculateEligibility(item); if (eligibilityCache.Count >= 200) { string key = eligibilityCache.Keys.First(); eligibilityCache.Remove(key); } eligibilityCache[name] = flag; return flag; } public static bool IsItemAllowed(ItemData item) { if (!cfg_UseYamlFiltering.Value) { return true; } if (item?.m_shared == null) { return false; } string name = item.m_shared.m_name; if (allowedCache.TryGetValue(name, out var value)) { return value; } bool flag = BlacksmithingExpanded.IsItemAllowed(item); if (allowedCache.Count >= 200) { string key = allowedCache.Keys.First(); allowedCache.Remove(key); } allowedCache[name] = flag; return flag; } private static bool CalculateEligibility(ItemData item) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Invalid comparison between Unknown and I4 //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Invalid comparison between Unknown and I4 //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Invalid comparison between Unknown and I4 //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Invalid comparison between Unknown and I4 //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Invalid comparison between Unknown and I4 if (!item.IsWeapon() && (int)item.m_shared.m_itemType != 5 && (int)item.m_shared.m_itemType != 6 && (int)item.m_shared.m_itemType != 7 && (int)item.m_shared.m_itemType != 11 && (int)item.m_shared.m_itemType != 17) { return false; } if (item.m_shared.m_maxStackSize > 1) { return false; } if (!IsItemAllowed(item)) { return false; } return true; } public static void ClearCache() { eligibilityCache.Clear(); allowedCache.Clear(); } } private class BlacksmithingItemData : ItemData { public static readonly Dictionary<SharedData, BlacksmithingItemData> activeItems = new Dictionary<SharedData, BlacksmithingItemData>(); [SerializeField] public int level = 0; [SerializeField] public int lastKnownQuality = 0; [SerializeField] public string infusion = ""; [SerializeField] public float baseDurability = 0f; [SerializeField] public float maxDurability = 0f; [SerializeField] public float armorBonus = 0f; [SerializeField] public float damageBlunt = 0f; [SerializeField] public float damageSlash = 0f; [SerializeField] public float damagePierce = 0f; [SerializeField] public float damageFire = 0f; [SerializeField] public float damageFrost = 0f; [SerializeField] public float damageLightning = 0f; [SerializeField] public float damagePoison = 0f; [SerializeField] public float damageSpirit = 0f; [SerializeField] public float blockPowerBonus = 0f; [SerializeField] public float timedBlockBonus = 0f; [SerializeField] public bool statsApplied = false; protected override bool AllowStackingIdenticalValues { get; set; } = true; ~BlacksmithingItemData() { activeItems.Remove(base.Item.m_shared); } public override void Load() { base.Load(); activeItems[base.Item.m_shared] = this; if (!base.IsCloned && level > 0) { ApplyStoredStats(); statsApplied = true; } } private void ApplyStoredStats() { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Expected O, but got Unknown //IL_00ff: 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_014d: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Unknown result type (might be due to invalid IL or missing references) //IL_01c2: Unknown result type (might be due to invalid IL or missing references) //IL_01e9: Unknown result type (might be due to invalid IL or missing references) //IL_0210: Unknown result type (might be due to invalid IL or missing references) //IL_0231: Unknown result type (might be due to invalid IL or missing references) //IL_0237: Invalid comparison between Unknown and I4 ItemBaseStats baseStats = GetBaseStats(base.Item); if (!baseStats.isCached) { return; } SharedData shared = base.Item.m_shared; base.Item.m_shared = (SharedData)((object)shared).GetType().GetMethod("MemberwiseClone", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(shared, null); if (cfg_EnableDurabilityBonus.Value && maxDurability > 0f) { base.Item.m_shared.m_maxDurability = maxDurability; base.Item.m_durability = Mathf.Min(base.Item.m_durability, maxDurability); } if (cfg_EnableArmorBonus.Value && armorBonus > 0f) { base.Item.m_shared.m_armor = baseStats.armor + armorBonus; } base.Item.m_shared.m_damages.m_blunt = baseStats.damages.m_blunt + damageBlunt; base.Item.m_shared.m_damages.m_slash = baseStats.damages.m_slash + damageSlash; base.Item.m_shared.m_damages.m_pierce = baseStats.damages.m_pierce + damagePierce; base.Item.m_shared.m_damages.m_fire = baseStats.damages.m_fire + damageFire; base.Item.m_shared.m_damages.m_frost = baseStats.damages.m_frost + damageFrost; base.Item.m_shared.m_damages.m_lightning = baseStats.damages.m_lightning + damageLightning; base.Item.m_shared.m_damages.m_poison = baseStats.damages.m_poison + damagePoison; base.Item.m_shared.m_damages.m_spirit = baseStats.damages.m_spirit + damageSpirit; if ((int)base.Item.m_shared.m_itemType == 5) { if (blockPowerBonus > 0f) { SharedData shared2 = base.Item.m_shared; shared2.m_blockPower += blockPowerBonus; } if (timedBlockBonus > 0f) { SharedData shared3 = base.Item.m_shared; shared3.m_timedBlockBonus += timedBlockBonus; } } } public override void Unload() { activeItems.Remove(base.Item.m_shared); } } [HarmonyPatch(typeof(InventoryGui), "DoCrafting")] public static class Patch_Crafting { private static void Prefix(InventoryGui __instance, out Recipe __state) { __state = __instance.m_craftRecipe; } private static void Postfix(InventoryGui __instance, Recipe __state) { Player localPlayer = Player.m_localPlayer; if (((localPlayer != null) ? ((Humanoid)localPlayer).GetInventory() : null) == null || (Object)(object)__state?.m_item == (Object)null) { return; } int playerBlacksmithingLevel = GetPlayerBlacksmithingLevel(localPlayer); ItemData val = FindMostRecentCraftedItem(localPlayer, __state); if (val == null) { return; } ApplyCraftingBonuses(val, Math.Max(playerBlacksmithingLevel, 1)); BlacksmithingItemData blacksmithingItemData = val.Data().Get<BlacksmithingItemData>(); if (blacksmithingItemData != null && blacksmithingItemData.maxDurability > 0f) { val.m_durability = blacksmithingItemData.maxDurability; } float num = HandleCraftingXP(localPlayer, val); if (num >= 1f) { Debug.Log((object)$"[BlacksmithingExpanded] Crafted {val.m_shared.m_name}: level={playerBlacksmithingLevel}, quality={val.m_quality}, xp={num:F2}"); } if (cfg_EnableExtraItemBonus.Value && ItemEligibilityCache.IsEligibleForBlacksmithingBonuses(val)) { float num2 = cfg_ChanceExtraItemAt100.Value * ((float)playerBlacksmithingLevel / 100f); if (Random.value <= num2) { ItemData val2 = val.Clone(); ((Humanoid)localPlayer).GetInventory().AddItem(val2); ((Character)localPlayer).Message((MessageType)1, "Masterwork crafting created an extra item!", 0, (Sprite)null); } } } private static ItemData FindMostRecentCraftedItem(Player player, Recipe recipe) { Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory == null || (Object)(object)recipe?.m_item == (Object)null) { return null; } ItemData templateItem = ((Component)recipe.m_item).GetComponent<ItemDrop>()?.m_itemData; if (templateItem?.m_shared == null) { return null; } List<ItemData> list = (from item in inventory.GetAllItems() where item.m_shared.m_name == templateItem.m_shared.m_name where item.m_quality == templateItem.m_quality orderby item.m_durability descending select item).ToList(); foreach (ItemData item in list) { BlacksmithingItemData blacksmithingItemData = item.Data().Get<BlacksmithingItemData>(); if (blacksmithingItemData == null || blacksmithingItemData.level == 0) { return item; } } return null; } } [HarmonyPatch(typeof(CraftingStation), "GetLevel")] public static class Patch_CraftingStationLevel { private static void Postfix(CraftingStation __instance, ref int __result) { if (!cfg_EnableStationLevelBonus.Value || __instance.m_craftRequireRoof || !(__instance.m_rangeBuild > 0f)) { return; } Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null)) { int playerBlacksmithingLevel = GetPlayerBlacksmithingLevel(localPlayer); int num = CalculateWorkbenchBonus(__instance.m_name, playerBlacksmithingLevel); if (num > 0) { __result += num; } } } private static int CalculateWorkbenchBonus(string stationName, int level) { switch (stationName) { case "piece_workbench": if (level >= 20) { return 2; } if (level >= 10) { return 1; } return 0; case "forge": if (level >= 40) { return 2; } if (level >= 30) { return 1; } return 0; case "blackforge": case "piece_galdrtable": if (level >= 60) { return 2; } if (level >= 50) { return 1; } return 0; default: return 0; } } } [HarmonyPatch(typeof(InventoryGui), "DoCrafting")] public static class Patch_UpgradeDetection_Alternative { private static void Postfix(InventoryGui __instance) { Player localPlayer = Player.m_localPlayer; if (((localPlayer != null) ? ((Humanoid)localPlayer).GetInventory() : null) == null) { return; } foreach (ItemData allItem in ((Humanoid)localPlayer).GetInventory().GetAllItems()) { if (ItemEligibilityCache.IsEligibleForBlacksmithingBonuses(allItem)) { BlacksmithingItemData blacksmithingItemData = allItem.Data().Get<BlacksmithingItemData>(); if (blacksmithingItemData != null && blacksmithingItemData.lastKnownQuality < allItem.m_quality) { int playerBlacksmithingLevel = GetPlayerBlacksmithingLevel(localPlayer); GiveBlacksmithingXP(localPlayer, cfg_XPPerUpgrade.Value); ApplyCraftingBonuses(allItem, playerBlacksmithingLevel); } } } } } [HarmonyPatch(typeof(ItemData), "GetTooltip", new Type[] { typeof(ItemData), typeof(int), typeof(bool), typeof(float), typeof(int) })] public static class Patch_Tooltip { public static void Postfix(ItemData item, bool crafting, ref string __result) { if (item == null) { return; } BlacksmithingItemData blacksmithingItemData = item.Data().Get<BlacksmithingItemData>(); if (blacksmithingItemData != null && blacksmithingItemData.level > 0) { if (cfg_ShowBlacksmithLevelInTooltip.Value) { __result += $"\n<color=orange>Forged at Blacksmithing {blacksmithingItemData.level}</color>"; } if (cfg_ShowInfusionInTooltip.Value && !string.IsNullOrEmpty(blacksmithingItemData.infusion)) { __result = __result + "\n<color=#87CEEB>Elemental Infusion: " + blacksmithingItemData.infusion + "</color>"; } } } } [HarmonyPatch(typeof(Player), "UpdatePlacementGhost")] public static class Patch_UpgradeDetection { public static readonly Dictionary<long, Dictionary<string, int>> playerItemQualities = new Dictionary<long, Dictionary<string, int>>(); private static float lastUpdateTime = 0f; private const float UPDATE_INTERVAL = 2f; private static void Postfix(Player __instance) { if (!(Time.time - lastUpdateTime < 2f)) { lastUpdateTime = Time.time; CheckForUpgrades(__instance); } } private static void CheckForUpgrades(Player player) { Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory == null) { return; } long playerID = player.GetPlayerID(); if (!playerItemQualities.TryGetValue(playerID, out var value)) { value = new Dictionary<string, int>(); playerItemQualities[playerID] = value; } HashSet<string> currentItems = new HashSet<string>(); foreach (ItemData allItem in inventory.GetAllItems()) { if (!ItemEligibilityCache.IsEligibleForBlacksmithingBonuses(allItem)) { continue; } string text = $"{allItem.m_shared.m_name}_{((object)allItem).GetHashCode()}"; currentItems.Add(text); if (value.TryGetValue(text, out var value2)) { if (allItem.m_quality > value2) { Debug.Log((object)$"[BlacksmithingExpanded] UPGRADE DETECTED: {allItem.m_shared.m_name} from quality {value2} to {allItem.m_quality}"); GiveBlacksmithingXP(player, cfg_XPPerUpgrade.Value); int playerBlacksmithingLevel = GetPlayerBlacksmithingLevel(player); Debug.Log((object)$"[BlacksmithingExpanded] Applying upgrade bonuses at level {playerBlacksmithingLevel}"); ApplyCraftingBonuses(allItem, playerBlacksmithingLevel); value[text] = allItem.m_quality; Debug.Log((object)("[BlacksmithingExpanded] Upgrade processing complete for " + allItem.m_shared.m_name)); } } else { value[text] = allItem.m_quality; } } if (!(Time.time % 20f < 2f)) { return; } List<string> list = value.Keys.Where((string key) => !currentItems.Contains(key)).ToList(); foreach (string item in list) { value.Remove(item); } } } [HarmonyPatch(typeof(Smelter), "OnAddOre")] public static class Patch_Smelter_AddOre { private static void Postfix(Smelter __instance, Humanoid user, bool __result) { //IL_028b: Unknown result type (might be due to invalid IL or missing references) //IL_0313: Unknown result type (might be due to invalid IL or missing references) //IL_02f9: Unknown result type (might be due to invalid IL or missing references) //IL_02d7: Unknown result type (might be due to invalid IL or missing references) //IL_02a2: Unknown result type (might be due to invalid IL or missing references) //IL_02bc: Unknown result type (might be due to invalid IL or missing references) //IL_03bf: Unknown result type (might be due to invalid IL or missing references) try { if (!__result) { return; } Player val = (Player)(object)((user is Player) ? user : null); if (val == null) { return; } GiveBlacksmithingXP(val, cfg_XPPerSmelt.Value); ZNetView nview = __instance.m_nview; ZDO val2 = ((nview != null) ? nview.GetZDO() : null); if (val2 == null) { return; } bool flag = __instance.m_name.Contains("charcoal_kiln"); bool flag2 = __instance.m_name.Contains("blastfurnace"); if ((flag && !cfg_EnableKilnSpeedBonus.Value) || (!flag && !cfg_EnableSmeltingSpeedBonus.Value)) { return; } int playerBlacksmithingLevel = GetPlayerBlacksmithingLevel(val); if (cfg_InfusionTierInterval.Value <= 0) { Debug.LogWarning((object)($"[BlacksmithingExpanded] Invalid tier interval ({cfg_InfusionTierInterval.Value}). " + "Must be greater than 0. Speed bonuses will not be applied.")); return; } int num = playerBlacksmithingLevel / cfg_InfusionTierInterval.Value; if (num <= 0) { return; } float num2 = (flag ? cfg_KilnSpeedBonusPerTier.Value : cfg_SmeltingSpeedBonusPerTier.Value); float num3 = 1f + (float)num * num2; if (num3 <= 0f) { string text = (flag ? "kiln" : (flag2 ? "blast furnace" : "smelter")); Debug.LogWarning((object)($"[BlacksmithingExpanded] Invalid speed multiplier ({num3}) for {text}. " + "This may be caused by conflicting mods modifying " + text + " speeds. Consider disabling 'Enable " + (flag ? "kiln" : "smelting") + " speed bonus' in the config.")); return; } if (__instance.m_secPerProduct <= 0f) { string arg = (flag ? "kiln" : (flag2 ? "blast furnace" : "smelter")); Debug.LogWarning((object)($"[BlacksmithingExpanded] Invalid original {arg} speed ({__instance.m_secPerProduct}). " + "This may indicate a conflict with another mod. Consider disabling 'Enable " + (flag ? "kiln" : "smelting") + " speed bonus' in the config.")); return; } WorkstationInfusion workstationInfusion = new WorkstationInfusion { tier = num, timestamp = Time.time, originalSpeed = __instance.m_secPerProduct, bonusSpeed = __instance.m_secPerProduct / num3 }; bool flag3 = false; if (flag) { if (kilnInfusions.ContainsKey(val2.m_uid)) { kilnInfusions[val2.m_uid].timestamp = Time.time; kilnInfusions[val2.m_uid].tier = num; } else { kilnInfusions[val2.m_uid] = workstationInfusion; flag3 = true; } } else if (flag2) { blastFurnaceInfusions[val2.m_uid] = workstationInfusion; flag3 = true; } else { smelterInfusions[val2.m_uid] = workstationInfusion; flag3 = true; } if (cfg_EnableOreSaveBonus.Value && !flag) { float num4 = cfg_SmelterSaveOreChanceAt100.Value * ((float)playerBlacksmithingLevel / 100f); if (Random.value <= num4 && __instance.GetFuel() < (float)__instance.m_maxFuel) { __instance.m_nview.GetZDO().Set("fuel", __instance.GetFuel() + 1f); } } __instance.m_secPerProduct = workstationInfusion.bonusSpeed; if (flag3) { ManageInfusionGlow(((Component)__instance).transform, val2.m_uid, enable: true); } } catch (DivideByZeroException ex) { Debug.LogError((object)("[BlacksmithingExpanded] Division by zero in smelting speed bonus calculation. Please check your config values (Tier Interval, Speed Bonus Per Tier). Consider disabling speed bonuses if the issue persists. Error: " + ex.Message)); } catch (Exception arg2) { Debug.LogError((object)$"[BlacksmithingExpanded] Unexpected error in Patch_Smelter_AddOre: {arg2}"); } } } [HarmonyPatch(typeof(Smelter), "UpdateSmelter")] public static class Patch_Smelter_Update { private static void Prefix(Smelter __instance) { ZNetView nview = __instance.m_nview; ZDO val = ((nview != null) ? nview.GetZDO() : null); if (val == null) { return; } bool flag = __instance.m_name.Contains("charcoal_kiln"); bool flag2 = __instance.m_name.Contains("blastfurnace"); if ((!flag || cfg_EnableKilnSpeedBonus.Value) && (flag || cfg_EnableSmeltingSpeedBonus.Value)) { if (flag) { HandleKilnInfusion(__instance, val); } else if (flag2) { HandleBlastFurnaceInfusion(__instance, val); } else { HandleSmelterInfusion(__instance, val); } } } private static void HandleBlastFurnaceInfusion(Smelter blastFurnace, ZDO zdo) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_013f: 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_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_0112: 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) if (!originalBlastFurnaceSpeeds.ContainsKey(zdo.m_uid)) { originalBlastFurnaceSpeeds[zdo.m_uid] = blastFurnace.m_secPerProduct; } if (blastFurnaceInfusions.TryGetValue(zdo.m_uid, out var value)) { float num = Time.time - value.timestamp; if (!(num < 1f) && (value.IsExpired || blastFurnace.GetQueueSize() == 0 || blastFurnace.GetFuel() <= 0f)) { blastFurnace.m_secPerProduct = originalBlastFurnaceSpeeds[zdo.m_uid]; blastFurnaceInfusions.Remove(zdo.m_uid); ManageInfusionGlow(((Component)blastFurnace).transform, zdo.m_uid, enable: false); } else if (value.bonusSpeed > 0f) { blastFurnace.m_secPerProduct = value.bonusSpeed; value.wasActive = true; } else { Debug.LogWarning((object)"[BlacksmithingExpanded] Invalid bonus speed detected for blast furnace. Reverting to original speed."); blastFurnace.m_secPerProduct = originalBlastFurnaceSpeeds[zdo.m_uid]; blastFurnaceInfusions.Remove(zdo.m_uid); } } else { blastFurnace.m_secPerProduct = originalBlastFurnaceSpeeds[zdo.m_uid]; } } private static void HandleKilnInfusion(Smelter kiln, ZDO zdo) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) if (!originalKilnSpeeds.ContainsKey(zdo.m_uid)) { originalKilnSpeeds[zdo.m_uid] = kiln.m_secPerProduct; } if (kilnInfusions.TryGetValue(zdo.m_uid, out var value)) { bool flag = kiln.GetFuel() > 0f || kiln.GetBakeTimer() > 0f; if (value.IsExpired || !flag) { kiln.m_secPerProduct = originalKilnSpeeds[zdo.m_uid]; kilnInfusions.Remove(zdo.m_uid); ManageInfusionGlow(((Component)kiln).transform, zdo.m_uid, enable: false); } else if (value.bonusSpeed > 0f) { kiln.m_secPerProduct = value.bonusSpeed; } else { Debug.LogWarning((object)"[BlacksmithingExpanded] Invalid bonus speed detected for kiln. Reverting to original speed."); kiln.m_secPerProduct = originalKilnSpeeds[zdo.m_uid]; kilnInfusions.Remove(zdo.m_uid); } } else { kiln.m_secPerProduct = originalKilnSpeeds[zdo.m_uid]; } } private static void HandleSmelterInfusion(Smelter smelter, ZDO zdo) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_013f: 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_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_0112: 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) if (!originalSmelterSpeeds.ContainsKey(zdo.m_uid)) { originalSmelterSpeeds[zdo.m_uid] = smelter.m_secPerProduct; } if (smelterInfusions.TryGetValue(zdo.m_uid, out var value)) { float num = Time.time - value.timestamp; if (!(num < 1f) && (value.IsExpired || smelter.GetQueueSize() == 0 || smelter.GetFuel() <= 0f)) { smelter.m_secPerProduct = originalSmelterSpeeds[zdo.m_uid]; smelterInfusions.Remove(zdo.m_uid); ManageInfusionGlow(((Component)smelter).transform, zdo.m_uid, enable: false); } else if (value.bonusSpeed > 0f) { smelter.m_secPerProduct = value.bonusSpeed; value.wasActive = true; } else { Debug.LogWarning((object)"[BlacksmithingExpanded] Invalid bonus speed detected for smelter. Reverting to original speed."); smelter.m_secPerProduct = originalSmelterSpeeds[zdo.m_uid]; smelterInfusions.Remove(zdo.m_uid); } } else { smelter.m_secPerProduct = originalSmelterSpeeds[zdo.m_uid]; } } } public class FlickerLight : MonoBehaviour { private Light lightSource; private float baseIntensity; private void Start() { lightSource = ((Component)this).GetComponent<Light>(); baseIntensity = lightSource.intensity; } private void Update() { if ((Object)(object)lightSource != (Object)null) { lightSource.intensity = baseIntensity + Random.Range(-0.2f, 0.2f); } } } [HarmonyPatch(typeof(Attack), "Start")] public static class Patch_AttackStart { private static void Prefix(Attack __instance, ItemData weapon, Humanoid character) { //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Invalid comparison between Unknown and I4 if (weapon == null || !weapon.IsWeapon()) { return; } Player val = (Player)(object)((character is Player) ? character : null); if (val == null || !weapon.m_shared.m_name.ToLower().Contains("spear") || (int)__instance.m_attackType != 2) { return; } BlacksmithingItemData blacksmithingItemData = weapon.Data().Get<BlacksmithingItemData>(); if (blacksmithingItemData != null && blacksmithingItemData.level > 0) { string key = GenerateSpearKey(weapon, val); BlacksmithingItemData blacksmithingItemData2 = new BlacksmithingItemData(); CopyBlacksmithingData(blacksmithingItemData, blacksmithingItemData2); if (tempSpearDataStorage.Count >= 50) { CleanupOldSpearData(); } tempSpearDataStorage[key] = blacksmithingItemData2; } } } [HarmonyPatch(typeof(ItemDrop), "Start")] public static class Patch_ItemDropStart { private static void Postfix(ItemDrop __instance) { if (__instance == null) { return; } ItemData itemData = __instance.m_itemData; if (!((itemData != null) ? new bool?(itemData.IsWeapon()) : null).GetValueOrDefault() || !__instance.m_itemData.m_shared.m_name.ToLower().Contains("spear")) { return; } ItemData itemData2 = __instance.m_itemData; BlacksmithingItemData blacksmithingItemData = itemData2.Data().Get<BlacksmithingItemData>(); if (blacksmithingItemData == null || blacksmithingItemData.level <= 0) { KeyValuePair<string, BlacksmithingItemData> keyValuePair = FindBestSpearMatch(itemData2); if (keyValuePair.Key != null && keyValuePair.Value != null) { BlacksmithingItemData orCreate = itemData2.Data().GetOrCreate<BlacksmithingItemData>(); CopyBlacksmithingData(keyValuePair.Value, orCreate); orCreate.Save(); ApplyStoredBlacksmithingStats(itemData2, orCreate); tempSpearDataStorage.Remove(keyValuePair.Key); } } } } [HarmonyPatch(typeof(ItemData), "Clone")] public static class Patch_ItemDataClone { private static void Postfix(ItemData __result, ItemData __instance) { if (__instance != null && __result != null) { BlacksmithingItemData blacksmithingItemData = __instance.Data().Get<BlacksmithingItemData>(); if (blacksmithingItemData != null && blacksmithingItemData.level > 0) { BlacksmithingItemData orCreate = __result.Data().GetOrCreate<BlacksmithingItemData>(); CopyBlacksmithingData(blacksmithingItemData, orCreate); orCreate.Save(); orCreate.Load(); } } } } [CompilerGenerated] private sealed class <>c__DisplayClass94_0 { public HashSet<long> connectedPlayerIds; internal bool <PerformanceMaintenance>b__0(long id) { return !connectedPlayerIds.Contains(id); } } [CompilerGenerated] private sealed class <PerformanceMaintenance>d__94 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public BlacksmithingExpanded <>4__this; private <>c__DisplayClass94_0 <>8__1; private List<long> <playersToRemove>5__2; private List<Player>.Enumerator <>s__3; private Player <player>5__4; private List<long>.Enumerator <>s__5; private long <playerId>5__6; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <PerformanceMaintenance>d__94(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>8__1 = null; <playersToRemove>5__2 = null; <>s__3 = default(List<Player>.Enumerator); <player>5__4 = null; <>s__5 = default(List<long>.Enumerator); <>1__state = -2; } private bool MoveNext() { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; break; case 1: <>1__state = -1; ItemEligibilityCache.ClearCache(); if (tempSpearDataStorage.Count > 25) { CleanupOldSpearData(); } <>8__1.connectedPlayerIds = new HashSet<long>(); <>s__3 = Player.GetAllPlayers().GetEnumerator(); try { while (<>s__3.MoveNext()) { <player>5__4 = <>s__3.Current; <>8__1.connectedPlayerIds.Add(<player>5__4.GetPlayerID()); <player>5__4 = null; } } finally { ((IDisposable)<>s__3).Dispose(); } <>s__3 = default(List<Player>.Enumerator); <playersToRemove>5__2 = Patch_UpgradeDetection.playerItemQualities.Keys.Where((long id) => !<>8__1.connectedPlayerIds.Contains(id)).ToList(); <>s__5 = <playersToRemove>5__2.GetEnumerator(); try { while (<>s__5.MoveNext()) { <playerId>5__6 = <>s__5.Current; Patch_UpgradeDetection.playerItemQualities.Remove(<playerId>5__6); } } finally { ((IDisposable)<>s__5).Dispose(); } <>s__5 = default(List<long>.Enumerator); <>8__1 = null; <playersToRemove>5__2 = null; break; } <>8__1 = new <>c__DisplayClass94_0(); <>2__current = (object)new WaitForSeconds(60f); <>1__state = 1; return true; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class <SpearDataCleanupRoutine>d__93 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public BlacksmithingExpanded <>4__this; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <SpearDataCleanupRoutine>d__93(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; break; case 1: <>1__state = -1; CleanupOldSpearData(); break; } <>2__current = (object)new WaitForSeconds(30f); <>1__state = 1; return true; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } internal const string ModName = "Blacksmithing Expanded"; internal const string ModVersion = "1.1.7"; internal const string ModGUID = "org.bepinex.plugins.blacksmithingexpanded"; private Harmony harmony; private static readonly ConfigSync configSync = new ConfigSync("org.bepinex.plugins.blacksmithingexpanded") { DisplayName = "Blacksmithing Expanded", CurrentVersion = "1.1.7", MinimumRequiredVersion = "1.1.7", ModRequired = true }; internal static Skill blacksmithSkill; private static readonly Dictionary<string, ItemBaseStats> baseStatsCache = new Dictionary<string, ItemBaseStats>(); private static readonly object cacheLocker = new object(); private static ItemFilterConfig itemFilterConfig; private static readonly HashSet<string> whitelistedItems = new HashSet<string>(); private static readonly HashSet<string> blacklistedItems = new HashSet<string>(); private static string configPath; public static readonly CustomSyncedValue<List<string>> syncedWhitelistItems = new CustomSyncedValue<List<string>>(configSync, "whitelist items", new List<string>()); public static readonly CustomSyncedValue<List<string>> syncedBlacklistItems = new CustomSyncedValue<List<string>>(configSync, "blacklist items", new List<string>()); internal static Dictionary<ZDOID, WorkstationInfusion> smelterInfusions = new Dictionary<ZDOID, WorkstationInfusion>(); internal static Dictionary<ZDOID, WorkstationInfusion> kilnInfusions = new Dictionary<ZDOID, WorkstationInfusion>(); internal static Dictionary<ZDOID, WorkstationInfusion> blastFurnaceInfusions = new Dictionary<ZDOID, WorkstationInfusion>(); private static readonly Dictionary<ZDOID, float> originalBlastFurnaceSpeeds = new Dictionary<ZDOID, float>(); private static readonly Dictionary<ZDOID, float> originalSmelterSpeeds = new Dictionary<ZDOID, float>(); private static readonly Dictionary<ZDOID, float> originalKilnSpeeds = new Dictionary<ZDOID, float>(); private static readonly Dictionary<ZDOID, GameObject> activeGlowEffects = new Dictionary<ZDOID, GameObject>(); private static readonly Dictionary<string, BlacksmithingItemData> tempSpearDataStorage = new Dictionary<string, BlacksmithingItemData>(); private const int MAX_SPEAR_DATA_ENTRIES = 50; private const float SPEAR_DATA_CLEANUP_INTERVAL = 30f; private static float lastSpearCleanup = 0f; internal static ConfigEntry<float> cfg_SkillGainFactor; internal static ConfigEntry<float> cfg_SkillEffectFactor; internal static ConfigEntry<int> cfg_InfusionTierInterval; internal static ConfigEntry<float> cfg_ChanceExtraItemAt100; internal static ConfigEntry<float> cfg_SmelterSaveOreChanceAt100; internal static ConfigEntry<bool> cfg_EnableSmeltingSpeedBonus; internal static ConfigEntry<bool> cfg_EnableKilnSpeedBonus; internal static ConfigEntry<float> cfg_SmeltingSpeedBonusPerTier; internal static ConfigEntry<float> cfg_KilnSpeedBonusPerTier; internal static ConfigEntry<float> cfg_InfusionExpireTime; internal static ConfigEntry<bool> cfg_ShowInfusionVisualEffect; internal static ConfigEntry<bool> cfg_ShowBlacksmithLevelInTooltip; internal static ConfigEntry<bool> cfg_ShowInfusionInTooltip; internal static ConfigEntry<float> cfg_FirstCraftBonusXP; internal static ConfigEntry<float> UpgradeBonus; internal static ConfigEntry<bool> cfg_EnableStationLevelBonus; internal static ConfigEntry<bool> cfg_UseYamlFiltering; internal static ConfigEntry<bool> cfg_LogFilteredItems; internal static ConfigEntry<int> cfg_DurabilityTierInterval; internal static ConfigEntry<float> cfg_DurabilityBonusPerTier; internal static ConfigEntry<float> cfg_DurabilityBonusPerUpgrade; internal static ConfigEntry<bool> cfg_RespectOriginalDurability; internal static ConfigEntry<float> cfg_MaxDurabilityCap; internal static ConfigEntry<bool> cfg_AllowNonRepairableItems; internal static ConfigEntry<bool> cfg_EnableDurabilityBonus; internal static ConfigEntry<bool> cfg_EnableDamageBonus; internal static ConfigEntry<bool> cfg_EnableArmorBonus; internal static ConfigEntry<int> cfg_StatTierInterval; internal static ConfigEntry<float> cfg_ArmorBonusPerTier; internal static ConfigEntry<float> cfg_ArmorBonusPerUpgrade; internal static ConfigEntry<float> cfg_ArmorCap; internal static ConfigEntry<int> cfg_DamageBonusPerTier; internal static ConfigEntry<float> cfg_StatBonusPerUpgrade; internal static ConfigEntry<bool> cfg_UsePercentageDamageBonus; internal static ConfigEntry<float> cfg_DamagePercentageBonusPerTier; internal static ConfigEntry<bool> cfg_UsePercentageUpgradeBonus; internal static ConfigEntry<float> cfg_StatPercentageBonusPerUpgrade; internal static ConfigEntry<bool> cfg_EnableElementalBonus; internal static ConfigEntry<bool> cfg_AlwaysAddElementalAtMax; internal static ConfigEntry<int> cfg_ElementalUnlockLevel; internal static ConfigEntry<float> cfg_FireBonusPerTier; internal static ConfigEntry<float> cfg_FrostBonusPerTier; internal static ConfigEntry<float> cfg_LightningBonusPerTier; internal static ConfigEntry<float> cfg_PoisonBonusPerTier; internal static ConfigEntry<float> cfg_SpiritBonusPerTier; internal static ConfigEntry<bool> cfg_BoostElementalWeapons; internal static ConfigEntry<float> cfg_ElementalWeaponBoostChance; internal static ConfigEntry<bool> cfg_UsePercentageElementalBonus; internal static ConfigEntry<float> cfg_ElementalPercentageBonusPerTier; internal static ConfigEntry<bool> cfg_EnableShieldBonus; internal static ConfigEntry<float> cfg_TimedBlockBonusPerTier; internal static ConfigEntry<float> cfg_TimedBlockBonusPerUpgrade; internal static ConfigEntry<float> cfg_BlockPowerBonusPerTier; internal static ConfigEntry<float> cfg_BlockPowerBonusPerUpgrade; internal static ConfigEntry<bool> cfg_EnableExtraItemBonus; internal static ConfigEntry<bool> cfg_EnableOreSaveBonus; internal static ConfigEntry<float> cfg_XPPerCraft; internal static ConfigEntry<float> cfg_XPPerSmelt; internal static ConfigEntry<float> cfg_XPPerRepair; internal static ConfigEntry<float> cfg_XPPerUpgrade; private static Sprite s_skillIcon; private ConfigEntry<T> AddConfig<T>(string group, string name, T value, string description, bool sync = true) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown ConfigEntry<T> val = ((BaseUnityPlugin)this).Config.Bind<T>(group, name, value, new ConfigDescription(description, (AcceptableValueBase)null, Array.Empty<object>())); SyncedConfigEntry<T> syncedConfigEntry = configSync.AddConfigEntry<T>(val); syncedConfigEntry.SynchronizedConfig = sync; return val; } private void Awake() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown harmony = new Harmony("org.bepinex.plugins.blacksmithingexpanded"); configPath = Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)this).Config.ConfigFilePath), "BlacksmithExpItemList.yml"); try { s_skillIcon = LoadEmbeddedSprite("smithing.png", 64, 64); if ((Object)(object)s_skillIcon == (Object)null) { throw new Exception("Failed to load embedded sprite: smithing.png"); } blacksmithSkill = new Skill("Blacksmithing", s_skillIcon); blacksmithSkill.Name.English("Blacksmithing"); blacksmithSkill.Description.English("Craft better, last longer. Improves durability, damage, and armor of crafted items."); blacksmithSkill.Configurable = true; } catch (Exception arg) { Debug.LogError((object)$"[BlacksmithingExpanded] Skill setup failed: {arg}"); } SetupConfigs(); if (blacksmithSkill != null) { blacksmithSkill.SkillGainFactor = cfg_SkillGainFactor.Value; blacksmithSkill.SkillEffectFactor = cfg_SkillEffectFactor.Value; cfg_SkillGainFactor.SettingChanged += delegate { blacksmithSkill.SkillGainFactor = cfg_SkillGainFactor.Value; }; cfg_SkillEffectFactor.SettingChanged += delegate { blacksmithSkill.SkillEffectFactor = cfg_SkillEffectFactor.Value; }; } InitializeYamlFiltering(); syncedWhitelistItems.ValueChanged += delegate { whitelistedItems.Clear(); foreach (string item in syncedWhitelistItems.Value) { whitelistedItems.Add(item); } ((BaseUnityPlugin)this).Logger.LogDebug((object)$"[BlacksmithingExpanded] Received synced whitelist: {whitelistedItems.Count} items"); }; syncedBlacklistItems.ValueChanged += delegate { blacklistedItems.Clear(); foreach (string item2 in syncedBlacklistItems.Value) { blacklistedItems.Add(item2); } ((BaseUnityPlugin)this).Logger.LogDebug((object)$"[BlacksmithingExpanded] Received synced blacklist: {blacklistedItems.Count} items"); }; cfg_UseYamlFiltering.SettingChanged += delegate { if ((Object)(object)ZNet.instance == (Object)null || ZNet.instance.IsServer()) { ReloadYamlConfiguration(); } }; ItemInfo.ForceLoadTypes.Add(typeof(BlacksmithingItemData)); ((MonoBehaviour)this).StartCoroutine(SpearDataCleanupRoutine()); ((MonoBehaviour)this).StartCoroutine(PerformanceMaintenance()); harmony.PatchAll(); ((BaseUnityPlugin)this).Logger.LogInfo((object)$"[BlacksmithingExpanded] Initialized successfully. Skill registered: {blacksmithSkill != null}"); } private void Update() { if (Time.time - lastSpearCleanup > 30f) { CleanupOldSpearData(); lastSpearCleanup = Time.time; } } [IteratorStateMachine(typeof(<SpearDataCleanupRoutine>d__93))] private IEnumerator SpearDataCleanupRoutine() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <SpearDataCleanupRoutine>d__93(0) { <>4__this = this }; } [IteratorStateMachine(typeof(<PerformanceMaintenance>d__94))] private IEnumerator PerformanceMaintenance() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <PerformanceMaintenance>d__94(0) { <>4__this = this }; } private static void CacheBaseStats(ItemData item) { //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) if (item?.m_shared == null) { return; } string name = item.m_shared.m_name; lock (cacheLocker) { if (!baseStatsCache.ContainsKey(name)) { baseStatsCache[name] = new ItemBaseStats { armor = item.m_shared.m_armor, damages = ((DamageTypes)(ref item.m_shared.m_damages)).Clone(), durability = item.m_shared.m_maxDurability, resistances = new List<DamageModPair>(item.m_shared.m_damageModifiers), isCached = true }; } } } private static ItemBaseStats GetBaseStats(ItemData item) { if (item?.m_shared == null) { ItemBaseStats result = default(ItemBaseStats); result.isCached = false; return result; } string name = item.m_shared.m_name; lock (cacheLocker) { if (!baseStatsCache.ContainsKey(name)) { CacheBaseStats(item); } return baseStatsCache[name]; } } private static void CleanupOldSpearData() { float time = Time.time; List<string> list = new List<string>(); foreach (KeyValuePair<string, BlacksmithingItemData> item in tempSpearDataStorage.ToList()) { string[] array = item.Key.Split(new char[1] { '_' }); if (array.Length >= 5 && float.TryParse(array[4], out var result)) { if (time - result > 120f) { list.Add(item.Key); } } else { list.Add(item.Key); } } if (tempSpearDataStorage.Count > 50) { List<string> collection = (from kvp in tempSpearDataStorage.OrderBy(delegate(KeyValuePair<string, BlacksmithingItemData> kvp) { string[] array2 = kvp.Key.Split(new char[1] { '_' }); float result2; return (array2.Length >= 5 && float.TryParse(array2[4], out result2)) ? result2 : 0f; }).Take(tempSpearDataStorage.Count - 50) select kvp.Key).ToList(); list.AddRange(collection); } foreach (string item2 in list) { tempSpearDataStorage.Remove(item2); } if (list.Count > 0) { Debug.Log((object)$"[BlacksmithingExpanded] Cleaned up {list.Count} old spear data entries"); } } private void SetupConfigs() { cfg_SkillGainFactor = AddConfig("General", "Skill gain factor", 1f, "Multiplier for blacksmithing XP gain rate (1.5 = 50% faster leveling)"); cfg_SkillEffectFactor = AddConfig("General", "Skill effect factor", 1f, "Global multiplier for all blacksmithing bonuses (damage, armor, durability, etc). Higher = stronger effects"); cfg_InfusionTierInterval = AddConfig("General", "Workstation infusion milestone interval", 10, "Every X blacksmithing levels unlocks a new tier of smelter/kiln speed bonus"); cfg_EnableSmeltingSpeedBonus = AddConfig("General", "Enable smelting speed bonus", value: true, "Enable/disable smelter (and blast furnace) speed bonuses. Disable this if you have other mods that modify smelter speeds to prevent conflicts"); cfg_EnableKilnSpeedBonus = AddConfig("General", "Enable kiln speed bonus", value: true, "Enable/disable kiln speed bonuses. Disable this if you have other mods that modify kiln speeds to prevent conflicts"); cfg_SmeltingSpeedBonusPerTier = AddConfig("General", "Smelting speed bonus per tier", 0.15f, "Speed bonus per tier - 0.15 = 15% faster smelting. Stacks with each tier"); cfg_KilnSpeedBonusPerTier = AddConfig("General", "Kiln speed bonus per tier", 0.15f, "Speed bonus per tier - 0.15 = 15% faster charcoal production. Stacks with each tier"); cfg_InfusionExpireTime = AddConfig("General", "Infusion expire time", 300f, "Seconds that speed bonuses last after adding fuel/ore to smelters/kilns (300 = 5 minutes)"); cfg_ShowInfusionVisualEffect = AddConfig("General", "Show infusion visual effect", value: true, "Show orange glowing light effect when smelters/kilns have speed bonuses active"); cfg_SmelterSaveOreChanceAt100 = AddConfig("General", "Ore save chance at 100", 0.2f, "At level 100: chance to not consume ore when smelting (0.2 = 20% ore savings)"); cfg_ChanceExtraItemAt100 = AddConfig("General", "Extra item chance at 100", 0.05f, "At level 100: chance to get bonus item when crafting (0.05 = 5% chance for double output)"); cfg_UsePercentageUpgradeBonus = AddConfig("PercentageSystem", "Use percentage upgrade bonus", value: true, "If enabled, upgrade bonuses are percentage-based instead of flat. When disabled, uses flat bonuses"); cfg_StatPercentageBonusPerUpgrade = AddConfig("PercentageSystem", "Stat percentage bonus per upgrade", 5f, "Percentage bonus per upgrade level when using percentage upgrade system (5 = 5% per upgrade level)"); cfg_EnableStationLevelBonus = AddConfig("General", "Enable station level bonus", value: true, "Allow blacksmithing skill to add virtual levels to workbenches and forges"); cfg_UseYamlFiltering = AddConfig("Item Filtering", "Use YAML item filtering", value: true, "Enable custom whitelist/blacklist system via BlacksmithExpItemList.yml file"); cfg_LogFilteredItems = AddConfig("Item Filtering", "Log filtered items", value: false, "Write to console when items are blocked by whitelist/blacklist filters"); cfg_XPPerCraft = AddConfig("XP", "XP per craft", 1f, "Base blacksmithing XP gained when crafting any item"); cfg_XPPerSmelt = AddConfig("XP", "XP per smelt", 0.75f, "Base blacksmithing XP gained when adding ore to smelters/kilns"); cfg_XPPerRepair = AddConfig("XP", "XP per repair", 0.1f, "Base blacksmithing XP gained when repairing items"); cfg_XPPerUpgrade = AddConfig("XP", "XP per upgrade", 3f, "Base blacksmithing XP gained when upgrading items at workbenches"); cfg_FirstCraftBonusXP = AddConfig("XP", "First craft bonus XP", 10f, "One-time bonus XP when crafting each item type for the first time"); cfg_ShowBlacksmithLevelInTooltip = AddConfig("Tooltip", "Show level in tooltip", value: true, "Display blacksmithing level used to craft item in item tooltips"); cfg_ShowInfusionInTooltip = AddConfig("Tooltip", "Show infusion in tooltip", value: false, "Display elemental infusion type in item tooltips (Fire, Frost, etc.)"); cfg_DurabilityTierInterval = AddConfig("Durability", "Durability tier interval", 10, "Every X blacksmithing levels unlocks next tier of durability bonuses"); cfg_DurabilityBonusPerTier = AddConfig("Durability", "Durability bonus per tier", 25f, "Flat durability points added per tier when crafting items"); cfg_DurabilityBonusPerUpgrade = AddConfig("Durability", "Durability bonus per upgrade", 50f, "Extra durability points per item quality level (star rating)"); cfg_RespectOriginalDurability = AddConfig("Durability", "Respect original durability", value: true, "Only boost durability on items that already have durability (prevents boosting consumables/arrows)"); cfg_MaxDurabilityCap = AddConfig("Durability", "Max durability cap", 2000f, "Maximum durability any item can reach (0 = no limit)"); cfg_AllowNonRepairableItems = AddConfig("Durability", "Allow non-repairable items", value: false, "Allow blacksmithing bonuses on items with no durability (torches, consumables, etc.)"); cfg_EnableDurabilityBonus = AddConfig("Durability", "Enable durability bonus", value: true, "Enable/disable the blacksmithing durability bonus entirely. Disable this if other mods manage durability to prevent conflicts (also fixes modded armor losing durability on login)"); cfg_EnableDamageBonus = AddConfig("Stats", "Enable damage bonus", value: true, "Enable/disable the blacksmithing damage bonus entirely. Disable if other mods handle weapon damage scaling to prevent conflicts"); cfg_EnableArmorBonus = AddConfig("Stats", "Enable armor bonus", value: true, "Enable/disable the blacksmithing armor bonus entirely. Disable if other mods handle armor scaling to prevent conflicts"); cfg_BoostElementalWeapons = AddConfig("Stats", "Boost elemental weapons", value: true, "Allow boosting weapons that already have elemental damage (like Frostner)"); cfg_ElementalWeaponBoostChance = AddConfig("Stats", "Elemental weapon boost chance", 0.25f, "For weapons with both physical and elemental damage: chance to boost elemental instead of physical (0.25 = 25% chance)"); cfg_StatTierInterval = AddConfig("Stats", "Stat tier interval", 5, "Every X blacksmithing levels unlocks next tier of damage/armor bonuses"); cfg_ArmorBonusPerTier = AddConfig("Stats", "Armor bonus per tier", 0.5f, "Flat armor points added per tier when crafting armor pieces"); cfg_ArmorBonusPerUpgrade = AddConfig("Stats", "Armor bonus per upgrade", 2f, "Extra armor points per item quality level (star rating)"); cfg_ArmorCap = AddConfig("Stats", "Armor cap", 150f, "Maximum armor value any piece can reach (0 = no limit)"); cfg_UsePercentageDamageBonus = AddConfig("PercentageSystem", "Use percentage damage bonus", value: true, "If enabled, damage bonuses are percentage-based instead of flat. Much more balanced for all weapon types"); cfg_DamagePercentageBonusPerTier = AddConfig("PercentageSystem", "Damage percentage bonus per tier", 1f, "Percentage damage bonus per tier when using percentage system (1 = 1% per tier)"); cfg_DamageBonusPerTier = AddConfig("Stats", "Damage bonus per tier", 5, "Flat damage bonus added per stat tier. Applied to one random damage type (slash/pierce/blunt). Only used if percentage system is disabled"); cfg_StatBonusPerUpgrade = AddConfig("Stats", "Stat bonus per upgrade", 4f, "Extra damage/armor bonus per item quality level (star rating). Only used if percentage upgrade system is disabled"); cfg_EnableElementalBonus = AddConfig("Elemental", "Enable elemental bonus", value: true, "Enable/disable the blacksmithing elemental damage bonus entirely. Disable if other mods handle elemental damage to prevent conflicts"); cfg_AlwaysAddElementalAtMax = AddConfig("Elemental", "Add elemental at milestone", value: true, "Automatically add random elemental damage when reaching elemental unlock level"); cfg_ElementalUnlockLevel = AddConfig("Elemental", "Elemental unlock level", 75, "Blacksmithing level required to add elemental damage bonuses to weapons"); cfg_FireBonusPerTier = AddConfig("Elemental", "Fire bonus per tier", 3f, "Fire damage points per tier (causes burning damage over time)"); cfg_FrostBonusPerTier = AddConfig("Elemental", "Frost bonus per tier", 6f, "Frost damage points per tier (causes instant cold damage)"); cfg_LightningBonusPerTier = AddConfig("Elemental", "Lightning bonus per tier", 5f, "Lightning damage points per tier (good vs wet enemies)"); cfg_PoisonBonusPerTier = AddConfig("Elemental", "Poison bonus per tier", 2.5f, "Poison damage points per tier (causes poison damage over time)"); cfg_SpiritBonusPerTier = AddConfig("Elemental", "Spirit bonus per tier", 4f, "Spirit damage points per tier (extra effective vs undead enemies)"); cfg_UsePercentageElementalBonus = AddConfig("PercentageSystem", "Use percentage elemental bonus", value: true, "If enabled, elemental bonuses are percentage-based instead of flat. Much more balanced for all weapon types"); cfg_ElementalPercentageBonusPerTier = AddConfig("PercentageSystem", "Elemental percentage bonus per tier", 0.75f, "Percentage elemental bonus per tierwhen using percentage system (0.75 = 0.75% per tier)"); cfg_EnableShieldBonus = AddConfig("Shields", "Enable shield bonus", value: true, "Enable/disable the blacksmithing shield block power and parry bonuses entirely. Disable if other mods handle shield stats to prevent conflicts"); cfg_TimedBlockBonusPerTier = AddConfig("Shields", "Timed block bonus per tier", 0.01f, "Parry/perfect block bonus per tier (0.01 = 1% better parry window/damage)"); cfg_TimedBlockBonusPerUpgrade = AddConfig("Shields", "Timed block bonus per upgrade", 0.05f, "Extra parry bonus per shield quality level (star rating)"); cfg_BlockPowerBonusPerTier = AddConfig("Shields", "Block power bonus per tier", 1f, "Block strength points per tier (reduces stamina cost when blocking)"); cfg_BlockPowerBonusPerUpgrade = AddConfig("Shields", "Block power bonus per upgrade", 1f, "Extra block power per shield quality level (star rating)"); cfg_EnableExtraItemBonus = AddConfig("General", "Enable extra item bonus", value: true, "Enable/disable the chance to receive a bonus crafted item at high blacksmithing levels"); cfg_EnableOreSaveBonus = AddConfig("General", "Enable ore save bonus", value: true, "Enable/disable the chance to save ore when smelting at high blacksmithing levels"); } private void InitializeYamlFiltering() { try { if (!File.Exists(configPath)) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("[BlacksmithingExpanded] YAML not found at " + configPath + ". Generating default YAML...")); GenerateDefaultYaml(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"[BlacksmithingExpanded] Default YAML created. Please restart the client."); } else { ReloadYamlConfiguration(); ((BaseUnityPlugin)this).Logger.LogDebug((object)("[BlacksmithingExpanded] YAML filtering system initialized. File: " + configPath)); } } catch (Exception arg) { ((BaseUnityPlugin)this).Logger.LogError((object)$"[BlacksmithingExpanded] Failed to initialize YAML filtering: {arg}"); } } private void ReloadYamlConfiguration() { whitelistedItems.Clear(); blacklistedItems.Clear(); if (!cfg_UseYamlFiltering.Value) { ((BaseUnityPlugin)this).Logger.LogInfo((object)"[BlacksmithingExpanded] YAML filtering disabled"); return; } try { IDeserializer deserializer = new DeserializerBuilder().WithNamingConvention(PascalCaseNamingConvention.Instance).IgnoreUnmatchedProperties().Build(); string input = File.ReadAllText(configPath); itemFilterConfig = deserializer.Deserialize<ItemFilterConfig>(input) ?? new ItemFilterConfig(); if (itemFilterConfig.Whitelist != null) { foreach (string item in itemFilterConfig.Whitelist.Where((string x) => !string.IsNullOrWhiteSpace(x))) { whitelistedItems.Add(item.Trim()); } } if (itemFilterConfig.Blacklist != null) { foreach (string item2 in itemFilterConfig.Blacklist.Where((string x) => !string.IsNullOrWhiteSpace(x))) { blacklistedItems.Add(item2.Trim()); } } ((BaseUnityPlugin)this).Logger.LogInfo((object)$"[BlacksmithingExpanded] Loaded YAML config - Whitelist: {whitelistedItems.Count} items, Blacklist: {blacklistedItems.Count} items"); if (whitelistedItems.Count > 0) { ((BaseUnityPlugin)this).Logger.LogDebug((object)("[BlacksmithingExpanded] Whitelisted items: " + string.Join(", ", whitelistedItems))); } if (blacklistedItems.Count > 0) { ((BaseUnityPlugin)this).Logger.LogDebug((object)("[BlacksmithingExpanded] Blacklisted items: " + string.Join(", ", blacklistedItems))); } } catch (YamlException ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("[BlacksmithingExpanded] YAML parsing error: " + ex.Message)); ((BaseUnityPlugin)this).Logger.LogError((object)("[BlacksmithingExpanded] Check your YAML syntax in: " + configPath)); } catch (Exception arg) { ((BaseUnityPlugin)this).Logger.LogError((object)$"[BlacksmithingExpanded] Failed to load YAML config: {arg}"); } } private void GenerateDefaultYaml() { try { string contents = "# If whitelist is empty:\r\n# Items not on blacklist → Enhanced (normal mod behavior)\r\n# Items on blacklist → Not enhanced\r\n\r\n# If whitelist has items:\r\n# Items on whitelist → Enhanced\r\n# Items NOT on whitelist → Not enhanced (regardless of blacklist)\r\n\r\nWhitelist:\r\n\r\nBlacklist:\r\n - Club\r\n - AxeStone\r\n - Torch\r\n - Tankard\r\n - TankardAnniversary\r\n - TrinketBronzeHealth\r\n - TrinketBronzeStamina\r\n - TrinketCarapaceEitr\r\n - TrinketBlackDamageHealth\r\n - TrinketFlametalEitr\r\n - TrinketChitinSwim\r\n - TrinketFlametalStaminaHealth\r\n - TrinketIronHealth\r\n - TrinketIronStamina\r\n - TrinketScaleStaminaDamage\r\n - TrinketSilverDamage\r\n - TrinketSilverResist\r\n - Demister\r\n - DvergrKey\r\n - SaddleAsksvin\r\n - SaddleLox\r\n"; File.WriteAllText(configPath, contents); ((BaseUnityPlugin)this).Logger.LogDebug((object)("[BlacksmithingExpanded] Created default YAML file at " + configPath)); } catch (Exception arg) { ((BaseUnityPlugin)this).Logger.LogError((object)$"[BlacksmithingExpanded] Failed to create default YAML: {arg}"); } } internal static bool IsItemAllowed(ItemData item) { if (!cfg_UseYamlFiltering.Value) { return true; } if (item?.m_shared == null) { return false; } string itemPrefabName = GetItemPrefabName(item); if (string.IsNullOrEmpty(itemPrefabName)) { if (cfg_LogFilteredItems.Value) { Debug.Log((object)"[BlacksmithingExpanded] Could not determine prefab name for item - allowing by default"); } return true; } if (whitelistedItems.Count > 0) { bool flag = whitelistedItems.Contains(itemPrefabName); if (cfg_LogFilteredItems.Value) { if (flag) { Debug.Log((object)("[BlacksmithingExpanded] Item prefab '" + itemPrefabName + "' found in whitelist - allowing bonuses")); } else { Debug.Log((object)("[BlacksmithingExpanded] Item prefab '" + itemPrefabName + "' not in whitelist - filtering out")); } } return flag; } if (blacklistedItems.Contains(itemPrefabName)) { if (cfg_LogFilteredItems.Value) { Debug.Log((object)("[BlacksmithingExpanded] Item prefab '" + itemPrefabName + "' is blacklisted - filtering out")); } return false; } if (cfg_LogFilteredItems.Value) { Debug.Log((object)("[BlacksmithingExpanded] Item prefab '" + itemPrefabName + "' allowed (not blacklisted)")); } return true; } private static string GetItemPrefabName(ItemData item) { try { if ((Object)(object)item.m_dropPrefab != (Object)null) { return ((Object)item.m_dropPrefab).name; } ObjectDB instance = ObjectDB.instance; GameObject val = ((instance != null) ? instance.GetItemPrefab(item.m_shared.m_name) : null); if ((Object)(object)val != (Object)null) { return ((Object)val).name; } string name = item.m_shared.m_name; if (name.StartsWith("$item_")) { string text = name.Substring(6); return char.ToUpper(text[0]) + text.Substring(1); } return name; } catch (Exception arg) { Debug.LogError((object)$"[BlacksmithingExpanded] Error getting prefab name: {arg}"); return item.m_shared?.m_name ?? "Unknown"; } } internal static int GetPlayerBlacksmithingLevel(Player player) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)((player != null) ? ((Component)player).GetComponent<Skills>() : null) == (Object)null) { return 0; } try { if (!Skill.skillByName.ContainsKey("Blacksmithing")) { Debug.LogWarning((object)"[BlacksmithingExpanded] Blacksmithing skill not found, returning 0"); return 0; } SkillType val = Skill.fromName("Blacksmithing"); float skillLevel = ((Component)player).GetComponent<Skills>().GetSkillLevel(val); return Mathf.FloorToInt(skillLevel); } catch (Exception arg) { Debug.LogError((object)$"[BlacksmithingExpanded] Error getting blacksmithing level: {arg}"); return 0; } } internal static void GiveBlacksmithingXP(Player player, float amount) { if ((Object)(object)player == (Object)null || amount <= 0f) { return; } try { float value = amount * cfg_SkillGainFactor.Value; ((Character)(object)player).RaiseSkill("Blacksmithing", value); } catch (Exception arg) { Debug.LogError((object)$"[BlacksmithingExpanded] XP grant failed: {arg}"); } } internal static void ApplyCraftingBonuses(ItemData item, int level) { //IL_044f: Unknown result type (might be due to invalid IL or missing references) //IL_0455: Invalid comparison between Unknown and I4 if (item?.m_shared == null || level <= 0 || item.m_shared.m_maxStackSize > 1) { return; } if (!IsItemAllowed(item)) { if (cfg_LogFilteredItems.Value) { Debug.Log((object)("[BlacksmithingExpanded] Item '" + item.m_shared.m_name + "' filtered out by YAML configuration")); } return; } ItemBaseStats baseStats = GetBaseStats(item); if (!baseStats.isCached) { Debug.LogError((object)("[BlacksmithingExpanded] Failed to get base stats for " + item.m_shared.m_name)); return; } bool flag = baseStats.durability > 0f; if (!flag && !cfg_AllowNonRepairableItems.Value) { if (cfg_LogFilteredItems.Value) { Debug.Log((object)("[BlacksmithingExpanded] Item '" + item.m_shared.m_name + "' has no durability and non-repairable items are disabled - skipping")); } return; } BlacksmithingItemData orCreate = item.Data().GetOrCreate<BlacksmithingItemData>(); string text = orCreate.infusion ?? ""; bool flag2 = false; if (orCreate.level != level || orCreate.level == 0) { flag2 = true; } else if (orCreate.lastKnownQuality > 0 && orCreate.lastKnownQuality != item.m_quality) { flag2 = true; } else if (orCreate.lastKnownQuality == 0 && orCreate.level > 0) { orCreate.lastKnownQuality = item.m_quality; orCreate.Save(); return; } if (!flag2) { return; } orCreate.level = level; orCreate.lastKnownQuality = item.m_quality; orCreate.baseDurability = baseStats.durability; orCreate.armorBonus = 0f; orCreate.damageBlunt = 0f; orCreate.damageSlash = 0f; orCreate.damagePierce = 0f; orCreate.damageFire = 0f; orCreate.damageFrost = 0f; orCreate.damageLightning = 0f; orCreate.damagePoison = 0f; orCreate.damageSpirit = 0f; orCreate.blockPowerBonus = 0f; orCreate.timedBlockBonus = 0f; orCreate.statsApplied = false; int num = level / cfg_StatTierInterval.Value; int num2 = level / cfg_DurabilityTierInterval.Value; if (cfg_EnableDurabilityBonus.Value && (flag || cfg_AllowNonRepairableItems.Value) && ((cfg_RespectOriginalDurability.Value && flag) || !cfg_RespectOriginalDurability.Value || cfg_AllowNonRepairableItems.Value)) { float num3 = (float)num2 * cfg_DurabilityBonusPerTier.Value + (float)item.m_quality * cfg_DurabilityBonusPerUpgrade.Value; orCreate.maxDurability = baseStats.durability + num3; if (cfg_MaxDurabilityCap.Value > 0f) { orCreate.maxDurability = Mathf.Min(orCreate.maxDurability, cfg_MaxDurabilityCap.Value); } } if (cfg_EnableDamageBonus.Value) { ApplyDamageBonuses(item, baseStats, num, orCreate); } if (cfg_EnableArmorBonus.Value && baseStats.armor > 0f) { float num4 = (float)num * cfg_ArmorBonusPerTier.Value; float num6; if (cfg_UsePercentageUpgradeBonus.Value) { float num5 = (float)item.m_quality * cfg_StatPercentageBonusPerUpgrade.Value / 100f; num6 = baseStats.armor * num5; } else { num6 = (float)item.m_quality * cfg_ArmorBonusPerUpgrade.Value; } orCreate.armorBonus = num4 + num6; if (cfg_ArmorCap.Value > 0f && baseStats.armor + orCreate.armorBonus > cfg_ArmorCap.Value) { orCreate.armorBonus = cfg_ArmorCap.Value - baseStats.armor; } } if (cfg_EnableShieldBonus.Value && (int)item.m_shared.m_itemType == 5) { if (item.m_shared.m_blockPower > 0f) { orCreate.blockPowerBonus = (float)num * cfg_BlockPowerBonusPerTier.Value + (float)item.m_quality * cfg_BlockPowerBonusPerUpgrade.Value; } if (item.m_shared.m_timedBlockBonus > 0f) { orCreate.timedBlockBonus = (float)num * cfg_TimedBlockBonusPerTier.Value + (float)item.m_quality * cfg_TimedBlockBonusPerUpgrade.Value; } } if (cfg_EnableElementalBonus.Value && level >= cfg_ElementalUnlockLevel.Value && cfg_AlwaysAddElementalAtMax.Value && item.IsWeapon()) { if (!string.IsNullOrEmpty(text)) { float effectiveTiers = CalculateElementalEffectiveTier(num, item.m_quality); ApplySpecificInfusion(text, baseStats, effectiveTiers, orCreate); } else if (!HasElementalDamageBonus(orCreate)) { float effectiveTiers2 = CalculateElementalEffectiveTier(num, item.m_quality); ApplyElementalInfusion(item, baseStats, effectiveTiers2, orCreate); } } orCreate.Save(); orCreate.Load(); Debug.Log((object)$"[BlacksmithingExpanded] Applied bonuses to {item.m_shared.m_name} (level {level}, quality {item.m_quality})"); } private static void ApplyDamageBonuses(ItemData item, ItemBaseStats baseStats, int statTier, BlacksmithingItemData data) { float tierBonus = ((!cfg_UsePercentageDamageBonus.Value) ? ((float)(statTier * cfg_DamageBonusPerTier.Value)) : ((float)statTier * cfg_DamagePercentageBonusPerTier.Value / 100f)); float upgradeBonus = 0f; if (item.m_quality > 0) { if (cfg_UsePercentageUpgradeBonus.Value) { upgradeBonus = (float)item.m_quality * cfg_StatPercentageBonusPerUpgrade.Value / 100f; } else if (cfg_UsePercentageDamageBonus.Value) { float totalPhysicalDamage = GetTotalPhysicalDamage(baseStats); if (totalPhysicalDamage > 0f) { upgradeBonus = (float)item.m_quality * cfg_StatBonusPerUpgrade.Value / totalPhysicalDamage; } } else { upgradeBonus = (float)item.m_quality * cfg_StatBonusPerUpgrade.Value; } } ApplyRandomDamageBonus(item, baseStats, tierBonus, upgradeBonus, data); } private static float CalculateElementalEffectiveTier(int statTier, int quality) { float num = statTier; if (cfg_UsePercentageUpgradeBonus.Value) { return num + (float)quality * cfg_StatPercentageBonusPerUpgrade.Value / cfg_ElementalPercentageBonusPerTier.Value; } return num + (float)quality * cfg_StatBonusPerUpgrade.Value / cfg_ElementalPercentageBonusPerTier.Value; } private static bool HasElementalDamageBonus(BlacksmithingItemData data) { return data.damageFire > 0f || data.damageFrost > 0f || data.damageLightning > 0f || data.damagePoison > 0f || data.damageSpirit > 0f; } private static void ApplySpecificInfusion(string infusionType, ItemBaseStats baseStats, float effectiveTiers, BlacksmithingItemData data) { if (cfg_UsePercentageElementalBonus.Value) { float num = effectiveTiers * cfg_ElementalPercentageBonusPerTier.Value / 100f; float totalPhysicalDamage = GetTotalPhysicalDamage(baseStats); switch (infusionType) { case "Fire": data.damageFire = totalPhysicalDamage * num; break; case "Frost": data.damageFrost = totalPhysicalDamage * num; break; case "Lightning": data.damageLightning = totalPhysicalDamage * num; break; case "Poison": data.damagePoison = totalPhysicalDamage * num; break; case "Spirit": data.damageSpirit = totalPhysicalDamage * num; break; } } else { switch (infusionType) { case "Fire": data.damageFire = effectiveTiers * cfg_FireBonusPerTier.Value; break; case "Frost": data.damageFrost = effectiveTiers * cfg_FrostBonusPerTier.Value; break; case "Lightning": data.damageLightning = effectiveTiers * cfg_LightningBonusPerTier.Value; break; case "Poison": data.damagePoison = effectiveTiers * cfg_PoisonBonusPerTier.Valu