using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("PaganFastCrafting")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("PaganFastCrafting")] [assembly: AssemblyTitle("PaganFastCrafting")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace PaganFastCrafting { [HarmonyPatch(typeof(InventoryGui), "UpdateRecipe")] internal static class CraftingPatch { private enum StationSkill { Crafting, Blacksmithing, Cooking, Magic } private static readonly SkillType BlacksmithingSkill = (SkillType)Math.Abs(StableHash("Blacksmithing")); private static readonly FieldRef CraftRecipeRef = AccessTools.FieldRefAccess("m_craftRecipe"); private static readonly Dictionary StationMap = new Dictionary { { "piece_workbench", StationSkill.Crafting }, { "forge", StationSkill.Blacksmithing }, { "blackforge", StationSkill.Blacksmithing }, { "piece_stonecutter", StationSkill.Crafting }, { "piece_artisanstation", StationSkill.Crafting }, { "piece_cauldron", StationSkill.Cooking }, { "piece_magetable", StationSkill.Magic }, { "ForgeOfAgesDO", StationSkill.Blacksmithing }, { "HunterTableDO", StationSkill.Crafting }, { "MunitionsBenchDO", StationSkill.Crafting }, { "NavalWorkshopDO", StationSkill.Crafting }, { "MagicWeaverDO", StationSkill.Magic }, { "EssenceCauldronDO", StationSkill.Cooking } }; private static readonly HashSet ExcludedItemSkills = new HashSet { (SkillType)100, (SkillType)101, (SkillType)102, (SkillType)103, (SkillType)108, (SkillType)110, (SkillType)106, (SkillType)104, (SkillType)13, (SkillType)107, (SkillType)105, (SkillType)0, (SkillType)999 }; private static readonly HashSet CombatItemTypes = new HashSet { (ItemType)3, (ItemType)4, (ItemType)5, (ItemType)9, (ItemType)14, (ItemType)20, (ItemType)22 }; private const float VanillaBase = 2f; private static void Prefix(InventoryGui __instance, Player player) { if ((Object)(object)player == (Object)null) { return; } string value = Plugin.Mode.Value; if (!value.Equals("Off", StringComparison.OrdinalIgnoreCase)) { __instance.m_craftDurationSkillMaxDecrease = 0f; float num; if (value.Equals("Flat", StringComparison.OrdinalIgnoreCase)) { num = Plugin.FlatDuration.Value; } else { float speedSkillFactor = GetSpeedSkillFactor(__instance, player); num = Mathf.Lerp(2f, Plugin.SkillDurationAtMax.Value, speedSkillFactor); } __instance.m_craftDuration = num; __instance.m_multiCraftDuration = 6f * (num / 2f); } } private static float GetSpeedSkillFactor(InventoryGui gui, Player player) { Recipe recipe = CraftRecipeRef.Invoke(gui); float itemPoolFactor = GetItemPoolFactor(recipe, player); float stationSkillFactor = GetStationSkillFactor(recipe, player); return Mathf.Max(itemPoolFactor, stationSkillFactor); } private static float GetItemPoolFactor(Recipe? recipe, Player player) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) SharedData val = recipe?.m_item?.m_itemData?.m_shared; if (val == null) { return 0f; } float num = 0f; if (CombatItemTypes.Contains(val.m_itemType) && !ExcludedItemSkills.Contains(val.m_skillType)) { num = Mathf.Max(num, ((Character)player).GetSkillFactor(val.m_skillType)); } if (Plugin.BlacksmithingLoaded && IsBlacksmithingItem(val)) { num = Mathf.Max(num, ((Character)player).GetSkillFactor(BlacksmithingSkill)); } return num; } private static float GetStationSkillFactor(Recipe? recipe, Player player) { //IL_0060: Unknown result type (might be due to invalid IL or missing references) CraftingStation val = recipe?.m_craftingStation; if ((Object)(object)val == (Object)null) { return ((Character)player).GetSkillFactor((SkillType)107); } string name = ((Object)((Component)val).gameObject).name; if (!StationMap.TryGetValue(name, out var value)) { value = StationSkill.Crafting; } if (value == StationSkill.Blacksmithing && !Plugin.BlacksmithingLoaded) { value = StationSkill.Crafting; } return value switch { StationSkill.Blacksmithing => ((Character)player).GetSkillFactor(BlacksmithingSkill), StationSkill.Cooking => ((Character)player).GetSkillFactor((SkillType)105), StationSkill.Magic => Mathf.Max(((Character)player).GetSkillFactor((SkillType)9), ((Character)player).GetSkillFactor((SkillType)10)), _ => ((Character)player).GetSkillFactor((SkillType)107), }; } private static bool IsBlacksmithingItem(SharedData item) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Expected I4, but got Unknown switch ((int)item.m_itemType) { case 4: case 5: case 6: case 7: case 11: case 12: case 14: case 17: case 22: return true; case 3: return !item.m_attack.m_consumeItem; default: return false; } } private static int StableHash(string str) { int num = 5381; int num2 = num; for (int i = 0; i < str.Length && str[i] != 0; i += 2) { num = ((num << 5) + num) ^ str[i]; if (i == str.Length - 1 || str[i + 1] == '\0') { break; } num2 = ((num2 << 5) + num2) ^ str[i + 1]; } return num + num2 * 1566083941; } } [BepInPlugin("pagan.fastcrafting", "PaganFastCrafting", "1.0.7")] public class Plugin : BaseUnityPlugin { public const string PluginGUID = "pagan.fastcrafting"; public const string PluginName = "PaganFastCrafting"; public const string PluginVersion = "1.0.7"; private static readonly Dictionary VersionHistory = new Dictionary { { "1.0.0", "Initial build — flat and skill modes, station map, Blacksmithing soft dep" }, { "1.0.1", "Fixed enum arithmetic in IsBlacksmithingItem switch (CS0266)" }, { "1.0.2", "Fixed FieldAccessException: m_craftRecipe via AccessTools.FieldRefAccess; item skill pool with Max()" }, { "1.0.3", "Fixed station skill fallback: use recipe.m_craftingStation instead of GetCurrentCraftingStation()" }, { "1.0.4", "ItemType whitelist gates m_skillType pool; station always contributes via Max(); fixed IsBlacksmithingItem comments" }, { "1.0.5", "Fixed DO mod station prefab names; null station defaults to Crafting skill; added EssenceCauldronDO and NavalWorkshopDO" }, { "1.0.6", "EssenceCauldronDO corrected to Cooking skill (food/potions, not magic)" }, { "1.0.7", "Soft detect original FasterCrafting mod — warn and disable gracefully instead of hard incompatibility block" } }; internal static ConfigEntry Mode = null; internal static ConfigEntry FlatDuration = null; internal static ConfigEntry SkillDurationAtMax = null; internal static bool BlacksmithingLoaded; private static readonly Harmony _harmony = new Harmony("pagan.fastcrafting"); private void Awake() { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Expected O, but got Unknown //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Expected O, but got Unknown //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Expected O, but got Unknown Mode = ((BaseUnityPlugin)this).Config.Bind("General", "Mode", "Skill", new ConfigDescription("Controls crafting speed behavior.\n off = vanilla, this mod does nothing.\n flat = every craft takes FlatDuration seconds regardless of skill.\n skill = crafting speed scales with the relevant skill per station.", (AcceptableValueBase)(object)new AcceptableValueList(new string[3] { "Off", "Flat", "Skill" }), Array.Empty())); FlatDuration = ((BaseUnityPlugin)this).Config.Bind("Flat", "FlatDuration", 0.5f, new ConfigDescription("Duration in seconds for every craft when Mode = flat.", (AcceptableValueBase)(object)new AcceptableValueRange(0.1f, 2f), Array.Empty())); SkillDurationAtMax = ((BaseUnityPlugin)this).Config.Bind("Skill", "SkillDurationAtMax", 0.85f, new ConfigDescription("Seconds a craft takes at skill 100. Skill 0 always uses the vanilla base of 2.0s.\n 0.85 = matches vanilla's own maximum skill bonus — safe starting point.\n Lower values = faster crafting at high skill. Minimum allowed: 0.1s.", (AcceptableValueBase)(object)new AcceptableValueRange(0.1f, 0.85f), Array.Empty())); if (Chainloader.PluginInfos.ContainsKey("1010101110.fastercrafting")) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"[PaganFastCrafting] Original FasterCrafting mod detected — PaganFastCrafting is disabled to avoid conflict. Remove FasterCrafting to use PaganFastCrafting."); return; } BlacksmithingLoaded = Chainloader.PluginInfos.ContainsKey("org.bepinex.plugins.blacksmithing"); if (BlacksmithingLoaded) { ((BaseUnityPlugin)this).Logger.LogInfo((object)"[PaganFastCrafting] Blacksmithing mod detected — forge-type stations use Blacksmithing skill."); } else { ((BaseUnityPlugin)this).Logger.LogInfo((object)"[PaganFastCrafting] Blacksmithing mod not detected — forge-type stations fall back to Crafting skill."); } _harmony.PatchAll(); ((BaseUnityPlugin)this).Logger.LogInfo((object)("[PaganFastCrafting] v1.0.7 loaded. Mode = " + Mode.Value)); ((BaseUnityPlugin)this).Logger.LogInfo((object)("[PaganFastCrafting] " + VersionHistory["1.0.7"])); } private void OnDestroy() { _harmony.UnpatchSelf(); } } }