diff --git a/SmithingPlus/Core.Cache.cs b/SmithingPlus/Core.Cache.cs index a1727a5..16b0920 100644 --- a/SmithingPlus/Core.Cache.cs +++ b/SmithingPlus/Core.Cache.cs @@ -24,8 +24,8 @@ public partial class Core public static Dictionary RecipeVoxelCountCache => ObjectCacheUtil.GetOrCreate(Api, RecipeVoxelCountCacheKey, () => new Dictionary()); - public static Dictionary ToolToRecipeCache => - ObjectCacheUtil.GetOrCreate(Api, ToolToRecipeCacheKey, () => new Dictionary()); + public static Dictionary ToolToRecipeCache => + ObjectCacheUtil.GetOrCreate(Api, ToolToRecipeCacheKey, () => new Dictionary()); public static Dictionary MetalMaterialCache => ObjectCacheUtil.GetOrCreate(Api, MetalMaterialCacheKey, () => new Dictionary()); @@ -50,4 +50,4 @@ public static string[]? CastableMetalVariantsCache get => Api.ObjectCache.TryGetValue(CastableMetalVariantsCacheKey, out var variants) ? (string[])variants : null; set => Api.ObjectCache[CastableMetalVariantsCacheKey] = value; } -} \ No newline at end of file +} diff --git a/SmithingPlus/ToolRecovery/CollectibleBehaviorRepairableTool.cs b/SmithingPlus/ToolRecovery/CollectibleBehaviorRepairableTool.cs index 6bb4ee7..3ddb1b2 100644 --- a/SmithingPlus/ToolRecovery/CollectibleBehaviorRepairableTool.cs +++ b/SmithingPlus/ToolRecovery/CollectibleBehaviorRepairableTool.cs @@ -1,9 +1,15 @@ #nullable enable +using System.Linq; using System.Text; + +using SmithingPlus.Common.Metal; using SmithingPlus.Util; + using Vintagestory.API.Common; +using Vintagestory.API.Common.Entities; using Vintagestory.API.Config; using Vintagestory.API.Util; +using Vintagestory.GameContent; namespace SmithingPlus.ToolRecovery; @@ -32,10 +38,119 @@ public override void GetHeldItemInfo(ItemSlot? inSlot, StringBuilder dsc, IWorld if (!WildcardUtil.Match(Core.Config.RepairableToolSelector, code.ToString())) return; - var brokenCount = inSlot.Itemstack.GetBrokenCount(); + + var brokenCount = itemstack!.GetBrokenCount(); if (brokenCount <= 0) return; + if (Core.Config.ShowRepairedCount) dsc.AppendLine(Lang.Get($"{LangKey} {{0}} times", brokenCount)); - if (Core.Config.ShowRepairSmithName && inSlot.Itemstack.GetRepairSmith() is { } repairSmith) + if (Core.Config.ShowRepairSmithName && itemstack!.GetRepairSmith() is { } repairSmith) dsc.AppendLine(Lang.Get("Last repaired by {0}", repairSmith)); } -} \ No newline at end of file + + public override void OnDestroyItem(IWorldAccessor world, Entity byEntity, ItemSlot itemSlot, ref EnumHandling bhHandling) + { + if (world.Api.Side.IsClient()) + return; + + var durability = itemSlot.Itemstack?.GetRemainingDurability(); + if (durability is null or > 0) return; // Was this item (likely) destroyed due to its durability reaching zero? + + Core.Logger.VerboseDebug( + "Broken tool in InventoryID: {0}, Entity: {1}", + itemSlot.Inventory?.InventoryID, + byEntity.GetName() + ); + var entityPlayer = byEntity as EntityPlayer; + var itemStack = itemSlot.Itemstack; + if (itemStack is null) return; + + var toolCode = itemStack.Collectible.Code.ToString(); + var smithingRecipe = CacheHelper.GetOrAdd( + Core.ToolToRecipeCache, + toolCode, + () => GetHeadSmithingRecipe(world.Api, itemStack) + ); + + if (smithingRecipe == null) + { + Core.Logger.VerboseDebug("Head or tool smithing recipe not found for: {0}", toolCode); + return; + } + + var metalMaterial = itemStack.GetOrCacheMetalMaterial(byEntity.Api); + var workItem = metalMaterial?.WorkItem; + + if (workItem is null) + { + Core.Logger.VerboseDebug( + $"Work item not found. Metal material: {metalMaterial?.IngotCode}, " + $"collectible: {itemStack.Collectible.Code}" + ); + return; + } + + Core.Logger.VerboseDebug("Found work item: {0}", workItem.Code); + var wItemStack = new ItemStack(workItem); + Core.Logger.VerboseDebug( + "Found smithing recipe: {0}", + smithingRecipe.Output.ResolvedItemstack!.Collectible.Code + ); + var byteVoxels = ByteVoxelsFromRecipe(smithingRecipe, smithingRecipe.Output.ResolvedItemstack.StackSize); + wItemStack.Attributes.SetBytes("voxels", BlockEntityAnvil.serializeVoxels(byteVoxels)); + wItemStack.Attributes.SetInt("selectedRecipeId", smithingRecipe.RecipeId); + var cloneStack = itemStack.Clone(); + cloneStack.CloneBrokenCount(itemStack, 1); + wItemStack.SetRepairedToolStack(cloneStack); + + var gaveStack = false; + if (entityPlayer != null) gaveStack = entityPlayer.TryGiveItemStack(wItemStack); + if (!gaveStack) world.SpawnItemEntity(wItemStack, byEntity.Pos.XYZ); + Core.Logger.VerboseDebug( + gaveStack ? "Gave work item {0} to player {1}" : "Dropped work item {0} to player {1}", + wItemStack.Collectible.Code, + entityPlayer?.Player.PlayerName + ); + itemSlot.MarkDirty(); + } + + private static SmithingRecipe? GetHeadSmithingRecipe(ICoreAPI api, ItemStack itemStack) + { + var toolHead = GetToolHead(api, itemStack); + var smithingRecipe = toolHead.GetSmithingRecipe(api); + return smithingRecipe; + } + + private static ItemStack GetToolHead(ICoreAPI api, ItemStack itemStack) + { + var toolRecipe = itemStack + .GetGridRecipes(api) + .FirstOrDefault(r => + r.Output?.ResolvedItemStack?.StackSize == 1 + ); + var toolHead = toolRecipe?.RecipeIngredients + .FirstOrDefault(k => + k.ResolvedItemStack?.Collectible?.HasBehavior() ?? false + ) + ?.ResolvedItemStack; + + if (toolHead == null) + { + toolHead = itemStack; + Core.Logger.VerboseDebug("Tool head not found for: {0}", itemStack); + } + + Core.Logger.VerboseDebug("Tool head: {0}", toolHead); + return toolHead; + } + + private static byte[,,] ByteVoxelsFromRecipe(SmithingRecipe recipe, int stackSize = 1) + { + var recipeVoxels = recipe.Voxels; + if (Core.Config.BrokenToolVoxelPercent < 0.2) + Core.Logger.Warning( + $"[ItemDamagedPatches#ByteVoxelsFromRecipe] Config setting {nameof(Core.Config.BrokenToolVoxelPercent)}" + + $"has a very low value, your broken tools well be almost or fully empty." + ); + var byteVoxels = recipeVoxels.ErodeToPercentage(Core.Config.BrokenToolVoxelPercent); + return byteVoxels; + } +} diff --git a/SmithingPlus/ToolRecovery/ItemDamagedPatches.cs b/SmithingPlus/ToolRecovery/ItemDamagedPatches.cs index d68a4c8..2fa17f1 100644 --- a/SmithingPlus/ToolRecovery/ItemDamagedPatches.cs +++ b/SmithingPlus/ToolRecovery/ItemDamagedPatches.cs @@ -51,101 +51,4 @@ public static void Postfix_OnCreatedByCrafting( foreach (var attribute in repairedAttributes) outputAttributes[attribute.Key] = attribute.Value; } - - [HarmonyPrefix] - [HarmonyPatch(nameof(CollectibleObject.DamageItem))] - private static void Prefix_DamageItem( - IWorldAccessor world, - Entity byEntity, - ItemSlot itemSlot, - int amount = 1, - bool destroyOnZeroDurability = true) - { - if (world.Api.Side.IsClient()) - return; - if (!destroyOnZeroDurability) - return; - var durability = itemSlot?.Itemstack?.GetRemainingDurability(); - if (!durability.HasValue || durability > amount) return; - if (itemSlot.Itemstack?.Collectible.HasBehavior() != true) return; - Core.Logger.VerboseDebug("Broken tool in InventoryID: {0}, Entity: {1}", itemSlot.Inventory?.InventoryID, - byEntity.GetName()); - var entityPlayer = byEntity as EntityPlayer; - var itemStack = itemSlot.Itemstack; - var toolCode = itemStack?.Collectible.Code.ToString(); - var smithingRecipe = CacheHelper.GetOrAdd(Core.ToolToRecipeCache, toolCode, - () => GetHeadSmithingRecipe(world.Api, itemStack)); - if (smithingRecipe == null) - { - Core.Logger.VerboseDebug("Head or tool smithing recipe not found for: {0}", toolCode); - return; - } - - var metalMaterial = itemStack?.GetOrCacheMetalMaterial(byEntity.Api); - var workItem = metalMaterial?.WorkItem; - if (workItem is null) - { - Core.Logger.VerboseDebug( - $"Work item not found. Metal material: {metalMaterial?.IngotCode}, " + - $"collectible: {itemStack?.Collectible.Code}"); - return; - } - - Core.Logger.VerboseDebug("Found work item: {0}", workItem.Code); - var wItemStack = new ItemStack(workItem); - Core.Logger.VerboseDebug("Found smithing recipe: {0}", - smithingRecipe.Output.ResolvedItemstack.Collectible.Code); - var byteVoxels = ByteVoxelsFromRecipe(smithingRecipe, smithingRecipe.Output.ResolvedItemstack.StackSize); - wItemStack.Attributes.SetBytes("voxels", BlockEntityAnvil.serializeVoxels(byteVoxels)); - wItemStack.Attributes.SetInt("selectedRecipeId", smithingRecipe.RecipeId); - var cloneStack = itemStack?.Clone(); - cloneStack.CloneBrokenCount(itemStack, 1); - wItemStack.SetRepairedToolStack(cloneStack); - - var gaveStack = false; - if (entityPlayer != null) gaveStack = entityPlayer.TryGiveItemStack(wItemStack); - if (!gaveStack) world.SpawnItemEntity(wItemStack, byEntity.Pos.XYZ); - Core.Logger.VerboseDebug(gaveStack ? "Gave work item {0} to player {1}" : "Dropped work item {0} to player {1}", - wItemStack.Collectible.Code, entityPlayer?.Player.PlayerName); - itemSlot.MarkDirty(); - } - - private static SmithingRecipe GetHeadSmithingRecipe(ICoreAPI api, ItemStack itemStack) - { - var toolHead = GetToolHead(api, itemStack); - var smithingRecipe = toolHead.GetSmithingRecipe(api); - return smithingRecipe; - } - - private static ItemStack GetToolHead(ICoreAPI api, ItemStack itemStack) - { - var toolRecipe = itemStack - .GetGridRecipes(api) - .FirstOrDefault(r => - r.Output?.ResolvedItemStack?.StackSize == 1); - var toolHead = toolRecipe?.RecipeIngredients - .FirstOrDefault(k => - k?.ResolvedItemStack?.Collectible?.HasBehavior() ?? false) - ?.ResolvedItemStack; - if (toolHead == null) - { - toolHead = itemStack; - Core.Logger.VerboseDebug("Tool head not found for: {0}", itemStack); - } - - Core.Logger.VerboseDebug("Tool head: {0}", toolHead); - return toolHead; - } - - private static byte[,,] ByteVoxelsFromRecipe(SmithingRecipe recipe, int stackSize = 1) - { - var recipeVoxels = recipe.Voxels; - if (Core.Config.BrokenToolVoxelPercent < 0.2) - Core.Logger.Warning( - $"[ItemDamagedPatches#ByteVoxelsFromRecipe] Config setting {nameof(Core.Config.BrokenToolVoxelPercent)}" + - $"has a very low value, your broken tools well be almost or fully empty."); - ; - var byteVoxels = recipeVoxels.ErodeToPercentage(Core.Config.BrokenToolVoxelPercent); - return byteVoxels; - } -} \ No newline at end of file +}