using System; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; 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("DvergrCraftsmanship")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Building piece structural integrity scales with the placer's Crafting skill.")] [assembly: AssemblyFileVersion("0.1.0.0")] [assembly: AssemblyInformationalVersion("0.1.0+bee29c368a3b3a04c8242bab80c2c7dd5e5d3f95")] [assembly: AssemblyProduct("DvergrCraftsmanship")] [assembly: AssemblyTitle("DvergrCraftsmanship")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.1.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [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 DvergrCraftsmanship { internal static class CraftsmanshipService { private static readonly MethodInfo CheckCanRemovePieceMethod = AccessTools.Method(typeof(Player), "CheckCanRemovePiece", (Type[])null, (Type[])null); private static readonly MethodInfo GetBuildStaminaMethod = AccessTools.Method(typeof(Player), "GetBuildStamina", (Type[])null, (Type[])null); private static readonly MethodInfo ClearCachedSupportMethod = AccessTools.Method(typeof(WearNTear), "ClearCachedSupport", (Type[])null, (Type[])null); private static readonly FieldInfo ClearCachedSupportField = AccessTools.Field(typeof(WearNTear), "m_clearCachedSupport"); private static readonly FieldInfo SkillsPlayerField = AccessTools.Field(typeof(Skills), "m_player"); private static readonly FieldInfo CharacterZanimField = AccessTools.Field(typeof(Character), "m_zanim"); private static readonly FieldInfo HudHoverNameField = AccessTools.Field(typeof(Hud), "m_hoverName"); private static bool s_hasPendingPlacement; private static long s_pendingPlayerId; private static float s_pendingSkillLevel; private static float s_pendingMultiplier; internal static void CapturePlacementSkill(Player player) { s_hasPendingPlacement = false; if (IsEnabled() && !((Object)(object)player == (Object)null)) { float craftingSkillLevel = GetCraftingSkillLevel(player); s_pendingPlayerId = player.GetPlayerID(); s_pendingSkillLevel = craftingSkillLevel; s_pendingMultiplier = ComputeMultiplier(craftingSkillLevel); s_hasPendingPlacement = true; } } internal static void ClearPendingPlacement() { s_hasPendingPlacement = false; } internal static void StampPieceFromPendingPlacement(Piece piece, long creator) { if (!s_hasPendingPlacement || (Object)(object)piece == (Object)null || creator != s_pendingPlayerId) { return; } try { WearNTear component = ((Component)piece).GetComponent(); if ((Object)(object)component != (Object)null) { StampWearNTear(component, s_pendingSkillLevel, s_pendingMultiplier, force: true); } } finally { ClearPendingPlacement(); } } internal static void RegisterReinforceRpc(WearNTear wear) { if ((Object)(object)wear == (Object)null) { return; } ZNetView component = ((Component)wear).GetComponent(); if (!((Object)(object)component == (Object)null) && component.GetZDO() != null) { component.Register("DvergrCraft_Reinforce", (Action)delegate(long _, float skillLevel, float multiplier) { TryApplyReinforcement(wear, skillLevel, multiplier); }); } } internal static void ApplyCraftsmanshipToSupportLoss(WearNTear wear, float maxSupport, ref float horizontalLoss, ref float verticalLoss) { //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) if (!IsEnabled() || (Object)(object)wear == (Object)null || maxSupport <= 0f) { return; } ZNetView component = ((Component)wear).GetComponent(); bool flag = (Object)(object)component != (Object)null && component.IsValid(); bool flag2 = flag && component.IsOwner(); ZDO val = (flag ? component.GetZDO() : null); string prefabName = Utils.GetPrefabName(((Object)((Component)wear).gameObject).name); if (val == null) { SupportTraceLog($"Support material patch hit for {prefabName}: no valid ZDO, material={wear.m_materialType}, owner={flag2}, validView={flag}, baseMax={maxSupport:0.###}, frame={Time.frameCount}"); return; } float storedMultiplier = GetStoredMultiplier(val, 1f); float num = val.GetFloat(ModConstants.ZdoCraftSkillHash, 0f); float num2 = val.GetFloat(ZDOVars.s_support, -1f); if (storedMultiplier <= 1f) { SupportTraceLog($"Support material patch hit for {prefabName}: no bonus, material={wear.m_materialType}, owner={flag2}, skill={num:0.#}, multiplier={storedMultiplier:0.###}, max={maxSupport:0.###}, hLoss={horizontalLoss:0.###}, vLoss={verticalLoss:0.###}, currentSupport={num2:0.###}, frame={Time.frameCount}"); } else { float num3 = horizontalLoss; float num4 = verticalLoss; horizontalLoss /= storedMultiplier; verticalLoss /= storedMultiplier; SupportTraceLog($"Support material patch hit for {prefabName}: material={wear.m_materialType}, owner={flag2}, skill={num:0.#}, multiplier={storedMultiplier:0.###}, max={maxSupport:0.###}, hLoss={num3:0.###}->{horizontalLoss:0.###}, vLoss={num4:0.###}->{verticalLoss:0.###}, currentSupport={num2:0.###}, frame={Time.frameCount}"); } } internal static bool TryHandleReinforceRepair(Player player, ItemData toolItem) { if (!IsEnabled() || !ModConfig.EnableReinforce.Value || (Object)(object)player == (Object)null || toolItem == null) { return false; } if (!((Character)player).InPlaceMode()) { return false; } Piece hoveringPiece = player.GetHoveringPiece(); if ((Object)(object)hoveringPiece == (Object)null) { return false; } WearNTear component = ((Component)hoveringPiece).GetComponent(); if ((Object)(object)component == (Object)null || !IsFullHealth(component)) { return false; } float craftingSkillLevel = GetCraftingSkillLevel(player); float multiplier = ComputeMultiplier(craftingSkillLevel); if (!CanImprove(component, craftingSkillLevel, multiplier)) { return false; } if (!HasRepairAccess(player, hoveringPiece)) { return true; } if (!RequestReinforcement(component, craftingSkillLevel, multiplier)) { return false; } ConsumeRepairSwing(player, hoveringPiece, toolItem, craftingSkillLevel, multiplier); return true; } internal static int GetCraftingSkillLevelForTierNotification(Skills skills) { if (!IsEnabled() || !ModConfig.EnableTierNotifications.Value || (Object)(object)skills == (Object)null) { return -1; } return Mathf.FloorToInt(skills.GetSkillLevel((SkillType)107)); } internal static void ShowTierNotification(Skills skills, int oldLevel) { if (oldLevel < 0 || !IsEnabled() || !ModConfig.EnableTierNotifications.Value || (Object)(object)skills == (Object)null) { return; } int newLevel = Mathf.FloorToInt(skills.GetSkillLevel((SkillType)107)); int crossedTierThreshold = GetCrossedTierThreshold(oldLevel, newLevel); if (crossedTierThreshold > 0) { object? obj = SkillsPlayerField?.GetValue(skills); Player val = (Player)((obj is Player) ? obj : null); if (!((Object)(object)val == (Object)null)) { ((Character)val).Message((MessageType)1, "Dvergr Craftsmanship: " + GetTierName(crossedTierThreshold) + " reached. Future builds gain stronger integrity.", 0, (Sprite)null); } } } internal static void AppendIntegrityHoverText(Hud hud, Player player) { if (!IsEnabled()) { return; } ConfigEntry showIntegrityHoverText = ModConfig.ShowIntegrityHoverText; if (showIntegrityHoverText == null || !showIntegrityHoverText.Value || (Object)(object)hud == (Object)null || (Object)(object)player == (Object)null) { return; } Piece hoveringPiece = player.GetHoveringPiece(); if ((Object)(object)hoveringPiece == (Object)null) { return; } ZDO zdo = GetZdo(((Component)hoveringPiece).GetComponent()); if (zdo == null) { return; } float storedMultiplier = GetStoredMultiplier(zdo, 0f); if (storedMultiplier <= 0f) { return; } float num = zdo.GetFloat(ModConstants.ZdoCraftSkillHash, 0f); string text = ((Localization.instance != null) ? Localization.instance.Localize(hoveringPiece.m_name) : hoveringPiece.m_name); string arg = FormatBonusPercent(GetSupportLossReductionPercent(storedMultiplier)); string text2 = $"{arg}% support loss (Crafting {num:0})"; object obj = HudHoverNameField?.GetValue(hud); if (obj != null) { PropertyInfo property = obj.GetType().GetProperty("text"); if (!(property == null) && property.CanRead && property.CanWrite) { string text3 = property.GetValue(obj) as string; string value = (string.IsNullOrWhiteSpace(text3) ? (text + "\n" + text2) : (text3 + "\n" + text2)); property.SetValue(obj, value); } } } private static bool IsEnabled() { return ModConfig.EnableMod?.Value ?? false; } private static float GetStoredMultiplier(ZDO zdo, float defaultValue) { if (zdo == null) { return defaultValue; } return zdo.GetFloat(ModConstants.ZdoIntegrityMultiplierHash, defaultValue); } private static float GetSupportLossReductionPercent(float multiplier) { if (!(multiplier <= 0f)) { return (1f - 1f / multiplier) * 100f; } return 0f; } private static float GetCraftingSkillLevel(Player player) { Skills skills = ((Character)player).GetSkills(); return Mathf.Clamp((skills != null) ? skills.GetSkillLevel((SkillType)107) : 0f, 0f, 100f); } private static float ComputeMultiplier(float skillLevel) { float num = Mathf.Clamp(ModConfig.MaxIntegrityBonusPercent.Value, 20f, 60f); return 1f + Mathf.Clamp01(skillLevel / 100f) * (num / 100f); } private static void StampWearNTear(WearNTear wear, float skillLevel, float multiplier, bool force) { ZNetView component = ((Component)wear).GetComponent(); if (!((Object)(object)component == (Object)null) && component.IsValid() && component.IsOwner()) { ZDO zDO = component.GetZDO(); if (zDO != null && (force || CanImprove(zDO, skillLevel, multiplier))) { zDO.Set(ModConstants.ZdoCraftSkillHash, skillLevel); zDO.Set(ModConstants.ZdoIntegrityMultiplierHash, multiplier); MarkSupportDirty(wear); DebugLog($"Stamped {((Object)wear).name}: Crafting {skillLevel:0}, integrity x{multiplier:0.###}"); } } } private static bool RequestReinforcement(WearNTear wear, float skillLevel, float multiplier) { ZNetView component = ((Component)wear).GetComponent(); if ((Object)(object)component == (Object)null || !component.IsValid()) { return false; } ZDO zDO = component.GetZDO(); if (zDO == null || !CanImprove(zDO, skillLevel, multiplier)) { return false; } if (component.IsOwner()) { return TryApplyReinforcement(wear, skillLevel, multiplier); } component.InvokeRPC("DvergrCraft_Reinforce", new object[2] { skillLevel, multiplier }); return true; } private static bool TryApplyReinforcement(WearNTear wear, float skillLevel, float multiplier) { ZNetView component = ((Component)wear).GetComponent(); if ((Object)(object)component == (Object)null || !component.IsValid() || !component.IsOwner()) { return false; } ZDO zDO = component.GetZDO(); if (zDO == null || !CanImprove(zDO, skillLevel, multiplier)) { return false; } zDO.Set(ModConstants.ZdoCraftSkillHash, skillLevel); zDO.Set(ModConstants.ZdoIntegrityMultiplierHash, multiplier); MarkSupportDirty(wear); DebugLog($"Reinforced {((Object)wear).name}: Crafting {skillLevel:0}, integrity x{multiplier:0.###}"); return true; } private static bool CanImprove(WearNTear wear, float skillLevel, float multiplier) { ZDO zdo = GetZdo(wear); if (zdo != null) { return CanImprove(zdo, skillLevel, multiplier); } return false; } private static bool CanImprove(ZDO zdo, float skillLevel, float multiplier) { float num = zdo.GetFloat(ModConstants.ZdoCraftSkillHash, 0f); float num2 = zdo.GetFloat(ModConstants.ZdoIntegrityMultiplierHash, 1f); int num3 = Mathf.Max(1, ModConfig.MinimumReinforceSkillDelta.Value); if (skillLevel >= num + (float)num3) { return multiplier > num2; } return false; } private static bool HasRepairAccess(Player player, Piece piece) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) if (!PrivateArea.CheckAccess(((Component)piece).transform.position, 0f, true, false)) { return false; } if (CheckCanRemovePieceMethod == null) { ManualLogSource log = DvergrCraftsmanshipPlugin.Log; if (log != null) { log.LogWarning((object)"Unable to validate build-station repair access; reinforcement skipped."); } return false; } object obj = CheckCanRemovePieceMethod.Invoke(player, new object[1] { piece }); bool flag = default(bool); int num; if (obj is bool) { flag = (bool)obj; num = 1; } else { num = 0; } return (byte)((uint)num & (flag ? 1u : 0u)) != 0; } private static bool IsFullHealth(WearNTear wear) { ZDO zdo = GetZdo(wear); if (zdo != null) { return zdo.GetFloat(ZDOVars.s_health, wear.m_health) >= wear.m_health; } return false; } private static ZDO GetZdo(WearNTear wear) { ZNetView component = ((Component)wear).GetComponent(); if ((Object)(object)component == (Object)null || !component.IsValid()) { return null; } return component.GetZDO(); } private static void MarkSupportDirty(WearNTear wear) { ClearCachedSupportField?.SetValue(wear, true); ClearCachedSupportMethod?.Invoke(wear, Array.Empty()); } private static void ConsumeRepairSwing(Player player, Piece piece, ItemData toolItem, float skillLevel, float multiplier) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) player.FaceLookDirection(); TriggerRepairAnimation(player, toolItem); piece.m_placeEffect.Create(((Component)piece).transform.position, ((Component)piece).transform.rotation, (Transform)null, 1f, -1); string arg = ((Localization.instance != null) ? Localization.instance.Localize(piece.m_name) : piece.m_name); float supportLossReductionPercent = GetSupportLossReductionPercent(multiplier); ((Character)player).Message((MessageType)1, $"Reinforced {arg}: Crafting {skillLevel:0}, support loss -{supportLossReductionPercent:0.#}%", 0, (Sprite)null); ConsumeBuildStamina(player); ((Character)player).UseEitr(toolItem.m_shared.m_attack.m_attackEitr); if (toolItem.m_shared.m_useDurability) { toolItem.m_durability -= toolItem.m_shared.m_useDurabilityDrain; } } private static void TriggerRepairAnimation(Player player, ItemData toolItem) { object obj = CharacterZanimField?.GetValue(player); if (obj != null) { AccessTools.Method(obj.GetType(), "SetTrigger", new Type[1] { typeof(string) }, (Type[])null)?.Invoke(obj, new object[1] { toolItem.m_shared.m_attack.m_attackAnimation }); } } private static void ConsumeBuildStamina(Player player) { if (GetBuildStaminaMethod?.Invoke(player, Array.Empty()) is float num) { ((Character)player).UseStamina(num); } } private static int GetCrossedTierThreshold(int oldLevel, int newLevel) { int result = 0; int[] array = new int[4] { 25, 50, 75, 100 }; foreach (int num in array) { if (oldLevel < num && newLevel >= num) { result = num; } } return result; } private static string GetTierName(int threshold) { return threshold switch { 25 => "Apprentice Craftsman", 50 => "Journeyman Craftsman", 75 => "Artisan Craftsman", 100 => "Master Craftsman", _ => "Craftsman", }; } private static string FormatBonusPercent(float value) { if (!(Math.Abs(value - Mathf.Round(value)) < 0.05f)) { return value.ToString("+0.#;-0.#;0"); } return value.ToString("+0;-0;0"); } private static void DebugLog(string message) { ConfigEntry debugLogging = ModConfig.DebugLogging; if (debugLogging != null && debugLogging.Value) { ManualLogSource log = DvergrCraftsmanshipPlugin.Log; if (log != null) { log.LogInfo((object)message); } } } private static void SupportTraceLog(string message) { DebugLog(message); } } [BepInPlugin("com.cdjensen.dvergrcraftsmanship", "Dvergr Craftsmanship", "0.1.0")] public sealed class DvergrCraftsmanshipPlugin : BaseUnityPlugin { internal static ManualLogSource Log; private Harmony harmony; private void Awake() { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; ModConfig.Bind(((BaseUnityPlugin)this).Config); harmony = new Harmony("com.cdjensen.dvergrcraftsmanship"); harmony.PatchAll(typeof(DvergrCraftsmanshipPlugin).Assembly); Log.LogInfo((object)"Dvergr Craftsmanship 0.1.0 loaded."); } private void OnDestroy() { Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } } } internal static class ModConfig { internal static ConfigEntry EnableMod; internal static ConfigEntry MaxIntegrityBonusPercent; internal static ConfigEntry EnableReinforce; internal static ConfigEntry MinimumReinforceSkillDelta; internal static ConfigEntry EnableTierNotifications; internal static ConfigEntry ShowIntegrityHoverText; internal static ConfigEntry DebugLogging; internal static void Bind(ConfigFile config) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Expected O, but got Unknown //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Expected O, but got Unknown EnableMod = config.Bind("DvergrCraftsmanship", "EnableMod", true, "Enable Crafting-skill-based structural integrity bonuses."); MaxIntegrityBonusPercent = config.Bind("DvergrCraftsmanship", "MaxIntegrityBonusPercent", 40f, new ConfigDescription("Maximum craftsmanship bonus at Crafting skill 100. Default 40 means support loss is divided by 1.4 at skill 100.", (AcceptableValueBase)(object)new AcceptableValueRange(20f, 60f), Array.Empty())); EnableReinforce = config.Bind("DvergrCraftsmanship", "EnableReinforce", true, "Allow hammer repair on full-health pieces to reinforce them when the repairer's Crafting skill is high enough."); MinimumReinforceSkillDelta = config.Bind("DvergrCraftsmanship", "MinimumReinforceSkillDelta", 5, new ConfigDescription("Minimum Crafting skill improvement required to reinforce an already-crafted piece.", (AcceptableValueBase)(object)new AcceptableValueRange(1, 100), Array.Empty())); EnableTierNotifications = config.Bind("DvergrCraftsmanship", "EnableTierNotifications", true, "Show a short message when Crafting reaches Dvergr Craftsmanship thresholds: 25, 50, 75, and 100."); ShowIntegrityHoverText = config.Bind("DvergrCraftsmanship", "ShowIntegrityHoverText", true, "Show a concise Crafting skill and support-loss reduction line while hammer-hovering crafted pieces."); DebugLogging = config.Bind("DvergrCraftsmanship", "DebugLogging", false, "Enable verbose debug logging for placement stamps and reinforcement."); } } internal static class ModConstants { internal const string ModName = "Dvergr Craftsmanship"; internal const string ModVersion = "0.1.0"; internal const string ModGuid = "com.cdjensen.dvergrcraftsmanship"; internal const string ConfigFolder = "DvergrCraftsmanship"; internal const string ZdoIntegrityMultiplier = "DvergrCraft_IntegrityMult"; internal const string ZdoCraftSkill = "DvergrCraft_CraftSkill"; internal const string RpcReinforce = "DvergrCraft_Reinforce"; internal static readonly int ZdoIntegrityMultiplierHash = StringExtensionMethods.GetStableHashCode("DvergrCraft_IntegrityMult"); internal static readonly int ZdoCraftSkillHash = StringExtensionMethods.GetStableHashCode("DvergrCraft_CraftSkill"); } } namespace DvergrCraftsmanship.Patches { [HarmonyPatch(typeof(Player), "PlacePiece")] internal static class PlayerPlacePiecePatch { private static void Prefix(Player __instance) { CraftsmanshipService.CapturePlacementSkill(__instance); } private static void Finalizer() { CraftsmanshipService.ClearPendingPlacement(); } } [HarmonyPatch(typeof(Piece), "SetCreator")] internal static class PieceSetCreatorPatch { private static void Postfix(Piece __instance, long uid) { CraftsmanshipService.StampPieceFromPendingPlacement(__instance, uid); } } [HarmonyPatch(typeof(WearNTear), "Awake")] internal static class WearNTearAwakePatch { private static void Postfix(WearNTear __instance) { CraftsmanshipService.RegisterReinforceRpc(__instance); } } [HarmonyPatch(typeof(WearNTear), "GetMaterialProperties")] internal static class WearNTearGetMaterialPropertiesPatch { private static void Postfix(WearNTear __instance, float maxSupport, ref float horizontalLoss, ref float verticalLoss) { CraftsmanshipService.ApplyCraftsmanshipToSupportLoss(__instance, maxSupport, ref horizontalLoss, ref verticalLoss); } } [HarmonyPatch(typeof(Player), "Repair")] internal static class PlayerRepairPatch { private static bool Prefix(Player __instance, ItemData toolItem) { return !CraftsmanshipService.TryHandleReinforceRepair(__instance, toolItem); } } [HarmonyPatch(typeof(Hud), "UpdateCrosshair")] internal static class HudUpdateCrosshairPatch { private static void Postfix(Hud __instance, Player player) { CraftsmanshipService.AppendIntegrityHoverText(__instance, player); } } [HarmonyPatch(typeof(Skills), "RaiseSkill")] internal static class SkillsRaiseSkillPatch { private static void Prefix(Skills __instance, SkillType skillType, ref int __state) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0004: Invalid comparison between Unknown and I4 __state = (((int)skillType == 107) ? CraftsmanshipService.GetCraftingSkillLevelForTierNotification(__instance) : (-1)); } private static void Postfix(Skills __instance, SkillType skillType, int __state) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Invalid comparison between Unknown and I4 if ((int)skillType == 107) { CraftsmanshipService.ShowTierNotification(__instance, __state); } } } }