From 6adbd85473c1c91eb0652cde4597bf7e2f057a84 Mon Sep 17 00:00:00 2001 From: Annan Fay Yearian Date: Mon, 31 May 2021 21:11:23 +0200 Subject: [PATCH] Version 0.0.1: Mostly working. --- CraftingSkill.sln | 25 +++ CraftingSkill/ConfigVariable.cs | 54 +++++ CraftingSkill/CraftingConfig.cs | 124 +++++++++++ CraftingSkill/CraftingSkill.cs | 264 +++++++++++++++++++++++ CraftingSkill/CraftingSkill.csproj | 145 +++++++++++++ CraftingSkill/CraftingSkillIcon.png | Bin 0 -> 2002 bytes CraftingSkill/Properties/AssemblyInfo.cs | 35 +++ CraftingSkill/Quality.cs | 17 ++ CraftingSkill/QualityComponent.cs | 102 +++++++++ README.md | 47 +++- 10 files changed, 812 insertions(+), 1 deletion(-) create mode 100644 CraftingSkill.sln create mode 100644 CraftingSkill/ConfigVariable.cs create mode 100644 CraftingSkill/CraftingConfig.cs create mode 100644 CraftingSkill/CraftingSkill.cs create mode 100644 CraftingSkill/CraftingSkill.csproj create mode 100644 CraftingSkill/CraftingSkillIcon.png create mode 100644 CraftingSkill/Properties/AssemblyInfo.cs create mode 100644 CraftingSkill/Quality.cs create mode 100644 CraftingSkill/QualityComponent.cs diff --git a/CraftingSkill.sln b/CraftingSkill.sln new file mode 100644 index 0000000..2139b99 --- /dev/null +++ b/CraftingSkill.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31019.35 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{E2DE2B39-165F-4D75-A9B3-6B53C0B08B9A}") = "CraftingSkill", "CraftingSkill\CraftingSkill.csproj", "{92F90B77-522E-4A67-AAEF-E5B593F1B7C5}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {92F90B77-522E-4A67-AAEF-E5B593F1B7C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {92F90B77-522E-4A67-AAEF-E5B593F1B7C5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {92F90B77-522E-4A67-AAEF-E5B593F1B7C5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {92F90B77-522E-4A67-AAEF-E5B593F1B7C5}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {EA61C0C0-D5BC-4B47-982B-F31DE743218D} + EndGlobalSection +EndGlobal diff --git a/CraftingSkill/ConfigVariable.cs b/CraftingSkill/ConfigVariable.cs new file mode 100644 index 0000000..17fe067 --- /dev/null +++ b/CraftingSkill/ConfigVariable.cs @@ -0,0 +1,54 @@ +using BepInEx.Configuration; +using HarmonyLib; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace CraftingSkill +{ + class ConfigVariable + { + object backingStore; + + private string configSection; + private string varName; + private T defaultValue; + private string configDescription; + private bool localOnly; + + public T Value + { + get + { + return Traverse.Create(backingStore).Property("Value").GetValue(); + } + } + + public ConfigVariable(string configSection, string varName, T defaultValue, string configDescription = "", bool localOnly = false) + { + this.configSection = configSection; + this.varName = varName; + this.defaultValue = defaultValue; + this.configDescription = configDescription; + this.localOnly = localOnly; + } + + public void init(Assembly assembly, ConfigFile config, string id) { + if (assembly != null) + { + var method = assembly.GetType("ModConfigEnforcer.ConfigManager") + .GetMethods() + .First(x => x.Name == "RegisterModConfigVariable" && x.IsGenericMethod) + .MakeGenericMethod(typeof(T)); + backingStore = method.Invoke(null, new object[] { id, varName, defaultValue, configSection, configDescription, localOnly }); + } + else + { + backingStore = config.Bind(configSection, varName, defaultValue, configDescription); + } + } + } +} diff --git a/CraftingSkill/CraftingConfig.cs b/CraftingSkill/CraftingConfig.cs new file mode 100644 index 0000000..336a8f3 --- /dev/null +++ b/CraftingSkill/CraftingConfig.cs @@ -0,0 +1,124 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Reflection; +using BepInEx.Configuration; +using HarmonyLib; +using UnityEngine; + +// This code and associated ConfigVariable is largely copied from the sailing_skill mod +// It allows us to have the modconfigenforcer mod as a soft dependency +// https://github.com/gaijinx/valheim_mods/tree/main/sailing_skill + +namespace CraftingSkill +{ + public class CraftingConfig + { + // public int NEXUS_ID = 0; + + // Experience for crafting + + private ConfigVariable __ExpScapeTier = new ConfigVariable("ExpGain", "ExpScapeTier", 1.0f); + private ConfigVariable __ExpScapePower = new ConfigVariable("ExpGain", "ExpScapePower", 2.0f); + private ConfigVariable __ExpScapeLinear = new ConfigVariable("ExpGain", "ExpScapeLinear", 0.16f); + + // Effects of quality + private ConfigVariable __ArmorStart = new ConfigVariable("Attributes", "ArmorStart", 0.8f); + private ConfigVariable __ArmorStop = new ConfigVariable("Attributes", "ArmorStop", 1.3f); + private ConfigVariable __WeightStart = new ConfigVariable("Attributes", "WeightStart", 1.1f); + private ConfigVariable __WeightStop = new ConfigVariable("Attributes", "WeightStop", 0.7f); + private ConfigVariable __MaxDurabilityStart = new ConfigVariable("Attributes", "MaxDurabilityStart", 0.8f); + private ConfigVariable __MaxDurabilityStop = new ConfigVariable("Attributes", "MaxDurabilityStop", 1.6f); + private ConfigVariable __BaseBlockPowerStart = new ConfigVariable("Attributes", "BaseBlockPowerStart", 0.8f); + private ConfigVariable __BaseBlockPowerStop = new ConfigVariable("Attributes", "BaseBlockPowerStop", 1.3f); + private ConfigVariable __DeflectionForceStart = new ConfigVariable("Attributes", "DeflectionForceStart", 0.8f); + private ConfigVariable __DeflectionForceStop = new ConfigVariable("Attributes", "DeflectionForceStop", 1.3f); + private ConfigVariable __DamageStart = new ConfigVariable("Attributes", "DamageStart", 0.8f); + private ConfigVariable __DamageStop = new ConfigVariable("Attributes", "DamageStop", 1.3f); + + // Cauldron: It's not crafting so no experience, but if we did + // Stonecutter: should probably be 2 or 3, but only thing right now is shitty sharpening stone + // Artisan: is used by Epic Loot?, will likely have vanilla items in future + // Guessing at 2 for now + private ConfigVariable __TierModifierInventory = new ConfigVariable("StationBalancing", "TierModifierInventory", 0); + private ConfigVariable __TierModifierWorkbench = new ConfigVariable("StationBalancing", "TierModifierWorkbench", 0); + private ConfigVariable __TierModifierForge = new ConfigVariable("StationBalancing", "TierModifierForge", 2); + private ConfigVariable __TierModifierCauldron = new ConfigVariable("StationBalancing", "TierModifierCauldron", 3); + private ConfigVariable __TierModifierStonecutter = new ConfigVariable("StationBalancing", "TierModifierStonecutter", 1); + private ConfigVariable __TierModifierArtisan = new ConfigVariable("StationBalancing", "TierModifierArtisan", 2); + private ConfigVariable __TierModifierDefault = new ConfigVariable("StationBalancing", "TierModifierDefault", 0); + + // Utility getters + public float ExpScapeTier {get => __ExpScapeTier.Value; } + public float ExpScapePower {get => __ExpScapePower.Value; } + public float ExpScapeLinear {get => __ExpScapeLinear.Value; } + public float ArmorStart {get => __ArmorStart.Value; } + public float ArmorStop {get => __ArmorStop.Value; } + public float WeightStart {get => __WeightStart.Value; } + public float WeightStop {get => __WeightStop.Value; } + public float MaxDurabilityStart {get => __MaxDurabilityStart.Value; } + public float MaxDurabilityStop {get => __MaxDurabilityStop.Value; } + public float BaseBlockPowerStart {get => __BaseBlockPowerStart.Value; } + public float BaseBlockPowerStop {get => __BaseBlockPowerStop.Value; } + public float DeflectionForceStart {get => __DeflectionForceStart.Value; } + public float DeflectionForceStop {get => __DeflectionForceStop.Value; } + public float DamageStart {get => __DamageStart.Value; } + public float DamageStop {get => __DamageStop.Value; } + public int TierModifierInventory {get => __TierModifierInventory.Value; } + public int TierModifierWorkbench {get => __TierModifierWorkbench.Value; } + public int TierModifierForge {get => __TierModifierForge.Value; } + public int TierModifierCauldron {get => __TierModifierCauldron.Value; } + public int TierModifierStonecutter {get => __TierModifierStonecutter.Value; } + public int TierModifierArtisan {get => __TierModifierArtisan.Value; } + public int TierModifierDefault {get => __TierModifierDefault.Value; } + + public CraftingConfig() + { + + } + + public void InitConfig(string mod_id, ConfigFile config) + { + // Bind("General", "NexusID", NEXUS_ID, "Nexus mod ID for updates"); + + Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.GetName().Name == "ModConfigEnforcer"); + + if (assembly != null) + { + Debug.Log("[CraftingSkill] Mod Config Enforcer detected, registering mod..."); + var configManagerType = assembly.GetType("ModConfigEnforcer.ConfigManager"); + Traverse.Create(configManagerType).Method("RegisterMod", mod_id, config).GetValue(mod_id, config); + } + else + { + Debug.Log("Mod Config Enforcer not detected."); + } + + __ExpScapeTier.init(assembly, config, mod_id); + __ExpScapePower.init(assembly, config, mod_id); + __ExpScapeLinear.init(assembly, config, mod_id); + __ArmorStart.init(assembly, config, mod_id); + __ArmorStop.init(assembly, config, mod_id); + __WeightStart.init(assembly, config, mod_id); + __WeightStop.init(assembly, config, mod_id); + __MaxDurabilityStart.init(assembly, config, mod_id); + __MaxDurabilityStop.init(assembly, config, mod_id); + __BaseBlockPowerStart.init(assembly, config, mod_id); + __BaseBlockPowerStop.init(assembly, config, mod_id); + __DeflectionForceStart.init(assembly, config, mod_id); + __DeflectionForceStop.init(assembly, config, mod_id); + __DamageStart.init(assembly, config, mod_id); + __DamageStop.init(assembly, config, mod_id); + __TierModifierInventory.init(assembly, config, mod_id); + __TierModifierWorkbench.init(assembly, config, mod_id); + __TierModifierForge.init(assembly, config, mod_id); + __TierModifierCauldron.init(assembly, config, mod_id); + __TierModifierStonecutter.init(assembly, config, mod_id); + __TierModifierArtisan.init(assembly, config, mod_id); + __TierModifierDefault.init(assembly, config, mod_id); + + } + } +} diff --git a/CraftingSkill/CraftingSkill.cs b/CraftingSkill/CraftingSkill.cs new file mode 100644 index 0000000..3d283c6 --- /dev/null +++ b/CraftingSkill/CraftingSkill.cs @@ -0,0 +1,264 @@ +using BepInEx; +using HarmonyLib; +using Pipakin.SkillInjectorMod; +using System; +using UnityEngine; +using ExtendedItemDataFramework; +using System.IO; +using System.Reflection; + +// using BepInEx.Configuration; +using System.Collections.Generic; +using System.Linq; + +namespace CraftingSkill +{ + + [BepInPlugin("annanfay.mod.crafting_skill", CraftingSkillsPlugin.MOD_NAME, CraftingSkillsPlugin.MOD_VERSION)] + [BepInDependency("pfhoenix.modconfigenforcer", BepInDependency.DependencyFlags.SoftDependency)] + [BepInDependency("com.pipakin.SkillInjectorMod")] + [BepInDependency("randyknapp.mods.extendeditemdataframework")] + public class CraftingSkillsPlugin : BaseUnityPlugin + { + public const String MOD_ID = "annanfay.mod.crafting_skill"; + public const string MOD_NAME = "CraftingSkill"; + public const string MOD_VERSION = "0.0.1"; + public const int CRAFTING_SKILL_ID = 1605; + + Harmony harmony; + + private static CraftingConfig config = new CraftingConfig(); + + void Awake() + { + config.InitConfig(MOD_ID, Config); + + harmony = new Harmony(MOD_ID); + harmony.PatchAll(); + + SkillInjector.RegisterNewSkill(CRAFTING_SKILL_ID, "Crafting", "Describes crafting ability", 1.0f, LoadIconTexture(), Skills.SkillType.Unarmed); + + ExtendedItemData.LoadExtendedItemData += QualityComponent.OnNewExtendedItemData; + ExtendedItemData.NewExtendedItemData += QualityComponent.OnNewExtendedItemData; + } + + private static Sprite LoadIconTexture() + { + string directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + string filepath = Path.Combine(directoryName, "CraftingSkillIcon.png"); + if (File.Exists(filepath)) + { + Texture2D texture2D = LoadTexture(filepath); + return Sprite.Create(texture2D, new Rect(0f, 0f, 64f, 64f), Vector2.zero); + } + else + { + Debug.LogError("Unable to load skill icon! filepath:" + filepath); + return null; + } + } + + private static Dictionary cachedTextures = new Dictionary(); + private static Texture2D LoadTexture(string filepath) + { + if (cachedTextures.ContainsKey(filepath)) + { + return cachedTextures[filepath]; + } + Texture2D texture2D = new Texture2D(0, 0); + ImageConversion.LoadImage(texture2D, File.ReadAllBytes(filepath)); + return texture2D; + } + + [HarmonyPatch(typeof(ItemDrop.ItemData))] + public static class ItemDataPatcher + { + static String CraftQualityLabel = "Craft Quality"; + static String CraftExperienceLabel = "Craft Exp."; + // public static string GetTooltip(ItemData item, int qualityLevel, bool crafting) + [HarmonyPostfix] + [HarmonyPatch("GetTooltip", typeof(ItemDrop.ItemData), typeof(int), typeof(bool))] + static void GetTooltip(ItemDrop.ItemData item, int qualityLevel, bool crafting, ref string __result) + { + QualityComponent qualityComp = item.Extended()?.GetComponent(); + if(qualityComp != null) { + __result += String.Format( + "\n{0}: Level {1}", + CraftQualityLabel, + (qualityComp.Quality.Skill * 100f).ToString("0") + ); + } + Recipe recipe = ObjectDB.instance.GetRecipe(item); + if (recipe != null) { + __result += String.Format( + "\n{0}: {1}", + CraftExperienceLabel, + GetCraftExperience(recipe, qualityLevel).ToString("0") + ); + } + } + + public static float GetItemQualityScalingFactor(ItemDrop.ItemData item, float min, float max) + { + QualityComponent qualityComp = item.Extended()?.GetComponent(); + if (qualityComp == null) { + return 1.0f; + } + float scalingFactor = min + (max - min) * qualityComp.Quality.Skill; + return scalingFactor; + } + + // First override the simple methods which returns floats + // public float GetArmor(int quality){} + // public float GetWeight(){} + // public float GetMaxDurability(int quality){} + // public float GetBaseBlockPower(int quality){} + // public float GetDeflectionForce(int quality){} + // public float GetArmor(int quality){} + [HarmonyPostfix] + [HarmonyPatch("GetArmor", typeof(int))] + public static float GetArmor(float __result, ItemDrop.ItemData __instance) + { + return __result * GetItemQualityScalingFactor(__instance, config.ArmorStart, config.ArmorStop); + } + + // public float GetWeight() + [HarmonyPostfix] + [HarmonyPatch("GetWeight")] + public static float GetWeight(float __result, ItemDrop.ItemData __instance) + { + return __result * GetItemQualityScalingFactor(__instance, config.WeightStart, config.WeightStop); + } + + // public float GetMaxDurability(int quality) + [HarmonyPostfix] + [HarmonyPatch("GetMaxDurability", typeof(int))] + public static float GetMaxDurability(float __result, ItemDrop.ItemData __instance) + { + return __result * GetItemQualityScalingFactor(__instance, config.MaxDurabilityStart, config.MaxDurabilityStop); + } + + // public float GetBaseBlockPower(int quality){} + [HarmonyPostfix] + [HarmonyPatch("GetBaseBlockPower", typeof(int))] + public static float GetBaseBlockPower(float __result, ItemDrop.ItemData __instance) + { + return __result * GetItemQualityScalingFactor(__instance, config.BaseBlockPowerStart, config.BaseBlockPowerStop); + } + + // public float GetDeflectionForce(int quality){} + [HarmonyPostfix] + [HarmonyPatch("GetDeflectionForce", typeof(int))] + public static float GetDeflectionForce(float __result, ItemDrop.ItemData __instance) + { + return __result * GetItemQualityScalingFactor(__instance, config.DeflectionForceStart, config.DeflectionForceStop); + } + + // public HitData.DamageTypes GetDamage(int quality) + [HarmonyPostfix] + [HarmonyPatch("GetDamage", typeof(int))] + public static void GetDamage(ref HitData.DamageTypes __result, ItemDrop.ItemData __instance) + { + float scalingFactor = GetItemQualityScalingFactor(__instance, config.DamageStart, config.DamageStop); + __result.Modify(scalingFactor); + } + } + + public static float GetCraftTierMod(Recipe recipe) + { + switch (recipe.m_craftingStation?.m_name) + { + case null: // Crafting from inventory + return config.TierModifierInventory; + case "$piece_forge": + return config.TierModifierForge; + case "$piece_workbench": + return config.TierModifierWorkbench; + case "$piece_cauldron": + return config.TierModifierCauldron; + case "$piece_stonecutter": + return config.TierModifierStonecutter; + case "$piece_artisanstation": + return config.TierModifierArtisan; + default: + return config.TierModifierDefault; + } + } + + public static int CountIngredients(Recipe recipe, int craftLevel) + { + int count = 0; + foreach (Piece.Requirement req in recipe.m_resources) + { + int amount = req.GetAmount(craftLevel); + count += amount; + } + return count; + } + + public static float GetCraftExperience(Recipe recipe, int craftLevel) + { + int ingredientCount = CountIngredients(recipe, craftLevel); + float craftTypeTierMod = GetCraftTierMod(recipe); + float craftTier = recipe.m_minStationLevel + craftLevel; + float effectiveCraftTier = craftTier + craftTypeTierMod; + float exp = ingredientCount * config.ExpScapeLinear * (float)Math.Pow(config.ExpScapeTier * effectiveCraftTier, config.ExpScapePower); + return exp; + } + + [HarmonyPatch(typeof(InventoryGui), "DoCrafting")] + public static class InventoryGuiPatcher + { + static int m_crafts; + static int craftLevel; + + // private void DoCrafting(Player player) + static void Prefix(InventoryGui __instance, Player player, + // private fields + ItemDrop.ItemData ___m_craftUpgradeItem, + int ___m_craftVariant + ) + { + // ZLog.LogError($"[{nameof(InventoryGui)}] pre crafting hook"); + craftLevel = ((___m_craftUpgradeItem == null) ? 1 : (___m_craftUpgradeItem.m_quality + 1)); + m_crafts = Game.instance.GetPlayerProfile().m_playerStats.m_crafts; + } + + static void Postfix(InventoryGui __instance, Player player, + // private fields + Recipe ___m_craftRecipe + ) + { + + // ZLog.LogError($"[{nameof(InventoryGui)}] post crafting hook"); + int new_m_crafts = Game.instance.GetPlayerProfile().m_playerStats.m_crafts; + + // craft failed + if (m_crafts >= new_m_crafts) { + // ZLog.LogError($"Craft Failed? {___m_craftRecipe.m_item.m_itemData.m_shared.m_name} {craftLevel} {___m_craftRecipe.m_craftingStation?.m_name}"); + return; + } + // ZLog.LogError($"Craft Succeeded? {___m_craftRecipe.m_item.m_itemData.m_shared.m_name} {craftLevel} {___m_craftRecipe.m_craftingStation?.m_name}"); + + // # Full list of stations used in recipes as of 0.147.3: + // # - identifier: `$piece_forge` in game name: Forge + // # - identifier: `$piece_workbench` in game name: Workbench + // # - identifier: `$piece_cauldron` in game name: Cauldron + // # - identifier: `$piece_stonecutter` in game name: Stonecutter + // Also $piece_artisanstation (added at some point after above list) + string craftingStationName = ___m_craftRecipe.m_craftingStation?.m_name; + bool isNoStation = craftingStationName == null; + bool isForgeRecipe = craftingStationName == "$piece_forge"; + bool isWorkbenchRecipe = craftingStationName == "$piece_workbench"; + bool isStonecutterRecipe = craftingStationName == "$piece_stonecutter"; + bool isArtisanRecipe = craftingStationName == "$piece_artisanstation"; + if (isWorkbenchRecipe || isForgeRecipe || isNoStation || isStonecutterRecipe || isArtisanRecipe) { + float craftExperience = GetCraftExperience(___m_craftRecipe, craftLevel); + player.RaiseSkill((Skills.SkillType)CRAFTING_SKILL_ID, craftExperience); + float SkillLevel = player.GetSkillFactor((Skills.SkillType)CRAFTING_SKILL_ID); + // ZLog.LogError($"Craft Succeeded => exp={craftExperience}, {SkillLevel}"); + } + } + } + } +} diff --git a/CraftingSkill/CraftingSkill.csproj b/CraftingSkill/CraftingSkill.csproj new file mode 100644 index 0000000..261dc76 --- /dev/null +++ b/CraftingSkill/CraftingSkill.csproj @@ -0,0 +1,145 @@ + + + + + Debug + AnyCPU + {92F90B77-522E-4A67-AAEF-E5B593F1B7C5} + Library + Properties + CraftingSkill + CraftingSkill + v4.7.2 + 512 + true + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + true + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + ..\..\ValheimLibs\0Harmony.dll + + + ..\..\ValheimLibs\assembly_guiutils.dll + + + ..\..\ValheimLibs\assembly_lux.dll + + + False + ..\..\ValheimLibs\assembly_postprocessing.dll + + + ..\..\ValheimLibs\assembly_simplemeshcombine.dll + + + False + ..\..\ValheimLibs\assembly_steamworks.dll + + + False + ..\..\ValheimLibs\assembly_sunshafts.dll + + + ..\..\ValheimLibs\assembly_utils.dll + + + ..\..\ValheimLibs\assembly_valheim.dll + + + ..\..\ValheimLibs\BepInEx.dll + + + ..\..\ValheimLibs\BepInEx.Harmony.dll + + + ..\..\ValheimLibs\BepInEx.Preloader.dll + + + ..\..\ValheimLibs\ExtendedItemDataFramework.dll + + + ..\..\ValheimLibs\fastJSON.dll + + + ..\..\ValheimLibs\HarmonyXInterop.dll + + + ..\..\ValheimLibs\Mono.Cecil.dll + + + ..\..\ValheimLibs\Mono.Cecil.Mdb.dll + + + ..\..\ValheimLibs\Mono.Cecil.Pdb.dll + + + ..\..\ValheimLibs\Mono.Cecil.Rocks.dll + + + ..\..\ValheimLibs\Mono.Security.dll + + + ..\..\ValheimLibs\MonoMod.RuntimeDetour.dll + + + ..\..\ValheimLibs\MonoMod.Utils.dll + + + ..\..\ValheimLibs\SkillInjector.dll + + + + + + + + + + + ..\..\ValheimLibs\UnityEngine.dll + + + ..\..\ValheimLibs\UnityEngine.CoreModule.dll + + + ..\..\ValheimLibs\UnityEngine.ImageConversionModule.dll + + + ..\..\ValheimLibs\UnityEngine.UI.dll + + + + + + + + + + + + + + xcopy /y /d "$(TargetDir)CraftingSkill.dll" "M:\Valheim\Valheim_solo\BepInEx\plugins" + xcopy /y /d "$(ProjectDir)CraftingSkillIcon.png" "M:\Valheim\Valheim_solo\BepInEx\plugins" + + + diff --git a/CraftingSkill/CraftingSkillIcon.png b/CraftingSkill/CraftingSkillIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..9ca180145040f074ee34f345f629fafbc2a2739f GIT binary patch literal 2002 zcmV;@2QB!CP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi=>Px@=>ZNiLL2}92WLq{K~#8N?U;FN z6jdC@e{XiTr7eZhL#c8Vk)tRAYE3i<3P{ArS-en@Xgrc)FdiX_(LcOmOf(TiM3fLf zjevp&Vnj?-P$?)v3ka;0TR{%(c3ZkLsI5;>s{4Y^1n}}r%coVt-qFL);$TjSY0tPfH9idR7E~qoa5o@>PZ8Gc_L8}H}#HrP2 z4uLA6&tSqNo@>SlJEkj94Uc&jfC=2@#z45a1veY_-mmlauhlV`RzX5U6KDS zez+c-MyqQ;G>|wez9rCn=vm$+SOhP@LiaPzH)AIhfc8M#DfS@~M8)=Tw0&5he7dQ8 ze#!8!@%lMEIL%h^cZZeas?JjHaf{(WrI~>)$mIILTbfC^(8tl`^x}XrQtYE?4NyHA zHQ1?WY6NXEfWU+<2ya3Ap@$~+c8N#nThP7;3g?9e@jS80+t|_?!QbKqGwC}aPA{5p zD-3uKF{P*pC9Z9nGEd+lsxcB(*ruv$jm)aUdZ$A>_8&asZHMpBzz3j98E?A4njVir z;XIF0_;I`41inl9LrnU1s5|rz^c}PgS`7oXBBm5IzQi@xrOYziR{gL)(|HRNFvpPJ zc3fwbX{u@%Q$=I`&~p!O-+w5=7$h?&&?wWV4v@>y=JW`s4!<)Dd$3g`;XI!UZh@Hi zKxjS0{h3K`LehqzkT3Iq{zAQld2PDizDQ!>_2IQA%dc$QVZ%*sRHn~{o zrWuTofi@ye20sKu%T$5INLr!S2$-&21uC<{V68)sJmpe_F1MazYDUM7+JWxAoG%I` zdzEe!eKm7XE|aHe@X8Fv(D8q(czBz!ZqD-MDXTG5#pKCAZ7~A@k!yiKH0hT9J?n-UV&eRjqyt7 ze7|g~ES9RxU8V69uPtW2hq&+OhKbJ?Ap_dVIvVgd8vMb8$jz&lD@WGIn&sTtqLY(* zN`{Z!EoH;^%AV>%sr38Ys-48Hd@X?l!xT%0e2nHOA3>t^h)E^xHJ~+M z0^b$h7CdSb9O=7sr54R?wq>P)mh0BGljv)#O4BgfC3%tx0}_2bzWcQgKeU^vClv-* zHFg5T^_-k2sW2eXvapXG+l5pZ!0j**24Epg#dSp`75}J*9oq_D3MBPO=uD)-fPQvN z1Xi5wLMjYM#^^nWX;5UhQ%X_`p^8b9G(6Nljb`X~gU^nUG1Ml}&WGaXsQg$}Iyg8u kI5;>sI5;>sBpH!^0l>apo`32vw*UYD07*qoM6N<$f=R2jQvd(} literal 0 HcmV?d00001 diff --git a/CraftingSkill/Properties/AssemblyInfo.cs b/CraftingSkill/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..39aa8d6 --- /dev/null +++ b/CraftingSkill/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("CraftingSkill")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("CraftingSkill")] +[assembly: AssemblyCopyright("Copyright © 2021")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("9dd4d496-473e-4571-82fe-1cdc7ea4b887")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("0.0.1.*")] +[assembly: AssemblyFileVersion("0.0.1.*")] diff --git a/CraftingSkill/Quality.cs b/CraftingSkill/Quality.cs new file mode 100644 index 0000000..26305bc --- /dev/null +++ b/CraftingSkill/Quality.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using ExtendedItemDataFramework; +using UnityEngine; + +namespace CraftingSkill +{ + [Serializable] + public class Quality + { + public float Material = 0; + public float Skill = 0; + public int StationLevel = 0; + public float Repair = 0; + } +} diff --git a/CraftingSkill/QualityComponent.cs b/CraftingSkill/QualityComponent.cs new file mode 100644 index 0000000..e8ec02c --- /dev/null +++ b/CraftingSkill/QualityComponent.cs @@ -0,0 +1,102 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using ExtendedItemDataFramework; +using fastJSON; +using HarmonyLib; +using UnityEngine; +using UnityEngine.UI; +using Object = UnityEngine.Object; + +namespace CraftingSkill +{ + public class QualityComponent : BaseExtendedItemComponent + { + public Quality Quality; + + private static readonly JSONParameters _saveParams = new JSONParameters { UseExtensions = false }; + + public QualityComponent(ExtendedItemData parent) + : base(typeof(QualityComponent).AssemblyQualifiedName, parent) + { + } + + public void SetQuality(Quality quality) + { + Quality = quality; + Save(); + } + + public override string Serialize() + { + return JSON.ToJSON(Quality, _saveParams); + } + + public override void Deserialize(string data) + { + try + { + Quality = JSON.ToObject(data, _saveParams); + } + catch (Exception) + { + ZLog.LogError($"[{nameof(QualityComponent)}] Could not deserialize Quality json data! ({ItemData?.m_shared?.m_name})"); + throw; + } + } + + public override BaseExtendedItemComponent Clone() + { + return MemberwiseClone() as BaseExtendedItemComponent; + } + + public static void OnNewExtendedItemData(ExtendedItemData itemdata) + { + // ZLog.LogError("OnNewExtendedItemData!"); + + if (itemdata.GetComponent() != null) + { + return; + } + + Recipe recipe = ObjectDB.instance.GetRecipe(itemdata); + if (recipe == null) + { + return; + } + + + Player player = Player.m_localPlayer; + if (player == null) + { + return; + } + + string crafterName = itemdata.GetCrafterName(); + string playerName = player.GetPlayerName(); + if (crafterName != playerName) { + return; + } + + var quality = new Quality(); + quality.Skill = player.GetSkillFactor((Skills.SkillType)CraftingSkillsPlugin.CRAFTING_SKILL_ID); + + CraftingStation station = player.GetCurrentCraftingStation(); + if (station) { + quality.StationLevel = station.GetLevel(); + } + + itemdata.AddComponent().SetQuality(quality); + } + + public static void OnLoadExtendedItemData(ExtendedItemData itemdata) + { + // ZLog.LogError("OnLoadExtendedItemData!"); + // QualityComponent qualityComponent = itemdata.GetComponent(); + // if (qualityComponent != null) + // { + // ZLog.LogError("qualityComponent! q.Skill=" + qualityComponent.Quality.Skill); + // } + } + } +} diff --git a/README.md b/README.md index 1b8d894..fde1a97 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,47 @@ # ValheimCraftingSkill -Adds a simple crafting skill to the game +Adds a simple crafting skill to the game. + +### Requirements: + +- [BepInEx](https://github.com/BepInEx/BepInEx) ([thunderstore](https://valheim.thunderstore.io/package/denikson/BepInExPack_Valheim/)) +- [Skill Injector](https://github.com/pipakin/PipakinsMods/tree/master/SkillInjector) ([nexus](https://www.nexusmods.com/valheim/mods/341)) +- [optional] [Mod Config Enforcer](https://github.com/Pfhoenix/ModConfigEnforcer) ([nexus](https://www.nexusmods.com/valheim/mods/460)) + +### Configuration + +The config file is created after first run. You can probably find it in `.\BepInEx\config\` directory. + +`____Start` and `____Stop` config settings map skill level to item attributes bounded by the start and stop. The start value is what the attribute will be at level 0 and Stop at level 100. + +`Exp____` configures the rate you gain experience at. Only the `Linear` setting is recomended to change, increase for more Exp, decrease for less. + +`TierModifier` affects experience gain also but dependant on the work station. Increase the number to give a **big** boost to items from that workstation. (These settings are here mostly just to balance Troll Armour) + +Default configuration: + +- ArmorStart/Stop: 80% to 130% +- WeightStart/Stop: 110% to 70% +- MaxDurabilityStart/Stop: 80% to 160% +- BaseBlockPowerStart/Stop: 80% to 130% +- DeflectionForceStart/Stop: 80% to 130% +- DamageStart/Stop: 80% to 130% + +- ExpScapeTier 1.00 +- ExpScapePower 2.00 +- ExpScapeLinear 0.16 + +- TierModifierInventory 0 +- TierModifierWorkbench 0 +- TierModifierForge 2 +- TierModifierCauldron 3 +- TierModifierStonecutter 1 +- TierModifierArtisan 2 +- TierModifierDefault 0 + +### Code inspired by: + +- For general project layout and config system: [SailingSkill by gaijinx](https://github.com/gaijinx/valheim_mods/tree/main/sailing_skill) +- For examples of *using* ExtendedItemData: [EpicLoot by RandyKnapp](https://github.com/RandyKnapp/ValheimMods/tree/main/EpicLoot) +- For custom icon handling: [GatheringSkillMod by Pipakin](https://github.com/pipakin/PipakinsMods/tree/master/GatheringSkillMod) + +GitHub Link: [/AnnanFay/ValheimCraftingSkill](https://github.com/AnnanFay/ValheimCraftingSkill)