From 69d0b75ac7a156e40ee4f272c6c30e6c79bc7cf4 Mon Sep 17 00:00:00 2001 From: Benjamin Fennema Date: Sun, 21 Jun 2020 03:25:18 -0700 Subject: [PATCH 001/132] Switch to Harmony2 Signed-off-by: Benjamin Fennema --- CraftMagicItems/Accessors.cs | 69 ++---- CraftMagicItems/BondedItemComponent.cs | 2 +- CraftMagicItems/CraftMagicItems.csproj | 4 +- CraftMagicItems/CraftMagicItemsAccessors.cs | 139 ++++++----- .../CraftMagicItemsBlueprintPatcher.cs | 89 ++++--- CraftMagicItems/CustomBlueprintBuilder.cs | 33 +-- CraftMagicItems/L10NString.cs | 10 +- CraftMagicItems/L10n.cs | 7 +- CraftMagicItems/Main.cs | 221 +++++++++++------- CraftMagicItems/QuiverAbility.cs | 25 +- CraftMagicItems/SustenanceEnchantment.cs | 40 ++-- CraftMagicItems/WeaponBaseSizeChange.cs | 16 +- CraftMagicItems/WeaponSizeChange.cs | 6 +- CraftMagicItems/WildEnchantment.cs | 22 +- Repository.json | 2 +- info.json | 4 +- 16 files changed, 329 insertions(+), 360 deletions(-) diff --git a/CraftMagicItems/Accessors.cs b/CraftMagicItems/Accessors.cs index a28cfb0..9af1c43 100644 --- a/CraftMagicItems/Accessors.cs +++ b/CraftMagicItems/Accessors.cs @@ -37,74 +37,39 @@ namespace CraftMagicItems { public delegate TResult FastStaticInvoker(T1 arg1, T2 arg2, T3 arg3); public class Accessors { - public static Harmony12.AccessTools.FieldRef CreateFieldRef(string name) { + public static HarmonyLib.AccessTools.FieldRef CreateFieldRef(string name) { var classType = typeof(TClass); var resultType = typeof(TResult); - var fieldInfo = Harmony12.AccessTools.Field(classType, name); + var fieldInfo = HarmonyLib.AccessTools.Field(classType, name); if (fieldInfo == null) { throw new Exception($"{classType} does not contain field {name}"); } - if (!resultType.IsAssignableFrom(fieldInfo.FieldType)) { + if (!resultType.IsAssignableFrom(fieldInfo.FieldType) && (!fieldInfo.FieldType.IsEnum || resultType != typeof(int))) { throw new InvalidCastException($"Cannot cast field type {resultType} as {fieldInfo.FieldType} for class {classType} field {name}"); } - var fieldRef = Harmony12.AccessTools.FieldRefAccess(name); + var fieldRef = HarmonyLib.AccessTools.FieldRefAccess(name); return fieldRef; } - public static FastGetter CreateGetter(Type classType, Type resultType, string name) { - var fieldInfo = Harmony12.AccessTools.Field(classType, name); - var propInfo = Harmony12.AccessTools.Property(classType, name); - if (fieldInfo == null && propInfo == null) { - throw new Exception($"{classType} does not contain field or property {name}"); - } - - bool isProp = propInfo != null; - Type memberType = isProp ? propInfo.PropertyType : fieldInfo.FieldType; - string memberTypeName = isProp ? "property" : "field"; - if (!resultType.IsAssignableFrom(memberType)) { - throw new InvalidCastException($"Cannot cast field type {resultType} as {memberType} for class {classType} {memberTypeName} {name}"); - } - - var handler = isProp ? Harmony12.FastAccess.CreateGetterHandler(propInfo) : Harmony12.FastAccess.CreateGetterHandler(fieldInfo); - return new FastGetter(handler); - } - - public static FastGetter CreateGetter(string name) { + public static FastSetter CreateSetter(string name) { var classType = typeof(TClass); - var resultType = typeof(TResult); - var handler = CreateGetter(classType, resultType, name); - return instance => (TResult) handler.Invoke(instance); - } - - public static FastSetter CreateSetter(Type classType, Type valueType, string name) { - var propertyInfo = Harmony12.AccessTools.Property(classType, name); - var fieldInfo = Harmony12.AccessTools.Field(classType, name); - if (propertyInfo == null && fieldInfo == null) { + var propertySetter = HarmonyLib.AccessTools.PropertySetter(classType, name); + if (propertySetter == null) { throw new Exception($"{classType} does not contain a field or property {name}"); } - - var isProperty = propertyInfo != null; - var memberType = isProperty ? propertyInfo.PropertyType : fieldInfo.FieldType; - var memberTypeName = isProperty ? "property" : "field"; + var propertyInfo = HarmonyLib.AccessTools.Property(classType, name); + var memberType = propertyInfo.PropertyType; + var valueType = typeof(TValue); if (!valueType.IsAssignableFrom(memberType) && (!memberType.IsEnum || valueType != typeof(int))) { - throw new Exception($"Cannot cast property type {valueType} as {memberType} for class {classType} {memberTypeName} {name}"); + throw new Exception($"Cannot cast property type {valueType} as {memberType} for class {classType} property {name}"); } - - var handler = isProperty ? Harmony12.FastAccess.CreateSetterHandler(propertyInfo) : Harmony12.FastAccess.CreateSetterHandler(fieldInfo); - return new FastSetter(handler); - } - - public static FastSetter CreateSetter(string name) { - var classType = typeof(TClass); - var valueType = typeof(TValue); - var handler = CreateSetter(classType, valueType, name); - return (instance, value) => handler.Invoke(instance, value); + return new FastSetter(HarmonyLib.AccessTools.MethodDelegate>(propertySetter)); } private static MethodInfo GetMethodInfoValidated(Type classType, string name, Type resultType, Type[] args, Type[] typeArgs) { - var methodInfo = Harmony12.AccessTools.Method(classType, name, args, typeArgs); + var methodInfo = HarmonyLib.AccessTools.Method(classType, name, args, typeArgs); if (methodInfo == null) { var argString = string.Join(", ", args.Select(t => t.ToString())); throw new Exception($"{classType} does not contain method {name} with arguments {argString}"); @@ -119,7 +84,7 @@ private static MethodInfo GetMethodInfoValidated(Type classType, string name, Ty private static FastInvoker CreateInvoker(Type classType, string name, Type resultType, Type[] args, Type[] typeArgs = null) { var methodInfo = GetMethodInfoValidated(classType, name, resultType, args, typeArgs); - return new FastInvoker(Harmony12.MethodInvoker.GetHandler(methodInfo)); + return new FastInvoker(HarmonyLib.MethodInvoker.GetHandler(methodInfo)); } public static FastInvoker CreateInvoker(string name) { @@ -156,9 +121,9 @@ public static FastInvoker CreateInvoker CreateStaticInvoker(Type classType, string name) { diff --git a/CraftMagicItems/BondedItemComponent.cs b/CraftMagicItems/BondedItemComponent.cs index 1c4f7f0..fb71567 100644 --- a/CraftMagicItems/BondedItemComponent.cs +++ b/CraftMagicItems/BondedItemComponent.cs @@ -43,7 +43,7 @@ public void HandleEquipmentSlotUpdated(ItemSlot slot, ItemEntity previousItem) { } // If the owner of a bonded object casts a spell when not wielding it, they need to make a Concentration check or lose the spell. - [Harmony12.HarmonyPatch(typeof(UnitUseAbility), "MakeConcentrationCheckIfCastingIsDifficult")] + [HarmonyLib.HarmonyPatch(typeof(UnitUseAbility), "MakeConcentrationCheckIfCastingIsDifficult")] // ReSharper disable once UnusedMember.Local private static class UnitUseAbilityMakeConcentrationCheckIfCastingIsDifficultPatch { // ReSharper disable once UnusedMember.Local diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index 89ba380..1e03e41 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -60,9 +60,9 @@ - + False - ..\..\..\..\..\..\Program Files\UnityModManager-0.14.2a\0Harmony12.dll + ..\..\..\..\..\..\Program Files\UnityModManager\0Harmony.dll ..\..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\Pathfinder Kingmaker\Kingmaker_Data\Managed\Assembly-CSharp.dll diff --git a/CraftMagicItems/CraftMagicItemsAccessors.cs b/CraftMagicItems/CraftMagicItemsAccessors.cs index 0482276..11535e9 100644 --- a/CraftMagicItems/CraftMagicItemsAccessors.cs +++ b/CraftMagicItems/CraftMagicItemsAccessors.cs @@ -29,115 +29,112 @@ namespace CraftMagicItems { * Spacehamster's idea: create reflection-based accessors up front, so the mod fails on startup if the Kingmaker code changes in an incompatible way. */ public class CraftMagicItemsAccessors { - public readonly FastGetter> GetSpellbookSpecialLists = - Accessors.CreateGetter>("m_SpecialLists"); + public readonly HarmonyLib.AccessTools.FieldRef> GetSpellbookSpecialLists = + Accessors.CreateFieldRef>("m_SpecialLists"); - public readonly FastGetter> GetFeatureCollectionFacts = - Accessors.CreateGetter>("m_Facts"); + public readonly HarmonyLib.AccessTools.FieldRef GetActionBarManagerNeedReset = Accessors.CreateFieldRef("m_NeedReset"); - public readonly FastGetter GetActionBarManagerNeedReset = Accessors.CreateGetter("m_NeedReset"); + public readonly HarmonyLib.AccessTools.FieldRef GetActionBarManagerSelected = + Accessors.CreateFieldRef("m_Selected"); - public readonly FastGetter GetActionBarManagerSelected = - Accessors.CreateGetter("m_Selected"); + public readonly HarmonyLib.AccessTools.FieldRef[]> GetSpellbookKnownSpells = + Accessors.CreateFieldRef[]>("m_KnownSpells"); - public readonly FastGetter[]> GetSpellbookKnownSpells = - Accessors.CreateGetter[]>("m_KnownSpells"); + public readonly HarmonyLib.AccessTools.FieldRef> GetSpellbookKnownSpellLevels = + Accessors.CreateFieldRef>("m_KnownSpellLevels"); - public readonly FastGetter> GetSpellbookKnownSpellLevels = - Accessors.CreateGetter>("m_KnownSpellLevels"); + public readonly HarmonyLib.AccessTools.FieldRef SetBlueprintUnitFactDisplayName = + Accessors.CreateFieldRef("m_DisplayName"); - public readonly FastSetter SetBlueprintUnitFactDisplayName = - Accessors.CreateSetter("m_DisplayName"); + public readonly HarmonyLib.AccessTools.FieldRef SetBlueprintUnitFactDescription = + Accessors.CreateFieldRef("m_Description"); - public readonly FastSetter SetBlueprintUnitFactDescription = - Accessors.CreateSetter("m_Description"); + public readonly HarmonyLib.AccessTools.FieldRef SetBlueprintUnitFactIcon = Accessors.CreateFieldRef("m_Icon"); - public readonly FastSetter SetBlueprintUnitFactIcon = Accessors.CreateSetter("m_Icon"); + public readonly HarmonyLib.AccessTools.FieldRef> SetBlueprintItemCachedEnchantments = + Accessors.CreateFieldRef>("m_CachedEnchantments"); - public readonly FastSetter SetBlueprintItemEquipmentUsableCost = - Accessors.CreateSetter("m_Cost"); + public readonly HarmonyLib.AccessTools.FieldRef SetBlueprintItemShieldArmorComponent = + Accessors.CreateFieldRef("m_ArmorComponent"); - public readonly FastSetter> SetBlueprintItemCachedEnchantments = - Accessors.CreateSetter>("m_CachedEnchantments"); + public readonly HarmonyLib.AccessTools.FieldRef SetBlueprintItemShieldWeaponComponent = + Accessors.CreateFieldRef("m_WeaponComponent"); - public readonly FastSetter SetBlueprintItemShieldArmorComponent = - Accessors.CreateSetter("m_ArmorComponent"); + public readonly HarmonyLib.AccessTools.FieldRef SetBlueprintItemWeaponDamageType = + Accessors.CreateFieldRef("m_DamageType"); - public readonly FastSetter SetBlueprintItemShieldWeaponComponent = - Accessors.CreateSetter("m_WeaponComponent"); + public readonly HarmonyLib.AccessTools.FieldRef SetBlueprintItemWeaponOverrideDamageType = + Accessors.CreateFieldRef("m_OverrideDamageType"); - public readonly FastSetter SetBlueprintItemWeaponDamageType = - Accessors.CreateSetter("m_DamageType"); + public readonly HarmonyLib.AccessTools.FieldRef SetBlueprintItemBaseDamage = + Accessors.CreateFieldRef("m_BaseDamage"); - public readonly FastSetter SetBlueprintItemWeaponOverrideDamageType = - Accessors.CreateSetter("m_OverrideDamageType"); + public readonly HarmonyLib.AccessTools.FieldRef SetBlueprintItemIcon = Accessors.CreateFieldRef("m_Icon"); - public readonly FastSetter SetBlueprintItemBaseDamage = - Accessors.CreateSetter("m_BaseDamage"); + public readonly HarmonyLib.AccessTools.FieldRef SetBlueprintItemEquipmentHandVisualParameters = + Accessors.CreateFieldRef("m_VisualParameters"); - public readonly FastSetter SetBlueprintItemIcon = Accessors.CreateSetter("m_Icon"); + public readonly HarmonyLib.AccessTools.FieldRef SetWeaponVisualParametersModel = + Accessors.CreateFieldRef("m_WeaponModel"); - public readonly FastSetter SetBlueprintItemEquipmentHandVisualParameters = - Accessors.CreateSetter("m_VisualParameters"); + public readonly HarmonyLib.AccessTools.FieldRef SetBlueprintItemEquipmentWeaponAnimationStyle = + Accessors.CreateFieldRef("m_WeaponAnimationStyle"); - public readonly FastSetter SetWeaponVisualParametersModel = - Accessors.CreateSetter("m_WeaponModel"); + public readonly HarmonyLib.AccessTools.FieldRef SetBlueprintItemArmorVisualParameters = + Accessors.CreateFieldRef("m_VisualParameters"); - public readonly FastSetter SetBlueprintItemEquipmentWeaponAnimationStyle = - Accessors.CreateSetter("m_WeaponAnimationStyle"); + //public readonly SetterHandler SetBlueprintBuffFlags = Accessors.CreateSetter("m_Flags"); + public void SetBlueprintBuffFlags(BlueprintBuff buff, int flags) { + HarmonyLib.AccessTools.Field(typeof(BlueprintBuff), "m_Flags").SetValue(buff, flags); + } - public readonly FastSetter SetBlueprintItemArmorVisualParameters = - Accessors.CreateSetter("m_VisualParameters"); + public readonly HarmonyLib.AccessTools.FieldRef SetBlueprintItemDisplayNameText = + Accessors.CreateFieldRef("m_DisplayNameText"); - public readonly FastSetter SetBlueprintBuffFlags = Accessors.CreateSetter("m_Flags"); + public readonly HarmonyLib.AccessTools.FieldRef SetBlueprintItemDescriptionText = + Accessors.CreateFieldRef("m_DescriptionText"); - public readonly FastSetter SetBlueprintItemDisplayNameText = - Accessors.CreateSetter("m_DisplayNameText"); + public readonly HarmonyLib.AccessTools.FieldRef SetBlueprintItemFlavorText = + Accessors.CreateFieldRef("m_FlavorText"); - public readonly FastSetter SetBlueprintItemDescriptionText = - Accessors.CreateSetter("m_DescriptionText"); + public readonly HarmonyLib.AccessTools.FieldRef SetBlueprintItemCost = Accessors.CreateFieldRef("m_Cost"); - public readonly FastSetter SetBlueprintItemFlavorText = - Accessors.CreateSetter("m_FlavorText"); + public readonly HarmonyLib.AccessTools.FieldRef SetBlueprintItemIsStackable = Accessors.CreateFieldRef("m_IsStackable"); - public readonly FastSetter SetBlueprintItemCost = Accessors.CreateSetter("m_Cost"); + public readonly HarmonyLib.AccessTools.FieldRef GetBlueprintItemEnchantmentEnchantName = + Accessors.CreateFieldRef("m_EnchantName"); - public readonly FastSetter SetBlueprintItemIsStackable = Accessors.CreateSetter("m_IsStackable"); + public readonly HarmonyLib.AccessTools.FieldRef SetBlueprintItemEnchantmentEnchantName = + Accessors.CreateFieldRef("m_EnchantName"); - public readonly FastGetter GetBlueprintItemEnchantmentEnchantName = - Accessors.CreateGetter("m_EnchantName"); + public readonly HarmonyLib.AccessTools.FieldRef GetBlueprintItemEnchantmentDescription = + Accessors.CreateFieldRef("m_Description"); - public readonly FastSetter SetBlueprintItemEnchantmentEnchantName = - Accessors.CreateSetter("m_EnchantName"); + public readonly HarmonyLib.AccessTools.FieldRef SetBlueprintItemEnchantmentDescription = + Accessors.CreateFieldRef("m_Description"); - public readonly FastGetter GetBlueprintItemEnchantmentDescription = - Accessors.CreateGetter("m_Description"); + public readonly HarmonyLib.AccessTools.FieldRef SetBlueprintItemEnchantmentPrefix = + Accessors.CreateFieldRef("m_Prefix"); - public readonly FastSetter SetBlueprintItemEnchantmentDescription = - Accessors.CreateSetter("m_Description"); + public readonly HarmonyLib.AccessTools.FieldRef SetBlueprintItemEnchantmentSuffix = + Accessors.CreateFieldRef("m_Suffix"); - public readonly FastSetter SetBlueprintItemEnchantmentPrefix = - Accessors.CreateSetter("m_Prefix"); + public readonly HarmonyLib.AccessTools.FieldRef SetBlueprintItemEnchantmentEnchantmentCost = + Accessors.CreateFieldRef("m_EnchantmentCost"); - public readonly FastSetter SetBlueprintItemEnchantmentSuffix = - Accessors.CreateSetter("m_Suffix"); + public readonly HarmonyLib.AccessTools.FieldRef SetBlueprintItemEnchantmentEnchantmentIdentifyDC = + Accessors.CreateFieldRef("m_IdentifyDC"); - public readonly FastSetter SetBlueprintItemEnchantmentEnchantmentCost = - Accessors.CreateSetter("m_EnchantmentCost"); + public readonly HarmonyLib.AccessTools.FieldRef SetBlueprintScriptableObjectAssetGuid = + Accessors.CreateFieldRef("m_AssetGuid"); - public readonly FastSetter SetBlueprintItemEnchantmentEnchantmentIdentifyDC = - Accessors.CreateSetter("m_IdentifyDC"); + public readonly HarmonyLib.AccessTools.FieldRef SetLootItemsPackFixedItem = Accessors.CreateFieldRef("m_Item"); - public readonly FastSetter SetBlueprintScriptableObjectAssetGuid = - Accessors.CreateSetter("m_AssetGuid"); - - public readonly FastSetter SetLootItemsPackFixedItem = Accessors.CreateSetter("m_Item"); - - public readonly FastSetter SetLootItemItem = Accessors.CreateSetter("m_Item"); + public readonly HarmonyLib.AccessTools.FieldRef SetLootItemItem = Accessors.CreateFieldRef("m_Item"); public readonly FastSetter SetRuleDealDamageDamage = Accessors.CreateSetter("Damage"); - public readonly FastSetter SetBlueprintItemWeight = Accessors.CreateSetter("m_Weight"); + public readonly HarmonyLib.AccessTools.FieldRef SetBlueprintItemWeight = Accessors.CreateFieldRef("m_Weight"); public readonly FastStaticInvoker CallUIUtilityItemGetQualities = Accessors.CreateStaticInvoker(typeof(UIUtilityItem), "GetQualities"); diff --git a/CraftMagicItems/CraftMagicItemsBlueprintPatcher.cs b/CraftMagicItems/CraftMagicItemsBlueprintPatcher.cs index e0c1843..bdc401a 100644 --- a/CraftMagicItems/CraftMagicItemsBlueprintPatcher.cs +++ b/CraftMagicItems/CraftMagicItemsBlueprintPatcher.cs @@ -23,7 +23,6 @@ using Kingmaker.UnitLogic.Buffs.Blueprints; using Kingmaker.View.Animation; using Kingmaker.Utility; -using Kingmaker.UnitLogic.Abilities.Components; #if PATCH21_BETA using Kingmaker.Blueprints.DirectSerialization; #else @@ -302,7 +301,7 @@ private void ApplyBuffBlueprintPatch(BlueprintBuff blueprint, BlueprintComponent blueprint.FxOnStart = new PrefabLink(); blueprint.FxOnRemove = new PrefabLink(); // Set the display name - it's hidden in the UI, but someone might find it in Bag of Tricks. - accessors.SetBlueprintUnitFactDisplayName(blueprint, new L10NString(nameId)); + accessors.SetBlueprintUnitFactDisplayName(blueprint) = new L10NString(nameId); } private string ApplyTimerBlueprintPatch(BlueprintBuff blueprint) { @@ -326,10 +325,10 @@ private string ApplyBondedItemBlueprintPatch(BlueprintBuff blueprint) { private string ApplyFeatBlueprintPatch(BlueprintFeature blueprint, Match match) { var feat = match.Groups["feat"].Value; - accessors.SetBlueprintUnitFactDisplayName(blueprint, new L10NString($"craftMagicItems-feat-{feat}-displayName")); - accessors.SetBlueprintUnitFactDescription(blueprint, new L10NString($"craftMagicItems-feat-{feat}-description")); + accessors.SetBlueprintUnitFactDisplayName(blueprint) = new L10NString($"craftMagicItems-feat-{feat}-displayName"); + accessors.SetBlueprintUnitFactDescription(blueprint) = new L10NString($"craftMagicItems-feat-{feat}-description"); var icon = Image2Sprite.Create($"{Main.ModEntry.Path}/Icons/craft-{feat}.png"); - accessors.SetBlueprintUnitFactIcon(blueprint, icon); + accessors.SetBlueprintUnitFactIcon(blueprint) = icon; #if PATCH21_BETA var prerequisite = SerializedScriptableObject.CreateInstance(); #else @@ -362,7 +361,7 @@ private string ApplySpellItemBlueprintPatch(BlueprintItemEquipmentUsable bluepri blueprint.DC = 10 + blueprint.SpellLevel * 3 / 2; } - accessors.SetBlueprintItemEquipmentUsableCost(blueprint, 0); // Allow the game to auto-calculate the cost + accessors.SetBlueprintItemCost(blueprint) = 0; // Allow the game to auto-calculate the cost // Also store the new item blueprint in our spell-to-item lookup dictionary. var itemBlueprintsForSpell = Main.FindItemBlueprintsForSpell(blueprint.Ability, blueprint.Type); if (itemBlueprintsForSpell == null || !itemBlueprintsForSpell.Contains(blueprint)) { @@ -439,14 +438,14 @@ private string ApplyRecipeItemBlueprintPatch(BlueprintItemEquipment blueprint, M if (secondEndGuid != null) { var weaponComponent = ResourcesLibrary.TryGetBlueprint(secondEndGuid); if ((weaponComponent.DamageType.Physical.Form & PhysicalDamageForm.Piercing) != 0) { - accessors.SetBlueprintItemWeight(weaponComponent, 5.0f); + accessors.SetBlueprintItemWeight(weaponComponent) = 5.0f; } else { - accessors.SetBlueprintItemWeight(weaponComponent, 0.0f); + accessors.SetBlueprintItemWeight(weaponComponent) = 0.0f; } - accessors.SetBlueprintItemShieldWeaponComponent(shield, weaponComponent); - accessors.SetBlueprintItemWeight(armorComponentClone, armorComponentClone.Weight + weaponComponent.Weight); + accessors.SetBlueprintItemShieldWeaponComponent(shield) = weaponComponent; + accessors.SetBlueprintItemWeight(armorComponentClone) = armorComponentClone.Weight + weaponComponent.Weight; } - accessors.SetBlueprintItemShieldArmorComponent(shield, armorComponentClone); + accessors.SetBlueprintItemShieldArmorComponent(shield) = armorComponentClone; } var initiallyMundane = blueprint.Enchantments.Count == 0 && blueprint.Ability == null && blueprint.ActivatableAbility == null; @@ -455,7 +454,7 @@ private string ApplyRecipeItemBlueprintPatch(BlueprintItemEquipment blueprint, M // Copy Enchantments so we leave base blueprint alone var enchantmentsCopy = blueprint.Enchantments.ToList(); if (!(blueprint is BlueprintItemShield)) { - accessors.SetBlueprintItemCachedEnchantments(blueprint, enchantmentsCopy); + accessors.SetBlueprintItemCachedEnchantments(blueprint) = enchantmentsCopy; } // Remove enchantments first, to see if we end up with an item with no abilities. var removed = new List(); @@ -499,13 +498,13 @@ private string ApplyRecipeItemBlueprintPatch(BlueprintItemEquipment blueprint, M blueprint.Charges = charges; blueprint.SpendCharges = true; blueprint.RestoreChargesOnRest = false; - accessors.SetBlueprintItemIsStackable(blueprint, true); + accessors.SetBlueprintItemIsStackable(blueprint) = true; } int weight = -1; if (match.Groups["weight"].Success) { weight = int.Parse(match.Groups["weight"].Value); - accessors.SetBlueprintItemWeight(blueprint, weight * .01f); + accessors.SetBlueprintItemWeight(blueprint) = weight * .01f; } var priceAdjust = 0; @@ -536,16 +535,16 @@ private string ApplyRecipeItemBlueprintPatch(BlueprintItemEquipment blueprint, M enchantmentsForDescription.Add(enchantment); if (blueprint is BlueprintItemArmor && guid == Main.MithralArmorEnchantmentGuid) { // Mithral equipment has half weight - accessors.SetBlueprintItemWeight(blueprint, blueprint.Weight / 2); + accessors.SetBlueprintItemWeight(blueprint) = blueprint.Weight / 2; } if (blueprint is BlueprintItemEquipmentHand) { var weaponBaseSizeChange = enchantment.GetComponent(); if (weaponBaseSizeChange != null) { sizeCategoryChange = weaponBaseSizeChange.SizeCategoryChange; if (sizeCategoryChange > 0) { - accessors.SetBlueprintItemWeight(blueprint, blueprint.Weight * 2); + accessors.SetBlueprintItemWeight(blueprint) = blueprint.Weight * 2; } else if (sizeCategoryChange < 0) { - accessors.SetBlueprintItemWeight(blueprint, blueprint.Weight / 2); + accessors.SetBlueprintItemWeight(blueprint) = blueprint.Weight / 2; } } } @@ -561,8 +560,8 @@ private string ApplyRecipeItemBlueprintPatch(BlueprintItemEquipment blueprint, M if (match.Groups["material"].Success) { Enum.TryParse(match.Groups["material"].Value, out material); if (blueprint is BlueprintItemWeapon weapon) { - accessors.SetBlueprintItemWeaponDamageType(weapon, TraverseCloneAndSetField(weapon.DamageType, "Physical.Material", material.ToString())); - accessors.SetBlueprintItemWeaponOverrideDamageType(weapon, true); + accessors.SetBlueprintItemWeaponDamageType(weapon) = TraverseCloneAndSetField(weapon.DamageType, "Physical.Material", material.ToString()); + accessors.SetBlueprintItemWeaponOverrideDamageType(weapon) = true; var materialGuid = PhysicalDamageMaterialEnchantments[material]; var enchantment = ResourcesLibrary.TryGetBlueprint(materialGuid); enchantmentsCopy.Add(enchantment); @@ -570,7 +569,7 @@ private string ApplyRecipeItemBlueprintPatch(BlueprintItemEquipment blueprint, M enchantmentsForDescription.Add(enchantment); if (material == PhysicalDamageMaterial.Silver) { // PhysicalDamageMaterial.Silver is really Mithral, and Mithral equipment has half weight - accessors.SetBlueprintItemWeight(blueprint, blueprint.Weight / 2); + accessors.SetBlueprintItemWeight(blueprint) = blueprint.Weight / 2; } } } @@ -582,11 +581,11 @@ private string ApplyRecipeItemBlueprintPatch(BlueprintItemEquipment blueprint, M // Copy icon from a different item var copyFromBlueprint = visual == "null" ? null : ResourcesLibrary.TryGetBlueprint(visual); var iconSprite = copyFromBlueprint == null ? null : copyFromBlueprint.Icon; - accessors.SetBlueprintItemIcon(blueprint, iconSprite); + accessors.SetBlueprintItemIcon(blueprint) = iconSprite; if (equipmentHand != null && copyFromBlueprint is BlueprintItemEquipmentHand srcEquipmentHand) { - accessors.SetBlueprintItemEquipmentHandVisualParameters(equipmentHand, srcEquipmentHand.VisualParameters); + accessors.SetBlueprintItemEquipmentHandVisualParameters(equipmentHand) = srcEquipmentHand.VisualParameters; } else if (blueprint is BlueprintItemArmor armor && copyFromBlueprint is BlueprintItemArmor srcArmor) { - accessors.SetBlueprintItemArmorVisualParameters(armor, srcArmor.VisualParameters); + accessors.SetBlueprintItemArmorVisualParameters(armor) = srcArmor.VisualParameters; } } @@ -596,7 +595,7 @@ private string ApplyRecipeItemBlueprintPatch(BlueprintItemEquipment blueprint, M WeaponAnimationStyle weaponAnimation; if (Enum.TryParse(animation, out weaponAnimation)) { if (equipmentHand != null) { - accessors.SetBlueprintItemEquipmentWeaponAnimationStyle(equipmentHand.VisualParameters, weaponAnimation); + accessors.SetBlueprintItemEquipmentWeaponAnimationStyle(equipmentHand.VisualParameters) = weaponAnimation; } } } @@ -629,13 +628,13 @@ private string ApplyRecipeItemBlueprintPatch(BlueprintItemEquipment blueprint, M string name = null; if (match.Groups["name"].Success) { name = match.Groups["name"].Value; - accessors.SetBlueprintItemDisplayNameText(blueprint, new FakeL10NString(name)); + accessors.SetBlueprintItemDisplayNameText(blueprint) = new FakeL10NString(name); } string nameId = null; if (name == null && match.Groups["nameId"].Success) { nameId = match.Groups["nameId"].Value; - accessors.SetBlueprintItemDisplayNameText(blueprint, new L10NString(nameId)); + accessors.SetBlueprintItemDisplayNameText(blueprint) = new L10NString(nameId); } string descriptionId = null; @@ -656,16 +655,16 @@ private string ApplyRecipeItemBlueprintPatch(BlueprintItemEquipment blueprint, M skipped.Clear(); } if (descriptionId != null) { - accessors.SetBlueprintItemDescriptionText(blueprint, new L10NString(descriptionId)); - accessors.SetBlueprintItemFlavorText(blueprint, new L10NString("")); + accessors.SetBlueprintItemDescriptionText(blueprint) = new L10NString(descriptionId); + accessors.SetBlueprintItemFlavorText(blueprint) = new L10NString(""); } else if ((blueprint is BlueprintItemShield || Main.GetItemType(blueprint) != ItemsFilter.ItemType.Shield) && (!DoesBlueprintShowEnchantments(blueprint) || enchantmentsForDescription.Count != skipped.Count || removed.Count > 0)) { - accessors.SetBlueprintItemDescriptionText(blueprint, - Main.BuildCustomRecipeItemDescription(blueprint, enchantmentsForDescription, skipped, removed, replaceAbility, ability, casterLevel, perDay)); - accessors.SetBlueprintItemFlavorText(blueprint, new L10NString("")); + accessors.SetBlueprintItemDescriptionText(blueprint) = + Main.BuildCustomRecipeItemDescription(blueprint, enchantmentsForDescription, skipped, removed, replaceAbility, ability, casterLevel, perDay); + accessors.SetBlueprintItemFlavorText(blueprint) = new L10NString(""); } - accessors.SetBlueprintItemCost(blueprint, Main.RulesRecipeItemCost(blueprint) + priceDelta); + accessors.SetBlueprintItemCost(blueprint) = Main.RulesRecipeItemCost(blueprint) + priceDelta; return BuildCustomRecipeItemGuid(blueprint.AssetGuid, enchantmentIds, removedIds.Count > 0 ? removedIds.ToArray() : null, name, ability, activatableAbility, charges, weight, material, visual, animation, casterLevel, spellLevel, perDay, nameId, descriptionId, secondEndGuid, priceAdjust); } @@ -736,10 +735,10 @@ private T TraverseCloneAndSetField(T original, string field, string value) wh var clone = CloneObject(original); var fieldNameEnd = field.IndexOf('.'); if (fieldNameEnd < 0) { - var fieldAccess = Harmony12.Traverse.Create(clone).Field(field); + var fieldAccess = HarmonyLib.Traverse.Create(clone).Field(field); if (!fieldAccess.FieldExists()) { throw new Exception( - $"Field {field} does not exist on original of type {clone.GetType().FullName}, available fields: {string.Join(", ", Harmony12.Traverse.Create(clone).Fields())}"); + $"Field {field} does not exist on original of type {clone.GetType().FullName}, available fields: {string.Join(", ", HarmonyLib.Traverse.Create(clone).Fields())}"); } if (value == "null") { @@ -762,10 +761,10 @@ private T TraverseCloneAndSetField(T original, string field, string value) wh var remainingFields = field.Substring(fieldNameEnd + 1); var arrayPos = thisField.IndexOf('['); if (arrayPos < 0) { - var fieldAccess = Harmony12.Traverse.Create(clone).Field(thisField); + var fieldAccess = HarmonyLib.Traverse.Create(clone).Field(thisField); if (!fieldAccess.FieldExists()) { throw new Exception( - $"Field {thisField} does not exist on original of type {clone.GetType().FullName}, available fields: {string.Join(", ", Harmony12.Traverse.Create(clone).Fields())}"); + $"Field {thisField} does not exist on original of type {clone.GetType().FullName}, available fields: {string.Join(", ", HarmonyLib.Traverse.Create(clone).Fields())}"); } if (fieldAccess.GetValueType().IsArray) { @@ -776,10 +775,10 @@ private T TraverseCloneAndSetField(T original, string field, string value) wh } else { var index = int.Parse(new string(thisField.Skip(arrayPos + 1).TakeWhile(char.IsDigit).ToArray())); thisField = field.Substring(0, arrayPos); - var fieldAccess = Harmony12.Traverse.Create(clone).Field(thisField); + var fieldAccess = HarmonyLib.Traverse.Create(clone).Field(thisField); if (!fieldAccess.FieldExists()) { throw new Exception( - $"Field {thisField} does not exist on original of type {clone.GetType().FullName}, available fields: {string.Join(", ", Harmony12.Traverse.Create(clone).Fields())}"); + $"Field {thisField} does not exist on original of type {clone.GetType().FullName}, available fields: {string.Join(", ", HarmonyLib.Traverse.Create(clone).Fields())}"); } if (!fieldAccess.GetValueType().IsArray) { @@ -850,11 +849,11 @@ private string ApplyItemEnchantmentBlueprintPatch(BlueprintScriptableObject blue if (match.Groups["nameId"].Success) { nameId = match.Groups["nameId"].Value; if (enchantment != null) { - accessors.SetBlueprintItemEnchantmentEnchantName(enchantment, new L10NString(nameId)); + accessors.SetBlueprintItemEnchantmentEnchantName(enchantment) = new L10NString(nameId); } else if (feature != null) { - accessors.SetBlueprintUnitFactDisplayName(feature, new L10NString(nameId)); + accessors.SetBlueprintUnitFactDisplayName(feature) = new L10NString(nameId); } else if (buff != null) { - accessors.SetBlueprintUnitFactDisplayName(buff, new L10NString(nameId)); + accessors.SetBlueprintUnitFactDisplayName(buff) = new L10NString(nameId); } } @@ -862,13 +861,13 @@ private string ApplyItemEnchantmentBlueprintPatch(BlueprintScriptableObject blue if (match.Groups["descriptionId"].Success) { descriptionId = match.Groups["descriptionId"].Value; if (enchantment != null) { - accessors.SetBlueprintItemEnchantmentDescription(enchantment, new L10NString(descriptionId)); + accessors.SetBlueprintItemEnchantmentDescription(enchantment) = new L10NString(descriptionId); } else if (feature != null) { - accessors.SetBlueprintUnitFactDescription(feature, new L10NString(descriptionId)); + accessors.SetBlueprintUnitFactDescription(feature) = new L10NString(descriptionId); } else if (buff != null) { - accessors.SetBlueprintUnitFactDescription(buff, new L10NString(descriptionId)); + accessors.SetBlueprintUnitFactDescription(buff) = new L10NString(descriptionId); } else if (ability != null) { - accessors.SetBlueprintUnitFactDescription(ability, new L10NString(descriptionId)); + accessors.SetBlueprintUnitFactDescription(ability) = new L10NString(descriptionId); } } diff --git a/CraftMagicItems/CustomBlueprintBuilder.cs b/CraftMagicItems/CustomBlueprintBuilder.cs index 5da90f5..9a5206d 100644 --- a/CraftMagicItems/CustomBlueprintBuilder.cs +++ b/CraftMagicItems/CustomBlueprintBuilder.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Reflection; using System.Text.RegularExpressions; using Kingmaker.Blueprints; #if !PATCH21_BETA @@ -102,7 +100,7 @@ private static BlueprintScriptableObject PatchBlueprint(string assetId, Blueprin } #endif // Insert patched blueprint into ResourcesLibrary under the new GUID. - Main.Accessors.SetBlueprintScriptableObjectAssetGuid(blueprint, newAssetId); + Main.Accessors.SetBlueprintScriptableObjectAssetGuid(blueprint) = newAssetId; if (ResourcesLibrary.LibraryObject.BlueprintsByAssetId != null) { ResourcesLibrary.LibraryObject.BlueprintsByAssetId[newAssetId] = blueprint; } @@ -127,19 +125,7 @@ public static string AssetGuidWithoutMatch(string assetGuid, Match match = null) } // This patch is generic, and makes custom blueprints fall back to their initial version. - [Harmony12.HarmonyPatch(typeof(ResourcesLibrary), "TryGetBlueprint")] - // ReSharper disable once UnusedMember.Local - private static class ResourcesLibraryTryGetBlueprintFallbackPatch { - // ReSharper disable once UnusedMember.Local - private static MethodBase TargetMethod() { - // ResourcesLibrary.TryGetBlueprint has two definitions which only differ by return type :( - var allMethods = typeof(ResourcesLibrary).GetMethods(); - return allMethods.Single(info => info.Name == "TryGetBlueprint" && info.ReturnType == typeof(BlueprintScriptableObject)); - } - - // ReSharper disable once UnusedMember.Local - [Harmony12.HarmonyPriority(Harmony12.Priority.First)] - // ReSharper disable once InconsistentNaming + public static class ResourcesLibraryTryGetBlueprintFallbackPatch { private static void Postfix(string assetId, ref BlueprintScriptableObject __result) { if (__result == null && assetId.Length > VanillaAssetIdLength) { // Failed to load custom blueprint - return the original. @@ -149,26 +135,13 @@ private static void Postfix(string assetId, ref BlueprintScriptableObject __resu } } - [Harmony12.HarmonyPatch(typeof(ResourcesLibrary), "TryGetBlueprint")] - // ReSharper disable once UnusedMember.Local - private static class ResourcesLibraryTryGetBlueprintModPatch { - // ReSharper disable once UnusedMember.Local - private static MethodBase TargetMethod() { - // ResourcesLibrary.TryGetBlueprint has two definitions which only differ by return type :( - var allMethods = typeof(ResourcesLibrary).GetMethods(); - return allMethods.Single(info => info.Name == "TryGetBlueprint" && info.ReturnType == typeof(BlueprintScriptableObject)); - } - - // ReSharper disable once UnusedMember.Local + public static class ResourcesLibraryTryGetBlueprintModPatch { private static void Prefix(ref string assetId) { // Perform any backward compatibility substitutions for (var index = 0; index < substitutions.Length; index ++) { assetId = assetId.Replace(substitutions[index].oldGuid, substitutions[index].newGuid); } } - - // ReSharper disable once UnusedMember.Local - // ReSharper disable once InconsistentNaming private static void Postfix(string assetId, ref BlueprintScriptableObject __result) { if (__result != null && assetId != __result.AssetGuid) { __result = PatchBlueprint(assetId, __result); diff --git a/CraftMagicItems/L10NString.cs b/CraftMagicItems/L10NString.cs index a012823..b242d7a 100644 --- a/CraftMagicItems/L10NString.cs +++ b/CraftMagicItems/L10NString.cs @@ -18,7 +18,7 @@ public L10NString(string key) { LocalizationManager.CurrentPack.Strings[key] = result; } } - Harmony12.Traverse.Create(this).Field("m_Key").SetValue(key); + HarmonyLib.Traverse.Create(this).Field("m_Key").SetValue(key); } } @@ -27,10 +27,10 @@ public class FakeL10NString : LocalizedString { public FakeL10NString(string fakeValue) { this.fakeValue = fakeValue; - Harmony12.Traverse.Create(this).Field("m_Key").SetValue(fakeValue); + HarmonyLib.Traverse.Create(this).Field("m_Key").SetValue(fakeValue); } - [Harmony12.HarmonyPatch(typeof(LocalizedString), "LoadString")] + [HarmonyLib.HarmonyPatch(typeof(LocalizedString), "LoadString")] // ReSharper disable once UnusedMember.Local private static class LocalizedStringLoadStringPatch { // ReSharper disable once UnusedMember.Local @@ -44,7 +44,7 @@ private static bool Prefix(LocalizedString __instance, ref string __result) { } } - [Harmony12.HarmonyPatch(typeof(LocalizedString), "IsSet")] + [HarmonyLib.HarmonyPatch(typeof(LocalizedString), "IsSet")] // ReSharper disable once UnusedMember.Local private static class LocalizedStringIsSetPatch { // ReSharper disable once UnusedMember.Local @@ -58,7 +58,7 @@ private static bool Prefix(LocalizedString __instance, ref bool __result) { } } - [Harmony12.HarmonyPatch(typeof(LocalizedString), "IsEmpty")] + [HarmonyLib.HarmonyPatch(typeof(LocalizedString), "IsEmpty")] // ReSharper disable once UnusedMember.Local private static class LocalizedStringIsEmptyPatch { // ReSharper disable once UnusedMember.Local diff --git a/CraftMagicItems/L10n.cs b/CraftMagicItems/L10n.cs index f7789bd..7ab82a6 100644 --- a/CraftMagicItems/L10n.cs +++ b/CraftMagicItems/L10n.cs @@ -69,8 +69,7 @@ public static void SetEnabled(bool newEnabled) { } } - [Harmony12.HarmonyPatch(typeof(LocalizationManager))] - [Harmony12.HarmonyPatch("CurrentLocale", Harmony12.MethodType.Setter)] + [HarmonyLib.HarmonyPatch(typeof(LocalizationManager), "CurrentLocale", HarmonyLib.MethodType.Setter)] private static class LocalizationManagerCurrentLocaleSetterPatch { // ReSharper disable once UnusedMember.Local private static void Postfix() { @@ -78,7 +77,7 @@ private static void Postfix() { } } - [Harmony12.HarmonyPatch(typeof(MainMenu), "Start")] + [HarmonyLib.HarmonyPatch(typeof(MainMenu), "Start")] private static class MainMenuStartPatch { private static void Prefix() { // Kingmaker Mod Loader doesn't appear to patch the game before LocalizationManager.CurrentLocale has been set. @@ -89,7 +88,7 @@ private static void Prefix() { } #if PATCH21 - [Harmony12.HarmonyPatch(typeof(MainMenuUiContext), "Initialize")] + [HarmonyLib.HarmonyPatch(typeof(MainMenuUiContext), "Initialize")] private static class MainMenuUiContextInitializePatch { private static void Prefix() { // Kingmaker Mod Loader doesn't appear to patch the game before LocalizationManager.CurrentLocale has been set. diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 878b387..2511c6e 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -120,7 +120,6 @@ public static class Main { private const string MartialWeaponProficiencies = "203992ef5b35c864390b4e4a1e200629"; private const string ChannelEnergyFeatureGuid = "a79013ff4bcd4864cb669622a29ddafb"; private const string ShieldMasterGuid = "dbec636d84482944f87435bd31522fcc"; - private const string ProdigiousTwoWeaponFightingGuid = "ddba046d03074037be18ad33ea462028"; private const string TwoWeaponFightingBasicMechanicsGuid = "6948b379c0562714d9f6d58ccbfa8faa"; private const string LongshankBaneGuid = "92a1f5db1a03c5b468828c25dd375806"; private const string WeaponLightShieldGuid = "1fd965e522502fe479fdd423cca07684"; @@ -202,6 +201,45 @@ public IkPatch(string uuid, float x, float y, float z) { public float m_x, m_y, m_z; } + struct MethodPatch { + public MethodPatch(MethodBase original, HarmonyLib.HarmonyMethod prefix = null, HarmonyLib.HarmonyMethod postfix = null) { + m_original = original; + m_prefix = prefix; + m_postfix = postfix; + } + public MethodInfo Patch(HarmonyLib.Harmony instance) { + return instance.Patch(m_original, m_prefix, m_postfix); + } + public bool MatchOriginal(MethodBase method) { + return m_original != null & m_original == method; + } + public bool MatchPrefix(MethodBase method) { + return m_prefix != null && m_prefix.method == method; + } + public bool MatchPostfix(MethodBase method) { + return m_postfix != null && m_postfix.method == method; + } + MethodBase m_original; + HarmonyLib.HarmonyMethod m_prefix; + HarmonyLib.HarmonyMethod m_postfix; + } + + private static readonly MethodPatch[] MethodPatchList = + { + new MethodPatch( + typeof(ResourcesLibrary).GetMethods().Single(m => m.Name == "TryGetBlueprint" && m.ReturnType == typeof(BlueprintScriptableObject)), + postfix: new HarmonyLib.HarmonyMethod(typeof(CustomBlueprintBuilder.ResourcesLibraryTryGetBlueprintFallbackPatch).GetMethod("Postfix", BindingFlags.NonPublic | BindingFlags.Static)) { priority = HarmonyLib.Priority.First }), + new MethodPatch(typeof(ResourcesLibrary).GetMethods().Single(m => m.Name == "TryGetBlueprint" && m.ReturnType == typeof(BlueprintScriptableObject)), + new HarmonyLib.HarmonyMethod(typeof(CustomBlueprintBuilder.ResourcesLibraryTryGetBlueprintModPatch).GetMethod("Prefix", BindingFlags.NonPublic | BindingFlags.Static)), + new HarmonyLib.HarmonyMethod(typeof(CustomBlueprintBuilder.ResourcesLibraryTryGetBlueprintModPatch).GetMethod("Postfix", BindingFlags.NonPublic | BindingFlags.Static))), + new MethodPatch( + typeof(Game).GetMethod("OnAreaLoaded", BindingFlags.NonPublic | BindingFlags.Instance), + postfix: new HarmonyLib.HarmonyMethod(typeof(GameOnAreaLoadedPatch).GetMethod("Postfix", BindingFlags.NonPublic | BindingFlags.Static))), + new MethodPatch( + typeof(Player).GetMethod("PostLoad"), + postfix: new HarmonyLib.HarmonyMethod(typeof(PlayerPostLoadPatch).GetMethod("Postfix", BindingFlags.NonPublic | BindingFlags.Static))), + }; + private static readonly IkPatch[] IkPatchList = { new IkPatch("a6f7e3dc443ff114ba68b4648fd33e9f", 0.00f, -0.10f, 0.01f), // dueling sword new IkPatch("13fa38737d46c9e4abc7f4d74aaa59c3", 0.00f, -0.36f, 0.00f), // tongi @@ -221,7 +259,7 @@ public IkPatch(string uuid, float x, float y, float z) { public static CustomLootItem[] CustomLootItems; private static bool modEnabled = true; - private static Harmony12.HarmonyInstance harmonyInstance; + private static HarmonyLib.Harmony harmonyInstance; private static CraftMagicItemsBlueprintPatcher blueprintPatcher; private static OpenSection currentSection = OpenSection.CraftMagicItemsSection; private static readonly Dictionary SelectedIndex = new Dictionary(); @@ -260,30 +298,40 @@ public IkPatch(string uuid, float x, float y, float z) { /** * Patch all HarmonyPatch classes in the assembly, starting in the order of the methods named in methodNameOrder, and the rest after that. */ - private static void PatchAllOrdered(params string[] methodNameOrder) { - var allAttributes = Assembly.GetExecutingAssembly().GetTypes() - .Select(type => new {type, methods = Harmony12.HarmonyMethodExtensions.GetHarmonyMethods(type)}) - .Where(pair => pair.methods != null && pair.methods.Count > 0) - .Select(pair => new {pair.type, attributes = Harmony12.HarmonyMethod.Merge(pair.methods)}) - .OrderBy(pair => methodNameOrder - .Select((name, index) => new {name, index}) - .FirstOrDefault(nameIndex => nameIndex.name.Equals(pair.attributes.methodName))?.index - ?? methodNameOrder.Length) - ; - foreach (var pair in allAttributes) { - new Harmony12.PatchProcessor(harmonyInstance, pair.type, pair.attributes).Patch(); + private static void PatchAllOrdered(params MethodPatch[] orderedMethods) { + foreach (var method in orderedMethods) { + method.Patch(harmonyInstance); } + harmonyInstance.PatchAll(Assembly.GetExecutingAssembly()); } /** * Unpatch all HarmonyPatch classes for harmonyInstance, except the ones whose method names match exceptMethodName */ - private static void UnpatchAllExcept(params string[] exceptMethodName) { + private static void UnpatchAllExcept(params MethodPatch[] exceptMethods) { if (harmonyInstance != null) { try { foreach (var method in harmonyInstance.GetPatchedMethods().ToArray()) { - if (!exceptMethodName.Contains(method.Name) && harmonyInstance.GetPatchInfo(method).Owners.Contains(harmonyInstance.Id)) { - harmonyInstance.Unpatch(method, Harmony12.HarmonyPatchType.All, harmonyInstance.Id); + var patchInfo = HarmonyLib.Harmony.GetPatchInfo(method); + if (patchInfo.Owners.Contains(harmonyInstance.Id)) { + var methodPatches = exceptMethods.Where(m => m.MatchOriginal(method)); + if (methodPatches.Count() > 0) { + foreach (var patch in patchInfo.Prefixes) { + if (!methodPatches.Any(m => m.MatchPrefix(patch.PatchMethod))) { + harmonyInstance.Unpatch(method, patch.PatchMethod); + } + } + foreach (var patch in patchInfo.Postfixes) { + if (!methodPatches.Any(m => m.MatchPostfix(patch.PatchMethod))) { + harmonyInstance.Unpatch(method, patch.PatchMethod); + } + } + harmonyInstance.Unpatch(method, HarmonyLib.HarmonyPatchType.Finalizer, harmonyInstance.Id); + harmonyInstance.Unpatch(method, HarmonyLib.HarmonyPatchType.Transpiler, harmonyInstance.Id); + harmonyInstance.Unpatch(method, HarmonyLib.HarmonyPatchType.ReversePatch, harmonyInstance.Id); + } else { + harmonyInstance.Unpatch(method, HarmonyLib.HarmonyPatchType.All, harmonyInstance.Id); + } } } } catch (Exception e) { @@ -304,9 +352,9 @@ private static void Load(UnityModManager.ModEntry modEntry) { modEntry.OnToggle = OnToggle; modEntry.OnGUI = OnGui; CustomBlueprintBuilder.InitialiseBlueprintRegex(CraftMagicItemsBlueprintPatcher.BlueprintRegex); - harmonyInstance = Harmony12.HarmonyInstance.Create("kingmaker.craftMagicItems"); + harmonyInstance = new HarmonyLib.Harmony("kingmaker.craftMagicItems"); // Patch the recovery methods first. - PatchAllOrdered("TryGetBlueprint", "PostLoad", "OnAreaLoaded"); + PatchAllOrdered(MethodPatchList); Accessors = new CraftMagicItemsAccessors(); blueprintPatcher = new CraftMagicItemsBlueprintPatcher(Accessors, modEnabled); } catch (Exception e) { @@ -314,7 +362,7 @@ private static void Load(UnityModManager.ModEntry modEntry) { modEnabled = false; CustomBlueprintBuilder.Enabled = false; // Unpatch everything except methods involved in recovering a save when mod is disabled. - UnpatchAllExcept("TryGetBlueprint", "PostLoad", "OnAreaLoaded"); + UnpatchAllExcept(MethodPatchList); throw; } } @@ -2143,7 +2191,7 @@ private static void RenderFeatReassignmentSection() { var addedFeature = caster.Descriptor.Progression.Features.AddFeature(learnFeat); addedFeature.Source = feature.Source; - var mFacts = Accessors.GetFeatureCollectionFacts(caster.Descriptor.Progression.Features); + var mFacts = caster.Descriptor.Progression.Features.RawFacts; if (removedFeatIndex < mFacts.Count) { // Move the new feat to the place in the list originally occupied by the removed one. mFacts.Remove(addedFeature); @@ -3053,7 +3101,7 @@ private static bool ReverseEngineerEnchantmentCost(BlueprintItemEquipment bluepr return true; } - [Harmony12.HarmonyPatch(typeof(MainMenu), "Start")] + [HarmonyLib.HarmonyPatch(typeof(MainMenu), "Start")] private static class MainMenuStartPatch { private static bool mainMenuStarted; @@ -3215,28 +3263,28 @@ private static void AddAllCraftingFeats() { } } - [Harmony12.HarmonyPatch(typeof(TwoWeaponFightingAttackPenalty), "OnEventAboutToTrigger", new Type[] { typeof(RuleCalculateAttackBonusWithoutTarget) })] + [HarmonyLib.HarmonyPatch(typeof(TwoWeaponFightingAttackPenalty), "OnEventAboutToTrigger", new Type[] { typeof(RuleCalculateAttackBonusWithoutTarget) })] private static class TwoWeaponFightingAttackPenaltyOnEventAboutToTriggerPatch { static public BlueprintFeature ShieldMaster; static MethodInfo methodToFind; private static bool Prepare() { try { - methodToFind = Harmony12.AccessTools.Property(typeof(ItemEntityWeapon), nameof(ItemEntityWeapon.IsShield)).GetGetMethod(); + methodToFind = HarmonyLib.AccessTools.Property(typeof(ItemEntityWeapon), nameof(ItemEntityWeapon.IsShield)).GetGetMethod(); } catch (Exception ex) { Main.ModEntry.Logger.Log($"Error Preparing: {ex.Message}"); return false; } return true; } - private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator il) { + private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator il) { Label start = il.DefineLabel(); - yield return new Harmony12.CodeInstruction(OpCodes.Ldarg_0); - yield return new Harmony12.CodeInstruction(OpCodes.Ldarg_1); - yield return new Harmony12.CodeInstruction(OpCodes.Call, new Func(CheckShieldMaster).Method); - yield return new Harmony12.CodeInstruction(OpCodes.Brfalse_S, start); - yield return new Harmony12.CodeInstruction(OpCodes.Ret); + yield return new HarmonyLib.CodeInstruction(OpCodes.Ldarg_0); + yield return new HarmonyLib.CodeInstruction(OpCodes.Ldarg_1); + yield return new HarmonyLib.CodeInstruction(OpCodes.Call, new Func(CheckShieldMaster).Method); + yield return new HarmonyLib.CodeInstruction(OpCodes.Brfalse_S, start); + yield return new HarmonyLib.CodeInstruction(OpCodes.Ret); var skip = 0; - Harmony12.CodeInstruction prev = instructions.First(); + HarmonyLib.CodeInstruction prev = instructions.First(); prev.labels.Add(start); foreach (var inst in instructions.Skip(1)) { if (prev.opcode == OpCodes.Ldloc_1 && inst.opcode == OpCodes.Callvirt && inst.operand as MethodInfo == methodToFind) { @@ -3319,7 +3367,7 @@ private static void PatchBlueprints() { var shieldMaster = ResourcesLibrary.TryGetBlueprint(ShieldMasterGuid); var twoWeaponFighting = ResourcesLibrary.TryGetBlueprint(TwoWeaponFightingBasicMechanicsGuid); TwoWeaponFightingAttackPenaltyOnEventAboutToTriggerPatch.ShieldMaster = shieldMaster; - Accessors.SetBlueprintUnitFactDisplayName(twoWeaponFighting, new L10NString("e32ce256-78dc-4fd0-bf15-21f9ebdf9921")); + Accessors.SetBlueprintUnitFactDisplayName(twoWeaponFighting) = new L10NString("e32ce256-78dc-4fd0-bf15-21f9ebdf9921"); for (int i = 0; i < shieldMaster.ComponentsArray.Length; i++) { if (shieldMaster.ComponentsArray[i] is ShieldMaster component) { @@ -3330,15 +3378,15 @@ private static void PatchBlueprints() { } var lightShield = ResourcesLibrary.TryGetBlueprint(WeaponLightShieldGuid); - Accessors.SetBlueprintItemBaseDamage(lightShield, new DiceFormula(1, DiceType.D3)); + Accessors.SetBlueprintItemBaseDamage(lightShield) = new DiceFormula(1, DiceType.D3); var heavyShield = ResourcesLibrary.TryGetBlueprint(WeaponHeavyShieldGuid); - Accessors.SetBlueprintItemBaseDamage(heavyShield, new DiceFormula(1, DiceType.D4)); + Accessors.SetBlueprintItemBaseDamage(heavyShield) = new DiceFormula(1, DiceType.D4); for (int i = 0; i < ItemEnchantmentGuids.Length; i += 2) { var source = ResourcesLibrary.TryGetBlueprint(ItemEnchantmentGuids[i]); var dest = ResourcesLibrary.TryGetBlueprint(ItemEnchantmentGuids[i + 1]); - Accessors.SetBlueprintItemEnchantmentEnchantName(dest, Accessors.GetBlueprintItemEnchantmentEnchantName(source)); - Accessors.SetBlueprintItemEnchantmentDescription(dest, Accessors.GetBlueprintItemEnchantmentDescription(source)); + Accessors.SetBlueprintItemEnchantmentEnchantName(dest) = Accessors.GetBlueprintItemEnchantmentEnchantName(source); + Accessors.SetBlueprintItemEnchantmentDescription(dest) = Accessors.GetBlueprintItemEnchantmentDescription(source); } var longshankBane = ResourcesLibrary.TryGetBlueprint(LongshankBaneGuid); @@ -3382,7 +3430,7 @@ private static void InitialiseMod() { } } - [Harmony12.HarmonyPriority(Harmony12.Priority.Last)] + [HarmonyLib.HarmonyPriority(HarmonyLib.Priority.Last)] public static void Postfix() { if (!mainMenuStarted) { mainMenuStarted = true; @@ -3397,6 +3445,8 @@ public static void ModEnabledChanged() { SustenanceEnchantment.MainMenuStartPatch.Postfix(); WildEnchantment.MainMenuStartPatch.Postfix(); CreateQuiverAbility.MainMenuStartPatch.Postfix(); + InitialiseMod(); + return; } if (!modEnabled) { @@ -3408,8 +3458,10 @@ public static void ModEnabledChanged() { EnchantmentIdToItem.Clear(); EnchantmentIdToCost.Clear(); EnchantmentIdToRecipe.Clear(); + UnpatchAllExcept(MethodPatchList); } else if (mainMenuStarted) { // If the mod is enabled and we're past the Start of main menu, (re-)initialise. + PatchAllOrdered(); InitialiseMod(); } L10n.SetEnabled(modEnabled); @@ -3417,9 +3469,9 @@ public static void ModEnabledChanged() { } #if PATCH21 - [Harmony12.HarmonyPatch(typeof(MainMenuUiContext), "Initialize")] + [HarmonyLib.HarmonyPatch(typeof(MainMenuUiContext), "Initialize")] private static class MainMenuUiContextInitializePatch { - [Harmony12.HarmonyPriority(Harmony12.Priority.Last)] + [HarmonyLib.HarmonyPriority(HarmonyLib.Priority.Last)] private static void Postfix() { MainMenuStartPatch.Postfix(); } @@ -3428,7 +3480,7 @@ private static void Postfix() { #if !PATCH21 // Fix issue in Owlcat's UI - ActionBarManager.Update does not refresh the Groups (spells/Actions/Belt) - [Harmony12.HarmonyPatch(typeof(ActionBarManager), "Update")] + [HarmonyLib.HarmonyPatch(typeof(ActionBarManager), "Update")] private static class ActionBarManagerUpdatePatch { private static void Prefix(ActionBarManager __instance) { var mNeedReset = Accessors.GetActionBarManagerNeedReset(__instance); @@ -3440,7 +3492,7 @@ private static void Prefix(ActionBarManager __instance) { } #endif - [Harmony12.HarmonyPatch(typeof(BlueprintItemEquipmentUsable), "Cost", Harmony12.MethodType.Getter)] + [HarmonyLib.HarmonyPatch(typeof(BlueprintItemEquipmentUsable), "Cost", HarmonyLib.MethodType.Getter)] // ReSharper disable once UnusedMember.Local private static class BlueprintItemEquipmentUsableCostPatch { // ReSharper disable once UnusedMember.Local @@ -3467,7 +3519,7 @@ private static void Postfix(BlueprintItemEquipmentUsable __instance, ref int __r } // Load Variant spells into m_KnownSpellLevels - [Harmony12.HarmonyPatch(typeof(Spellbook), "PostLoad")] + [HarmonyLib.HarmonyPatch(typeof(Spellbook), "PostLoad")] // ReSharper disable once UnusedMember.Local private static class SpellbookPostLoadPatch { // ReSharper disable once UnusedMember.Local @@ -3491,7 +3543,7 @@ private static void Postfix(Spellbook __instance) { } // Owlcat's code doesn't correctly detect that a variant spell is in a spellList when its parent spell is. - [Harmony12.HarmonyPatch(typeof(BlueprintAbility), "IsInSpellList")] + [HarmonyLib.HarmonyPatch(typeof(BlueprintAbility), "IsInSpellList")] // ReSharper disable once UnusedMember.Global public static class BlueprintAbilityIsInSpellListPatch { // ReSharper disable once UnusedMember.Local @@ -3516,7 +3568,7 @@ public static void AddBattleLogMessage(string message, object tooltip = null, Co } #if !PATCH21 - [Harmony12.HarmonyPatch(typeof(LogDataManager.LogItemData), "UpdateSize")] + [HarmonyLib.HarmonyPatch(typeof(LogDataManager.LogItemData), "UpdateSize")] private static class LogItemDataUpdateSizePatch { // ReSharper disable once UnusedMember.Local private static bool Prefix() { @@ -3528,7 +3580,7 @@ private static bool Prefix() { // Add "pending" log items when the battle log becomes available again, so crafting messages sent when e.g. camping // in the overland map are still shown eventually. - [Harmony12.HarmonyPatch(typeof(BattleLogManager), "Initialize")] + [HarmonyLib.HarmonyPatch(typeof(BattleLogManager), "Initialize")] // ReSharper disable once UnusedMember.Local private static class BattleLogManagerInitializePatch { // ReSharper disable once UnusedMember.Local @@ -3922,7 +3974,7 @@ private static void WorkOnProjects(UnitDescriptor caster, bool returningToCapita } } - [Harmony12.HarmonyPatch(typeof(CapitalCompanionLogic), "OnFactActivate")] + [HarmonyLib.HarmonyPatch(typeof(CapitalCompanionLogic), "OnFactActivate")] // ReSharper disable once UnusedMember.Local private static class CapitalCompanionLogicOnFactActivatePatch { // ReSharper disable once UnusedMember.Local @@ -3937,7 +3989,7 @@ private static void Prefix() { } // Make characters in the party work on their crafting projects when they rest. - [Harmony12.HarmonyPatch(typeof(RestController), "ApplyRest")] + [HarmonyLib.HarmonyPatch(typeof(RestController), "ApplyRest")] // ReSharper disable once UnusedMember.Local private static class RestControllerApplyRestPatch { // ReSharper disable once UnusedMember.Local @@ -3968,13 +4020,13 @@ private static void AddToLootTables(BlueprintItem blueprint, string[] tableNames } } else if (!unitLoot.ComponentsArray.Any(component => component is LootItemsPackFixed pack && pack.Item.Item == blueprint)) { var lootItem = new LootItem(); - Accessors.SetLootItemItem(lootItem, blueprint); + Accessors.SetLootItemItem(lootItem) = blueprint; #if PATCH21_BETA var lootComponent = SerializedScriptableObject.CreateInstance(); #else var lootComponent = ScriptableObject.CreateInstance(); #endif - Accessors.SetLootItemsPackFixedItem(lootComponent, lootItem); + Accessors.SetLootItemsPackFixedItem(lootComponent) = lootItem; blueprintPatcher.EnsureComponentNameUnique(lootComponent, unitLoot.ComponentsArray); var components = unitLoot.ComponentsArray.ToList(); components.Add(lootComponent); @@ -3983,7 +4035,7 @@ private static void AddToLootTables(BlueprintItem blueprint, string[] tableNames } } if (tableCount > 0) { - Harmony12.FileLog.Log($"!!! Failed to match all loot table names for {blueprint.Name}. {tableCount} table names not found."); + HarmonyLib.FileLog.Log($"!!! Failed to match all loot table names for {blueprint.Name}. {tableCount} table names not found."); } } @@ -3992,18 +4044,14 @@ private static void UpgradeSave(Version version) { var firstTime = (version == null || version.CompareTo(lootItem.AddInVersion) < 0); var item = ResourcesLibrary.TryGetBlueprint(lootItem.AssetGuid); if (item == null) { - Harmony12.FileLog.Log($"!!! Loot item not created: {lootItem.AssetGuid}"); + HarmonyLib.FileLog.Log($"!!! Loot item not created: {lootItem.AssetGuid}"); } else { AddToLootTables(item, lootItem.LootTables, firstTime); } } } - // After loading a save, perform various backward compatibility and initialisation operations. - [Harmony12.HarmonyPatch(typeof(Player), "PostLoad")] - // ReSharper disable once UnusedMember.Local - private static class PlayerPostLoadPatch { - // ReSharper disable once UnusedMember.Local + public static class PlayerPostLoadPatch { private static void Postfix() { ItemUpgradeProjects.Clear(); ItemCreationProjects.Clear(); @@ -4069,10 +4117,7 @@ private static void Postfix() { } } - [Harmony12.HarmonyPatch(typeof(Game), "OnAreaLoaded")] - // ReSharper disable once UnusedMember.Local - private static class GameOnAreaLoadedPatch { - // ReSharper disable once UnusedMember.Local + public static class GameOnAreaLoadedPatch { private static void Postfix() { if (CustomBlueprintBuilder.DidDowngrade) { UIUtility.ShowMessageBox("Craft Magic Items is disabled. All your custom enchanted items and crafting feats have been replaced with " + @@ -4087,7 +4132,7 @@ private static void Postfix() { } } - [Harmony12.HarmonyPatch(typeof(WeaponParametersAttackBonus), "OnEventAboutToTrigger")] + [HarmonyLib.HarmonyPatch(typeof(WeaponParametersAttackBonus), "OnEventAboutToTrigger")] // ReSharper disable once UnusedMember.Local private static class WeaponParametersAttackBonusOnEventAboutToTriggerPatch { private static bool Prefix(WeaponParametersAttackBonus __instance, RuleCalculateAttackBonusWithoutTarget evt) { @@ -4100,7 +4145,7 @@ private static bool Prefix(WeaponParametersAttackBonus __instance, RuleCalculate } } - [Harmony12.HarmonyPatch(typeof(WeaponParametersDamageBonus), "OnEventAboutToTrigger", new Type[] { typeof(RuleCalculateWeaponStats) })] + [HarmonyLib.HarmonyPatch(typeof(WeaponParametersDamageBonus), "OnEventAboutToTrigger", new Type[] { typeof(RuleCalculateWeaponStats) })] // ReSharper disable once UnusedMember.Local private static class WeaponParametersDamageBonusOnEventAboutToTriggerPatch { private static bool Prefix(WeaponParametersDamageBonus __instance, RuleCalculateWeaponStats evt) { @@ -4113,7 +4158,7 @@ private static bool Prefix(WeaponParametersDamageBonus __instance, RuleCalculate } } - [Harmony12.HarmonyPatch(typeof(AttackStatReplacement), "OnEventAboutToTrigger")] + [HarmonyLib.HarmonyPatch(typeof(AttackStatReplacement), "OnEventAboutToTrigger")] // ReSharper disable once UnusedMember.Local private static class AttackStatReplacementOnEventAboutToTriggerPatch { private static bool Prefix(AttackStatReplacement __instance, RuleCalculateAttackBonusWithoutTarget evt) { @@ -4126,7 +4171,7 @@ private static bool Prefix(AttackStatReplacement __instance, RuleCalculateAttack } } - [Harmony12.HarmonyPatch(typeof(DamageGrace), "OnEventAboutToTrigger")] + [HarmonyLib.HarmonyPatch(typeof(DamageGrace), "OnEventAboutToTrigger")] // ReSharper disable once UnusedMember.Local private static class DamageGraceOnEventAboutToTriggerPatch { private static bool Prefix(DamageGrace __instance, RuleCalculateWeaponStats evt) { @@ -4139,7 +4184,7 @@ private static bool Prefix(DamageGrace __instance, RuleCalculateWeaponStats evt) } } - [Harmony12.HarmonyPatch(typeof(UIUtilityItem), "GetQualities")] + [HarmonyLib.HarmonyPatch(typeof(UIUtilityItem), "GetQualities")] // ReSharper disable once UnusedMember.Local private static class UIUtilityItemGetQualitiesPatch { // ReSharper disable once UnusedMember.Local @@ -4161,7 +4206,7 @@ private static void Postfix(ItemEntity item, ref string __result) { } } - [Harmony12.HarmonyPatch(typeof(UIUtilityItem), "FillArmorEnchantments")] + [HarmonyLib.HarmonyPatch(typeof(UIUtilityItem), "FillArmorEnchantments")] // ReSharper disable once UnusedMember.Local private static class UIUtilityItemFillArmorEnchantmentsPatch { // ReSharper disable once UnusedMember.Local @@ -4187,7 +4232,7 @@ private static void Postfix(TooltipData data, ItemEntityShield armor) { } } - [Harmony12.HarmonyPatch(typeof(UIUtilityItem), "FillEnchantmentDescription")] + [HarmonyLib.HarmonyPatch(typeof(UIUtilityItem), "FillEnchantmentDescription")] // ReSharper disable once UnusedMember.Local private static class UIUtilityItemFillEnchantmentDescriptionPatch { // ReSharper disable once UnusedMember.Local @@ -4286,7 +4331,7 @@ private static void Postfix(ItemEntity item, TooltipData data, ref string __resu } } - [Harmony12.HarmonyPatch(typeof(UIUtility), "IsMagicItem")] + [HarmonyLib.HarmonyPatch(typeof(UIUtility), "IsMagicItem")] // ReSharper disable once UnusedMember.Local private static class UIUtilityIsMagicItem { // ReSharper disable once UnusedMember.Local @@ -4297,7 +4342,7 @@ private static void Postfix(ItemEntity item, ref bool __result) { } } - [Harmony12.HarmonyPatch(typeof(ItemEntity), "VendorDescription", Harmony12.MethodType.Getter)] + [HarmonyLib.HarmonyPatch(typeof(ItemEntity), "VendorDescription", HarmonyLib.MethodType.Getter)] // ReSharper disable once UnusedMember.Local private static class ItemEntityVendorDescriptionPatch { // ReSharper disable once UnusedMember.Local @@ -4326,7 +4371,7 @@ private static bool Prefix(ItemEntity __instance, ref string __result) { // Owlcat's code doesn't filter out undamaged characters, so it will always return someone. This meant that with the "auto-cast healing" camping // option enabled on, healers would burn all their spell slots healing undamaged characters when they started resting, leaving them no spells to cast // when crafting. Change it so it returns null if the most damaged character is undamaged. - [Harmony12.HarmonyPatch(typeof(UnitUseSpellsOnRest), "GetUnitWithMaxDamage")] + [HarmonyLib.HarmonyPatch(typeof(UnitUseSpellsOnRest), "GetUnitWithMaxDamage")] // ReSharper disable once UnusedMember.Local private static class UnitUseSpellsOnRestGetUnitWithMaxDamagePatch { // ReSharper disable once UnusedMember.Local @@ -4346,7 +4391,7 @@ private static bool EquipmentEnchantmentValid(ItemEntityWeapon weapon, ItemEntit } } - [Harmony12.HarmonyPatch(typeof(WeaponEnergyDamageDice), "OnEventAboutToTrigger")] + [HarmonyLib.HarmonyPatch(typeof(WeaponEnergyDamageDice), "OnEventAboutToTrigger")] // ReSharper disable once UnusedMember.Local private static class WeaponEnergyDamageDiceOnEventAboutToTriggerPatch { // ReSharper disable once UnusedMember.Local @@ -4371,7 +4416,7 @@ private static bool Prefix(WeaponEnergyDamageDice __instance, RuleCalculateWeapo } } - [Harmony12.HarmonyPatch(typeof(WeaponEnergyBurst), "OnEventAboutToTrigger")] + [HarmonyLib.HarmonyPatch(typeof(WeaponEnergyBurst), "OnEventAboutToTrigger")] // ReSharper disable once UnusedMember.Local private static class WeaponEnergyBurstOnEventAboutToTriggerPatch { // ReSharper disable once UnusedMember.Local @@ -4392,7 +4437,7 @@ private static bool Prefix(WeaponEnergyBurst __instance, RuleDealDamage evt) { } } - [Harmony12.HarmonyPatch(typeof(WeaponExtraAttack), "OnEventAboutToTrigger")] + [HarmonyLib.HarmonyPatch(typeof(WeaponExtraAttack), "OnEventAboutToTrigger")] // ReSharper disable once UnusedMember.Local private static class WeaponExtraAttackOnEventAboutToTriggerPatch { // ReSharper disable once UnusedMember.Local @@ -4411,7 +4456,7 @@ private static bool Prefix(WeaponExtraAttack __instance, RuleCalculateAttacksCou } } - [Harmony12.HarmonyPatch(typeof(WeaponDamageAgainstAlignment), "OnEventAboutToTrigger")] + [HarmonyLib.HarmonyPatch(typeof(WeaponDamageAgainstAlignment), "OnEventAboutToTrigger")] // ReSharper disable once UnusedMember.Local private static class WeaponDamageAgainstAlignmentOnEventAboutToTriggerPatch { // ReSharper disable once UnusedMember.Local @@ -4436,7 +4481,7 @@ private static bool Prefix(WeaponDamageAgainstAlignment __instance, RulePrepareD } } - [Harmony12.HarmonyPatch(typeof(WeaponConditionalEnhancementBonus), "OnEventAboutToTrigger", new Type[] { typeof(RuleCalculateWeaponStats) })] + [HarmonyLib.HarmonyPatch(typeof(WeaponConditionalEnhancementBonus), "OnEventAboutToTrigger", new Type[] { typeof(RuleCalculateWeaponStats) })] // ReSharper disable once UnusedMember.Local private static class WeaponConditionalEnhancementBonusOnEventAboutToTriggerRuleCalculateWeaponStatsPatch { // ReSharper disable once UnusedMember.Local @@ -4471,7 +4516,7 @@ private static bool Prefix(WeaponConditionalEnhancementBonus __instance, RuleCal } } - [Harmony12.HarmonyPatch(typeof(WeaponConditionalEnhancementBonus), "OnEventAboutToTrigger", new Type[] { typeof(RuleCalculateAttackBonus) })] + [HarmonyLib.HarmonyPatch(typeof(WeaponConditionalEnhancementBonus), "OnEventAboutToTrigger", new Type[] { typeof(RuleCalculateAttackBonus) })] // ReSharper disable once UnusedMember.Local private static class WeaponConditionalEnhancementBonusOnEventAboutToTriggerRuleCalculateAttackBonusPatch { // ReSharper disable once UnusedMember.Local @@ -4502,7 +4547,7 @@ private static bool Prefix(WeaponConditionalEnhancementBonus __instance, RuleCal } } - [Harmony12.HarmonyPatch(typeof(WeaponConditionalDamageDice), "OnEventAboutToTrigger")] + [HarmonyLib.HarmonyPatch(typeof(WeaponConditionalDamageDice), "OnEventAboutToTrigger")] // ReSharper disable once UnusedMember.Local private static class WeaponConditionalDamageDiceOnEventAboutToTriggerPatch { // ReSharper disable once UnusedMember.Local @@ -4538,7 +4583,7 @@ private static bool Prefix(WeaponConditionalDamageDice __instance, RulePrepareDa } } - [Harmony12.HarmonyPatch(typeof(BrilliantEnergy), "OnEventAboutToTrigger")] + [HarmonyLib.HarmonyPatch(typeof(BrilliantEnergy), "OnEventAboutToTrigger")] // ReSharper disable once UnusedMember.Local private static class BrilliantEnergyOnEventAboutToTriggerPatch { // ReSharper disable once UnusedMember.Local @@ -4554,7 +4599,7 @@ private static bool Prefix(BrilliantEnergy __instance, RuleCalculateAC evt) { } } - [Harmony12.HarmonyPatch(typeof(MissAgainstFactOwner), "OnEventAboutToTrigger")] + [HarmonyLib.HarmonyPatch(typeof(MissAgainstFactOwner), "OnEventAboutToTrigger")] // ReSharper disable once UnusedMember.Local private static class MissAgainstFactOwnerOnEventAboutToTriggerPatch { // ReSharper disable once UnusedMember.Local @@ -4575,7 +4620,7 @@ private static bool Prefix(MissAgainstFactOwner __instance, RuleAttackRoll evt) } } - [Harmony12.HarmonyPatch(typeof(WeaponReality), "OnEventAboutToTrigger")] + [HarmonyLib.HarmonyPatch(typeof(WeaponReality), "OnEventAboutToTrigger")] // ReSharper disable once UnusedMember.Local private static class WeaponRealityOnEventAboutToTriggerPatch { // ReSharper disable once UnusedMember.Local @@ -4594,7 +4639,7 @@ private static bool Prefix(WeaponReality __instance, RulePrepareDamage evt) { } } - [Harmony12.HarmonyPatch(typeof(AddInitiatorAttackRollTrigger), "CheckConditions")] + [HarmonyLib.HarmonyPatch(typeof(AddInitiatorAttackRollTrigger), "CheckConditions")] // ReSharper disable once UnusedMember.Local private static class AddInitiatorAttackRollTriggerCheckConditionsPatch { // ReSharper disable once UnusedMember.Local @@ -4617,7 +4662,7 @@ private static bool Prefix(AddInitiatorAttackRollTrigger __instance, RuleAttackR } #if !PATCH21 - [Harmony12.HarmonyPatch(typeof(RuleCalculateAttacksCount), "OnTrigger")] + [HarmonyLib.HarmonyPatch(typeof(RuleCalculateAttacksCount), "OnTrigger")] private static class RuleCalculateAttacksCountOnTriggerPatch { private static void Postfix(RuleCalculateAttacksCount __instance) { int num = __instance.Initiator.Stats.BaseAttackBonus; @@ -4644,7 +4689,7 @@ private static void Postfix(RuleCalculateAttacksCount __instance) { } } - [Harmony12.HarmonyPatch(typeof(ItemEntityWeapon), "HoldInTwoHands", Harmony12.MethodType.Getter)] + [HarmonyLib.HarmonyPatch(typeof(ItemEntityWeapon), "HoldInTwoHands", MethodType.Getter)] private static class ItemEntityWeaponHoldInTwoHandsPatch { private static void Postfix(ItemEntityWeapon __instance, ref bool __result) { if (!__result) { @@ -4657,7 +4702,7 @@ private static void Postfix(ItemEntityWeapon __instance, ref bool __result) { } #endif - [Harmony12.HarmonyPatch(typeof(DescriptionTemplatesItem), "ItemEnergy")] + [HarmonyLib.HarmonyPatch(typeof(DescriptionTemplatesItem), "ItemEnergy")] private static class DescriptionTemplatesItemItemEnergyPatch { private static void Postfix(TooltipData data, bool __result) { if (__result) { @@ -4668,7 +4713,7 @@ private static void Postfix(TooltipData data, bool __result) { } } - [Harmony12.HarmonyPatch(typeof(DescriptionTemplatesItem), "ItemEnhancement")] + [HarmonyLib.HarmonyPatch(typeof(DescriptionTemplatesItem), "ItemEnhancement")] private static class DescriptionTemplatesItemItemEnhancementPatch { private static void Postfix(TooltipData data) { if (data.Texts.ContainsKey(Enum.GetValues(typeof(TooltipElement)).Cast().Max() + 1)) { @@ -4679,7 +4724,7 @@ private static void Postfix(TooltipData data) { } } - [Harmony12.HarmonyPatch(typeof(DescriptionTemplatesItem), "ItemEnergyResisit")] + [HarmonyLib.HarmonyPatch(typeof(DescriptionTemplatesItem), "ItemEnergyResisit")] private static class DescriptionTemplatesItemItemEnergyResisitPatch { private static bool Prefix(ref bool __result) { __result = false; @@ -4687,7 +4732,7 @@ private static bool Prefix(ref bool __result) { } } - [Harmony12.HarmonyPatch(typeof(UnitViewHandSlotData), "OwnerWeaponScale", Harmony12.MethodType.Getter)] + [HarmonyLib.HarmonyPatch(typeof(UnitViewHandSlotData), "OwnerWeaponScale", HarmonyLib.MethodType.Getter)] private static class UnitViewHandSlotDataWeaponScalePatch { private static void Postfix(UnitViewHandSlotData __instance, ref float __result) { if (__instance.VisibleItem is ItemEntityWeapon weapon && !weapon.Blueprint.AssetGuid.Contains(",visual=")) { @@ -4707,7 +4752,7 @@ private static void Postfix(UnitViewHandSlotData __instance, ref float __result) } #if !PATCH21 - [Harmony12.HarmonyPatch(typeof(ActivatableAbility), "OnEventDidTrigger", new Type[] { typeof(RuleAttackWithWeaponResolve) })] + [HarmonyLib.HarmonyPatch(typeof(ActivatableAbility), "OnEventDidTrigger", new Type[] { typeof(RuleAttackWithWeaponResolve) })] private static class ActivatableAbilityOnEventDidTriggerRuleAttackWithWeaponResolvePatch { private static bool Prefix(ActivatableAbility __instance, RuleAttackWithWeaponResolve evt) { if (evt.Damage != null && evt.AttackRoll.IsHit) { diff --git a/CraftMagicItems/QuiverAbility.cs b/CraftMagicItems/QuiverAbility.cs index 7e8ba2a..6e64292 100644 --- a/CraftMagicItems/QuiverAbility.cs +++ b/CraftMagicItems/QuiverAbility.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Reflection; using System.Reflection.Emit; -using Harmony12; using Kingmaker; #if PATCH21 using Kingmaker.Assets.UI.Context; @@ -23,7 +22,7 @@ namespace CraftMagicItems { public class CreateQuiverAbility : ScriptableObject { private static bool initialised; - [Harmony12.HarmonyPatch(typeof(MainMenu), "Start")] + [HarmonyLib.HarmonyPatch(typeof(MainMenu), "Start")] // ReSharper disable once UnusedMember.Local public static class MainMenuStartPatch { private static void AddQuiver(BlueprintActivatableAbility ability, BlueprintBuff buff, string guid, PhysicalDamageMaterial material) { @@ -43,8 +42,8 @@ private static void AddQuiver(BlueprintActivatableAbility ability, BlueprintBuff #endif quiverBuff.ComponentsArray = new BlueprintComponent[] { component }; - Main.Accessors.SetBlueprintUnitFactDisplayName(quiverBuff, new L10NString($"craftMagicItems-mundane-{material.ToString().ToLower()}-quiver-name")); - Main.Accessors.SetBlueprintUnitFactDescription(quiverBuff, new L10NString($"craftMagicItems-mundane-{material.ToString().ToLower()}-quiver-description")); + Main.Accessors.SetBlueprintUnitFactDisplayName(quiverBuff) = new L10NString($"craftMagicItems-mundane-{material.ToString().ToLower()}-quiver-name"); + Main.Accessors.SetBlueprintUnitFactDescription(quiverBuff) = new L10NString($"craftMagicItems-mundane-{material.ToString().ToLower()}-quiver-description"); #if PATCH21_BETA quiverBuff.OnEnable(); foreach (var c in quiverBuff.ComponentsArray) { @@ -54,7 +53,7 @@ private static void AddQuiver(BlueprintActivatableAbility ability, BlueprintBuff var buffGuid = $"{guid}#CraftMagicItems({material.ToString()}QuiverBuff)"; - Main.Accessors.SetBlueprintScriptableObjectAssetGuid(quiverBuff, buffGuid); + Main.Accessors.SetBlueprintScriptableObjectAssetGuid(quiverBuff) = buffGuid; ResourcesLibrary.LibraryObject.BlueprintsByAssetId?.Add(buffGuid, quiverBuff); ResourcesLibrary.LibraryObject.GetAllBlueprints()?.Add(quiverBuff); @@ -65,8 +64,8 @@ private static void AddQuiver(BlueprintActivatableAbility ability, BlueprintBuff #endif quiverAbility.Buff = quiverBuff; - Main.Accessors.SetBlueprintUnitFactDisplayName(quiverAbility, new L10NString($"craftMagicItems-mundane-{material.ToString().ToLower()}-quiver-name")); - Main.Accessors.SetBlueprintUnitFactDescription(quiverAbility, new L10NString($"craftMagicItems-mundane-{material.ToString().ToLower()}-quiver-description")); + Main.Accessors.SetBlueprintUnitFactDisplayName(quiverAbility) = new L10NString($"craftMagicItems-mundane-{material.ToString().ToLower()}-quiver-name"); + Main.Accessors.SetBlueprintUnitFactDescription(quiverAbility) = new L10NString($"craftMagicItems-mundane-{material.ToString().ToLower()}-quiver-description"); #if PATCH21_BETA quiverBuff.OnEnable(); foreach (var c in quiverAbility.ComponentsArray) { @@ -76,7 +75,7 @@ private static void AddQuiver(BlueprintActivatableAbility ability, BlueprintBuff var abilityGuid = $"{guid}#CraftMagicItems({material.ToString()}QuiverAbility)"; - Main.Accessors.SetBlueprintScriptableObjectAssetGuid(quiverAbility, abilityGuid); + Main.Accessors.SetBlueprintScriptableObjectAssetGuid(quiverAbility) = abilityGuid; ResourcesLibrary.LibraryObject.BlueprintsByAssetId?.Add(abilityGuid, quiverAbility); ResourcesLibrary.LibraryObject.GetAllBlueprints()?.Add(quiverAbility); } @@ -97,7 +96,7 @@ public static void Postfix() { } #if PATCH21 - [Harmony12.HarmonyPatch(typeof(MainMenuUiContext), "Initialize")] + [HarmonyLib.HarmonyPatch(typeof(MainMenuUiContext), "Initialize")] private static class MainMenuUiContextInitializePatch { private static void Postfix() { MainMenuStartPatch.Postfix(); @@ -105,17 +104,17 @@ private static void Postfix() { } #endif - [Harmony12.HarmonyPatch(typeof(ItemSlot), "InsertItem")] + [HarmonyLib.HarmonyPatch(typeof(ItemSlot), "InsertItem")] // ReSharper disable once UnusedMember.Local private static class ItemSlotInsertItemPatch { - static readonly MethodInfo methodToReplace = AccessTools.Property(typeof(ItemEntity), "IsStackable").GetGetMethod(); + static readonly MethodInfo methodToReplace = HarmonyLib.AccessTools.Property(typeof(ItemEntity), "IsStackable").GetGetMethod(); static readonly string quiverGuid = "25f9b5ef564cbef49a1e54c48e67dfc1#CraftMagicItems"; // ReSharper disable once UnusedMember.Local - private static IEnumerable Transpiler(IEnumerable instructions) { + private static IEnumerable Transpiler(IEnumerable instructions) { foreach (var inst in instructions) { if (inst.opcode == OpCodes.Callvirt && inst.operand as MethodInfo == methodToReplace) { - yield return new Harmony12.CodeInstruction(OpCodes.Call, new Func(IsStackable).Method); + yield return new HarmonyLib.CodeInstruction(OpCodes.Call, new Func(IsStackable).Method); } else { yield return inst; } diff --git a/CraftMagicItems/SustenanceEnchantment.cs b/CraftMagicItems/SustenanceEnchantment.cs index 43469de..819e591 100644 --- a/CraftMagicItems/SustenanceEnchantment.cs +++ b/CraftMagicItems/SustenanceEnchantment.cs @@ -30,10 +30,10 @@ public class SustenanceEnchantment : BlueprintItemEnchantment { private static bool initialised; - [Harmony12.HarmonyPatch(typeof(MainMenu), "Start")] + [HarmonyLib.HarmonyPatch(typeof(MainMenu), "Start")] public static class MainMenuStartPatch { private static void AddBlueprint(string guid, BlueprintScriptableObject blueprint) { - Main.Accessors.SetBlueprintScriptableObjectAssetGuid(blueprint, guid); + Main.Accessors.SetBlueprintScriptableObjectAssetGuid(blueprint) = guid; ResourcesLibrary.LibraryObject.BlueprintsByAssetId?.Add(guid, blueprint); ResourcesLibrary.LibraryObject.GetAllBlueprints()?.Add(blueprint); } @@ -44,14 +44,14 @@ public static void Postfix() { initialised = true; AddBlueprint(SustenanceEnchantmentGuid, BlueprintSustenanceEnchantment); AddBlueprint(SustenanceFactGuid, BlueprintSustenanceFact); - Main.Accessors.SetBlueprintItemEnchantmentEnchantName(BlueprintSustenanceEnchantment, - new L10NString("craftMagicItems-enchantment-sustenance-name")); - Main.Accessors.SetBlueprintItemEnchantmentDescription(BlueprintSustenanceEnchantment, - new L10NString("craftMagicItems-enchantment-sustenance-description")); - Main.Accessors.SetBlueprintItemEnchantmentPrefix(BlueprintSustenanceEnchantment, new L10NString("")); - Main.Accessors.SetBlueprintItemEnchantmentSuffix(BlueprintSustenanceEnchantment, new L10NString("")); - Main.Accessors.SetBlueprintItemEnchantmentEnchantmentCost(BlueprintSustenanceEnchantment, 1); - Main.Accessors.SetBlueprintItemEnchantmentEnchantmentIdentifyDC(BlueprintSustenanceEnchantment, 5); + Main.Accessors.SetBlueprintItemEnchantmentEnchantName(BlueprintSustenanceEnchantment) = + new L10NString("craftMagicItems-enchantment-sustenance-name"); + Main.Accessors.SetBlueprintItemEnchantmentDescription(BlueprintSustenanceEnchantment) = + new L10NString("craftMagicItems-enchantment-sustenance-description"); + Main.Accessors.SetBlueprintItemEnchantmentPrefix(BlueprintSustenanceEnchantment) = new L10NString(""); + Main.Accessors.SetBlueprintItemEnchantmentSuffix(BlueprintSustenanceEnchantment) = new L10NString(""); + Main.Accessors.SetBlueprintItemEnchantmentEnchantmentCost(BlueprintSustenanceEnchantment) = 1; + Main.Accessors.SetBlueprintItemEnchantmentEnchantmentIdentifyDC(BlueprintSustenanceEnchantment) = 5; var addSustenanceFact = CreateInstance(); addSustenanceFact.Blueprint = BlueprintSustenanceFact; addSustenanceFact.name = "AddUnitFactEquipment-SustenanceFact"; @@ -61,15 +61,15 @@ public static void Postfix() { BlueprintSustenanceFact.Frequency = DurationRate.Rounds; BlueprintSustenanceFact.FxOnStart = new PrefabLink(); BlueprintSustenanceFact.FxOnRemove = new PrefabLink(); - Main.Accessors.SetBlueprintUnitFactDisplayName(BlueprintSustenanceFact, new L10NString("craftMagicItems-enchantment-sustenance-name")); - Main.Accessors.SetBlueprintUnitFactDescription(BlueprintSustenanceFact, - new L10NString("craftMagicItems-enchantment-sustenance-description")); + Main.Accessors.SetBlueprintUnitFactDisplayName(BlueprintSustenanceFact) = new L10NString("craftMagicItems-enchantment-sustenance-name"); + Main.Accessors.SetBlueprintUnitFactDescription(BlueprintSustenanceFact) = + new L10NString("craftMagicItems-enchantment-sustenance-description"); } } } #if PATCH21 - [Harmony12.HarmonyPatch(typeof(MainMenuUiContext), "Initialize")] + [HarmonyLib.HarmonyPatch(typeof(MainMenuUiContext), "Initialize")] private static class MainMenuUiContextInitializePatch { private static void Postfix() { MainMenuStartPatch.Postfix(); @@ -81,7 +81,7 @@ private static bool UnitHasSustenance(UnitEntityData unit) { return unit?.Descriptor.GetFact(BlueprintSustenanceFact) != null; } - [Harmony12.HarmonyPatch(typeof(RestController), "CalculateNeededRations")] + [HarmonyLib.HarmonyPatch(typeof(RestController), "CalculateNeededRations")] // ReSharper disable once UnusedMember.Local private static class RestControllerCalculateNeededRationsPatch { // ReSharper disable once UnusedMember.Local @@ -103,7 +103,7 @@ private static int CountRoles(UnitEntityData unit) { return roles; } - [Harmony12.HarmonyPatch(typeof(CampManager), "RemoveAllCompanionRoles")] + [HarmonyLib.HarmonyPatch(typeof(CampManager), "RemoveAllCompanionRoles")] // ReSharper disable once UnusedMember.Local private static class CampManagerRemoveAllCompanionRolesPatch { // ReSharper disable once UnusedMember.Local @@ -117,7 +117,7 @@ private static bool Prefix(UnitEntityData unit) { } } - [Harmony12.HarmonyPatch(typeof(MemberUIBody), "CheckHasRole")] + [HarmonyLib.HarmonyPatch(typeof(MemberUIBody), "CheckHasRole")] private static class MemberUiBodyCheckHasRolePatch { // ReSharper disable once UnusedMember.Local private static bool Prefix(MemberUIBody __instance, ref bool __result) { @@ -125,7 +125,7 @@ private static bool Prefix(MemberUIBody __instance, ref bool __result) { if (UnitHasSustenance(unit) && CountRoles(unit) < 2) { // The unit can still be assigned to another role. __instance.HasRole = false; - Harmony12.Traverse.Create(__instance).Method("SetupRoleView").GetValue(); + HarmonyLib.Traverse.Create(__instance).Method("SetupRoleView").GetValue(); __result = false; return false; } @@ -138,7 +138,7 @@ private static List FindBestRoleToDrop(UnitEntityData unit, List< return current.Contains(unit) && (best == null || current.Count > best.Count) ? current : best; } - [Harmony12.HarmonyPatch(typeof(CampingState), "CleanupRoles")] + [HarmonyLib.HarmonyPatch(typeof(CampingState), "CleanupRoles")] // ReSharper disable once UnusedMember.Local private static class CampingStateCleanupRolesPatch { // ReSharper disable once UnusedMember.Local @@ -171,7 +171,7 @@ private static void Postfix() { } } - [Harmony12.HarmonyPatch(typeof(CampingState), "GetRolesCount")] + [HarmonyLib.HarmonyPatch(typeof(CampingState), "GetRolesCount")] // ReSharper disable once UnusedMember.Local private static class RestControllerIsTiredPatch { // ReSharper disable once UnusedMember.Local diff --git a/CraftMagicItems/WeaponBaseSizeChange.cs b/CraftMagicItems/WeaponBaseSizeChange.cs index f4a0128..480b366 100644 --- a/CraftMagicItems/WeaponBaseSizeChange.cs +++ b/CraftMagicItems/WeaponBaseSizeChange.cs @@ -1,13 +1,7 @@ using Kingmaker.Blueprints; -using Kingmaker.Blueprints.Items.Ecnchantments; using Kingmaker.Blueprints.Items.Weapons; using Kingmaker.EntitySystem.Stats; -using Kingmaker.Enums; -using Kingmaker.Items; -using Kingmaker.PubSubSystem; using Kingmaker.RuleSystem; -using Kingmaker.RuleSystem.Rules; -using UnityEngine; using Object = UnityEngine.Object; namespace CraftMagicItems { @@ -19,7 +13,7 @@ namespace CraftMagicItems { public class WeaponBaseSizeChange : GameLogicComponent { public int SizeCategoryChange; - [Harmony12.HarmonyPatch(typeof(BlueprintItemWeapon), "BaseDamage", Harmony12.MethodType.Getter)] + [HarmonyLib.HarmonyPatch(typeof(BlueprintItemWeapon), "BaseDamage", HarmonyLib.MethodType.Getter)] // ReSharper disable once UnusedMember.Local private static class BlueprintItemWeaponBaseDamage { private static void Postfix(BlueprintItemWeapon __instance, ref DiceFormula __result) { @@ -33,7 +27,7 @@ private static void Postfix(BlueprintItemWeapon __instance, ref DiceFormula __re } } - [Harmony12.HarmonyPatch(typeof(BlueprintItemWeapon), "AttackBonusStat", Harmony12.MethodType.Getter)] + [HarmonyLib.HarmonyPatch(typeof(BlueprintItemWeapon), "AttackBonusStat", HarmonyLib.MethodType.Getter)] // ReSharper disable once UnusedMember.Local private static class BlueprintItemWeaponAttackBonusStat { private static void Postfix(BlueprintItemWeapon __instance, ref StatType __result) { @@ -50,7 +44,7 @@ private static void Postfix(BlueprintItemWeapon __instance, ref StatType __resul } - [Harmony12.HarmonyPatch(typeof(BlueprintItemWeapon), "IsTwoHanded", Harmony12.MethodType.Getter)] + [HarmonyLib.HarmonyPatch(typeof(BlueprintItemWeapon), "IsTwoHanded", HarmonyLib.MethodType.Getter)] // ReSharper disable once UnusedMember.Local private static class BlueprintItemWeaponIsTwoHanded { private static void Postfix(BlueprintItemWeapon __instance, ref bool __result) { @@ -68,7 +62,7 @@ private static void Postfix(BlueprintItemWeapon __instance, ref bool __result) { } } - [Harmony12.HarmonyPatch(typeof(BlueprintItemWeapon), "IsLight", Harmony12.MethodType.Getter)] + [HarmonyLib.HarmonyPatch(typeof(BlueprintItemWeapon), "IsLight", HarmonyLib.MethodType.Getter)] // ReSharper disable once UnusedMember.Local private static class BlueprintItemWeaponIsLight { private static void Postfix(BlueprintItemWeapon __instance, ref bool __result) { @@ -86,7 +80,7 @@ private static void Postfix(BlueprintItemWeapon __instance, ref bool __result) { } } - [Harmony12.HarmonyPatch(typeof(BlueprintItemWeapon), "SubtypeName", Harmony12.MethodType.Getter)] + [HarmonyLib.HarmonyPatch(typeof(BlueprintItemWeapon), "SubtypeName", HarmonyLib.MethodType.Getter)] // ReSharper disable once UnusedMember.Local private static class BlueprintItemWeaponSubtypeName { private static void Postfix(BlueprintItemWeapon __instance, ref string __result) { diff --git a/CraftMagicItems/WeaponSizeChange.cs b/CraftMagicItems/WeaponSizeChange.cs index eec8317..72b516a 100644 --- a/CraftMagicItems/WeaponSizeChange.cs +++ b/CraftMagicItems/WeaponSizeChange.cs @@ -1,8 +1,6 @@ using Kingmaker.Blueprints; -using Kingmaker.Blueprints.Items.Ecnchantments; using Kingmaker.Blueprints.Items.Weapons; using Kingmaker.Enums; -using Kingmaker.PubSubSystem; using Kingmaker.RuleSystem; using Kingmaker.RuleSystem.Rules; @@ -11,7 +9,7 @@ namespace CraftMagicItems { public class WeaponSizeChange : GameLogicComponent { public int SizeCategoryChange; - [Harmony12.HarmonyPatch(typeof(BlueprintItemWeapon), "BaseDamage", Harmony12.MethodType.Getter)] + [HarmonyLib.HarmonyPatch(typeof(BlueprintItemWeapon), "BaseDamage", HarmonyLib.MethodType.Getter)] // ReSharper disable once UnusedMember.Local private static class BlueprintItemWeaponBaseDamage { private static void Postfix(BlueprintItemWeapon __instance, ref DiceFormula __result) { @@ -25,7 +23,7 @@ private static void Postfix(BlueprintItemWeapon __instance, ref DiceFormula __re } } - [Harmony12.HarmonyPatch(typeof(RuleCalculateWeaponStats), "WeaponSize", Harmony12.MethodType.Getter)] + [HarmonyLib.HarmonyPatch(typeof(RuleCalculateWeaponStats), "WeaponSize", HarmonyLib.MethodType.Getter)] // ReSharper disable once UnusedMember.Local private static class RuleCalculateWeaponStatsWeaponSizePatch { private static void Prefix(RuleCalculateWeaponStats __instance, ref int ___m_SizeShift, ref int __state, ref Size __result) { diff --git a/CraftMagicItems/WildEnchantment.cs b/CraftMagicItems/WildEnchantment.cs index 46730fe..d9027b7 100644 --- a/CraftMagicItems/WildEnchantment.cs +++ b/CraftMagicItems/WildEnchantment.cs @@ -23,8 +23,8 @@ public WildFact() { Frequency = DurationRate.Rounds; FxOnStart = new PrefabLink(); FxOnRemove = new PrefabLink(); - Main.Accessors.SetBlueprintUnitFactDisplayName(this, new L10NString("craftMagicItems-enchantment-wild-name")); - Main.Accessors.SetBlueprintUnitFactDescription(this, new L10NString("craftMagicItems-enchantment-wild-description")); + Main.Accessors.SetBlueprintUnitFactDisplayName(this) = new L10NString("craftMagicItems-enchantment-wild-name"); + Main.Accessors.SetBlueprintUnitFactDescription(this) = new L10NString("craftMagicItems-enchantment-wild-description"); } } @@ -94,19 +94,19 @@ public class WildEnchantment : BlueprintItemEnchantment { public WildEnchantment() { this.name = "Wild"; - Main.Accessors.SetBlueprintItemEnchantmentEnchantName(this, new L10NString("craftMagicItems-enchantment-wild-name")); - Main.Accessors.SetBlueprintItemEnchantmentDescription(this, new L10NString("craftMagicItems-enchantment-wild-description")); - Main.Accessors.SetBlueprintItemEnchantmentPrefix(this, new L10NString("")); - Main.Accessors.SetBlueprintItemEnchantmentSuffix(this, new L10NString("")); - Main.Accessors.SetBlueprintItemEnchantmentEnchantmentCost(this, 1); - Main.Accessors.SetBlueprintItemEnchantmentEnchantmentIdentifyDC(this, 5); + Main.Accessors.SetBlueprintItemEnchantmentEnchantName(this) = new L10NString("craftMagicItems-enchantment-wild-name"); + Main.Accessors.SetBlueprintItemEnchantmentDescription(this) = new L10NString("craftMagicItems-enchantment-wild-description"); + Main.Accessors.SetBlueprintItemEnchantmentPrefix(this) = new L10NString(""); + Main.Accessors.SetBlueprintItemEnchantmentSuffix(this) = new L10NString(""); + Main.Accessors.SetBlueprintItemEnchantmentEnchantmentCost(this) = 1; + Main.Accessors.SetBlueprintItemEnchantmentEnchantmentIdentifyDC(this) = 5; } - [Harmony12.HarmonyPatch(typeof(MainMenu), "Start")] + [HarmonyLib.HarmonyPatch(typeof(MainMenu), "Start")] // ReSharper disable once UnusedMember.Local public static class MainMenuStartPatch { private static void AddBlueprint(string guid, BlueprintScriptableObject blueprint) { - Main.Accessors.SetBlueprintScriptableObjectAssetGuid(blueprint, guid); + Main.Accessors.SetBlueprintScriptableObjectAssetGuid(blueprint) = guid; ResourcesLibrary.LibraryObject.BlueprintsByAssetId?.Add(guid, blueprint); ResourcesLibrary.LibraryObject.GetAllBlueprints()?.Add(blueprint); } @@ -126,7 +126,7 @@ public static void Postfix() { } } #if PATCH21 - [Harmony12.HarmonyPatch(typeof(MainMenuUiContext), "Initialize")] + [HarmonyLib.HarmonyPatch(typeof(MainMenuUiContext), "Initialize")] private static class MainMenuUiContextInitializePatch { private static void Postfix() { MainMenuStartPatch.Postfix(); diff --git a/Repository.json b/Repository.json index 785188a..0ed9a40 100644 --- a/Repository.json +++ b/Repository.json @@ -3,7 +3,7 @@ [ { "Id": "CraftMagicItems", - "Version": "1.11.1" + "Version": "2.0.0" } ] } \ No newline at end of file diff --git a/info.json b/info.json index 19f1506..f3c6fe1 100644 --- a/info.json +++ b/info.json @@ -2,8 +2,8 @@ "Id": "CraftMagicItems", "DisplayName": "Craft Magic Items", "Author": "RobRendell", - "Version": "1.11.1", - "ManagerVersion": "0.14.1", + "Version": "2.0.0", + "ManagerVersion": "0.22.9", "EntryMethod": "CraftMagicItems.Main.Load", "HomePage": "https://www.nexusmods.com/pathfinderkingmaker/mods/54", "Repository": "https://raw.githubusercontent.com/bfennema/OwlcatKingmakerModCraftMagicItems/master/Repository.json" From 62284506c9317697a1a201fbc52c6b41e4436302 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Thu, 25 Jun 2020 15:30:42 -0500 Subject: [PATCH 002/132] Adding in comments to help understand where in the rendering code things are happening --- CraftMagicItems/Main.cs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 2511c6e..b57f5d9 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -402,6 +402,7 @@ private static void OnGui(UnityModManager.ModEntry modEntry) { GetSelectedCrafter(true); + //render toggleable views in the main functionality of the mod if (RenderToggleSection(ref currentSection, OpenSection.CraftMagicItemsSection, "Craft Magic Items")) { RenderCraftMagicItemsSection(); } @@ -526,12 +527,14 @@ public static BondedItemComponent GetBondedItemComponentForCaster(UnitDescriptor return bondedItemBuff.SelectComponents().First(); } + /// Renders the crafting menu (by selected character) private static void RenderCraftMagicItemsSection() { var caster = GetSelectedCrafter(false); if (caster == null) { return; } + //can the character have a bonded item? var hasBondedItemFeature = caster.Descriptor.Progression.Features.Enumerable.Any(feature => BondedItemFeatures.Contains(feature.Blueprint.AssetGuid)); var bondedItemData = GetBondedItemComponentForCaster(caster.Descriptor); @@ -549,6 +552,7 @@ private static void RenderCraftMagicItemsSection() { } } + //what crafting options are available (which feats are available for the selected character) var itemTypes = ItemCraftingData .Where(data => data.FeatGuid != null && (ModSettings.IgnoreCraftingFeats || CharacterHasFeat(caster, data.FeatGuid))) .ToArray(); @@ -557,12 +561,18 @@ private static void RenderCraftMagicItemsSection() { return; } + //list the selection items var itemTypeNames = itemTypes.Select(data => new L10NString(data.NameId).ToString()) .PrependConditional(hasBondedItemFeature, new L10NString("craftMagicItems-bonded-object-name")).ToArray(); + + //render whatever the user has selected var selectedItemTypeIndex = RenderSelection("Crafting: ", itemTypeNames, 6, ref selectedCustomName, false); + + //render options for actual selection if (hasBondedItemFeature && selectedItemTypeIndex == 0) { RenderBondedItemCrafting(caster); - } else { + } + else { var craftingData = itemTypes[hasBondedItemFeature ? selectedItemTypeIndex - 1 : selectedItemTypeIndex]; if (craftingData is SpellBasedItemCraftingData spellBased) { RenderSpellBasedCrafting(caster, spellBased); @@ -571,6 +581,7 @@ private static void RenderCraftMagicItemsSection() { } } + //render current cash RenderLabel($"Current Money: {Game.Instance.Player.Money}"); } @@ -1220,13 +1231,17 @@ private static bool DoesItemMatchLocationFilter(ItemEntity item, UnitEntityData private static void RenderRecipeBasedCrafting(UnitEntityData caster, RecipeBasedItemCraftingData craftingData, ItemEntity upgradeItem = null) { ItemsFilter.ItemType selectedSlot; + + //specific item if (upgradeItem != null) { selectedSlot = upgradeItem.Blueprint.ItemType; while (ItemUpgradeProjects.ContainsKey(upgradeItem)) { upgradeItem = ItemUpgradeProjects[upgradeItem].ResultItem; } RenderLabel($"Enchanting {upgradeItem.Name}"); - } else { + } + //slot + else { // Choose slot/weapon type. var selectedItemSlotIndex = 0; if (craftingData.Slots.Length > 1) { From f9568e6ffbccf41c5a9a9ce0a79979045817b7bd Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Fri, 26 Jun 2020 11:06:40 -0500 Subject: [PATCH 003/132] Removing assembly references that are not needed --- CraftMagicItems/CraftMagicItems.csproj | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index 1e03e41..b2b866a 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -75,18 +75,7 @@ ..\..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\Pathfinder Kingmaker\Kingmaker_Data\Managed\Newtonsoft.Json.dll - - - - - - - ..\..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\Pathfinder Kingmaker\Kingmaker_Data\Managed\UnityEngine.dll - - - ..\..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\Pathfinder Kingmaker\Kingmaker_Data\Managed\UnityEngine.AnimationModule.dll - ..\..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\Pathfinder Kingmaker\Kingmaker_Data\Managed\UnityEngine.CoreModule.dll @@ -97,9 +86,6 @@ ..\..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\Pathfinder Kingmaker\Kingmaker_Data\Managed\UnityEngine.IMGUIModule.dll - - ..\..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\Pathfinder Kingmaker\Kingmaker_Data\Managed\UnityEngine.UI.dll - ..\..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\Pathfinder Kingmaker\Kingmaker_Data\Managed\UnityModManager\UnityModManager.dll From f3b68be5003fd01e56b70b9370fdd64f24724c5c Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Fri, 26 Jun 2020 11:34:37 -0500 Subject: [PATCH 004/132] Adding in empty test project --- CraftMagicItems.sln | 10 ++ .../CraftMagicItemsTests.csproj | 92 +++++++++++++++++++ .../Properties/AssemblyInfo.cs | 20 ++++ CraftMagicItemsTests/packages.config | 5 + 4 files changed, 127 insertions(+) create mode 100644 CraftMagicItemsTests/CraftMagicItemsTests.csproj create mode 100644 CraftMagicItemsTests/Properties/AssemblyInfo.cs create mode 100644 CraftMagicItemsTests/packages.config diff --git a/CraftMagicItems.sln b/CraftMagicItems.sln index fc80f38..48f8197 100644 --- a/CraftMagicItems.sln +++ b/CraftMagicItems.sln @@ -5,6 +5,8 @@ VisualStudioVersion = 16.0.30104.148 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CraftMagicItems", "CraftMagicItems\CraftMagicItems.csproj", "{9379C37F-B226-4F81-893D-372F0CCEAAE5}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CraftMagicItemsTests", "CraftMagicItemsTests\CraftMagicItemsTests.csproj", "{FDC2E2FA-48A9-4F1B-9C9B-1B1D43628884}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug (2.1)|Any CPU = Debug (2.1)|Any CPU @@ -22,6 +24,14 @@ Global {9379C37F-B226-4F81-893D-372F0CCEAAE5}.Release|Any CPU.ActiveCfg = Release|Any CPU {9379C37F-B226-4F81-893D-372F0CCEAAE5}.Release|Any CPU.Build.0 = Release|Any CPU {9379C37F-B226-4F81-893D-372F0CCEAAE5}.Release|Any CPU.Deploy.0 = Release|Any CPU + {FDC2E2FA-48A9-4F1B-9C9B-1B1D43628884}.Debug (2.1)|Any CPU.ActiveCfg = Debug (2.1)|Any CPU + {FDC2E2FA-48A9-4F1B-9C9B-1B1D43628884}.Debug (2.1)|Any CPU.Build.0 = Debug (2.1)|Any CPU + {FDC2E2FA-48A9-4F1B-9C9B-1B1D43628884}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FDC2E2FA-48A9-4F1B-9C9B-1B1D43628884}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FDC2E2FA-48A9-4F1B-9C9B-1B1D43628884}.Release (2.1)|Any CPU.ActiveCfg = Release|Any CPU + {FDC2E2FA-48A9-4F1B-9C9B-1B1D43628884}.Release (2.1)|Any CPU.Build.0 = Release|Any CPU + {FDC2E2FA-48A9-4F1B-9C9B-1B1D43628884}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FDC2E2FA-48A9-4F1B-9C9B-1B1D43628884}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/CraftMagicItemsTests/CraftMagicItemsTests.csproj b/CraftMagicItemsTests/CraftMagicItemsTests.csproj new file mode 100644 index 0000000..86e8041 --- /dev/null +++ b/CraftMagicItemsTests/CraftMagicItemsTests.csproj @@ -0,0 +1,92 @@ + + + + + + Debug + AnyCPU + {FDC2E2FA-48A9-4F1B-9C9B-1B1D43628884} + Library + Properties + CraftMagicItemsTests + CraftMagicItemsTests + v4.6 + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 15.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages + False + UnitTest + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE;PATCH20 + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE;PATCH20 + prompt + 4 + + + true + bin\Debug %282.1%29\ + TRACE;DEBUG;PATCH21 + full + AnyCPU + 7.3 + prompt + MinimumRecommendedRules.ruleset + + + bin\Release %282.1%29\ + TRACE;PATCH21 + true + AnyCPU + 7.3 + prompt + MinimumRecommendedRules.ruleset + + + + ..\packages\MSTest.TestFramework.2.1.0\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll + + + ..\packages\MSTest.TestFramework.2.1.0\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll + + + + + + + + + + + + + {9379c37f-b226-4f81-893d-372f0cceaae5} + CraftMagicItems + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + \ No newline at end of file diff --git a/CraftMagicItemsTests/Properties/AssemblyInfo.cs b/CraftMagicItemsTests/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..f87436d --- /dev/null +++ b/CraftMagicItemsTests/Properties/AssemblyInfo.cs @@ -0,0 +1,20 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("CraftMagicItemsUnitTests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("CraftMagicItemsUnitTests")] +[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +[assembly: ComVisible(false)] + +[assembly: Guid("fdc2e2fa-48a9-4f1b-9c9b-1b1d43628884")] + +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/CraftMagicItemsTests/packages.config b/CraftMagicItemsTests/packages.config new file mode 100644 index 0000000..bd445ce --- /dev/null +++ b/CraftMagicItemsTests/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file From b22394a0f463ba4b9c5f988565cfed8a7e3f0530 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Fri, 26 Jun 2020 11:46:48 -0500 Subject: [PATCH 005/132] Moving RenderCheckbox to a new class handling UI methods --- CraftMagicItems/CraftMagicItems.csproj | 1 + CraftMagicItems/Main.cs | 26 +++++++++---------------- CraftMagicItems/UI/UmmUiRenderer.cs | 27 ++++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 17 deletions(-) create mode 100644 CraftMagicItems/UI/UmmUiRenderer.cs diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index b2b866a..d06628e 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -94,6 +94,7 @@ + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index b57f5d9..3014121 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -7,6 +7,7 @@ using System.Reflection.Emit; using System.Runtime.Serialization; using System.Text.RegularExpressions; +using CraftMagicItems.UI; using Kingmaker; #if PATCH21 using Kingmaker.Assets.UI.Context; @@ -2224,7 +2225,7 @@ private static void RenderFeatReassignmentSection() { } private static void RenderCheatsSection() { - RenderCheckbox(ref ModSettings.CraftingCostsNoGold, "Crafting costs no gold and no material components."); + UmmUiRenderer.RenderCheckbox(ref ModSettings.CraftingCostsNoGold, "Crafting costs no gold and no material components."); if (!ModSettings.CraftingCostsNoGold) { var selectedCustomPriceScaleIndex = RenderSelection(CustomPriceLabel, CraftingPriceStrings, 4); if (selectedCustomPriceScaleIndex == 2) { @@ -2244,10 +2245,10 @@ private static void RenderCheatsSection() { } } - RenderCheckbox(ref ModSettings.IgnoreCraftingFeats, "Crafting does not require characters to take crafting feats."); - RenderCheckbox(ref ModSettings.CraftingTakesNoTime, "Crafting takes no time to complete."); + UmmUiRenderer.RenderCheckbox(ref ModSettings.IgnoreCraftingFeats, "Crafting does not require characters to take crafting feats."); + UmmUiRenderer.RenderCheckbox(ref ModSettings.CraftingTakesNoTime, "Crafting takes no time to complete."); if (!ModSettings.CraftingTakesNoTime) { - RenderCheckbox(ref ModSettings.CustomCraftRate, "Craft at a non-standard rate."); + UmmUiRenderer.RenderCheckbox(ref ModSettings.CustomCraftRate, "Craft at a non-standard rate."); if (ModSettings.CustomCraftRate) { var maxMagicRate = ((ModSettings.MagicCraftingRate + 1000) / 1000) * 1000; RenderIntSlider(ref ModSettings.MagicCraftingRate, "Magic Item Crafting Rate", 1, maxMagicRate); @@ -2258,11 +2259,11 @@ private static void RenderCheatsSection() { ModSettings.MundaneCraftingRate = Settings.MundaneCraftingProgressPerDay; } } - RenderCheckbox(ref ModSettings.CasterLevelIsSinglePrerequisite, + UmmUiRenderer.RenderCheckbox(ref ModSettings.CasterLevelIsSinglePrerequisite, "When crafting, a Caster Level less than the prerequisite counts as a single missing prerequisite."); - RenderCheckbox(ref ModSettings.CraftAtFullSpeedWhileAdventuring, "Characters craft at full speed while adventuring (instead of 25% speed)."); - RenderCheckbox(ref ModSettings.IgnorePlusTenItemMaximum, "Ignore the rule that limits arms and armor to a maximum of +10 equivalent."); - RenderCheckbox(ref ModSettings.IgnoreFeatCasterLevelRestriction, "Ignore the crafting feat Caster Level prerequisites when learning feats."); + UmmUiRenderer.RenderCheckbox(ref ModSettings.CraftAtFullSpeedWhileAdventuring, "Characters craft at full speed while adventuring (instead of 25% speed)."); + UmmUiRenderer.RenderCheckbox(ref ModSettings.IgnorePlusTenItemMaximum, "Ignore the rule that limits arms and armor to a maximum of +10 equivalent."); + UmmUiRenderer.RenderCheckbox(ref ModSettings.IgnoreFeatCasterLevelRestriction, "Ignore the crafting feat Caster Level prerequisites when learning feats."); } private static void RenderLabel(string label) { @@ -2371,15 +2372,6 @@ private static void RenderCustomNameField(string defaultValue) { GUILayout.EndHorizontal(); } - private static void RenderCheckbox(ref bool value, string label) { - GUILayout.BeginHorizontal(); - if (GUILayout.Button($"{(value ? "" : "")} {label}", GUILayout.ExpandWidth(false))) { - value = !value; - } - - GUILayout.EndHorizontal(); - } - public static void AddItemBlueprintForSpell(UsableItemType itemType, BlueprintItemEquipment itemBlueprint) { if (!SpellIdToItem.ContainsKey(itemType)) { SpellIdToItem.Add(itemType, new Dictionary>()); diff --git a/CraftMagicItems/UI/UmmUiRenderer.cs b/CraftMagicItems/UI/UmmUiRenderer.cs new file mode 100644 index 0000000..51eca59 --- /dev/null +++ b/CraftMagicItems/UI/UmmUiRenderer.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace CraftMagicItems.UI +{ + /// Class that handles the Unity Mod Manager UI rendering + public class UmmUiRenderer + { + /// Renders a checkbox in Unity Mod Manager + /// Currently selected value + /// Label for the checkbox + public static void RenderCheckbox(ref bool value, string label) + { + GUILayout.BeginHorizontal(); + if (GUILayout.Button($"{(value ? "" : "")} {label}", GUILayout.ExpandWidth(false))) + { + value = !value; + } + + GUILayout.EndHorizontal(); + } + } +} \ No newline at end of file From 75d54d020e54c61487bc785b2339a5d2d99a40c7 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Fri, 26 Jun 2020 11:46:58 -0500 Subject: [PATCH 006/132] Adding in an empty unit test class --- CraftMagicItemsTests/CraftMagicItemsTests.csproj | 1 + CraftMagicItemsTests/MainRenderTests.cs | 15 +++++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 CraftMagicItemsTests/MainRenderTests.cs diff --git a/CraftMagicItemsTests/CraftMagicItemsTests.csproj b/CraftMagicItemsTests/CraftMagicItemsTests.csproj index 86e8041..a1d3880 100644 --- a/CraftMagicItemsTests/CraftMagicItemsTests.csproj +++ b/CraftMagicItemsTests/CraftMagicItemsTests.csproj @@ -68,6 +68,7 @@ + diff --git a/CraftMagicItemsTests/MainRenderTests.cs b/CraftMagicItemsTests/MainRenderTests.cs new file mode 100644 index 0000000..c2f5fb6 --- /dev/null +++ b/CraftMagicItemsTests/MainRenderTests.cs @@ -0,0 +1,15 @@ +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using CraftMagicItems; + +namespace CraftMagicItemsTests +{ + [TestClass] + public class MainRenderTests + { + [TestMethod] + public void RenderMultiLayerSkills_Works() + { + } + } +} \ No newline at end of file From 7e8c6138dd2f02a737c0403276ece1d39cd7426a Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Fri, 26 Jun 2020 11:52:57 -0500 Subject: [PATCH 007/132] Moving RenderCustomNameField to UmmUiRenderer --- CraftMagicItems/Main.cs | 23 ++++------------------- CraftMagicItems/UI/UmmUiRenderer.cs | 23 +++++++++++++++++++++++ 2 files changed, 27 insertions(+), 19 deletions(-) diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 3014121..b297e6e 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -1495,7 +1495,7 @@ private static void RenderRecipeBasedCrafting(UnitEntityData caster, RecipeBased } else if (upgradeItem != null) { // Upgrading to a custom blueprint var name = upgradeItemShield?.Blueprint?.Name ?? upgradeItem.Blueprint.Name; - RenderCustomNameField(name); + selectedCustomName = UmmUiRenderer.RenderCustomNameField(name, selectedCustomName); name = selectedCustomName == name ? null : selectedCustomName; IEnumerable enchantments; string supersededEnchantmentId; @@ -1530,7 +1530,7 @@ private static void RenderRecipeBasedCrafting(UnitEntityData caster, RecipeBased // Crafting a new custom blueprint from scratch. SelectRandomApplicableBaseGuid(craftingData, selectedSlot); var baseBlueprint = selectedBaseBlueprint; - RenderCustomNameField($"{selectedRecipe.NameId} {new L10NString(GetSlotStringKey(selectedSlot, craftingData.SlotRestrictions))}"); + selectedCustomName = UmmUiRenderer.RenderCustomNameField($"{selectedRecipe.NameId} {new L10NString(GetSlotStringKey(selectedSlot, craftingData.SlotRestrictions))}", selectedCustomName); var enchantmentsToRemove = GetEnchantments(baseBlueprint, selectedRecipe).Select(enchantment => enchantment.AssetGuid).ToArray(); IEnumerable enchantments; if (selectedRecipe.EnchantmentsCumulative) { @@ -1729,7 +1729,7 @@ private static void RenderCastSpellNTimes(UnitEntityData caster, RecipeBasedItem string itemGuid; if (upgradeItem == null) { // Option to rename item - RenderCustomNameField($"{ability.Name} {new L10NString(GetSlotStringKey(selectedSlot, craftingData.SlotRestrictions))}"); + selectedCustomName = UmmUiRenderer.RenderCustomNameField($"{ability.Name} {new L10NString(GetSlotStringKey(selectedSlot, craftingData.SlotRestrictions))}", selectedCustomName); // Pick random base item SelectRandomApplicableBaseGuid(craftingData, selectedSlot); // Create customised item GUID @@ -1739,7 +1739,7 @@ private static void RenderCastSpellNTimes(UnitEntityData caster, RecipeBasedItem ability.AssetGuid, "null", casterLevel: selectedCasterLevel, spellLevel: spellLevel, perDay: selectedCastsPerDay); } else { // Option to rename item - RenderCustomNameField(upgradeItem.Blueprint.Name); + selectedCustomName = UmmUiRenderer.RenderCustomNameField(upgradeItem.Blueprint.Name, selectedCustomName); // Create customised item GUID itemGuid = blueprintPatcher.BuildCustomRecipeItemGuid(upgradeItem.Blueprint.AssetGuid, new List(), null, selectedCustomName == upgradeItem.Blueprint.Name ? null : selectedCustomName, ability.AssetGuid, @@ -2357,21 +2357,6 @@ private static void RenderIntSlider(ref int value, string label, int min, int ma GUILayout.EndHorizontal(); } - private static void RenderCustomNameField(string defaultValue) { - GUILayout.BeginHorizontal(); - GUILayout.Label("Name: ", GUILayout.ExpandWidth(false)); - if (string.IsNullOrEmpty(selectedCustomName)) { - selectedCustomName = defaultValue; - } - - selectedCustomName = GUILayout.TextField(selectedCustomName, GUILayout.Width(300)); - if (selectedCustomName.Trim().Length == 0) { - selectedCustomName = null; - } - - GUILayout.EndHorizontal(); - } - public static void AddItemBlueprintForSpell(UsableItemType itemType, BlueprintItemEquipment itemBlueprint) { if (!SpellIdToItem.ContainsKey(itemType)) { SpellIdToItem.Add(itemType, new Dictionary>()); diff --git a/CraftMagicItems/UI/UmmUiRenderer.cs b/CraftMagicItems/UI/UmmUiRenderer.cs index 51eca59..9947c3f 100644 --- a/CraftMagicItems/UI/UmmUiRenderer.cs +++ b/CraftMagicItems/UI/UmmUiRenderer.cs @@ -23,5 +23,28 @@ public static void RenderCheckbox(ref bool value, string label) GUILayout.EndHorizontal(); } + + /// Renders a text box on screen in Unity Mod Manager for the item's customized name + /// Default value for the item's name + /// Currently-selected custom name + public static string RenderCustomNameField(string defaultValue, string selectedCustomName) + { + GUILayout.BeginHorizontal(); + GUILayout.Label("Name: ", GUILayout.ExpandWidth(false)); + if (string.IsNullOrEmpty(selectedCustomName)) + { + selectedCustomName = defaultValue; + } + + selectedCustomName = GUILayout.TextField(selectedCustomName, GUILayout.Width(300)); + if (selectedCustomName.Trim().Length == 0) + { + selectedCustomName = null; + } + + GUILayout.EndHorizontal(); + + return selectedCustomName; + } } } \ No newline at end of file From b003206c55d2644ea0a803a528a340c996d88449 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Fri, 26 Jun 2020 11:55:08 -0500 Subject: [PATCH 008/132] Flipping ref to a return value --- CraftMagicItems/Main.cs | 19 ++++++++++--------- CraftMagicItems/UI/UmmUiRenderer.cs | 4 +++- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index b297e6e..16d8af7 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -2225,7 +2225,7 @@ private static void RenderFeatReassignmentSection() { } private static void RenderCheatsSection() { - UmmUiRenderer.RenderCheckbox(ref ModSettings.CraftingCostsNoGold, "Crafting costs no gold and no material components."); + ModSettings.CraftingCostsNoGold = UmmUiRenderer.RenderCheckbox(ModSettings.CraftingCostsNoGold, "Crafting costs no gold and no material components."); if (!ModSettings.CraftingCostsNoGold) { var selectedCustomPriceScaleIndex = RenderSelection(CustomPriceLabel, CraftingPriceStrings, 4); if (selectedCustomPriceScaleIndex == 2) { @@ -2245,10 +2245,11 @@ private static void RenderCheatsSection() { } } - UmmUiRenderer.RenderCheckbox(ref ModSettings.IgnoreCraftingFeats, "Crafting does not require characters to take crafting feats."); - UmmUiRenderer.RenderCheckbox(ref ModSettings.CraftingTakesNoTime, "Crafting takes no time to complete."); - if (!ModSettings.CraftingTakesNoTime) { - UmmUiRenderer.RenderCheckbox(ref ModSettings.CustomCraftRate, "Craft at a non-standard rate."); + ModSettings.IgnoreCraftingFeats = UmmUiRenderer.RenderCheckbox(ModSettings.IgnoreCraftingFeats, "Crafting does not require characters to take crafting feats."); + ModSettings.CraftingTakesNoTime = UmmUiRenderer.RenderCheckbox(ModSettings.CraftingTakesNoTime, "Crafting takes no time to complete."); + if (!ModSettings.CraftingTakesNoTime) + { + ModSettings.CustomCraftRate = UmmUiRenderer.RenderCheckbox(ModSettings.CustomCraftRate, "Craft at a non-standard rate."); if (ModSettings.CustomCraftRate) { var maxMagicRate = ((ModSettings.MagicCraftingRate + 1000) / 1000) * 1000; RenderIntSlider(ref ModSettings.MagicCraftingRate, "Magic Item Crafting Rate", 1, maxMagicRate); @@ -2259,11 +2260,11 @@ private static void RenderCheatsSection() { ModSettings.MundaneCraftingRate = Settings.MundaneCraftingProgressPerDay; } } - UmmUiRenderer.RenderCheckbox(ref ModSettings.CasterLevelIsSinglePrerequisite, + ModSettings.CasterLevelIsSinglePrerequisite = UmmUiRenderer.RenderCheckbox(ModSettings.CasterLevelIsSinglePrerequisite, "When crafting, a Caster Level less than the prerequisite counts as a single missing prerequisite."); - UmmUiRenderer.RenderCheckbox(ref ModSettings.CraftAtFullSpeedWhileAdventuring, "Characters craft at full speed while adventuring (instead of 25% speed)."); - UmmUiRenderer.RenderCheckbox(ref ModSettings.IgnorePlusTenItemMaximum, "Ignore the rule that limits arms and armor to a maximum of +10 equivalent."); - UmmUiRenderer.RenderCheckbox(ref ModSettings.IgnoreFeatCasterLevelRestriction, "Ignore the crafting feat Caster Level prerequisites when learning feats."); + ModSettings.CraftAtFullSpeedWhileAdventuring = UmmUiRenderer.RenderCheckbox(ModSettings.CraftAtFullSpeedWhileAdventuring, "Characters craft at full speed while adventuring (instead of 25% speed)."); + ModSettings.IgnorePlusTenItemMaximum = UmmUiRenderer.RenderCheckbox(ModSettings.IgnorePlusTenItemMaximum, "Ignore the rule that limits arms and armor to a maximum of +10 equivalent."); + ModSettings.IgnoreFeatCasterLevelRestriction = UmmUiRenderer.RenderCheckbox(ModSettings.IgnoreFeatCasterLevelRestriction, "Ignore the crafting feat Caster Level prerequisites when learning feats."); } private static void RenderLabel(string label) { diff --git a/CraftMagicItems/UI/UmmUiRenderer.cs b/CraftMagicItems/UI/UmmUiRenderer.cs index 9947c3f..0c3dccc 100644 --- a/CraftMagicItems/UI/UmmUiRenderer.cs +++ b/CraftMagicItems/UI/UmmUiRenderer.cs @@ -13,7 +13,7 @@ public class UmmUiRenderer /// Renders a checkbox in Unity Mod Manager /// Currently selected value /// Label for the checkbox - public static void RenderCheckbox(ref bool value, string label) + public static bool RenderCheckbox(bool value, string label) { GUILayout.BeginHorizontal(); if (GUILayout.Button($"{(value ? "" : "")} {label}", GUILayout.ExpandWidth(false))) @@ -22,6 +22,8 @@ public static void RenderCheckbox(ref bool value, string label) } GUILayout.EndHorizontal(); + + return value; } /// Renders a text box on screen in Unity Mod Manager for the item's customized name From 5fbc8b388255730ff1c1c26469927c10209df773 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Fri, 26 Jun 2020 11:59:39 -0500 Subject: [PATCH 009/132] Moving RenderIntSlider into UmmUiRenderer --- CraftMagicItems/Main.cs | 26 ++++++++++---------------- CraftMagicItems/UI/UmmUiRenderer.cs | 25 +++++++++++++++++++------ 2 files changed, 29 insertions(+), 22 deletions(-) diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 16d8af7..518f116 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -766,7 +766,7 @@ private static void RenderSpellBasedCrafting(UnitEntityData caster, SpellBasedIt var minCasterLevel = Math.Max(1, 2 * spellLevel - 1); var maxCasterLevel = CharacterCasterLevel(caster.Descriptor, spellbook); if (minCasterLevel < maxCasterLevel) { - RenderIntSlider(ref selectedCasterLevel, "Caster level: ", minCasterLevel, maxCasterLevel); + selectedCasterLevel = UmmUiRenderer.RenderIntSlider(selectedCasterLevel, "Caster level: ", minCasterLevel, maxCasterLevel); } else { selectedCasterLevel = minCasterLevel; RenderLabel($"Caster level: {selectedCasterLevel}"); @@ -1714,10 +1714,10 @@ private static void RenderCastSpellNTimes(UnitEntityData caster, RecipeBasedItem // Choose a caster level var minCasterLevel = Math.Max(equipment == null ? 0 : equipment.CasterLevel, Math.Max(1, 2 * spellLevel - 1)); - RenderIntSlider(ref selectedCasterLevel, "Caster level: ", minCasterLevel, 20); + selectedCasterLevel = UmmUiRenderer.RenderIntSlider(selectedCasterLevel, "Caster level: ", minCasterLevel, 20); // Choose number of times per day var maxCastsPerDay = equipment == null ? 10 : ((equipment.Charges + 10) / 10) * 10; - RenderIntSlider(ref selectedCastsPerDay, "Casts per day: ", equipment == null ? 1 : equipment.Charges, maxCastsPerDay); + selectedCastsPerDay = UmmUiRenderer.RenderIntSlider(selectedCastsPerDay, "Casts per day: ", equipment == null ? 1 : equipment.Charges, maxCastsPerDay); if (equipment != null && ability == equipment.Ability && selectedCasterLevel == equipment.CasterLevel && selectedCastsPerDay == equipment.Charges) { RenderLabel($"No changes made to {equipment.Name}"); return; @@ -2250,12 +2250,15 @@ private static void RenderCheatsSection() { if (!ModSettings.CraftingTakesNoTime) { ModSettings.CustomCraftRate = UmmUiRenderer.RenderCheckbox(ModSettings.CustomCraftRate, "Craft at a non-standard rate."); - if (ModSettings.CustomCraftRate) { + if (ModSettings.CustomCraftRate) + { var maxMagicRate = ((ModSettings.MagicCraftingRate + 1000) / 1000) * 1000; - RenderIntSlider(ref ModSettings.MagicCraftingRate, "Magic Item Crafting Rate", 1, maxMagicRate); + ModSettings.MagicCraftingRate = UmmUiRenderer.RenderIntSlider(ModSettings.MagicCraftingRate, "Magic Item Crafting Rate", 1, maxMagicRate); var maxMundaneRate = ((ModSettings.MundaneCraftingRate + 10) / 10) * 10; - RenderIntSlider(ref ModSettings.MundaneCraftingRate, "Mundane Item Crafting Rate", 1, maxMundaneRate); - } else { + ModSettings.MundaneCraftingRate = UmmUiRenderer.RenderIntSlider(ModSettings.MundaneCraftingRate, "Mundane Item Crafting Rate", 1, maxMundaneRate); + } + else + { ModSettings.MagicCraftingRate = Settings.MagicCraftingProgressPerDay; ModSettings.MundaneCraftingRate = Settings.MundaneCraftingProgressPerDay; } @@ -2349,15 +2352,6 @@ private static int RenderSelection(string label, string[] options, int xCount return newIndex; } - private static void RenderIntSlider(ref int value, string label, int min, int max) { - value = Mathf.Clamp(value, min, max); - GUILayout.BeginHorizontal(); - GUILayout.Label(label, GUILayout.ExpandWidth(false)); - value = Mathf.RoundToInt(GUILayout.HorizontalSlider(value, min, max, GUILayout.Width(300))); - GUILayout.Label($"{value}", GUILayout.ExpandWidth(false)); - GUILayout.EndHorizontal(); - } - public static void AddItemBlueprintForSpell(UsableItemType itemType, BlueprintItemEquipment itemBlueprint) { if (!SpellIdToItem.ContainsKey(itemType)) { SpellIdToItem.Add(itemType, new Dictionary>()); diff --git a/CraftMagicItems/UI/UmmUiRenderer.cs b/CraftMagicItems/UI/UmmUiRenderer.cs index 0c3dccc..11ca09d 100644 --- a/CraftMagicItems/UI/UmmUiRenderer.cs +++ b/CraftMagicItems/UI/UmmUiRenderer.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using UnityEngine; +using UnityEngine; namespace CraftMagicItems.UI { @@ -48,5 +43,23 @@ public static string RenderCustomNameField(string defaultValue, string selectedC return selectedCustomName; } + + /// Renders an integer selection slider + /// Initial value + /// Label for the slider + /// Minimum possible value + /// Maximum possible value + /// Returns the initial value, clamped and rounded after rendering controls to the creeen + public static int RenderIntSlider(int value, string label, int min, int max) + { + value = Mathf.Clamp(value, min, max); + GUILayout.BeginHorizontal(); + GUILayout.Label(label, GUILayout.ExpandWidth(false)); + value = Mathf.RoundToInt(GUILayout.HorizontalSlider(value, min, max, GUILayout.Width(300))); + GUILayout.Label($"{value}", GUILayout.ExpandWidth(false)); + GUILayout.EndHorizontal(); + + return value; + } } } \ No newline at end of file From d3426907aaf07d768420573d51c31943822fb663 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Fri, 26 Jun 2020 12:07:38 -0500 Subject: [PATCH 010/132] Adding return values to XML comments --- CraftMagicItems/UI/UmmUiRenderer.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CraftMagicItems/UI/UmmUiRenderer.cs b/CraftMagicItems/UI/UmmUiRenderer.cs index 11ca09d..5f95388 100644 --- a/CraftMagicItems/UI/UmmUiRenderer.cs +++ b/CraftMagicItems/UI/UmmUiRenderer.cs @@ -8,6 +8,7 @@ public class UmmUiRenderer /// Renders a checkbox in Unity Mod Manager /// Currently selected value /// Label for the checkbox + /// The inverse of when the button is clicked, otherwise public static bool RenderCheckbox(bool value, string label) { GUILayout.BeginHorizontal(); @@ -24,6 +25,7 @@ public static bool RenderCheckbox(bool value, string label) /// Renders a text box on screen in Unity Mod Manager for the item's customized name /// Default value for the item's name /// Currently-selected custom name + /// The updated text as entered by user, otherwise public static string RenderCustomNameField(string defaultValue, string selectedCustomName) { GUILayout.BeginHorizontal(); @@ -49,7 +51,7 @@ public static string RenderCustomNameField(string defaultValue, string selectedC /// Label for the slider /// Minimum possible value /// Maximum possible value - /// Returns the initial value, clamped and rounded after rendering controls to the creeen + /// Returns the value selected by the user, clamped and rounded after rendering controls to the screen public static int RenderIntSlider(int value, string label, int min, int max) { value = Mathf.Clamp(value, min, max); From d8947e5ead56262e9e6727a825cdaab13176ab06 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Fri, 26 Jun 2020 12:21:16 -0500 Subject: [PATCH 011/132] Moving enum to public --- CraftMagicItems/CraftMagicItems.csproj | 1 + CraftMagicItems/Main.cs | 8 -------- CraftMagicItems/UI/OpenSection.cs | 12 ++++++++++++ 3 files changed, 13 insertions(+), 8 deletions(-) create mode 100644 CraftMagicItems/UI/OpenSection.cs diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index d06628e..10ac69a 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -94,6 +94,7 @@ + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 518f116..7463496 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -177,14 +177,6 @@ public static class Main { "aa34ca4f3cd5e5d49b2475fcfdf56b24" }; - private enum OpenSection { - CraftMagicItemsSection, - CraftMundaneItemsSection, - ProjectsSection, - FeatsSection, - CheatsSection - } - private enum ItemLocationFilter { All, diff --git a/CraftMagicItems/UI/OpenSection.cs b/CraftMagicItems/UI/OpenSection.cs new file mode 100644 index 0000000..d000ab7 --- /dev/null +++ b/CraftMagicItems/UI/OpenSection.cs @@ -0,0 +1,12 @@ +namespace CraftMagicItems.UI +{ + /// Enumeration of the sections of the UI that can be rendered + public enum OpenSection + { + CraftMagicItemsSection, + CraftMundaneItemsSection, + ProjectsSection, + FeatsSection, + CheatsSection + } +} \ No newline at end of file From 016da40ece5af08a11af69facacfd636838c9483 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Fri, 26 Jun 2020 13:20:45 -0500 Subject: [PATCH 012/132] Moving RenderToggleSection over to UmmUiRenderer --- CraftMagicItems/Main.cs | 37 ++++++++++++++--------------- CraftMagicItems/UI/UmmUiRenderer.cs | 15 ++++++++++++ 2 files changed, 33 insertions(+), 19 deletions(-) diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 7463496..77b8439 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -396,28 +396,40 @@ private static void OnGui(UnityModManager.ModEntry modEntry) { GetSelectedCrafter(true); //render toggleable views in the main functionality of the mod - if (RenderToggleSection(ref currentSection, OpenSection.CraftMagicItemsSection, "Craft Magic Items")) { + if (UmmUiRenderer.RenderToggleSection(currentSection == OpenSection.CraftMagicItemsSection, "Craft Magic Items")) + { + currentSection = OpenSection.CraftMagicItemsSection; RenderCraftMagicItemsSection(); } - if (RenderToggleSection(ref currentSection, OpenSection.CraftMundaneItemsSection, "Craft Mundane Items")) { + if (UmmUiRenderer.RenderToggleSection(currentSection == OpenSection.CraftMundaneItemsSection, "Craft Mundane Items")) + { + currentSection = OpenSection.CraftMundaneItemsSection; RenderCraftMundaneItemsSection(); } - if (RenderToggleSection(ref currentSection, OpenSection.ProjectsSection, "Work in Progress")) { + if (UmmUiRenderer.RenderToggleSection(currentSection == OpenSection.ProjectsSection, "Work in Progress")) + { + currentSection = OpenSection.ProjectsSection; RenderProjectsSection(); } - if (RenderToggleSection(ref currentSection, OpenSection.FeatsSection, "Feat Reassignment")) { + if (UmmUiRenderer.RenderToggleSection(currentSection == OpenSection.FeatsSection, "Feat Reassignment")) + { + currentSection = OpenSection.FeatsSection; RenderFeatReassignmentSection(); } - if (RenderToggleSection(ref currentSection, OpenSection.CheatsSection, "Cheats")) { + if (UmmUiRenderer.RenderToggleSection(currentSection == OpenSection.CheatsSection, "Cheats")) + { + currentSection = OpenSection.CheatsSection; RenderCheatsSection(); } GUILayout.EndVertical(); - } catch (Exception e) { + } + catch (Exception e) + { modEntry.Logger.Error($"Error rendering GUI: {e}"); } } @@ -435,19 +447,6 @@ private static string L10NFormat(string key, params object[] args) { return L10NFormat(currentCaster ?? GetSelectedCrafter(false), key, args); } - private static bool RenderToggleSection(ref OpenSection current, OpenSection mySection, string label) { - GUILayout.BeginVertical("box"); - GUILayout.BeginHorizontal(); - bool toggledOn = GUILayout.Toggle(current == mySection, " " + label + ""); - if (toggledOn) { - current = mySection; - } - - GUILayout.EndHorizontal(); - GUILayout.EndVertical(); - return toggledOn; - } - public static T ReadJsonFile(string fileName, params JsonConverter[] converters) { try { var serializer = new JsonSerializer(); diff --git a/CraftMagicItems/UI/UmmUiRenderer.cs b/CraftMagicItems/UI/UmmUiRenderer.cs index 5f95388..728fa5c 100644 --- a/CraftMagicItems/UI/UmmUiRenderer.cs +++ b/CraftMagicItems/UI/UmmUiRenderer.cs @@ -63,5 +63,20 @@ public static int RenderIntSlider(int value, string label, int min, int max) return value; } + + /// Renders a toggle-able section selection in Unity Mod Manager for the user to show/hide + /// Flag indicating whether the toggle is active + /// Label for the toggle + /// Whether the toggle is currently active in Unity Mod Manager + public static bool RenderToggleSection(bool value, string label) + { + GUILayout.BeginVertical("box"); + GUILayout.BeginHorizontal(); + bool toggledOn = GUILayout.Toggle(value, " " + label + ""); + + GUILayout.EndHorizontal(); + GUILayout.EndVertical(); + return toggledOn; + } } } \ No newline at end of file From ed98f61a62468738efb65fd7ba21f7c02c34731e Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Fri, 26 Jun 2020 13:24:04 -0500 Subject: [PATCH 013/132] Making the Button call minorly more readable --- CraftMagicItems/UI/UmmUiRenderer.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CraftMagicItems/UI/UmmUiRenderer.cs b/CraftMagicItems/UI/UmmUiRenderer.cs index 728fa5c..ec991c5 100644 --- a/CraftMagicItems/UI/UmmUiRenderer.cs +++ b/CraftMagicItems/UI/UmmUiRenderer.cs @@ -12,7 +12,9 @@ public class UmmUiRenderer public static bool RenderCheckbox(bool value, string label) { GUILayout.BeginHorizontal(); - if (GUILayout.Button($"{(value ? "" : "")} {label}", GUILayout.ExpandWidth(false))) + var text = value ? "✔" : "✖"; + var color = value ? "green" : "red"; + if (GUILayout.Button($"{text} {label}", GUILayout.ExpandWidth(false))) { value = !value; } From 8c338240f71de01e6a6ce6ec3f996d44d5976062 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Fri, 26 Jun 2020 13:26:53 -0500 Subject: [PATCH 014/132] Moving RenderLabel over to UmmUiRenderer --- CraftMagicItems/Main.cs | 120 +++++++++++++--------------- CraftMagicItems/UI/UmmUiRenderer.cs | 9 +++ 2 files changed, 66 insertions(+), 63 deletions(-) diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 77b8439..629c12a 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -373,7 +373,7 @@ private static bool OnToggle(UnityModManager.ModEntry modEntry, bool enabled) { private static void OnGui(UnityModManager.ModEntry modEntry) { if (!modEnabled) { - RenderLabel("The mod is disabled. Loading saved games with custom items and feats will cause them to revert to regular versions."); + UmmUiRenderer.RenderLabel("The mod is disabled. Loading saved games with custom items and feats will cause them to revert to regular versions."); return; } @@ -385,13 +385,13 @@ private static void OnGui(UnityModManager.ModEntry modEntry) { && Game.Instance.CurrentMode != GameModeType.EscMode && Game.Instance.CurrentMode != GameModeType.Rest && Game.Instance.CurrentMode != GameModeType.Kingdom) { - RenderLabel("Item crafting is not available in this game state."); + UmmUiRenderer.RenderLabel("Item crafting is not available in this game state."); return; } GUILayout.BeginVertical(); - RenderLabel($"Number of custom Craft Magic Items blueprints loaded: {CustomBlueprintBuilder.CustomBlueprintIDs.Count}"); + UmmUiRenderer.RenderLabel($"Number of custom Craft Magic Items blueprints loaded: {CustomBlueprintBuilder.CustomBlueprintIDs.Count}"); GetSelectedCrafter(true); @@ -549,7 +549,7 @@ private static void RenderCraftMagicItemsSection() { .Where(data => data.FeatGuid != null && (ModSettings.IgnoreCraftingFeats || CharacterHasFeat(caster, data.FeatGuid))) .ToArray(); if (!Enumerable.Any(itemTypes) && !hasBondedItemFeature) { - RenderLabel($"{caster.CharacterName} does not know any crafting feats."); + UmmUiRenderer.RenderLabel($"{caster.CharacterName} does not know any crafting feats."); return; } @@ -574,7 +574,7 @@ private static void RenderCraftMagicItemsSection() { } //render current cash - RenderLabel($"Current Money: {Game.Instance.Player.Money}"); + UmmUiRenderer.RenderLabel($"Current Money: {Game.Instance.Player.Money}"); } private static RecipeBasedItemCraftingData GetBondedItemCraftingData(BondedItemComponent bondedComponent) { @@ -622,26 +622,26 @@ private static void RenderBondedItemCrafting(UnitEntityData caster) { var projects = GetCraftingTimerComponentForCaster(caster.Descriptor); var ritualProject = projects == null ? null : projects.CraftingProjects.FirstOrDefault(project => project.ItemType == BondedItemRitual); if (ritualProject != null) { - RenderLabel($"{caster.CharacterName} is in the process of bonding with {ritualProject.ResultItem.Name}"); + UmmUiRenderer.RenderLabel($"{caster.CharacterName} is in the process of bonding with {ritualProject.ResultItem.Name}"); return; } var bondedComponent = GetBondedItemComponentForCaster(caster.Descriptor); var characterCasterLevel = CharacterCasterLevel(caster.Descriptor); if (bondedComponent == null || bondedComponent.ownerItem == null || selectedBondWithNewObject) { if (selectedBondWithNewObject) { - RenderLabel("You may bond with a different object by performing a special ritual that costs 200 gp per caster level. This ritual takes 8 " + + UmmUiRenderer.RenderLabel("You may bond with a different object by performing a special ritual that costs 200 gp per caster level. This ritual takes 8 " + "hours to complete. Items replaced in this way do not possess any of the additional enchantments of the previous bonded item, " + "and the previous bonded item loses any enchantments you added via your bond."); if (GUILayout.Button("Cancel bonding to a new object")) { selectedBondWithNewObject = false; } } - RenderLabel( + UmmUiRenderer.RenderLabel( "You can enchant additional magic abilities to your bonded item as if you had the required Item Creation Feat, as long as you also " + "meet the caster level prerequisite of the feat. Abilities added in this fashion function only for you, and no-one else can add " + "enchantments to your bonded item."); - RenderLabel(new L10NString("craftMagicItems-bonded-item-glossary")); - RenderLabel("Choose your bonded item."); + UmmUiRenderer.RenderLabel(new L10NString("craftMagicItems-bonded-item-glossary")); + UmmUiRenderer.RenderLabel("Choose your bonded item."); var names = BondedItemSlots.Select(slot => new L10NString(GetSlotStringKey(slot, null)).ToString()).ToArray(); var selectedItemSlotIndex = RenderSelection("Item type", names, 10); var selectedSlot = BondedItemSlots[selectedItemSlotIndex]; @@ -655,7 +655,7 @@ private static void RenderBondedItemCrafting(UnitEntityData caster) { .OrderBy(item => item.Name) .ToArray(); if (items.Length == 0) { - RenderLabel("You do not have any item of that type currently equipped."); + UmmUiRenderer.RenderLabel("You do not have any item of that type currently equipped."); return; } var itemNames = items.Select(item => item.Name).ToArray(); @@ -665,7 +665,7 @@ private static void RenderBondedItemCrafting(UnitEntityData caster) { var canAfford = BuildCostString(out var cost, null, goldCost); var label = $"Make {selectedItem.Name} your bonded item{(goldCost == 0 ? "" : " for " + cost)}"; if (!canAfford) { - RenderLabel(label); + UmmUiRenderer.RenderLabel(label); } else if (GUILayout.Button(label)) { if (goldCost > 0) { Game.Instance.UI.Common.UISound.Play(UISoundType.LootCollectGold); @@ -695,9 +695,9 @@ private static void RenderBondedItemCrafting(UnitEntityData caster) { var craftingData = GetBondedItemCraftingData(bondedComponent); if (bondedComponent.ownerItem.Wielder != null && !IsPlayerInCapital() && !Game.Instance.Player.PartyCharacters.Contains(bondedComponent.ownerItem.Wielder.Unit)) { - RenderLabel($"You cannot enchant {bondedComponent.ownerItem.Name} because you cannot currently access it."); + UmmUiRenderer.RenderLabel($"You cannot enchant {bondedComponent.ownerItem.Name} because you cannot currently access it."); } else if (!ModSettings.IgnoreFeatCasterLevelRestriction && characterCasterLevel < craftingData.MinimumCasterLevel) { - RenderLabel($"You will not be able to enchant your bonded item until your caster level reaches {craftingData.MinimumCasterLevel} " + + UmmUiRenderer.RenderLabel($"You will not be able to enchant your bonded item until your caster level reaches {craftingData.MinimumCasterLevel} " + $"(currently {characterCasterLevel})."); } else { RenderRecipeBasedCrafting(caster, craftingData, bondedComponent.ownerItem); @@ -708,7 +708,7 @@ private static void RenderBondedItemCrafting(UnitEntityData caster) { private static void RenderSpellBasedCrafting(UnitEntityData caster, SpellBasedItemCraftingData craftingData) { var spellbooks = caster.Descriptor.Spellbooks.Where(book => book.CasterLevel > 0).ToList(); if (spellbooks.Count == 0) { - RenderLabel($"{caster.CharacterName} is not yet able to cast spells."); + UmmUiRenderer.RenderLabel($"{caster.CharacterName} is not yet able to cast spells."); return; } @@ -742,7 +742,7 @@ private static void RenderSpellBasedCrafting(UnitEntityData caster, SpellBasedIt // Cantrips/Orisons or Spontaneous spellcaster or showing all known spells if (spellLevel > 0 && spellbook.Blueprint.Spontaneous) { var spontaneousSlots = spellbook.GetSpontaneousSlots(spellLevel); - RenderLabel($"{caster.CharacterName} can cast {spontaneousSlots} more level {spellLevel} spells today."); + UmmUiRenderer.RenderLabel($"{caster.CharacterName} can cast {spontaneousSlots} more level {spellLevel} spells today."); if (spontaneousSlots == 0 && ModSettings.CraftingTakesNoTime) { return; } @@ -752,7 +752,7 @@ private static void RenderSpellBasedCrafting(UnitEntityData caster, SpellBasedIt } if (!spellOptions.Any()) { - RenderLabel($"{caster.CharacterName} does not know any level {spellLevel} spells."); + UmmUiRenderer.RenderLabel($"{caster.CharacterName} does not know any level {spellLevel} spells."); } else { var minCasterLevel = Math.Max(1, 2 * spellLevel - 1); var maxCasterLevel = CharacterCasterLevel(caster.Descriptor, spellbook); @@ -760,7 +760,7 @@ private static void RenderSpellBasedCrafting(UnitEntityData caster, SpellBasedIt selectedCasterLevel = UmmUiRenderer.RenderIntSlider(selectedCasterLevel, "Caster level: ", minCasterLevel, maxCasterLevel); } else { selectedCasterLevel = minCasterLevel; - RenderLabel($"Caster level: {selectedCasterLevel}"); + UmmUiRenderer.RenderLabel($"Caster level: {selectedCasterLevel}"); } RenderCraftingSkillInformation(caster, StatType.SkillKnowledgeArcana, 5 + selectedCasterLevel, selectedCasterLevel); @@ -1230,7 +1230,7 @@ private static void RenderRecipeBasedCrafting(UnitEntityData caster, RecipeBased while (ItemUpgradeProjects.ContainsKey(upgradeItem)) { upgradeItem = ItemUpgradeProjects[upgradeItem].ResultItem; } - RenderLabel($"Enchanting {upgradeItem.Name}"); + UmmUiRenderer.RenderLabel($"Enchanting {upgradeItem.Name}"); } //slot else { @@ -1274,7 +1274,7 @@ private static void RenderRecipeBasedCrafting(UnitEntityData caster, RecipeBased var itemNames = items.Select(item => item.Name).PrependConditional(canCreateNew, new L10NString("craftMagicItems-label-craft-new-item")) .ToArray(); if (itemNames.Length == 0) { - RenderLabel($"{caster.CharacterName} can not access any items of that type."); + UmmUiRenderer.RenderLabel($"{caster.CharacterName} can not access any items of that type."); return; } @@ -1321,9 +1321,9 @@ private static void RenderRecipeBasedCrafting(UnitEntityData caster, RecipeBased selectedShieldWeapon = false; } if (upgradeItemShield != null) { - RenderLabel(BuildItemDescription(upgradeItemShield)); + UmmUiRenderer.RenderLabel(BuildItemDescription(upgradeItemShield)); } else { - RenderLabel(BuildItemDescription(upgradeItem)); + UmmUiRenderer.RenderLabel(BuildItemDescription(upgradeItem)); } } @@ -1387,7 +1387,7 @@ private static void RenderRecipeBasedCrafting(UnitEntityData caster, RecipeBased } return false; }))) { - RenderLabel("This item cannot be further upgraded with this enchantment."); + UmmUiRenderer.RenderLabel("This item cannot be further upgraded with this enchantment."); return; } else if (availableEnchantments.Length > 0 && selectedRecipe.Enchantments.Length > 1) { var counter = selectedRecipe.Enchantments.Length - availableEnchantments.Length; @@ -1407,10 +1407,10 @@ private static void RenderRecipeBasedCrafting(UnitEntityData caster, RecipeBased : selectedRecipe.Enchantments.FindIndex(e => e == selectedEnchantment) * selectedRecipe.CasterLevelMultiplier); if (selectedEnchantment != null) { if (!string.IsNullOrEmpty(selectedEnchantment.Description)) { - RenderLabel(selectedEnchantment.Description); + UmmUiRenderer.RenderLabel(selectedEnchantment.Description); } if (selectedRecipe.CostType == RecipeCostType.EnhancementLevelSquared) { - RenderLabel($"Plus equivalent: +{GetPlusOfRecipe(selectedRecipe, selectedRecipe.Enchantments.FindIndex(e => e == selectedEnchantment) + 1)}"); + UmmUiRenderer.RenderLabel($"Plus equivalent: +{GetPlusOfRecipe(selectedRecipe, selectedRecipe.Enchantments.FindIndex(e => e == selectedEnchantment) + 1)}"); } } @@ -1535,13 +1535,13 @@ private static void RenderRecipeBasedCrafting(UnitEntityData caster, RecipeBased } if (!itemToCraft) { - RenderLabel($"Error: null custom item from looking up blueprint ID {itemGuid}"); + UmmUiRenderer.RenderLabel($"Error: null custom item from looking up blueprint ID {itemGuid}"); } else { if (IsItemLegalEnchantmentLevel(itemToCraft)) { RenderRecipeBasedCraftItemControl(caster, craftingData, selectedRecipe, casterLevel, itemToCraft, upgradeItem); } else { var maxEnchantmentLevel = ItemMaxEnchantmentLevel(itemToCraft); - RenderLabel($"This would result in {itemToCraft.Name} having an equivalent enhancement bonus of more than +{maxEnchantmentLevel}"); + UmmUiRenderer.RenderLabel($"This would result in {itemToCraft.Name} having an equivalent enhancement bonus of more than +{maxEnchantmentLevel}"); } } } @@ -1635,10 +1635,10 @@ private static void RenderCastSpellNTimes(UnitEntityData caster, RecipeBasedItem if (upgradeItem != null) { equipment = upgradeItem.Blueprint as BlueprintItemEquipment; if (equipment == null || equipment.Ability != null && equipment.SpendCharges && !equipment.RestoreChargesOnRest) { - RenderLabel($"{upgradeItem.Name} cannot cast a spell N times a day (this is unexpected - please let the mod author know)"); + UmmUiRenderer.RenderLabel($"{upgradeItem.Name} cannot cast a spell N times a day (this is unexpected - please let the mod author know)"); return; } else if (equipment.Ability != null && !equipment.Ability.IsSpell) { - RenderLabel($"{equipment.Ability.Name} is not a spell, so cannot be upgraded."); + UmmUiRenderer.RenderLabel($"{equipment.Ability.Name} is not a spell, so cannot be upgraded."); return; } } @@ -1662,7 +1662,7 @@ private static void RenderCastSpellNTimes(UnitEntityData caster, RecipeBasedItem .OrderBy(spell => spell.Name) .ToArray(); if (!spellOptions.Any()) { - RenderLabel($"There are no level {spellLevel} {spellbook.Blueprint.Name} spells"); + UmmUiRenderer.RenderLabel($"There are no level {spellLevel} {spellbook.Blueprint.Name} spells"); return; } @@ -1685,7 +1685,7 @@ private static void RenderCastSpellNTimes(UnitEntityData caster, RecipeBasedItem .OrderBy(item => item.Name) .ToArray(); if (itemBlueprints.Length == 0) { - RenderLabel("You are not wielding any items that can cast spells."); + UmmUiRenderer.RenderLabel("You are not wielding any items that can cast spells."); return; } var itemNames = itemBlueprints.Select(item => item.Name).ToArray(); @@ -1693,13 +1693,13 @@ private static void RenderCastSpellNTimes(UnitEntityData caster, RecipeBasedItem var selectedItemBlueprint = itemBlueprints[itemIndex]; ability = selectedItemBlueprint.Ability; spellLevel = selectedItemBlueprint.SpellLevel; - RenderLabel($"Spell: {ability.Name}"); + UmmUiRenderer.RenderLabel($"Spell: {ability.Name}"); } } else { ability = equipment.Ability; spellLevel = equipment.SpellLevel; GameLogContext.Count = equipment.Charges; - RenderLabel($"Current: {L10NFormat("craftMagicItems-label-cast-spell-n-times-details", ability.Name, equipment.CasterLevel)}"); + UmmUiRenderer.RenderLabel($"Current: {L10NFormat("craftMagicItems-label-cast-spell-n-times-details", ability.Name, equipment.CasterLevel)}"); GameLogContext.Clear(); } @@ -1710,7 +1710,7 @@ private static void RenderCastSpellNTimes(UnitEntityData caster, RecipeBasedItem var maxCastsPerDay = equipment == null ? 10 : ((equipment.Charges + 10) / 10) * 10; selectedCastsPerDay = UmmUiRenderer.RenderIntSlider(selectedCastsPerDay, "Casts per day: ", equipment == null ? 1 : equipment.Charges, maxCastsPerDay); if (equipment != null && ability == equipment.Ability && selectedCasterLevel == equipment.CasterLevel && selectedCastsPerDay == equipment.Charges) { - RenderLabel($"No changes made to {equipment.Name}"); + UmmUiRenderer.RenderLabel($"No changes made to {equipment.Name}"); return; } @@ -1743,7 +1743,7 @@ private static void RenderCastSpellNTimes(UnitEntityData caster, RecipeBasedItem // Render craft button GameLogContext.Count = selectedCastsPerDay; - RenderLabel(L10NFormat("craftMagicItems-label-cast-spell-n-times-details", ability.Name, selectedCasterLevel)); + UmmUiRenderer.RenderLabel(L10NFormat("craftMagicItems-label-cast-spell-n-times-details", ability.Name, selectedCasterLevel)); GameLogContext.Clear(); var recipe = new RecipeData { PrerequisiteSpells = new[] {ability}, @@ -1790,7 +1790,7 @@ private static int RenderCraftingSkillInformation(UnitEntityData crafter, StatTy CrafterPrerequisiteType[] crafterPrerequisites = null, bool render = true) { if (render) { - RenderLabel($"Base Crafting DC: {dc}"); + UmmUiRenderer.RenderLabel($"Base Crafting DC: {dc}"); } // ReSharper disable once UnusedVariable var missing = CheckSpellPrerequisites(prerequisiteSpells, anyPrerequisite, crafter.Descriptor, false, out var missingSpells, @@ -1805,11 +1805,11 @@ private static int RenderCraftingSkillInformation(UnitEntityData crafter, StatTy casterLevelShortfall = 0; } if (missing > 0 && render) { - RenderLabel( + UmmUiRenderer.RenderLabel( $"{crafter.CharacterName} is unable to meet {missing} of the prerequisites, raising the DC by {MissingPrerequisiteDCModifier * missing}"); } if (casterLevelShortfall > 0 && render) { - RenderLabel(L10NFormat("craftMagicItems-logMessage-low-caster-level", casterLevel, MissingPrerequisiteDCModifier * casterLevelShortfall)); + UmmUiRenderer.RenderLabel(L10NFormat("craftMagicItems-logMessage-low-caster-level", casterLevel, MissingPrerequisiteDCModifier * casterLevelShortfall)); } // Rob's ruling... if you're below the prerequisite caster level, you're considered to be missing a prerequisite for each // level you fall short. @@ -1818,18 +1818,18 @@ private static int RenderCraftingSkillInformation(UnitEntityData crafter, StatTy if (oppositionSchool != SpellSchool.None) { dc += OppositionSchoolDCModifier; if (render) { - RenderLabel(L10NFormat("craftMagicItems-logMessage-opposition-school", LocalizedTexts.Instance.SpellSchoolNames.GetText(oppositionSchool), + UmmUiRenderer.RenderLabel(L10NFormat("craftMagicItems-logMessage-opposition-school", LocalizedTexts.Instance.SpellSchoolNames.GetText(oppositionSchool), OppositionSchoolDCModifier)); } } var skillCheck = 10 + crafter.Stats.GetStat(skill).ModifiedValue; if (render) { - RenderLabel(L10NFormat("craftMagicItems-logMessage-made-progress-check", LocalizedTexts.Instance.Stats.GetText(skill), skillCheck, dc)); + UmmUiRenderer.RenderLabel(L10NFormat("craftMagicItems-logMessage-made-progress-check", LocalizedTexts.Instance.Stats.GetText(skill), skillCheck, dc)); } var skillMargin = skillCheck - dc; if (skillMargin < 0 && render) { - RenderLabel(ModSettings.CraftingTakesNoTime + UmmUiRenderer.RenderLabel(ModSettings.CraftingTakesNoTime ? $"This project would be too hard for {crafter.CharacterName} if \"Crafting Takes No Time\" cheat was disabled." : $"Warning: This project will be too hard for {crafter.CharacterName}"); } @@ -1950,7 +1950,7 @@ private static void RenderCraftMundaneItemsSection() { } if (!(selectedCraftingData is RecipeBasedItemCraftingData craftingData)) { - RenderLabel("Unable to find mundane crafting recipe."); + UmmUiRenderer.RenderLabel("Unable to find mundane crafting recipe."); return; } @@ -1958,7 +1958,7 @@ private static void RenderCraftMundaneItemsSection() { if (upgradingBlueprint != null) { baseBlueprint = upgradingBlueprint; - RenderLabel($"Applying upgrades to {baseBlueprint.Name}"); + UmmUiRenderer.RenderLabel($"Applying upgrades to {baseBlueprint.Name}"); } else { // Choose mundane item of selected type to create var blueprints = craftingData.NewItemBaseIDs @@ -1968,14 +1968,14 @@ private static void RenderCraftMundaneItemsSection() { .ToArray(); var blueprintNames = blueprints.Select(item => item.Name).ToArray(); if (blueprintNames.Length == 0) { - RenderLabel("No known items of that type."); + UmmUiRenderer.RenderLabel("No known items of that type."); return; } var selectedUpgradeItemIndex = RenderSelection("Item: ", blueprintNames, 5, ref selectedCustomName); baseBlueprint = blueprints[selectedUpgradeItemIndex]; // See existing item details and enchantments. - RenderLabel(baseBlueprint.Description); + UmmUiRenderer.RenderLabel(baseBlueprint.Description); } // Assume only one slot type per crafting data @@ -1993,9 +1993,9 @@ private static void RenderCraftMundaneItemsSection() { var selectedRecipe = availableRecipes.Any() ? availableRecipes[selectedRecipeIndex] : null; var selectedEnchantment = selectedRecipe?.Enchantments.Length == 1 ? selectedRecipe.Enchantments[0] : null; if (selectedRecipe != null && selectedRecipe.Material != 0) { - RenderLabel(GetWeaponMaterialDescription(selectedRecipe.Material)); + UmmUiRenderer.RenderLabel(GetWeaponMaterialDescription(selectedRecipe.Material)); } else if (selectedEnchantment != null && !string.IsNullOrEmpty(selectedEnchantment.Description)) { - RenderLabel(selectedEnchantment.Description); + UmmUiRenderer.RenderLabel(selectedEnchantment.Description); } var dc = craftingData.MundaneEnhancementsStackable @@ -2058,7 +2058,7 @@ private static void RenderCraftMundaneItemsSection() { } if (!itemToCraft) { - RenderLabel($"Error: null custom item from looking up blueprint ID {itemGuid}"); + UmmUiRenderer.RenderLabel($"Error: null custom item from looking up blueprint ID {itemGuid}"); } else { if (upgradingBlueprint != null && GUILayout.Button($"Cancel {baseBlueprint.Name}", GUILayout.ExpandWidth(false))) { upgradingBlueprint = null; @@ -2075,7 +2075,7 @@ private static void RenderCraftMundaneItemsSection() { } } - RenderLabel($"Current Money: {Game.Instance.Player.Money}"); + UmmUiRenderer.RenderLabel($"Current Money: {Game.Instance.Player.Money}"); } private static string GetWeaponMaterialDescription(PhysicalDamageMaterial material) { @@ -2129,11 +2129,11 @@ private static void RenderProjectsSection() { var timer = GetCraftingTimerComponentForCaster(caster.Descriptor); if (timer == null || timer.CraftingProjects.Count == 0) { - RenderLabel($"{caster.CharacterName} is not currently working on any crafting projects."); + UmmUiRenderer.RenderLabel($"{caster.CharacterName} is not currently working on any crafting projects."); return; } - RenderLabel($"{caster.CharacterName} currently has {timer.CraftingProjects.Count} crafting projects in progress."); + UmmUiRenderer.RenderLabel($"{caster.CharacterName} currently has {timer.CraftingProjects.Count} crafting projects in progress."); var firstItem = true; foreach (var project in timer.CraftingProjects.ToArray()) { GUILayout.BeginHorizontal(); @@ -2150,8 +2150,8 @@ private static void RenderProjectsSection() { timer.CraftingProjects.Insert(0, project); } GUILayout.EndHorizontal(); - RenderLabel($" {BuildItemDescription(project.ResultItem).Replace("\n", "\n ")}"); - RenderLabel($" {project.LastMessage}"); + UmmUiRenderer.RenderLabel($" {BuildItemDescription(project.ResultItem).Replace("\n", "\n ")}"); + UmmUiRenderer.RenderLabel($" {project.LastMessage}"); } } @@ -2166,11 +2166,11 @@ private static void RenderFeatReassignmentSection() { .Where(data => data.FeatGuid != null && !CharacterHasFeat(caster, data.FeatGuid) && data.MinimumCasterLevel <= casterLevel) .ToArray(); if (missingFeats.Length == 0) { - RenderLabel($"{caster.CharacterName} does not currently qualify for any crafting feats."); + UmmUiRenderer.RenderLabel($"{caster.CharacterName} does not currently qualify for any crafting feats."); return; } - RenderLabel( + UmmUiRenderer.RenderLabel( "Use this section to reassign previous feat choices for this character to crafting feats. Warning: This is a one-way assignment!"); var selectedFeatToLearn = RenderSelection("Feat to learn", missingFeats.Select(data => new L10NString(data.NameId).ToString()).ToArray(), 6); var learnFeatData = missingFeats[selectedFeatToLearn]; @@ -2230,7 +2230,7 @@ private static void RenderCheatsSection() { } if (selectedCustomPriceScaleIndex != 0) { - RenderLabel( + UmmUiRenderer.RenderLabel( "Note: The sale price of custom crafted items will also be scaled by this factor, but vanilla items crafted by this mod" + " will continue to use Owlcat's sale price, creating a price difference between the cost of crafting and sale price."); } @@ -2261,12 +2261,6 @@ private static void RenderCheatsSection() { ModSettings.IgnoreFeatCasterLevelRestriction = UmmUiRenderer.RenderCheckbox(ModSettings.IgnoreFeatCasterLevelRestriction, "Ignore the crafting feat Caster Level prerequisites when learning feats."); } - private static void RenderLabel(string label) { - GUILayout.BeginHorizontal(); - GUILayout.Label(label); - GUILayout.EndHorizontal(); - } - private static bool IsPlayerSomewhereSafe() { if (Game.Instance.CurrentlyLoadedArea != null && SafeBlueprintAreaGuids.Contains(Game.Instance.CurrentlyLoadedArea.AssetGuid)) { return true; @@ -2293,7 +2287,7 @@ private static UnitEntityData GetSelectedCrafter(bool render) { .ToArray(); if (characters.Length == 0) { if (render) { - RenderLabel("No living characters available."); + UmmUiRenderer.RenderLabel("No living characters available."); } return null; diff --git a/CraftMagicItems/UI/UmmUiRenderer.cs b/CraftMagicItems/UI/UmmUiRenderer.cs index ec991c5..3f00483 100644 --- a/CraftMagicItems/UI/UmmUiRenderer.cs +++ b/CraftMagicItems/UI/UmmUiRenderer.cs @@ -80,5 +80,14 @@ public static bool RenderToggleSection(bool value, string label) GUILayout.EndVertical(); return toggledOn; } + + /// Renders a Label control as its own line in Unity Mod Manager + /// Text to be displayed + public static void RenderLabel(string label) + { + GUILayout.BeginHorizontal(); + GUILayout.Label(label); + GUILayout.EndHorizontal(); + } } } \ No newline at end of file From 104cdbaf65132a90c466b44835b850acbe71a17a Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Fri, 26 Jun 2020 13:31:41 -0500 Subject: [PATCH 015/132] Needless clarity --- CraftMagicItems/Main.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 629c12a..b36615a 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -2309,7 +2309,7 @@ private static UnitEntityData GetSelectedCrafter(bool render) { private static int RenderSelection(string label, string[] options, int xCount) { var dummy = ""; - return RenderSelection(label, options, xCount, ref dummy); + return RenderSelection(label, options, xCount, ref dummy); } private static int GetSelectionIndex(string label) { From b77f935c98da8381d58efa90b94563ced898286f Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Fri, 26 Jun 2020 13:32:37 -0500 Subject: [PATCH 016/132] Re-ordering params for consistency --- CraftMagicItems/Main.cs | 38 ++++++++++++++--------------- CraftMagicItems/UI/UmmUiRenderer.cs | 12 ++++----- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index b36615a..c51cd31 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -396,31 +396,31 @@ private static void OnGui(UnityModManager.ModEntry modEntry) { GetSelectedCrafter(true); //render toggleable views in the main functionality of the mod - if (UmmUiRenderer.RenderToggleSection(currentSection == OpenSection.CraftMagicItemsSection, "Craft Magic Items")) + if (UmmUiRenderer.RenderToggleSection("Craft Magic Items", currentSection == OpenSection.CraftMagicItemsSection)) { currentSection = OpenSection.CraftMagicItemsSection; RenderCraftMagicItemsSection(); } - if (UmmUiRenderer.RenderToggleSection(currentSection == OpenSection.CraftMundaneItemsSection, "Craft Mundane Items")) + if (UmmUiRenderer.RenderToggleSection("Craft Mundane Items", currentSection == OpenSection.CraftMundaneItemsSection)) { currentSection = OpenSection.CraftMundaneItemsSection; RenderCraftMundaneItemsSection(); } - if (UmmUiRenderer.RenderToggleSection(currentSection == OpenSection.ProjectsSection, "Work in Progress")) + if (UmmUiRenderer.RenderToggleSection("Work in Progress", currentSection == OpenSection.ProjectsSection)) { currentSection = OpenSection.ProjectsSection; RenderProjectsSection(); } - if (UmmUiRenderer.RenderToggleSection(currentSection == OpenSection.FeatsSection, "Feat Reassignment")) + if (UmmUiRenderer.RenderToggleSection("Feat Reassignment", currentSection == OpenSection.FeatsSection)) { currentSection = OpenSection.FeatsSection; RenderFeatReassignmentSection(); } - if (UmmUiRenderer.RenderToggleSection(currentSection == OpenSection.CheatsSection, "Cheats")) + if (UmmUiRenderer.RenderToggleSection("Cheats", currentSection == OpenSection.CheatsSection)) { currentSection = OpenSection.CheatsSection; RenderCheatsSection(); @@ -757,7 +757,7 @@ private static void RenderSpellBasedCrafting(UnitEntityData caster, SpellBasedIt var minCasterLevel = Math.Max(1, 2 * spellLevel - 1); var maxCasterLevel = CharacterCasterLevel(caster.Descriptor, spellbook); if (minCasterLevel < maxCasterLevel) { - selectedCasterLevel = UmmUiRenderer.RenderIntSlider(selectedCasterLevel, "Caster level: ", minCasterLevel, maxCasterLevel); + selectedCasterLevel = UmmUiRenderer.RenderIntSlider("Caster level: ", selectedCasterLevel, minCasterLevel, maxCasterLevel); } else { selectedCasterLevel = minCasterLevel; UmmUiRenderer.RenderLabel($"Caster level: {selectedCasterLevel}"); @@ -1705,10 +1705,10 @@ private static void RenderCastSpellNTimes(UnitEntityData caster, RecipeBasedItem // Choose a caster level var minCasterLevel = Math.Max(equipment == null ? 0 : equipment.CasterLevel, Math.Max(1, 2 * spellLevel - 1)); - selectedCasterLevel = UmmUiRenderer.RenderIntSlider(selectedCasterLevel, "Caster level: ", minCasterLevel, 20); + selectedCasterLevel = UmmUiRenderer.RenderIntSlider("Caster level: ", selectedCasterLevel, minCasterLevel, 20); // Choose number of times per day var maxCastsPerDay = equipment == null ? 10 : ((equipment.Charges + 10) / 10) * 10; - selectedCastsPerDay = UmmUiRenderer.RenderIntSlider(selectedCastsPerDay, "Casts per day: ", equipment == null ? 1 : equipment.Charges, maxCastsPerDay); + selectedCastsPerDay = UmmUiRenderer.RenderIntSlider("Casts per day: ", selectedCastsPerDay, equipment == null ? 1 : equipment.Charges, maxCastsPerDay); if (equipment != null && ability == equipment.Ability && selectedCasterLevel == equipment.CasterLevel && selectedCastsPerDay == equipment.Charges) { UmmUiRenderer.RenderLabel($"No changes made to {equipment.Name}"); return; @@ -2216,7 +2216,7 @@ private static void RenderFeatReassignmentSection() { } private static void RenderCheatsSection() { - ModSettings.CraftingCostsNoGold = UmmUiRenderer.RenderCheckbox(ModSettings.CraftingCostsNoGold, "Crafting costs no gold and no material components."); + ModSettings.CraftingCostsNoGold = UmmUiRenderer.RenderCheckbox("Crafting costs no gold and no material components.", ModSettings.CraftingCostsNoGold); if (!ModSettings.CraftingCostsNoGold) { var selectedCustomPriceScaleIndex = RenderSelection(CustomPriceLabel, CraftingPriceStrings, 4); if (selectedCustomPriceScaleIndex == 2) { @@ -2236,17 +2236,17 @@ private static void RenderCheatsSection() { } } - ModSettings.IgnoreCraftingFeats = UmmUiRenderer.RenderCheckbox(ModSettings.IgnoreCraftingFeats, "Crafting does not require characters to take crafting feats."); - ModSettings.CraftingTakesNoTime = UmmUiRenderer.RenderCheckbox(ModSettings.CraftingTakesNoTime, "Crafting takes no time to complete."); + ModSettings.IgnoreCraftingFeats = UmmUiRenderer.RenderCheckbox("Crafting does not require characters to take crafting feats.", ModSettings.IgnoreCraftingFeats); + ModSettings.CraftingTakesNoTime = UmmUiRenderer.RenderCheckbox("Crafting takes no time to complete.", ModSettings.CraftingTakesNoTime); if (!ModSettings.CraftingTakesNoTime) { - ModSettings.CustomCraftRate = UmmUiRenderer.RenderCheckbox(ModSettings.CustomCraftRate, "Craft at a non-standard rate."); + ModSettings.CustomCraftRate = UmmUiRenderer.RenderCheckbox("Craft at a non-standard rate.", ModSettings.CustomCraftRate); if (ModSettings.CustomCraftRate) { var maxMagicRate = ((ModSettings.MagicCraftingRate + 1000) / 1000) * 1000; - ModSettings.MagicCraftingRate = UmmUiRenderer.RenderIntSlider(ModSettings.MagicCraftingRate, "Magic Item Crafting Rate", 1, maxMagicRate); + ModSettings.MagicCraftingRate = UmmUiRenderer.RenderIntSlider("Magic Item Crafting Rate", ModSettings.MagicCraftingRate, 1, maxMagicRate); var maxMundaneRate = ((ModSettings.MundaneCraftingRate + 10) / 10) * 10; - ModSettings.MundaneCraftingRate = UmmUiRenderer.RenderIntSlider(ModSettings.MundaneCraftingRate, "Mundane Item Crafting Rate", 1, maxMundaneRate); + ModSettings.MundaneCraftingRate = UmmUiRenderer.RenderIntSlider("Mundane Item Crafting Rate", ModSettings.MundaneCraftingRate, 1, maxMundaneRate); } else { @@ -2254,11 +2254,11 @@ private static void RenderCheatsSection() { ModSettings.MundaneCraftingRate = Settings.MundaneCraftingProgressPerDay; } } - ModSettings.CasterLevelIsSinglePrerequisite = UmmUiRenderer.RenderCheckbox(ModSettings.CasterLevelIsSinglePrerequisite, - "When crafting, a Caster Level less than the prerequisite counts as a single missing prerequisite."); - ModSettings.CraftAtFullSpeedWhileAdventuring = UmmUiRenderer.RenderCheckbox(ModSettings.CraftAtFullSpeedWhileAdventuring, "Characters craft at full speed while adventuring (instead of 25% speed)."); - ModSettings.IgnorePlusTenItemMaximum = UmmUiRenderer.RenderCheckbox(ModSettings.IgnorePlusTenItemMaximum, "Ignore the rule that limits arms and armor to a maximum of +10 equivalent."); - ModSettings.IgnoreFeatCasterLevelRestriction = UmmUiRenderer.RenderCheckbox(ModSettings.IgnoreFeatCasterLevelRestriction, "Ignore the crafting feat Caster Level prerequisites when learning feats."); + ModSettings.CasterLevelIsSinglePrerequisite = UmmUiRenderer.RenderCheckbox("When crafting, a Caster Level less than the prerequisite counts as a single missing prerequisite.", + ModSettings.CasterLevelIsSinglePrerequisite); + ModSettings.CraftAtFullSpeedWhileAdventuring = UmmUiRenderer.RenderCheckbox("Characters craft at full speed while adventuring (instead of 25% speed).", ModSettings.CraftAtFullSpeedWhileAdventuring); + ModSettings.IgnorePlusTenItemMaximum = UmmUiRenderer.RenderCheckbox("Ignore the rule that limits arms and armor to a maximum of +10 equivalent.", ModSettings.IgnorePlusTenItemMaximum); + ModSettings.IgnoreFeatCasterLevelRestriction = UmmUiRenderer.RenderCheckbox("Ignore the crafting feat Caster Level prerequisites when learning feats.", ModSettings.IgnoreFeatCasterLevelRestriction); } private static bool IsPlayerSomewhereSafe() { diff --git a/CraftMagicItems/UI/UmmUiRenderer.cs b/CraftMagicItems/UI/UmmUiRenderer.cs index 3f00483..a8e57c4 100644 --- a/CraftMagicItems/UI/UmmUiRenderer.cs +++ b/CraftMagicItems/UI/UmmUiRenderer.cs @@ -6,10 +6,10 @@ namespace CraftMagicItems.UI public class UmmUiRenderer { /// Renders a checkbox in Unity Mod Manager - /// Currently selected value /// Label for the checkbox + /// Currently selected value /// The inverse of when the button is clicked, otherwise - public static bool RenderCheckbox(bool value, string label) + public static bool RenderCheckbox(string label, bool value) { GUILayout.BeginHorizontal(); var text = value ? "✔" : "✖"; @@ -49,12 +49,12 @@ public static string RenderCustomNameField(string defaultValue, string selectedC } /// Renders an integer selection slider - /// Initial value /// Label for the slider + /// Initial value /// Minimum possible value /// Maximum possible value /// Returns the value selected by the user, clamped and rounded after rendering controls to the screen - public static int RenderIntSlider(int value, string label, int min, int max) + public static int RenderIntSlider(string label, int value, int min, int max) { value = Mathf.Clamp(value, min, max); GUILayout.BeginHorizontal(); @@ -67,10 +67,10 @@ public static int RenderIntSlider(int value, string label, int min, int max) } /// Renders a toggle-able section selection in Unity Mod Manager for the user to show/hide - /// Flag indicating whether the toggle is active /// Label for the toggle + /// Flag indicating whether the toggle is active /// Whether the toggle is currently active in Unity Mod Manager - public static bool RenderToggleSection(bool value, string label) + public static bool RenderToggleSection(string label, bool value) { GUILayout.BeginVertical("box"); GUILayout.BeginHorizontal(); From 77c2227aa556250909c47e143732568b81969a3f Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Fri, 26 Jun 2020 17:09:59 -0500 Subject: [PATCH 017/132] Moving RenderSelection to UmmUiRenderer --- CraftMagicItems/Main.cs | 54 ++++++++++------------------- CraftMagicItems/UI/UmmUiRenderer.cs | 38 +++++++++++++++++++- 2 files changed, 56 insertions(+), 36 deletions(-) diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index c51cd31..11dd8b4 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -558,7 +558,7 @@ private static void RenderCraftMagicItemsSection() { .PrependConditional(hasBondedItemFeature, new L10NString("craftMagicItems-bonded-object-name")).ToArray(); //render whatever the user has selected - var selectedItemTypeIndex = RenderSelection("Crafting: ", itemTypeNames, 6, ref selectedCustomName, false); + var selectedItemTypeIndex = UmmUiRenderer.RenderSelection("Crafting: ", itemTypeNames, 6, ref selectedCustomName, GetSelectionIndex, SetSelectionIndex, false); //render options for actual selection if (hasBondedItemFeature && selectedItemTypeIndex == 0) { @@ -1238,12 +1238,12 @@ private static void RenderRecipeBasedCrafting(UnitEntityData caster, RecipeBased var selectedItemSlotIndex = 0; if (craftingData.Slots.Length > 1) { var names = craftingData.Slots.Select(slot => new L10NString(GetSlotStringKey(slot, craftingData.SlotRestrictions)).ToString()).ToArray(); - selectedItemSlotIndex = RenderSelection("Item type", names, 10, ref selectedCustomName); + selectedItemSlotIndex = UmmUiRenderer.RenderSelection("Item type", names, 10, ref selectedCustomName, GetSelectionIndex, SetSelectionIndex); } var locationFilter = ItemLocationFilter.All; var locationNames = Enum.GetNames(typeof(ItemLocationFilter)); - locationFilter = (ItemLocationFilter)RenderSelection("Item location", locationNames, locationNames.Length, ref selectedCustomName); + locationFilter = (ItemLocationFilter)UmmUiRenderer.RenderSelection("Item location", locationNames, locationNames.Length, ref selectedCustomName, GetSelectionIndex, SetSelectionIndex); selectedSlot = craftingData.Slots[selectedItemSlotIndex]; var playerInCapital = IsPlayerInCapital(); @@ -1278,7 +1278,7 @@ private static void RenderRecipeBasedCrafting(UnitEntityData caster, RecipeBased return; } - var selectedUpgradeItemIndex = RenderSelection("Item: ", itemNames, 5, ref selectedCustomName); + var selectedUpgradeItemIndex = UmmUiRenderer.RenderSelection("Item: ", itemNames, 5, ref selectedCustomName, GetSelectionIndex, SetSelectionIndex); // See existing item details and enchantments. var index = selectedUpgradeItemIndex - (canCreateNew ? 1 : 0); upgradeItem = index < 0 ? null : items[index]; @@ -1340,7 +1340,7 @@ private static void RenderRecipeBasedCrafting(UnitEntityData caster, RecipeBased ? new string[0] : new[] {new L10NString("craftMagicItems-label-cast-spell-n-times").ToString()}) .ToArray(); - var selectedRecipeIndex = RenderSelection("Enchantment: ", recipeNames, 5, ref selectedCustomName); + var selectedRecipeIndex = UmmUiRenderer.RenderSelection("Enchantment: ", recipeNames, 5, ref selectedCustomName, GetSelectionIndex, SetSelectionIndex); if (selectedRecipeIndex == availableRecipes.Length) { // Cast spell N times RenderCastSpellNTimes(caster, craftingData, upgradeItemShield ?? upgradeItem, selectedSlot); @@ -1354,7 +1354,7 @@ private static void RenderRecipeBasedCrafting(UnitEntityData caster, RecipeBased .OrderBy(recipe => recipe.NameId) .ToArray(); recipeNames = availableSubRecipes.Select(recipe => recipe.NameId).ToArray(); - var selectedSubRecipeIndex = RenderSelection(category + ": ", recipeNames, 5, ref selectedCustomName); + var selectedSubRecipeIndex = UmmUiRenderer.RenderSelection(category + ": ", recipeNames, 5, ref selectedCustomName, GetSelectionIndex, SetSelectionIndex); selectedRecipe = availableSubRecipes[selectedSubRecipeIndex]; } @@ -1649,12 +1649,12 @@ private static void RenderCastSpellNTimes(UnitEntityData caster, RecipeBasedItem // Choose a spellbook known to the caster var spellbooks = caster.Descriptor.Spellbooks.ToList(); var spellBookNames = spellbooks.Select(book => book.Blueprint.Name.ToString()).Concat(Enumerable.Repeat("From Items", 1)).ToArray(); - var selectedSpellbookIndex = RenderSelection("Source: ", spellBookNames, 10, ref selectedCustomName); + var selectedSpellbookIndex = UmmUiRenderer.RenderSelection("Source: ", spellBookNames, 10, ref selectedCustomName, GetSelectionIndex, SetSelectionIndex); if (selectedSpellbookIndex < spellbooks.Count) { var spellbook = spellbooks[selectedSpellbookIndex]; // Choose a spell level var spellLevelNames = Enumerable.Range(0, spellbook.Blueprint.MaxSpellLevel + 1).Select(index => $"Level {index}").ToArray(); - spellLevel = RenderSelection("Spell level: ", spellLevelNames, 10, ref selectedCustomName); + spellLevel = UmmUiRenderer.RenderSelection("Spell level: ", spellLevelNames, 10, ref selectedCustomName, GetSelectionIndex, SetSelectionIndex); var specialSpellLists = Accessors.GetSpellbookSpecialLists(spellbook); var spellOptions = spellbook.Blueprint.SpellList.GetSpells(spellLevel) .Concat(specialSpellLists.Aggregate(new List(), (allSpecial, spellList) => spellList.GetSpells(spellLevel))) @@ -1667,11 +1667,11 @@ private static void RenderCastSpellNTimes(UnitEntityData caster, RecipeBasedItem } var spellNames = spellOptions.Select(spell => spell.Name).ToArray(); - var selectedSpellIndex = RenderSelection("Spell: ", spellNames, 4, ref selectedCustomName); + var selectedSpellIndex = UmmUiRenderer.RenderSelection("Spell: ", spellNames, 4, ref selectedCustomName, GetSelectionIndex, SetSelectionIndex); ability = spellOptions[selectedSpellIndex]; if (ability.HasVariants && ability.Variants != null) { var selectedVariantIndex = - RenderSelection("Variant: ", ability.Variants.Select(spell => spell.Name).ToArray(), 4, ref selectedCustomName); + UmmUiRenderer.RenderSelection("Variant: ", ability.Variants.Select(spell => spell.Name).ToArray(), 4, ref selectedCustomName, GetSelectionIndex, SetSelectionIndex); ability = ability.Variants[selectedVariantIndex]; } } else { @@ -1689,7 +1689,7 @@ private static void RenderCastSpellNTimes(UnitEntityData caster, RecipeBasedItem return; } var itemNames = itemBlueprints.Select(item => item.Name).ToArray(); - var itemIndex = RenderSelection("Cast from item: ", itemNames, 5, ref selectedCustomName); + var itemIndex = UmmUiRenderer.RenderSelection("Cast from item: ", itemNames, 5, ref selectedCustomName, GetSelectionIndex, SetSelectionIndex); var selectedItemBlueprint = itemBlueprints[itemIndex]; ability = selectedItemBlueprint.Ability; spellLevel = selectedItemBlueprint.SpellLevel; @@ -1935,7 +1935,7 @@ private static void RenderCraftMundaneItemsSection() { .ToArray(); var itemTypeNames = itemTypes.Select(data => new L10NString(data.ParentNameId ?? data.NameId).ToString()).ToArray(); var selectedItemTypeIndex = upgradingBlueprint == null - ? RenderSelection("Mundane Crafting: ", itemTypeNames, 6, ref selectedCustomName) + ? UmmUiRenderer.RenderSelection("Mundane Crafting: ", itemTypeNames, 6, ref selectedCustomName, GetSelectionIndex, SetSelectionIndex) : GetSelectionIndex("Mundane Crafting: "); var selectedCraftingData = itemTypes[selectedItemTypeIndex]; @@ -1972,7 +1972,7 @@ private static void RenderCraftMundaneItemsSection() { return; } - var selectedUpgradeItemIndex = RenderSelection("Item: ", blueprintNames, 5, ref selectedCustomName); + var selectedUpgradeItemIndex = UmmUiRenderer.RenderSelection("Item: ", blueprintNames, 5, ref selectedCustomName, GetSelectionIndex, SetSelectionIndex); baseBlueprint = blueprints[selectedUpgradeItemIndex]; // See existing item details and enchantments. UmmUiRenderer.RenderLabel(baseBlueprint.Description); @@ -1989,7 +1989,7 @@ private static void RenderCraftMundaneItemsSection() { .OrderBy(recipe => recipe.NameId) .ToArray(); var recipeNames = availableRecipes.Select(recipe => recipe.NameId).ToArray(); - var selectedRecipeIndex = RenderSelection("Craft: ", recipeNames, 6, ref selectedCustomName); + var selectedRecipeIndex = UmmUiRenderer.RenderSelection("Craft: ", recipeNames, 6, ref selectedCustomName, GetSelectionIndex, SetSelectionIndex); var selectedRecipe = availableRecipes.Any() ? availableRecipes[selectedRecipeIndex] : null; var selectedEnchantment = selectedRecipe?.Enchantments.Length == 1 ? selectedRecipe.Enchantments[0] : null; if (selectedRecipe != null && selectedRecipe.Material != 0) { @@ -2299,7 +2299,7 @@ private static UnitEntityData GetSelectedCrafter(bool render) { var partyNames = characters.Select(entity => $"{entity.CharacterName}" + $"{((GetCraftingTimerComponentForCaster(entity.Descriptor)?.CraftingProjects.Any() ?? false) ? "*" : "")}") .ToArray(); - selectedSpellcasterIndex = RenderSelection(label, partyNames, 8, ref upgradingBlueprint); + selectedSpellcasterIndex = UmmUiRenderer.RenderSelection(label, partyNames, 8, ref upgradingBlueprint, GetSelectionIndex, SetSelectionIndex); } if (selectedSpellcasterIndex >= characters.Length) { selectedSpellcasterIndex = 0; @@ -2309,32 +2309,16 @@ private static UnitEntityData GetSelectedCrafter(bool render) { private static int RenderSelection(string label, string[] options, int xCount) { var dummy = ""; - return RenderSelection(label, options, xCount, ref dummy); + return UmmUiRenderer.RenderSelection(label, options, xCount, ref dummy, GetSelectionIndex, SetSelectionIndex); } private static int GetSelectionIndex(string label) { return SelectedIndex.ContainsKey(label) ? SelectedIndex[label] : 0; } - private static int RenderSelection(string label, string[] options, int xCount, ref T emptyOnChange, bool addSpace = true) { - var index = GetSelectionIndex(label); - if (index >= options.Length) { - index = 0; - } - - if (addSpace) { - GUILayout.Space(20); - } - GUILayout.BeginHorizontal(); - GUILayout.Label(label, GUILayout.ExpandWidth(false)); - var newIndex = GUILayout.SelectionGrid(index, options, xCount); - if (index != newIndex) { - emptyOnChange = default(T); - } - - GUILayout.EndHorizontal(); - SelectedIndex[label] = newIndex; - return newIndex; + private static void SetSelectionIndex(string label, int value) + { + SelectedIndex[label] = value; } public static void AddItemBlueprintForSpell(UsableItemType itemType, BlueprintItemEquipment itemBlueprint) { diff --git a/CraftMagicItems/UI/UmmUiRenderer.cs b/CraftMagicItems/UI/UmmUiRenderer.cs index a8e57c4..0edd616 100644 --- a/CraftMagicItems/UI/UmmUiRenderer.cs +++ b/CraftMagicItems/UI/UmmUiRenderer.cs @@ -1,4 +1,5 @@ -using UnityEngine; +using System; +using UnityEngine; namespace CraftMagicItems.UI { @@ -89,5 +90,40 @@ public static void RenderLabel(string label) GUILayout.Label(label); GUILayout.EndHorizontal(); } + + /// Renders a selection of to Unity Mod Manager + /// Type of item being rendered + /// Label for the selection + /// Options for the selection + /// How many elements to fit in the horizontal direction + /// Value to write to when the indexes do not match + /// Used to retrieve the selection index + /// Used to write the value of the selection index + /// Space the UI by 20 pixels? + /// The new index of the selection + public static int RenderSelection(string label, string[] options, int horizontalCount, ref T emptyOnChange, Func GetSelectionIndex, Action SetSelectionIndex, bool addSpace = true) + { + var index = GetSelectionIndex(label); + if (index >= options.Length) + { + index = 0; + } + + if (addSpace) + { + GUILayout.Space(20); + } + GUILayout.BeginHorizontal(); + GUILayout.Label(label, GUILayout.ExpandWidth(false)); + var newIndex = GUILayout.SelectionGrid(index, options, horizontalCount); + if (index != newIndex) + { + emptyOnChange = default(T); + } + + GUILayout.EndHorizontal(); + SetSelectionIndex(label, newIndex); + return newIndex; + } } } \ No newline at end of file From 702ae9051de5d667a6da24e25e55a788bba2c250 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Fri, 26 Jun 2020 17:15:53 -0500 Subject: [PATCH 018/132] Moving overlaoded RenderSelection to UmmUiRenderer --- CraftMagicItems/Main.cs | 25 ++++++++++--------------- CraftMagicItems/UI/UmmUiRenderer.cs | 14 ++++++++++++++ 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 11dd8b4..a618a8a 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -643,7 +643,7 @@ private static void RenderBondedItemCrafting(UnitEntityData caster) { UmmUiRenderer.RenderLabel(new L10NString("craftMagicItems-bonded-item-glossary")); UmmUiRenderer.RenderLabel("Choose your bonded item."); var names = BondedItemSlots.Select(slot => new L10NString(GetSlotStringKey(slot, null)).ToString()).ToArray(); - var selectedItemSlotIndex = RenderSelection("Item type", names, 10); + var selectedItemSlotIndex = UmmUiRenderer.RenderSelection("Item type", names, 10, GetSelectionIndex, SetSelectionIndex); var selectedSlot = BondedItemSlots[selectedItemSlotIndex]; var items = Game.Instance.Player.Inventory .Where(item => item.Blueprint is BlueprintItemEquipment blueprint @@ -659,7 +659,7 @@ private static void RenderBondedItemCrafting(UnitEntityData caster) { return; } var itemNames = items.Select(item => item.Name).ToArray(); - var selectedUpgradeItemIndex = RenderSelection("Item: ", itemNames, 5); + var selectedUpgradeItemIndex = UmmUiRenderer.RenderSelection("Item: ", itemNames, 5, GetSelectionIndex, SetSelectionIndex); var selectedItem = items[selectedUpgradeItemIndex]; var goldCost = !selectedBondWithNewObject || ModSettings.CraftingCostsNoGold ? 0 : 200 * characterCasterLevel; var canAfford = BuildCostString(out var cost, null, goldCost); @@ -715,13 +715,13 @@ private static void RenderSpellBasedCrafting(UnitEntityData caster, SpellBasedIt var selectedSpellbookIndex = 0; if (spellbooks.Count != 1) { var spellBookNames = spellbooks.Select(book => book.Blueprint.Name.ToString()).ToArray(); - selectedSpellbookIndex = RenderSelection("Class: ", spellBookNames, 10); + selectedSpellbookIndex = UmmUiRenderer.RenderSelection("Class: ", spellBookNames, 10, GetSelectionIndex, SetSelectionIndex); } var spellbook = spellbooks[selectedSpellbookIndex]; var maxLevel = Math.Min(spellbook.MaxSpellLevel, craftingData.MaxSpellLevel); var spellLevelNames = Enumerable.Range(0, maxLevel + 1).Select(index => $"Level {index}").ToArray(); - var spellLevel = RenderSelection("Spell level: ", spellLevelNames, 10); + var spellLevel = UmmUiRenderer.RenderSelection("Spell level: ", spellLevelNames, 10, GetSelectionIndex, SetSelectionIndex); if (spellLevel > 0 && !spellbook.Blueprint.Spontaneous) { if (ModSettings.CraftingTakesNoTime) { selectedShowPreparedSpells = true; @@ -1395,7 +1395,7 @@ private static void RenderRecipeBasedCrafting(UnitEntityData caster, RecipeBased counter++; return enchantment.Name.Empty() ? GetBonusString(counter, selectedRecipe) : enchantment.Name; }); - selectedEnchantmentIndex = RenderSelection("", enchantmentNames.ToArray(), 6); + selectedEnchantmentIndex = UmmUiRenderer.RenderSelection("", enchantmentNames.ToArray(), 6, GetSelectionIndex, SetSelectionIndex); } selectedEnchantment = availableEnchantments[selectedEnchantmentIndex]; @@ -1943,7 +1943,7 @@ private static void RenderCraftMundaneItemsSection() { itemTypeNames = SubCraftingData[selectedCraftingData.ParentNameId].Select(data => new L10NString(data.NameId).ToString()).ToArray(); var label = new L10NString(selectedCraftingData.ParentNameId) + ": "; var selectedItemSubTypeIndex = upgradingBlueprint == null - ? RenderSelection(label, itemTypeNames, 6) + ? UmmUiRenderer.RenderSelection(label, itemTypeNames, 6, GetSelectionIndex, SetSelectionIndex) : GetSelectionIndex(label); selectedCraftingData = SubCraftingData[selectedCraftingData.ParentNameId][selectedItemSubTypeIndex]; @@ -2170,9 +2170,9 @@ private static void RenderFeatReassignmentSection() { return; } - UmmUiRenderer.RenderLabel( - "Use this section to reassign previous feat choices for this character to crafting feats. Warning: This is a one-way assignment!"); - var selectedFeatToLearn = RenderSelection("Feat to learn", missingFeats.Select(data => new L10NString(data.NameId).ToString()).ToArray(), 6); + UmmUiRenderer.RenderLabel("Use this section to reassign previous feat choices for this character to crafting feats. Warning: This is a one-way assignment!"); + var featOptions = missingFeats.Select(data => new L10NString(data.NameId).ToString()).ToArray(); + var selectedFeatToLearn = UmmUiRenderer.RenderSelection("Feat to learn", featOptions, 6, GetSelectionIndex, SetSelectionIndex); var learnFeatData = missingFeats[selectedFeatToLearn]; var learnFeat = ResourcesLibrary.TryGetBlueprint(learnFeatData.FeatGuid); if (learnFeat == null) { @@ -2218,7 +2218,7 @@ private static void RenderFeatReassignmentSection() { private static void RenderCheatsSection() { ModSettings.CraftingCostsNoGold = UmmUiRenderer.RenderCheckbox("Crafting costs no gold and no material components.", ModSettings.CraftingCostsNoGold); if (!ModSettings.CraftingCostsNoGold) { - var selectedCustomPriceScaleIndex = RenderSelection(CustomPriceLabel, CraftingPriceStrings, 4); + var selectedCustomPriceScaleIndex = UmmUiRenderer.RenderSelection(CustomPriceLabel, CraftingPriceStrings, 4, GetSelectionIndex, SetSelectionIndex); if (selectedCustomPriceScaleIndex == 2) { GUILayout.BeginHorizontal(); GUILayout.Label("Custom Cost Factor: ", GUILayout.ExpandWidth(false)); @@ -2307,11 +2307,6 @@ private static UnitEntityData GetSelectedCrafter(bool render) { return characters[selectedSpellcasterIndex]; } - private static int RenderSelection(string label, string[] options, int xCount) { - var dummy = ""; - return UmmUiRenderer.RenderSelection(label, options, xCount, ref dummy, GetSelectionIndex, SetSelectionIndex); - } - private static int GetSelectionIndex(string label) { return SelectedIndex.ContainsKey(label) ? SelectedIndex[label] : 0; } diff --git a/CraftMagicItems/UI/UmmUiRenderer.cs b/CraftMagicItems/UI/UmmUiRenderer.cs index 0edd616..1b24433 100644 --- a/CraftMagicItems/UI/UmmUiRenderer.cs +++ b/CraftMagicItems/UI/UmmUiRenderer.cs @@ -91,6 +91,20 @@ public static void RenderLabel(string label) GUILayout.EndHorizontal(); } + /// Renders a selection of to Unity Mod Manager + /// Type of item being rendered + /// Label for the selection + /// Options for the selection + /// How many elements to fit in the horizontal direction + /// Used to retrieve the selection index + /// Used to write the value of the selection index + /// The new index of the selection + public static int RenderSelection(string label, string[] options, int horizontalCount, Func GetSelectionIndex, Action SetSelectionIndex) + { + var dummy = ""; + return RenderSelection(label, options, horizontalCount, ref dummy, GetSelectionIndex, SetSelectionIndex); + } + /// Renders a selection of to Unity Mod Manager /// Type of item being rendered /// Label for the selection From 7c76894fe0d4a60f9a8da4bd3617ad42639de1f5 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 29 Jun 2020 15:15:40 -0500 Subject: [PATCH 019/132] Add param for rendering a Label on its own horizontal --- CraftMagicItems/UI/UmmUiRenderer.cs | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/CraftMagicItems/UI/UmmUiRenderer.cs b/CraftMagicItems/UI/UmmUiRenderer.cs index 1b24433..5df0886 100644 --- a/CraftMagicItems/UI/UmmUiRenderer.cs +++ b/CraftMagicItems/UI/UmmUiRenderer.cs @@ -86,9 +86,25 @@ public static bool RenderToggleSection(string label, bool value) /// Text to be displayed public static void RenderLabel(string label) { - GUILayout.BeginHorizontal(); + RenderLabel(label, true); + } + + /// Renders a Label control as its own line in Unity Mod Manager + /// Text to be displayed + /// Should the label be rendered on its own horizontal line? + public static void RenderLabel(string label, bool onItsOwnLine) + { + if (onItsOwnLine) + { + GUILayout.BeginHorizontal(); + } + GUILayout.Label(label); - GUILayout.EndHorizontal(); + + if (onItsOwnLine) + { + GUILayout.EndHorizontal(); + } } /// Renders a selection of to Unity Mod Manager From c1f9cf4a5e2579cfda2ad3ab08d2ef55086fa2e0 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 29 Jun 2020 15:21:19 -0500 Subject: [PATCH 020/132] Extracting the spell-based item creation from the render control into its own code. --- CraftMagicItems/Main.cs | 149 +++++++++++++++++++++++++--------------- 1 file changed, 93 insertions(+), 56 deletions(-) diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index a618a8a..c01238d 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -779,16 +779,23 @@ private static void RenderSpellBasedCrafting(UnitEntityData caster, SpellBasedIt } } - foreach (var spell in spellOptions.OrderBy(spell => spell.Name).GroupBy(spell => spell.Name).Select(group => group.First())) { - if (spell.MetamagicData != null && spell.MetamagicData.NotEmpty) { + foreach (var spell in spellOptions.OrderBy(spell => spell.Name).GroupBy(spell => spell.Name).Select(group => group.First())) + { + if (spell.MetamagicData != null && spell.MetamagicData.NotEmpty) + { GUILayout.Label($"Cannot craft {new L10NString(craftingData.NameId)} of {spell.Name} with metamagic applied."); - } else if (spell.Blueprint.Variants != null) { + } + else if (spell.Blueprint.Variants != null) + { // Spells with choices (e.g. Protection from Alignment, which can be Protection from Evil, Good, Chaos or Law) - foreach (var variant in spell.Blueprint.Variants) { - RenderSpellBasedCraftItemControl(caster, craftingData, spell, variant, spellLevel, selectedCasterLevel); + foreach (var variant in spell.Blueprint.Variants) + { + AttemptSpellBasedCraftItemAndRender(caster, craftingData, spell, variant, spellLevel, selectedCasterLevel); } - } else { - RenderSpellBasedCraftItemControl(caster, craftingData, spell, spell.Blueprint, spellLevel, selectedCasterLevel); + } + else + { + AttemptSpellBasedCraftItemAndRender(caster, craftingData, spell, spell.Blueprint, spellLevel, selectedCasterLevel); } } } @@ -2541,11 +2548,14 @@ private static void CalculateProjectEstimate(CraftingProjectData project) { AddBattleLogMessage(project.LastMessage); } - private static void RenderSpellBasedCraftItemControl(UnitEntityData caster, SpellBasedItemCraftingData craftingData, AbilityData spell, - BlueprintAbility spellBlueprint, int spellLevel, int casterLevel) { + private static void AttemptSpellBasedCraftItemAndRender(UnitEntityData caster, SpellBasedItemCraftingData craftingData, + AbilityData spell, BlueprintAbility spellBlueprint, int spellLevel, int casterLevel) + { var itemBlueprintList = FindItemBlueprintsForSpell(spellBlueprint, craftingData.UsableItemType); - if (itemBlueprintList == null && craftingData.NewItemBaseIDs == null) { - GUILayout.Label(L10NFormat("craftMagicItems-label-no-item-exists", new L10NString(craftingData.NamePrefixId), spellBlueprint.Name)); + if (itemBlueprintList == null && craftingData.NewItemBaseIDs == null) + { + var message = L10NFormat("craftMagicItems-label-no-item-exists", new L10NString(craftingData.NamePrefixId), spellBlueprint.Name); + UmmUiRenderer.RenderLabel(message, false); return; } @@ -2557,56 +2567,83 @@ private static void RenderSpellBasedCraftItemControl(UnitEntityData caster, Spel ? new L10NString("craftMagicItems-label-custom").ToString() : ""; var label = L10NFormat("craftMagicItems-label-craft-spell-item", custom, new L10NString(craftingData.NamePrefixId), spellBlueprint.Name, cost); - if (!canAfford) { - GUILayout.Label(label); - } else if (GUILayout.Button(label, GUILayout.ExpandWidth(false))) { - BlueprintItem itemBlueprint; - if (itemBlueprintList == null) { - // No items for that spell exist at all - create a custom blueprint with casterLevel, spellLevel and spellId - var blueprintId = - blueprintPatcher.BuildCustomSpellItemGuid(RandomBaseBlueprintId(craftingData).AssetGuid, casterLevel, spellLevel, spellBlueprint.AssetGuid); - itemBlueprint = ResourcesLibrary.TryGetBlueprint(blueprintId); - } else if (existingItemBlueprint == null) { - // No item for this spell & caster level - create a custom blueprint with casterLevel and optionally SpellLevel - var blueprintId = blueprintPatcher.BuildCustomSpellItemGuid(itemBlueprintList[0].AssetGuid, casterLevel, - itemBlueprintList[0].SpellLevel == spellLevel ? -1 : spellLevel); - itemBlueprint = ResourcesLibrary.TryGetBlueprint(blueprintId); - } else { - // Item with matching spell, level and caster level exists. Use that. - itemBlueprint = existingItemBlueprint; - } - if (itemBlueprint == null) { - throw new Exception( - $"Unable to build blueprint for spellId {spellBlueprint.AssetGuid}, spell level {spellLevel}, caster level {casterLevel}"); - } + //if the player cannot afford the time (not enough gold), alert them + if (!canAfford) + { + UmmUiRenderer.RenderLabel(label, false); + } + // ... otherwise let them spend their money + else if (GUILayout.Button(label, GUILayout.ExpandWidth(false))) + { + BeginCraftingSpellBasedItem(caster, craftingData, spell, spellBlueprint, spellLevel, casterLevel, itemBlueprintList, existingItemBlueprint, requiredProgress, goldCost, cost); + } + } - // Pay gold and material components up front. - if (ModSettings.CraftingCostsNoGold) { - goldCost = 0; - } else { - Game.Instance.UI.Common.UISound.Play(UISoundType.LootCollectGold); - Game.Instance.Player.SpendMoney(goldCost); - if (spellBlueprint.MaterialComponent.Item != null) { - Game.Instance.Player.Inventory.Remove(spellBlueprint.MaterialComponent.Item, - spellBlueprint.MaterialComponent.Count * craftingData.Charges); - } - } + private static void BeginCraftingSpellBasedItem(UnitEntityData caster, SpellBasedItemCraftingData craftingData, + AbilityData spell, BlueprintAbility spellBlueprint, Int32 spellLevel, Int32 casterLevel, + List itemBlueprintList, BlueprintItemEquipment existingItemBlueprint, + Int32 requiredProgress, Int32 goldCost, String cost) + { + BlueprintItem itemBlueprint; + if (itemBlueprintList == null) + { + // No items for that spell exist at all - create a custom blueprint with casterLevel, spellLevel and spellId + var blueprintId = blueprintPatcher.BuildCustomSpellItemGuid(RandomBaseBlueprintId(craftingData).AssetGuid, + casterLevel, spellLevel, spellBlueprint.AssetGuid); + itemBlueprint = ResourcesLibrary.TryGetBlueprint(blueprintId); + } + else if (existingItemBlueprint == null) + { + // No item for this spell & caster level - create a custom blueprint with casterLevel and optionally SpellLevel + var blueprintId = blueprintPatcher.BuildCustomSpellItemGuid(itemBlueprintList[0].AssetGuid, casterLevel, + itemBlueprintList[0].SpellLevel == spellLevel ? -1 : spellLevel); + itemBlueprint = ResourcesLibrary.TryGetBlueprint(blueprintId); + } + else + { + // Item with matching spell, level and caster level exists. Use that. + itemBlueprint = existingItemBlueprint; + } - var resultItem = BuildItemEntity(itemBlueprint, craftingData, caster); - AddBattleLogMessage(L10NFormat("craftMagicItems-logMessage-begin-crafting", cost, itemBlueprint.Name), resultItem); - if (ModSettings.CraftingTakesNoTime) { - AddBattleLogMessage(L10NFormat("craftMagicItems-logMessage-expend-spell", spell.Name)); - spell.SpendFromSpellbook(); - CraftItem(resultItem); - } else { - var project = new CraftingProjectData(caster, requiredProgress, goldCost, casterLevel, resultItem, craftingData.Name, null, - new[] {spellBlueprint}); - AddNewProject(caster.Descriptor, project); - CalculateProjectEstimate(project); - currentSection = OpenSection.ProjectsSection; + if (itemBlueprint == null) + { + throw new Exception( + $"Unable to build blueprint for spellId {spellBlueprint.AssetGuid}, spell level {spellLevel}, caster level {casterLevel}"); + } + + // Pay gold and material components up front. + if (ModSettings.CraftingCostsNoGold) + { + goldCost = 0; + } + else + { + Game.Instance.UI.Common.UISound.Play(UISoundType.LootCollectGold); + Game.Instance.Player.SpendMoney(goldCost); + if (spellBlueprint.MaterialComponent.Item != null) + { + Game.Instance.Player.Inventory.Remove(spellBlueprint.MaterialComponent.Item, + spellBlueprint.MaterialComponent.Count * craftingData.Charges); } } + + var resultItem = BuildItemEntity(itemBlueprint, craftingData, caster); + AddBattleLogMessage(L10NFormat("craftMagicItems-logMessage-begin-crafting", cost, itemBlueprint.Name), resultItem); + if (ModSettings.CraftingTakesNoTime) + { + AddBattleLogMessage(L10NFormat("craftMagicItems-logMessage-expend-spell", spell.Name)); + spell.SpendFromSpellbook(); + CraftItem(resultItem); + } + else + { + var project = new CraftingProjectData(caster, requiredProgress, goldCost, casterLevel, resultItem, craftingData.Name, null, + new[] { spellBlueprint }); + AddNewProject(caster.Descriptor, project); + CalculateProjectEstimate(project); + currentSection = OpenSection.ProjectsSection; + } } private static bool IsMundaneCraftingData(ItemCraftingData craftingData) { From 7c76388a6e458fff82a5d6ef753ba1fc96d17964 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 29 Jun 2020 16:21:04 -0500 Subject: [PATCH 021/132] Extracting Settings into its own file --- CraftMagicItems/CraftMagicItems.csproj | 1 + CraftMagicItems/Main.cs | 21 ------------------- CraftMagicItems/Settings.cs | 28 ++++++++++++++++++++++++++ 3 files changed, 29 insertions(+), 21 deletions(-) create mode 100644 CraftMagicItems/Settings.cs diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index 10ac69a..7d0b9b2 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -94,6 +94,7 @@ + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index c01238d..134d4d6 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -73,27 +73,6 @@ using Random = System.Random; namespace CraftMagicItems { - public class Settings : UnityModManager.ModSettings { - public const int MagicCraftingProgressPerDay = 500; - public const int MundaneCraftingProgressPerDay = 5; - - public bool CraftingCostsNoGold; - public bool IgnoreCraftingFeats; - public bool CraftingTakesNoTime; - public float CraftingPriceScale = 1; - public bool CraftAtFullSpeedWhileAdventuring; - public bool CasterLevelIsSinglePrerequisite; - public bool IgnoreFeatCasterLevelRestriction; - public bool IgnorePlusTenItemMaximum; - public bool CustomCraftRate; - public int MagicCraftingRate = MagicCraftingProgressPerDay; - public int MundaneCraftingRate = MundaneCraftingProgressPerDay; - - public override void Save(UnityModManager.ModEntry modEntry) { - Save(this, modEntry); - } - } - public static class Main { private const int MissingPrerequisiteDCModifier = 5; private const int OppositionSchoolDCModifier = 4; diff --git a/CraftMagicItems/Settings.cs b/CraftMagicItems/Settings.cs new file mode 100644 index 0000000..5d72ac4 --- /dev/null +++ b/CraftMagicItems/Settings.cs @@ -0,0 +1,28 @@ +using UnityModManagerNet; + +namespace CraftMagicItems +{ + /// Settings for the Crafting Mod + public class Settings : UnityModManager.ModSettings + { + public const int MagicCraftingProgressPerDay = 500; + public const int MundaneCraftingProgressPerDay = 5; + + public bool CraftingCostsNoGold; + public bool IgnoreCraftingFeats; + public bool CraftingTakesNoTime; + public float CraftingPriceScale = 1; + public bool CraftAtFullSpeedWhileAdventuring; + public bool CasterLevelIsSinglePrerequisite; + public bool IgnoreFeatCasterLevelRestriction; + public bool IgnorePlusTenItemMaximum; + public bool CustomCraftRate; + public int MagicCraftingRate = MagicCraftingProgressPerDay; + public int MundaneCraftingRate = MundaneCraftingProgressPerDay; + + public override void Save(UnityModManager.ModEntry modEntry) + { + Save(this, modEntry); + } + } +} \ No newline at end of file From b0150db6730062fc60d78c4fa7f918f1abe1580b Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Tue, 30 Jun 2020 15:04:02 -0500 Subject: [PATCH 022/132] Moving RenderCheatsSection to UserInterfaceEventHandlingLogic --- CraftMagicItems/CraftMagicItems.csproj | 1 + CraftMagicItems/Main.cs | 48 +------------ .../UI/UserInterfaceEventHandlingLogic.cs | 70 +++++++++++++++++++ 3 files changed, 72 insertions(+), 47 deletions(-) create mode 100644 CraftMagicItems/UI/UserInterfaceEventHandlingLogic.cs diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index 7d0b9b2..a1cbce9 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -97,6 +97,7 @@ + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 134d4d6..f0c7fea 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -402,7 +402,7 @@ private static void OnGui(UnityModManager.ModEntry modEntry) { if (UmmUiRenderer.RenderToggleSection("Cheats", currentSection == OpenSection.CheatsSection)) { currentSection = OpenSection.CheatsSection; - RenderCheatsSection(); + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(ModSettings, CustomPriceLabel, CraftingPriceStrings, GetSelectionIndex, SetSelectionIndex); } GUILayout.EndVertical(); @@ -2201,52 +2201,6 @@ private static void RenderFeatReassignmentSection() { } } - private static void RenderCheatsSection() { - ModSettings.CraftingCostsNoGold = UmmUiRenderer.RenderCheckbox("Crafting costs no gold and no material components.", ModSettings.CraftingCostsNoGold); - if (!ModSettings.CraftingCostsNoGold) { - var selectedCustomPriceScaleIndex = UmmUiRenderer.RenderSelection(CustomPriceLabel, CraftingPriceStrings, 4, GetSelectionIndex, SetSelectionIndex); - if (selectedCustomPriceScaleIndex == 2) { - GUILayout.BeginHorizontal(); - GUILayout.Label("Custom Cost Factor: ", GUILayout.ExpandWidth(false)); - ModSettings.CraftingPriceScale = GUILayout.HorizontalSlider(ModSettings.CraftingPriceScale * 100, 0, 500, GUILayout.Width(300)) / 100; - GUILayout.Label(Mathf.Round(ModSettings.CraftingPriceScale * 100).ToString(CultureInfo.InvariantCulture)); - GUILayout.EndHorizontal(); - } else { - ModSettings.CraftingPriceScale = 1 + selectedCustomPriceScaleIndex; - } - - if (selectedCustomPriceScaleIndex != 0) { - UmmUiRenderer.RenderLabel( - "Note: The sale price of custom crafted items will also be scaled by this factor, but vanilla items crafted by this mod" + - " will continue to use Owlcat's sale price, creating a price difference between the cost of crafting and sale price."); - } - } - - ModSettings.IgnoreCraftingFeats = UmmUiRenderer.RenderCheckbox("Crafting does not require characters to take crafting feats.", ModSettings.IgnoreCraftingFeats); - ModSettings.CraftingTakesNoTime = UmmUiRenderer.RenderCheckbox("Crafting takes no time to complete.", ModSettings.CraftingTakesNoTime); - if (!ModSettings.CraftingTakesNoTime) - { - ModSettings.CustomCraftRate = UmmUiRenderer.RenderCheckbox("Craft at a non-standard rate.", ModSettings.CustomCraftRate); - if (ModSettings.CustomCraftRate) - { - var maxMagicRate = ((ModSettings.MagicCraftingRate + 1000) / 1000) * 1000; - ModSettings.MagicCraftingRate = UmmUiRenderer.RenderIntSlider("Magic Item Crafting Rate", ModSettings.MagicCraftingRate, 1, maxMagicRate); - var maxMundaneRate = ((ModSettings.MundaneCraftingRate + 10) / 10) * 10; - ModSettings.MundaneCraftingRate = UmmUiRenderer.RenderIntSlider("Mundane Item Crafting Rate", ModSettings.MundaneCraftingRate, 1, maxMundaneRate); - } - else - { - ModSettings.MagicCraftingRate = Settings.MagicCraftingProgressPerDay; - ModSettings.MundaneCraftingRate = Settings.MundaneCraftingProgressPerDay; - } - } - ModSettings.CasterLevelIsSinglePrerequisite = UmmUiRenderer.RenderCheckbox("When crafting, a Caster Level less than the prerequisite counts as a single missing prerequisite.", - ModSettings.CasterLevelIsSinglePrerequisite); - ModSettings.CraftAtFullSpeedWhileAdventuring = UmmUiRenderer.RenderCheckbox("Characters craft at full speed while adventuring (instead of 25% speed).", ModSettings.CraftAtFullSpeedWhileAdventuring); - ModSettings.IgnorePlusTenItemMaximum = UmmUiRenderer.RenderCheckbox("Ignore the rule that limits arms and armor to a maximum of +10 equivalent.", ModSettings.IgnorePlusTenItemMaximum); - ModSettings.IgnoreFeatCasterLevelRestriction = UmmUiRenderer.RenderCheckbox("Ignore the crafting feat Caster Level prerequisites when learning feats.", ModSettings.IgnoreFeatCasterLevelRestriction); - } - private static bool IsPlayerSomewhereSafe() { if (Game.Instance.CurrentlyLoadedArea != null && SafeBlueprintAreaGuids.Contains(Game.Instance.CurrentlyLoadedArea.AssetGuid)) { return true; diff --git a/CraftMagicItems/UI/UserInterfaceEventHandlingLogic.cs b/CraftMagicItems/UI/UserInterfaceEventHandlingLogic.cs new file mode 100644 index 0000000..ddacf20 --- /dev/null +++ b/CraftMagicItems/UI/UserInterfaceEventHandlingLogic.cs @@ -0,0 +1,70 @@ +using System; +using System.Globalization; +using UnityEngine; + +namespace CraftMagicItems.UI +{ + /// Class that performs User Interface rendering and takes the results freom such and performs logical operations based on the UI state + public static class UserInterfaceEventHandlingLogic + { + /// Renders the Cheats section and retrieves the values specified by its rendered UI + /// to default to and to read from + /// Text to render for the price + /// Collection of containing the display text for various pricing guidelines + /// that retrieves the index for + /// callback that updates the currently selected index from the UI + public static void RenderCheatsSectionAndUpdateSettings(Settings modSettings, string priceLabel, string[] craftingPriceStrings, + Func GetSelectionIndex, Action SetSelectionIndex) + { + modSettings.CraftingCostsNoGold = UmmUiRenderer.RenderCheckbox("Crafting costs no gold and no material components.", modSettings.CraftingCostsNoGold); + if (!modSettings.CraftingCostsNoGold) + { + var selectedCustomPriceScaleIndex = UmmUiRenderer.RenderSelection(priceLabel, craftingPriceStrings, 4, GetSelectionIndex, SetSelectionIndex); + if (selectedCustomPriceScaleIndex == 2) + { + GUILayout.BeginHorizontal(); + GUILayout.Label("Custom Cost Factor: ", GUILayout.ExpandWidth(false)); + modSettings.CraftingPriceScale = GUILayout.HorizontalSlider(modSettings.CraftingPriceScale * 100, 0, 500, GUILayout.Width(300)) / 100; + GUILayout.Label(Mathf.Round(modSettings.CraftingPriceScale * 100).ToString(CultureInfo.InvariantCulture)); + GUILayout.EndHorizontal(); + } + else + { + modSettings.CraftingPriceScale = 1 + selectedCustomPriceScaleIndex; + } + + if (selectedCustomPriceScaleIndex != 0) + { + UmmUiRenderer.RenderLabel( + "Note: The sale price of custom crafted items will also be scaled by this factor, but vanilla items crafted by this mod" + + " will continue to use Owlcat's sale price, creating a price difference between the cost of crafting and sale price."); + } + } + + modSettings.IgnoreCraftingFeats = UmmUiRenderer.RenderCheckbox("Crafting does not require characters to take crafting feats.", modSettings.IgnoreCraftingFeats); + modSettings.CraftingTakesNoTime = UmmUiRenderer.RenderCheckbox("Crafting takes no time to complete.", modSettings.CraftingTakesNoTime); + if (!modSettings.CraftingTakesNoTime) + { + modSettings.CustomCraftRate = UmmUiRenderer.RenderCheckbox("Craft at a non-standard rate.", modSettings.CustomCraftRate); + if (modSettings.CustomCraftRate) + { + var maxMagicRate = ((modSettings.MagicCraftingRate + 1000) / 1000) * 1000; + modSettings.MagicCraftingRate = UmmUiRenderer.RenderIntSlider("Magic Item Crafting Rate", modSettings.MagicCraftingRate, 1, maxMagicRate); + var maxMundaneRate = ((modSettings.MundaneCraftingRate + 10) / 10) * 10; + modSettings.MundaneCraftingRate = UmmUiRenderer.RenderIntSlider("Mundane Item Crafting Rate", modSettings.MundaneCraftingRate, 1, maxMundaneRate); + } + else + { + modSettings.MagicCraftingRate = Settings.MagicCraftingProgressPerDay; + modSettings.MundaneCraftingRate = Settings.MundaneCraftingProgressPerDay; + } + } + + modSettings.CasterLevelIsSinglePrerequisite = UmmUiRenderer.RenderCheckbox("When crafting, a Caster Level less than the prerequisite counts as a single missing prerequisite.", + modSettings.CasterLevelIsSinglePrerequisite); + modSettings.CraftAtFullSpeedWhileAdventuring = UmmUiRenderer.RenderCheckbox("Characters craft at full speed while adventuring (instead of 25% speed).", modSettings.CraftAtFullSpeedWhileAdventuring); + modSettings.IgnorePlusTenItemMaximum = UmmUiRenderer.RenderCheckbox("Ignore the rule that limits arms and armor to a maximum of +10 equivalent.", modSettings.IgnorePlusTenItemMaximum); + modSettings.IgnoreFeatCasterLevelRestriction = UmmUiRenderer.RenderCheckbox("Ignore the crafting feat Caster Level prerequisites when learning feats.", modSettings.IgnoreFeatCasterLevelRestriction); + } + } +} \ No newline at end of file From 933a8f096dd908bded6ccd828b20839246bbae41 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Tue, 30 Jun 2020 17:13:10 -0500 Subject: [PATCH 023/132] Extracting an interface, implementation, and factory out for the battle log. --- CraftMagicItems/CraftMagicItems.csproj | 4 +++ CraftMagicItems/Main.cs | 11 +++--- .../UI/BattleLog/BattleLogFactory.cs | 35 +++++++++++++++++++ CraftMagicItems/UI/BattleLog/IBattleLog.cs | 15 ++++++++ .../UI/BattleLog/KingMakerBattleLog.cs | 33 +++++++++++++++++ 5 files changed, 92 insertions(+), 6 deletions(-) create mode 100644 CraftMagicItems/UI/BattleLog/BattleLogFactory.cs create mode 100644 CraftMagicItems/UI/BattleLog/IBattleLog.cs create mode 100644 CraftMagicItems/UI/BattleLog/KingMakerBattleLog.cs diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index a1cbce9..edc655f 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -95,6 +95,9 @@ + + + @@ -116,6 +119,7 @@ + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index f0c7fea..66281b3 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -229,6 +229,11 @@ public bool MatchPostfix(MethodBase method) { public static CraftMagicItemsAccessors Accessors; public static ItemCraftingData[] ItemCraftingData; public static CustomLootItem[] CustomLootItems; +#if PATCH21 + public static readonly List PendingLogItems = new List(); +#else + public static readonly List PendingLogItems = new List(); +#endif private static bool modEnabled = true; private static HarmonyLib.Harmony harmonyInstance; @@ -255,12 +260,6 @@ public bool MatchPostfix(MethodBase method) { private static readonly Dictionary> EnchantmentIdToRecipe = new Dictionary>(); private static readonly Dictionary> MaterialToRecipe = new Dictionary>(); private static readonly Dictionary EnchantmentIdToCost = new Dictionary(); -#if PATCH21 - private static readonly List PendingLogItems = new List(); -#else - private static readonly List PendingLogItems = new List(); - -#endif private static readonly Dictionary ItemUpgradeProjects = new Dictionary(); private static readonly List ItemCreationProjects = new List(); diff --git a/CraftMagicItems/UI/BattleLog/BattleLogFactory.cs b/CraftMagicItems/UI/BattleLog/BattleLogFactory.cs new file mode 100644 index 0000000..1ccafc3 --- /dev/null +++ b/CraftMagicItems/UI/BattleLog/BattleLogFactory.cs @@ -0,0 +1,35 @@ +using System; + +namespace CraftMagicItems.UI.BattleLog +{ + /// Factory for + public static class BattleLogFactory + { + private static Func construction; + + /// Static constructor + static BattleLogFactory() + { + Reset(); + } + + /// Resets the constructed instance to + public static void Reset() + { + SetConstructor(() => { return new KingmakerBattleLog(); }); + } + + /// Sets the constructed instance to + public static void SetConstructor(Func constructor) + { + construction = constructor; + } + + /// Constructs an instance of + /// An instance of + public static IBattleLog GetBattleLog() + { + return construction(); + } + } +} \ No newline at end of file diff --git a/CraftMagicItems/UI/BattleLog/IBattleLog.cs b/CraftMagicItems/UI/BattleLog/IBattleLog.cs new file mode 100644 index 0000000..fbdba91 --- /dev/null +++ b/CraftMagicItems/UI/BattleLog/IBattleLog.cs @@ -0,0 +1,15 @@ +using UnityEngine; + +namespace CraftMagicItems.UI.BattleLog +{ + /// Interface that defines logging of messages to the Battle Log in Kingmaker + /// Used for mocking the battle log for tests and such + public interface IBattleLog + { + /// Adds a message to the battle log + /// Message to add + /// Secondary object or for the tooltip to display + /// to use to render + void AddBattleLogMessage(System.String message, System.Object tooltip = null, Color? color = null); + } +} \ No newline at end of file diff --git a/CraftMagicItems/UI/BattleLog/KingMakerBattleLog.cs b/CraftMagicItems/UI/BattleLog/KingMakerBattleLog.cs new file mode 100644 index 0000000..eb38b97 --- /dev/null +++ b/CraftMagicItems/UI/BattleLog/KingMakerBattleLog.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; +using Kingmaker; +using Kingmaker.Blueprints.Root.Strings.GameLog; +using Kingmaker.UI.Log; +using UnityEngine; + +namespace CraftMagicItems.UI.BattleLog +{ + /// Class that drives logging of messages to the Battle Log in Kingmaker + public class KingmakerBattleLog : IBattleLog + { + /// Adds a message to the battle log + /// Message to add + /// Secondary object or for the tooltip to display + /// to use to render + public void AddBattleLogMessage(string message, object tooltip = null, Color? color = null) + { +#if PATCH21 + var data = new LogItemData(message, color ?? GameLogStrings.Instance.DefaultColor, tooltip, PrefixIcon.None, new List { LogChannel.Combat }); +#else + var data = new LogDataManager.LogItemData(message, color ?? GameLogStrings.Instance.DefaultColor, tooltip, PrefixIcon.None); +#endif + if (Game.Instance.UI.BattleLogManager) + { + Game.Instance.UI.BattleLogManager.LogView.AddLogEntry(data); + } + else + { + Main.PendingLogItems.Add(data); + } + } + } +} \ No newline at end of file From 6fc88bb7964e2b4f7b297a9276b0758a61548510 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Tue, 30 Jun 2020 17:13:25 -0500 Subject: [PATCH 024/132] Adding unit tests over BattleLogFactory --- .../CraftMagicItemsTests.csproj | 14 ++++ .../UI/BattleLog/BattleLogFactoryTests.cs | 64 +++++++++++++++++++ CraftMagicItemsTests/packages.config | 4 ++ 3 files changed, 82 insertions(+) create mode 100644 CraftMagicItemsTests/UI/BattleLog/BattleLogFactoryTests.cs diff --git a/CraftMagicItemsTests/CraftMagicItemsTests.csproj b/CraftMagicItemsTests/CraftMagicItemsTests.csproj index a1d3880..1a6b216 100644 --- a/CraftMagicItemsTests/CraftMagicItemsTests.csproj +++ b/CraftMagicItemsTests/CraftMagicItemsTests.csproj @@ -58,18 +58,32 @@ MinimumRecommendedRules.ruleset + + ..\packages\Castle.Core.4.4.0\lib\net45\Castle.Core.dll + ..\packages\MSTest.TestFramework.2.1.0\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll ..\packages\MSTest.TestFramework.2.1.0\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll + + ..\packages\Moq.4.14.4\lib\net45\Moq.dll + + + + ..\packages\System.Runtime.CompilerServices.Unsafe.4.5.0\lib\netstandard1.0\System.Runtime.CompilerServices.Unsafe.dll + + + ..\packages\System.Threading.Tasks.Extensions.4.5.1\lib\portable-net45+win8+wp8+wpa81\System.Threading.Tasks.Extensions.dll + + diff --git a/CraftMagicItemsTests/UI/BattleLog/BattleLogFactoryTests.cs b/CraftMagicItemsTests/UI/BattleLog/BattleLogFactoryTests.cs new file mode 100644 index 0000000..101d5ff --- /dev/null +++ b/CraftMagicItemsTests/UI/BattleLog/BattleLogFactoryTests.cs @@ -0,0 +1,64 @@ +using System; +using CraftMagicItems.UI.BattleLog; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; + +namespace CraftMagicItemsTests.UI.BattleLog +{ + /// Test class for + [TestClass] + public class BattleLogFactoryTests + { + [TestMethod] + public void BattleLogFactory_Defaults_KingmakerBattleLog() + { + //control + BattleLogFactory.Reset(); + + //invocation + var instance = BattleLogFactory.GetBattleLog(); + + //validation + Assert.AreEqual(typeof(KingmakerBattleLog), instance.GetType(), $"Expected an instance of {nameof(KingmakerBattleLog)} to be returned."); + } + + [TestMethod] + public void SetConstructor_Works() + { + //control + Func mockConstructor = () => { return new Mock().Object; }; + BattleLogFactory.SetConstructor(mockConstructor); + + //invocation + var instance = BattleLogFactory.GetBattleLog(); + + //validation + Assert.AreNotEqual(typeof(KingmakerBattleLog), instance.GetType(), $"Expected an instance of {nameof(KingmakerBattleLog)} to be returned."); + } + + [TestMethod] + public void GetBattleLog_Works() + { + //invocation + var instance = BattleLogFactory.GetBattleLog(); + + //validation + Assert.IsNotNull(instance, $"Expected an instance of {nameof(IBattleLog)} to be returned."); + } + + [TestMethod] + public void Reset_Works() + { + //control + Func mockConstructor = () => { return new Mock().Object; }; + BattleLogFactory.SetConstructor(mockConstructor); + BattleLogFactory.Reset(); + + //invocation + var instance = BattleLogFactory.GetBattleLog(); + + //validation + Assert.AreEqual(typeof(KingmakerBattleLog), instance.GetType(), $"Expected an instance of {nameof(KingmakerBattleLog)} to be returned."); + } + } +} \ No newline at end of file diff --git a/CraftMagicItemsTests/packages.config b/CraftMagicItemsTests/packages.config index bd445ce..469ed16 100644 --- a/CraftMagicItemsTests/packages.config +++ b/CraftMagicItemsTests/packages.config @@ -1,5 +1,9 @@  + + + + \ No newline at end of file From 2739ecb948b88295018a7d1903ee24ceef4d4470 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Tue, 30 Jun 2020 17:29:23 -0500 Subject: [PATCH 025/132] Adding in a clarifying comment --- CraftMagicItems/UI/UserInterfaceEventHandlingLogic.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CraftMagicItems/UI/UserInterfaceEventHandlingLogic.cs b/CraftMagicItems/UI/UserInterfaceEventHandlingLogic.cs index ddacf20..915215c 100644 --- a/CraftMagicItems/UI/UserInterfaceEventHandlingLogic.cs +++ b/CraftMagicItems/UI/UserInterfaceEventHandlingLogic.cs @@ -20,7 +20,7 @@ public static void RenderCheatsSectionAndUpdateSettings(Settings modSettings, st if (!modSettings.CraftingCostsNoGold) { var selectedCustomPriceScaleIndex = UmmUiRenderer.RenderSelection(priceLabel, craftingPriceStrings, 4, GetSelectionIndex, SetSelectionIndex); - if (selectedCustomPriceScaleIndex == 2) + if (selectedCustomPriceScaleIndex == 2) //if user selected "Custom" { GUILayout.BeginHorizontal(); GUILayout.Label("Custom Cost Factor: ", GUILayout.ExpandWidth(false)); From 576ec6ff2ff2a609d293e6ab6b847939b5eb42fd Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Tue, 30 Jun 2020 17:37:59 -0500 Subject: [PATCH 026/132] Adding in a rough implementation of the Cheat Section rendering code --- CraftMagicItems/CraftMagicItems.csproj | 1 + .../UI/Sections/CheatSectionRenderer.cs | 79 +++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 CraftMagicItems/UI/Sections/CheatSectionRenderer.cs diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index edc655f..86630b5 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -99,6 +99,7 @@ + diff --git a/CraftMagicItems/UI/Sections/CheatSectionRenderer.cs b/CraftMagicItems/UI/Sections/CheatSectionRenderer.cs new file mode 100644 index 0000000..dc24f3a --- /dev/null +++ b/CraftMagicItems/UI/Sections/CheatSectionRenderer.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CraftMagicItems.UI.Sections +{ + public class CheatSectionRenderer + { + /// Renders a checkbox indicating whether crafting costs any gold and returns its current UI value + /// Current value + /// The value of the UI checkbox + public bool Evaluate_CraftingCostsNoGold(bool currentSetting) + { + return UmmUiRenderer.RenderCheckbox("Crafting costs no gold and no material components.", currentSetting); + } + + public int Evaluate_CraftingCostSelection(string priceLabel, string[] craftingPriceStrings, Action SetSelectionIndex) + { + throw new NotImplementedException("TODO"); + } + + public int Evaluate_CustomCraftingCostSlider(int currentSetting) + { + throw new NotImplementedException("TODO"); + } + + public void RenderOnly_WarningAboutCustomItemVanillaItemCostDisparity() + { + throw new NotImplementedException("TODO"); + } + + public bool Evaluate_IgnoreCraftingFeats(bool currentSetting) + { + return UmmUiRenderer.RenderCheckbox("Crafting does not require characters to take crafting feats.", currentSetting); + } + + public bool Evaluate_CraftingTakesNoTime(bool currentSetting) + { + return UmmUiRenderer.RenderCheckbox("Crafting takes no time to complete.", currentSetting); + } + + public bool Evaluate_CustomCraftRate(bool currentSetting) + { + return UmmUiRenderer.RenderCheckbox("Craft at a non-standard rate.", currentSetting); + } + + public int Evaluate_MagicCraftingRateSlider(int currentSetting) + { + throw new NotImplementedException("TODO"); + } + + public int Evaluate_MundaneCraftingRateSlider(int currentSetting) + { + throw new NotImplementedException("TODO"); + } + + public bool Evaluate_CasterLevelIsSinglePrerequisite(bool currentSetting) + { + return UmmUiRenderer.RenderCheckbox("When crafting, a Caster Level less than the prerequisite counts as a single missing prerequisite.", currentSetting); + } + + public bool Evaluate_CraftAtFullSpeedWhileAdventuring(bool currentSetting) + { + return UmmUiRenderer.RenderCheckbox("Characters craft at full speed while adventuring (instead of 25% speed).", currentSetting); + } + + public bool Evaluate_IgnorePlusTenItemMaximum(bool currentSetting) + { + return UmmUiRenderer.RenderCheckbox("Ignore the rule that limits arms and armor to a maximum of +10 equivalent.", currentSetting); + } + + public bool Evaluate_IgnoreFeatCasterLevelRestriction(bool currentSetting) + { + return UmmUiRenderer.RenderCheckbox("Ignore the crafting feat Caster Level prerequisites when learning feats.", currentSetting); + } + } +} \ No newline at end of file From 7b62c90e3d58723257bd2fb8be5f0f3912cf38ca Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Wed, 1 Jul 2020 10:35:12 -0500 Subject: [PATCH 027/132] Refactoring the surrounding index logic out of the rendering code back into Main (for the time being) --- CraftMagicItems/Main.cs | 76 +++++++++++++------ CraftMagicItems/UI/UmmUiRenderer.cs | 40 ++-------- .../UI/UserInterfaceEventHandlingLogic.cs | 2 +- 3 files changed, 60 insertions(+), 58 deletions(-) diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 66281b3..0d4df8e 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.IO; using System.Linq; using System.Reflection; @@ -536,7 +535,7 @@ private static void RenderCraftMagicItemsSection() { .PrependConditional(hasBondedItemFeature, new L10NString("craftMagicItems-bonded-object-name")).ToArray(); //render whatever the user has selected - var selectedItemTypeIndex = UmmUiRenderer.RenderSelection("Crafting: ", itemTypeNames, 6, ref selectedCustomName, GetSelectionIndex, SetSelectionIndex, false); + var selectedItemTypeIndex = DrawSelectionUserInterfaceElements("Crafting: ", itemTypeNames, 6, ref selectedCustomName, false); //render options for actual selection if (hasBondedItemFeature && selectedItemTypeIndex == 0) { @@ -621,7 +620,7 @@ private static void RenderBondedItemCrafting(UnitEntityData caster) { UmmUiRenderer.RenderLabel(new L10NString("craftMagicItems-bonded-item-glossary")); UmmUiRenderer.RenderLabel("Choose your bonded item."); var names = BondedItemSlots.Select(slot => new L10NString(GetSlotStringKey(slot, null)).ToString()).ToArray(); - var selectedItemSlotIndex = UmmUiRenderer.RenderSelection("Item type", names, 10, GetSelectionIndex, SetSelectionIndex); + var selectedItemSlotIndex = DrawSelectionUserInterfaceElements("Item type", names, 10); var selectedSlot = BondedItemSlots[selectedItemSlotIndex]; var items = Game.Instance.Player.Inventory .Where(item => item.Blueprint is BlueprintItemEquipment blueprint @@ -637,7 +636,7 @@ private static void RenderBondedItemCrafting(UnitEntityData caster) { return; } var itemNames = items.Select(item => item.Name).ToArray(); - var selectedUpgradeItemIndex = UmmUiRenderer.RenderSelection("Item: ", itemNames, 5, GetSelectionIndex, SetSelectionIndex); + var selectedUpgradeItemIndex = DrawSelectionUserInterfaceElements("Item: ", itemNames, 5); var selectedItem = items[selectedUpgradeItemIndex]; var goldCost = !selectedBondWithNewObject || ModSettings.CraftingCostsNoGold ? 0 : 200 * characterCasterLevel; var canAfford = BuildCostString(out var cost, null, goldCost); @@ -693,13 +692,13 @@ private static void RenderSpellBasedCrafting(UnitEntityData caster, SpellBasedIt var selectedSpellbookIndex = 0; if (spellbooks.Count != 1) { var spellBookNames = spellbooks.Select(book => book.Blueprint.Name.ToString()).ToArray(); - selectedSpellbookIndex = UmmUiRenderer.RenderSelection("Class: ", spellBookNames, 10, GetSelectionIndex, SetSelectionIndex); + selectedSpellbookIndex = DrawSelectionUserInterfaceElements("Class: ", spellBookNames, 10); } var spellbook = spellbooks[selectedSpellbookIndex]; var maxLevel = Math.Min(spellbook.MaxSpellLevel, craftingData.MaxSpellLevel); var spellLevelNames = Enumerable.Range(0, maxLevel + 1).Select(index => $"Level {index}").ToArray(); - var spellLevel = UmmUiRenderer.RenderSelection("Spell level: ", spellLevelNames, 10, GetSelectionIndex, SetSelectionIndex); + var spellLevel = DrawSelectionUserInterfaceElements("Spell level: ", spellLevelNames, 10); if (spellLevel > 0 && !spellbook.Blueprint.Spontaneous) { if (ModSettings.CraftingTakesNoTime) { selectedShowPreparedSpells = true; @@ -1223,12 +1222,12 @@ private static void RenderRecipeBasedCrafting(UnitEntityData caster, RecipeBased var selectedItemSlotIndex = 0; if (craftingData.Slots.Length > 1) { var names = craftingData.Slots.Select(slot => new L10NString(GetSlotStringKey(slot, craftingData.SlotRestrictions)).ToString()).ToArray(); - selectedItemSlotIndex = UmmUiRenderer.RenderSelection("Item type", names, 10, ref selectedCustomName, GetSelectionIndex, SetSelectionIndex); + selectedItemSlotIndex = DrawSelectionUserInterfaceElements("Item type", names, 10, ref selectedCustomName); } var locationFilter = ItemLocationFilter.All; var locationNames = Enum.GetNames(typeof(ItemLocationFilter)); - locationFilter = (ItemLocationFilter)UmmUiRenderer.RenderSelection("Item location", locationNames, locationNames.Length, ref selectedCustomName, GetSelectionIndex, SetSelectionIndex); + locationFilter = (ItemLocationFilter)DrawSelectionUserInterfaceElements("Item location", locationNames, locationNames.Length, ref selectedCustomName); selectedSlot = craftingData.Slots[selectedItemSlotIndex]; var playerInCapital = IsPlayerInCapital(); @@ -1263,7 +1262,7 @@ private static void RenderRecipeBasedCrafting(UnitEntityData caster, RecipeBased return; } - var selectedUpgradeItemIndex = UmmUiRenderer.RenderSelection("Item: ", itemNames, 5, ref selectedCustomName, GetSelectionIndex, SetSelectionIndex); + var selectedUpgradeItemIndex = DrawSelectionUserInterfaceElements("Item: ", itemNames, 5, ref selectedCustomName); // See existing item details and enchantments. var index = selectedUpgradeItemIndex - (canCreateNew ? 1 : 0); upgradeItem = index < 0 ? null : items[index]; @@ -1325,7 +1324,7 @@ private static void RenderRecipeBasedCrafting(UnitEntityData caster, RecipeBased ? new string[0] : new[] {new L10NString("craftMagicItems-label-cast-spell-n-times").ToString()}) .ToArray(); - var selectedRecipeIndex = UmmUiRenderer.RenderSelection("Enchantment: ", recipeNames, 5, ref selectedCustomName, GetSelectionIndex, SetSelectionIndex); + var selectedRecipeIndex = DrawSelectionUserInterfaceElements("Enchantment: ", recipeNames, 5, ref selectedCustomName); if (selectedRecipeIndex == availableRecipes.Length) { // Cast spell N times RenderCastSpellNTimes(caster, craftingData, upgradeItemShield ?? upgradeItem, selectedSlot); @@ -1339,7 +1338,7 @@ private static void RenderRecipeBasedCrafting(UnitEntityData caster, RecipeBased .OrderBy(recipe => recipe.NameId) .ToArray(); recipeNames = availableSubRecipes.Select(recipe => recipe.NameId).ToArray(); - var selectedSubRecipeIndex = UmmUiRenderer.RenderSelection(category + ": ", recipeNames, 5, ref selectedCustomName, GetSelectionIndex, SetSelectionIndex); + var selectedSubRecipeIndex = DrawSelectionUserInterfaceElements(category + ": ", recipeNames, 5, ref selectedCustomName); selectedRecipe = availableSubRecipes[selectedSubRecipeIndex]; } @@ -1380,7 +1379,7 @@ private static void RenderRecipeBasedCrafting(UnitEntityData caster, RecipeBased counter++; return enchantment.Name.Empty() ? GetBonusString(counter, selectedRecipe) : enchantment.Name; }); - selectedEnchantmentIndex = UmmUiRenderer.RenderSelection("", enchantmentNames.ToArray(), 6, GetSelectionIndex, SetSelectionIndex); + selectedEnchantmentIndex = DrawSelectionUserInterfaceElements("", enchantmentNames.ToArray(), 6); } selectedEnchantment = availableEnchantments[selectedEnchantmentIndex]; @@ -1634,12 +1633,12 @@ private static void RenderCastSpellNTimes(UnitEntityData caster, RecipeBasedItem // Choose a spellbook known to the caster var spellbooks = caster.Descriptor.Spellbooks.ToList(); var spellBookNames = spellbooks.Select(book => book.Blueprint.Name.ToString()).Concat(Enumerable.Repeat("From Items", 1)).ToArray(); - var selectedSpellbookIndex = UmmUiRenderer.RenderSelection("Source: ", spellBookNames, 10, ref selectedCustomName, GetSelectionIndex, SetSelectionIndex); + var selectedSpellbookIndex = DrawSelectionUserInterfaceElements("Source: ", spellBookNames, 10, ref selectedCustomName); if (selectedSpellbookIndex < spellbooks.Count) { var spellbook = spellbooks[selectedSpellbookIndex]; // Choose a spell level var spellLevelNames = Enumerable.Range(0, spellbook.Blueprint.MaxSpellLevel + 1).Select(index => $"Level {index}").ToArray(); - spellLevel = UmmUiRenderer.RenderSelection("Spell level: ", spellLevelNames, 10, ref selectedCustomName, GetSelectionIndex, SetSelectionIndex); + spellLevel = DrawSelectionUserInterfaceElements("Spell level: ", spellLevelNames, 10, ref selectedCustomName); var specialSpellLists = Accessors.GetSpellbookSpecialLists(spellbook); var spellOptions = spellbook.Blueprint.SpellList.GetSpells(spellLevel) .Concat(specialSpellLists.Aggregate(new List(), (allSpecial, spellList) => spellList.GetSpells(spellLevel))) @@ -1652,11 +1651,11 @@ private static void RenderCastSpellNTimes(UnitEntityData caster, RecipeBasedItem } var spellNames = spellOptions.Select(spell => spell.Name).ToArray(); - var selectedSpellIndex = UmmUiRenderer.RenderSelection("Spell: ", spellNames, 4, ref selectedCustomName, GetSelectionIndex, SetSelectionIndex); + var selectedSpellIndex = DrawSelectionUserInterfaceElements("Spell: ", spellNames, 4, ref selectedCustomName); ability = spellOptions[selectedSpellIndex]; if (ability.HasVariants && ability.Variants != null) { var selectedVariantIndex = - UmmUiRenderer.RenderSelection("Variant: ", ability.Variants.Select(spell => spell.Name).ToArray(), 4, ref selectedCustomName, GetSelectionIndex, SetSelectionIndex); + DrawSelectionUserInterfaceElements("Variant: ", ability.Variants.Select(spell => spell.Name).ToArray(), 4, ref selectedCustomName); ability = ability.Variants[selectedVariantIndex]; } } else { @@ -1674,7 +1673,7 @@ private static void RenderCastSpellNTimes(UnitEntityData caster, RecipeBasedItem return; } var itemNames = itemBlueprints.Select(item => item.Name).ToArray(); - var itemIndex = UmmUiRenderer.RenderSelection("Cast from item: ", itemNames, 5, ref selectedCustomName, GetSelectionIndex, SetSelectionIndex); + var itemIndex = DrawSelectionUserInterfaceElements("Cast from item: ", itemNames, 5, ref selectedCustomName); var selectedItemBlueprint = itemBlueprints[itemIndex]; ability = selectedItemBlueprint.Ability; spellLevel = selectedItemBlueprint.SpellLevel; @@ -1920,7 +1919,7 @@ private static void RenderCraftMundaneItemsSection() { .ToArray(); var itemTypeNames = itemTypes.Select(data => new L10NString(data.ParentNameId ?? data.NameId).ToString()).ToArray(); var selectedItemTypeIndex = upgradingBlueprint == null - ? UmmUiRenderer.RenderSelection("Mundane Crafting: ", itemTypeNames, 6, ref selectedCustomName, GetSelectionIndex, SetSelectionIndex) + ? DrawSelectionUserInterfaceElements("Mundane Crafting: ", itemTypeNames, 6, ref selectedCustomName) : GetSelectionIndex("Mundane Crafting: "); var selectedCraftingData = itemTypes[selectedItemTypeIndex]; @@ -1928,7 +1927,7 @@ private static void RenderCraftMundaneItemsSection() { itemTypeNames = SubCraftingData[selectedCraftingData.ParentNameId].Select(data => new L10NString(data.NameId).ToString()).ToArray(); var label = new L10NString(selectedCraftingData.ParentNameId) + ": "; var selectedItemSubTypeIndex = upgradingBlueprint == null - ? UmmUiRenderer.RenderSelection(label, itemTypeNames, 6, GetSelectionIndex, SetSelectionIndex) + ? DrawSelectionUserInterfaceElements(label, itemTypeNames, 6) : GetSelectionIndex(label); selectedCraftingData = SubCraftingData[selectedCraftingData.ParentNameId][selectedItemSubTypeIndex]; @@ -1957,7 +1956,7 @@ private static void RenderCraftMundaneItemsSection() { return; } - var selectedUpgradeItemIndex = UmmUiRenderer.RenderSelection("Item: ", blueprintNames, 5, ref selectedCustomName, GetSelectionIndex, SetSelectionIndex); + var selectedUpgradeItemIndex = DrawSelectionUserInterfaceElements("Item: ", blueprintNames, 5, ref selectedCustomName); baseBlueprint = blueprints[selectedUpgradeItemIndex]; // See existing item details and enchantments. UmmUiRenderer.RenderLabel(baseBlueprint.Description); @@ -1974,7 +1973,7 @@ private static void RenderCraftMundaneItemsSection() { .OrderBy(recipe => recipe.NameId) .ToArray(); var recipeNames = availableRecipes.Select(recipe => recipe.NameId).ToArray(); - var selectedRecipeIndex = UmmUiRenderer.RenderSelection("Craft: ", recipeNames, 6, ref selectedCustomName, GetSelectionIndex, SetSelectionIndex); + var selectedRecipeIndex = DrawSelectionUserInterfaceElements("Craft: ", recipeNames, 6, ref selectedCustomName); var selectedRecipe = availableRecipes.Any() ? availableRecipes[selectedRecipeIndex] : null; var selectedEnchantment = selectedRecipe?.Enchantments.Length == 1 ? selectedRecipe.Enchantments[0] : null; if (selectedRecipe != null && selectedRecipe.Material != 0) { @@ -2157,7 +2156,7 @@ private static void RenderFeatReassignmentSection() { UmmUiRenderer.RenderLabel("Use this section to reassign previous feat choices for this character to crafting feats. Warning: This is a one-way assignment!"); var featOptions = missingFeats.Select(data => new L10NString(data.NameId).ToString()).ToArray(); - var selectedFeatToLearn = UmmUiRenderer.RenderSelection("Feat to learn", featOptions, 6, GetSelectionIndex, SetSelectionIndex); + var selectedFeatToLearn = DrawSelectionUserInterfaceElements("Feat to learn", featOptions, 6); var learnFeatData = missingFeats[selectedFeatToLearn]; var learnFeat = ResourcesLibrary.TryGetBlueprint(learnFeatData.FeatGuid); if (learnFeat == null) { @@ -2238,7 +2237,7 @@ private static UnitEntityData GetSelectedCrafter(bool render) { var partyNames = characters.Select(entity => $"{entity.CharacterName}" + $"{((GetCraftingTimerComponentForCaster(entity.Descriptor)?.CraftingProjects.Any() ?? false) ? "*" : "")}") .ToArray(); - selectedSpellcasterIndex = UmmUiRenderer.RenderSelection(label, partyNames, 8, ref upgradingBlueprint, GetSelectionIndex, SetSelectionIndex); + selectedSpellcasterIndex = DrawSelectionUserInterfaceElements(label, partyNames, 8, ref upgradingBlueprint); } if (selectedSpellcasterIndex >= characters.Length) { selectedSpellcasterIndex = 0; @@ -2246,6 +2245,37 @@ private static UnitEntityData GetSelectedCrafter(bool render) { return characters[selectedSpellcasterIndex]; } + /// Renders a selection of to Unity Mod Manager + /// Type of item being rendered + /// Label for the selection + /// Options for the selection + /// How many elements to fit in the horizontal direction + public static int DrawSelectionUserInterfaceElements(string label, string[] options, int horizontalCount) + { + var dummy = ""; + return DrawSelectionUserInterfaceElements(label, options, horizontalCount, ref dummy); + } + + public static int DrawSelectionUserInterfaceElements(string label, string[] options, int horizontalCount, ref T emptyOnChange, bool addSpace = true) + { + var index = GetSelectionIndex(label); + if (index >= options.Length) + { + index = 0; + } + + var newIndex = UmmUiRenderer.RenderSelection(label, options, index, horizontalCount, addSpace); + + if (index != newIndex) + { + emptyOnChange = default(T); + } + + SetSelectionIndex(label, newIndex); + + return newIndex; + } + private static int GetSelectionIndex(string label) { return SelectedIndex.ContainsKey(label) ? SelectedIndex[label] : 0; } diff --git a/CraftMagicItems/UI/UmmUiRenderer.cs b/CraftMagicItems/UI/UmmUiRenderer.cs index 5df0886..871c909 100644 --- a/CraftMagicItems/UI/UmmUiRenderer.cs +++ b/CraftMagicItems/UI/UmmUiRenderer.cs @@ -1,5 +1,4 @@ -using System; -using UnityEngine; +using UnityEngine; namespace CraftMagicItems.UI { @@ -108,51 +107,24 @@ public static void RenderLabel(string label, bool onItsOwnLine) } /// Renders a selection of to Unity Mod Manager - /// Type of item being rendered /// Label for the selection /// Options for the selection + /// Index within to use for current selection /// How many elements to fit in the horizontal direction - /// Used to retrieve the selection index - /// Used to write the value of the selection index - /// The new index of the selection - public static int RenderSelection(string label, string[] options, int horizontalCount, Func GetSelectionIndex, Action SetSelectionIndex) - { - var dummy = ""; - return RenderSelection(label, options, horizontalCount, ref dummy, GetSelectionIndex, SetSelectionIndex); - } - - /// Renders a selection of to Unity Mod Manager - /// Type of item being rendered - /// Label for the selection - /// Options for the selection - /// How many elements to fit in the horizontal direction - /// Value to write to when the indexes do not match - /// Used to retrieve the selection index - /// Used to write the value of the selection index /// Space the UI by 20 pixels? /// The new index of the selection - public static int RenderSelection(string label, string[] options, int horizontalCount, ref T emptyOnChange, Func GetSelectionIndex, Action SetSelectionIndex, bool addSpace = true) + public static int RenderSelection(string label, string[] options, int optionsIndex, int horizontalCount, bool addSpace) { - var index = GetSelectionIndex(label); - if (index >= options.Length) - { - index = 0; - } - if (addSpace) { GUILayout.Space(20); } + GUILayout.BeginHorizontal(); GUILayout.Label(label, GUILayout.ExpandWidth(false)); - var newIndex = GUILayout.SelectionGrid(index, options, horizontalCount); - if (index != newIndex) - { - emptyOnChange = default(T); - } - + var newIndex = GUILayout.SelectionGrid(optionsIndex, options, horizontalCount); GUILayout.EndHorizontal(); - SetSelectionIndex(label, newIndex); + return newIndex; } } diff --git a/CraftMagicItems/UI/UserInterfaceEventHandlingLogic.cs b/CraftMagicItems/UI/UserInterfaceEventHandlingLogic.cs index 915215c..6dfa50a 100644 --- a/CraftMagicItems/UI/UserInterfaceEventHandlingLogic.cs +++ b/CraftMagicItems/UI/UserInterfaceEventHandlingLogic.cs @@ -19,7 +19,7 @@ public static void RenderCheatsSectionAndUpdateSettings(Settings modSettings, st modSettings.CraftingCostsNoGold = UmmUiRenderer.RenderCheckbox("Crafting costs no gold and no material components.", modSettings.CraftingCostsNoGold); if (!modSettings.CraftingCostsNoGold) { - var selectedCustomPriceScaleIndex = UmmUiRenderer.RenderSelection(priceLabel, craftingPriceStrings, 4, GetSelectionIndex, SetSelectionIndex); + var selectedCustomPriceScaleIndex = Main.DrawSelectionUserInterfaceElements(priceLabel, craftingPriceStrings, 4); if (selectedCustomPriceScaleIndex == 2) //if user selected "Custom" { GUILayout.BeginHorizontal(); From fac9c6f37289e337b14331011f1ea3ed231cbd78 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Wed, 1 Jul 2020 14:00:19 -0500 Subject: [PATCH 028/132] Refactoring the "on its own line" stuff for RenderLabel --- CraftMagicItems/Main.cs | 116 ++++++++++++++-------------- CraftMagicItems/UI/UmmUiRenderer.cs | 37 +++++---- 2 files changed, 82 insertions(+), 71 deletions(-) diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 0d4df8e..61f04cb 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -350,7 +350,7 @@ private static bool OnToggle(UnityModManager.ModEntry modEntry, bool enabled) { private static void OnGui(UnityModManager.ModEntry modEntry) { if (!modEnabled) { - UmmUiRenderer.RenderLabel("The mod is disabled. Loading saved games with custom items and feats will cause them to revert to regular versions."); + UmmUiRenderer.RenderLabelRow("The mod is disabled. Loading saved games with custom items and feats will cause them to revert to regular versions."); return; } @@ -362,13 +362,13 @@ private static void OnGui(UnityModManager.ModEntry modEntry) { && Game.Instance.CurrentMode != GameModeType.EscMode && Game.Instance.CurrentMode != GameModeType.Rest && Game.Instance.CurrentMode != GameModeType.Kingdom) { - UmmUiRenderer.RenderLabel("Item crafting is not available in this game state."); + UmmUiRenderer.RenderLabelRow("Item crafting is not available in this game state."); return; } GUILayout.BeginVertical(); - UmmUiRenderer.RenderLabel($"Number of custom Craft Magic Items blueprints loaded: {CustomBlueprintBuilder.CustomBlueprintIDs.Count}"); + UmmUiRenderer.RenderLabelRow($"Number of custom Craft Magic Items blueprints loaded: {CustomBlueprintBuilder.CustomBlueprintIDs.Count}"); GetSelectedCrafter(true); @@ -526,7 +526,7 @@ private static void RenderCraftMagicItemsSection() { .Where(data => data.FeatGuid != null && (ModSettings.IgnoreCraftingFeats || CharacterHasFeat(caster, data.FeatGuid))) .ToArray(); if (!Enumerable.Any(itemTypes) && !hasBondedItemFeature) { - UmmUiRenderer.RenderLabel($"{caster.CharacterName} does not know any crafting feats."); + UmmUiRenderer.RenderLabelRow($"{caster.CharacterName} does not know any crafting feats."); return; } @@ -551,7 +551,7 @@ private static void RenderCraftMagicItemsSection() { } //render current cash - UmmUiRenderer.RenderLabel($"Current Money: {Game.Instance.Player.Money}"); + UmmUiRenderer.RenderLabelRow($"Current Money: {Game.Instance.Player.Money}"); } private static RecipeBasedItemCraftingData GetBondedItemCraftingData(BondedItemComponent bondedComponent) { @@ -599,26 +599,26 @@ private static void RenderBondedItemCrafting(UnitEntityData caster) { var projects = GetCraftingTimerComponentForCaster(caster.Descriptor); var ritualProject = projects == null ? null : projects.CraftingProjects.FirstOrDefault(project => project.ItemType == BondedItemRitual); if (ritualProject != null) { - UmmUiRenderer.RenderLabel($"{caster.CharacterName} is in the process of bonding with {ritualProject.ResultItem.Name}"); + UmmUiRenderer.RenderLabelRow($"{caster.CharacterName} is in the process of bonding with {ritualProject.ResultItem.Name}"); return; } var bondedComponent = GetBondedItemComponentForCaster(caster.Descriptor); var characterCasterLevel = CharacterCasterLevel(caster.Descriptor); if (bondedComponent == null || bondedComponent.ownerItem == null || selectedBondWithNewObject) { if (selectedBondWithNewObject) { - UmmUiRenderer.RenderLabel("You may bond with a different object by performing a special ritual that costs 200 gp per caster level. This ritual takes 8 " + + UmmUiRenderer.RenderLabelRow("You may bond with a different object by performing a special ritual that costs 200 gp per caster level. This ritual takes 8 " + "hours to complete. Items replaced in this way do not possess any of the additional enchantments of the previous bonded item, " + "and the previous bonded item loses any enchantments you added via your bond."); if (GUILayout.Button("Cancel bonding to a new object")) { selectedBondWithNewObject = false; } } - UmmUiRenderer.RenderLabel( + UmmUiRenderer.RenderLabelRow( "You can enchant additional magic abilities to your bonded item as if you had the required Item Creation Feat, as long as you also " + "meet the caster level prerequisite of the feat. Abilities added in this fashion function only for you, and no-one else can add " + "enchantments to your bonded item."); - UmmUiRenderer.RenderLabel(new L10NString("craftMagicItems-bonded-item-glossary")); - UmmUiRenderer.RenderLabel("Choose your bonded item."); + UmmUiRenderer.RenderLabelRow(new L10NString("craftMagicItems-bonded-item-glossary")); + UmmUiRenderer.RenderLabelRow("Choose your bonded item."); var names = BondedItemSlots.Select(slot => new L10NString(GetSlotStringKey(slot, null)).ToString()).ToArray(); var selectedItemSlotIndex = DrawSelectionUserInterfaceElements("Item type", names, 10); var selectedSlot = BondedItemSlots[selectedItemSlotIndex]; @@ -632,7 +632,7 @@ private static void RenderBondedItemCrafting(UnitEntityData caster) { .OrderBy(item => item.Name) .ToArray(); if (items.Length == 0) { - UmmUiRenderer.RenderLabel("You do not have any item of that type currently equipped."); + UmmUiRenderer.RenderLabelRow("You do not have any item of that type currently equipped."); return; } var itemNames = items.Select(item => item.Name).ToArray(); @@ -642,7 +642,7 @@ private static void RenderBondedItemCrafting(UnitEntityData caster) { var canAfford = BuildCostString(out var cost, null, goldCost); var label = $"Make {selectedItem.Name} your bonded item{(goldCost == 0 ? "" : " for " + cost)}"; if (!canAfford) { - UmmUiRenderer.RenderLabel(label); + UmmUiRenderer.RenderLabelRow(label); } else if (GUILayout.Button(label)) { if (goldCost > 0) { Game.Instance.UI.Common.UISound.Play(UISoundType.LootCollectGold); @@ -672,9 +672,9 @@ private static void RenderBondedItemCrafting(UnitEntityData caster) { var craftingData = GetBondedItemCraftingData(bondedComponent); if (bondedComponent.ownerItem.Wielder != null && !IsPlayerInCapital() && !Game.Instance.Player.PartyCharacters.Contains(bondedComponent.ownerItem.Wielder.Unit)) { - UmmUiRenderer.RenderLabel($"You cannot enchant {bondedComponent.ownerItem.Name} because you cannot currently access it."); + UmmUiRenderer.RenderLabelRow($"You cannot enchant {bondedComponent.ownerItem.Name} because you cannot currently access it."); } else if (!ModSettings.IgnoreFeatCasterLevelRestriction && characterCasterLevel < craftingData.MinimumCasterLevel) { - UmmUiRenderer.RenderLabel($"You will not be able to enchant your bonded item until your caster level reaches {craftingData.MinimumCasterLevel} " + + UmmUiRenderer.RenderLabelRow($"You will not be able to enchant your bonded item until your caster level reaches {craftingData.MinimumCasterLevel} " + $"(currently {characterCasterLevel})."); } else { RenderRecipeBasedCrafting(caster, craftingData, bondedComponent.ownerItem); @@ -685,7 +685,7 @@ private static void RenderBondedItemCrafting(UnitEntityData caster) { private static void RenderSpellBasedCrafting(UnitEntityData caster, SpellBasedItemCraftingData craftingData) { var spellbooks = caster.Descriptor.Spellbooks.Where(book => book.CasterLevel > 0).ToList(); if (spellbooks.Count == 0) { - UmmUiRenderer.RenderLabel($"{caster.CharacterName} is not yet able to cast spells."); + UmmUiRenderer.RenderLabelRow($"{caster.CharacterName} is not yet able to cast spells."); return; } @@ -719,7 +719,7 @@ private static void RenderSpellBasedCrafting(UnitEntityData caster, SpellBasedIt // Cantrips/Orisons or Spontaneous spellcaster or showing all known spells if (spellLevel > 0 && spellbook.Blueprint.Spontaneous) { var spontaneousSlots = spellbook.GetSpontaneousSlots(spellLevel); - UmmUiRenderer.RenderLabel($"{caster.CharacterName} can cast {spontaneousSlots} more level {spellLevel} spells today."); + UmmUiRenderer.RenderLabelRow($"{caster.CharacterName} can cast {spontaneousSlots} more level {spellLevel} spells today."); if (spontaneousSlots == 0 && ModSettings.CraftingTakesNoTime) { return; } @@ -729,7 +729,7 @@ private static void RenderSpellBasedCrafting(UnitEntityData caster, SpellBasedIt } if (!spellOptions.Any()) { - UmmUiRenderer.RenderLabel($"{caster.CharacterName} does not know any level {spellLevel} spells."); + UmmUiRenderer.RenderLabelRow($"{caster.CharacterName} does not know any level {spellLevel} spells."); } else { var minCasterLevel = Math.Max(1, 2 * spellLevel - 1); var maxCasterLevel = CharacterCasterLevel(caster.Descriptor, spellbook); @@ -737,7 +737,7 @@ private static void RenderSpellBasedCrafting(UnitEntityData caster, SpellBasedIt selectedCasterLevel = UmmUiRenderer.RenderIntSlider("Caster level: ", selectedCasterLevel, minCasterLevel, maxCasterLevel); } else { selectedCasterLevel = minCasterLevel; - UmmUiRenderer.RenderLabel($"Caster level: {selectedCasterLevel}"); + UmmUiRenderer.RenderLabelRow($"Caster level: {selectedCasterLevel}"); } RenderCraftingSkillInformation(caster, StatType.SkillKnowledgeArcana, 5 + selectedCasterLevel, selectedCasterLevel); @@ -1214,7 +1214,7 @@ private static void RenderRecipeBasedCrafting(UnitEntityData caster, RecipeBased while (ItemUpgradeProjects.ContainsKey(upgradeItem)) { upgradeItem = ItemUpgradeProjects[upgradeItem].ResultItem; } - UmmUiRenderer.RenderLabel($"Enchanting {upgradeItem.Name}"); + UmmUiRenderer.RenderLabelRow($"Enchanting {upgradeItem.Name}"); } //slot else { @@ -1258,7 +1258,7 @@ private static void RenderRecipeBasedCrafting(UnitEntityData caster, RecipeBased var itemNames = items.Select(item => item.Name).PrependConditional(canCreateNew, new L10NString("craftMagicItems-label-craft-new-item")) .ToArray(); if (itemNames.Length == 0) { - UmmUiRenderer.RenderLabel($"{caster.CharacterName} can not access any items of that type."); + UmmUiRenderer.RenderLabelRow($"{caster.CharacterName} can not access any items of that type."); return; } @@ -1305,9 +1305,9 @@ private static void RenderRecipeBasedCrafting(UnitEntityData caster, RecipeBased selectedShieldWeapon = false; } if (upgradeItemShield != null) { - UmmUiRenderer.RenderLabel(BuildItemDescription(upgradeItemShield)); + UmmUiRenderer.RenderLabelRow(BuildItemDescription(upgradeItemShield)); } else { - UmmUiRenderer.RenderLabel(BuildItemDescription(upgradeItem)); + UmmUiRenderer.RenderLabelRow(BuildItemDescription(upgradeItem)); } } @@ -1371,7 +1371,7 @@ private static void RenderRecipeBasedCrafting(UnitEntityData caster, RecipeBased } return false; }))) { - UmmUiRenderer.RenderLabel("This item cannot be further upgraded with this enchantment."); + UmmUiRenderer.RenderLabelRow("This item cannot be further upgraded with this enchantment."); return; } else if (availableEnchantments.Length > 0 && selectedRecipe.Enchantments.Length > 1) { var counter = selectedRecipe.Enchantments.Length - availableEnchantments.Length; @@ -1391,10 +1391,10 @@ private static void RenderRecipeBasedCrafting(UnitEntityData caster, RecipeBased : selectedRecipe.Enchantments.FindIndex(e => e == selectedEnchantment) * selectedRecipe.CasterLevelMultiplier); if (selectedEnchantment != null) { if (!string.IsNullOrEmpty(selectedEnchantment.Description)) { - UmmUiRenderer.RenderLabel(selectedEnchantment.Description); + UmmUiRenderer.RenderLabelRow(selectedEnchantment.Description); } if (selectedRecipe.CostType == RecipeCostType.EnhancementLevelSquared) { - UmmUiRenderer.RenderLabel($"Plus equivalent: +{GetPlusOfRecipe(selectedRecipe, selectedRecipe.Enchantments.FindIndex(e => e == selectedEnchantment) + 1)}"); + UmmUiRenderer.RenderLabelRow($"Plus equivalent: +{GetPlusOfRecipe(selectedRecipe, selectedRecipe.Enchantments.FindIndex(e => e == selectedEnchantment) + 1)}"); } } @@ -1519,13 +1519,13 @@ private static void RenderRecipeBasedCrafting(UnitEntityData caster, RecipeBased } if (!itemToCraft) { - UmmUiRenderer.RenderLabel($"Error: null custom item from looking up blueprint ID {itemGuid}"); + UmmUiRenderer.RenderLabelRow($"Error: null custom item from looking up blueprint ID {itemGuid}"); } else { if (IsItemLegalEnchantmentLevel(itemToCraft)) { RenderRecipeBasedCraftItemControl(caster, craftingData, selectedRecipe, casterLevel, itemToCraft, upgradeItem); } else { var maxEnchantmentLevel = ItemMaxEnchantmentLevel(itemToCraft); - UmmUiRenderer.RenderLabel($"This would result in {itemToCraft.Name} having an equivalent enhancement bonus of more than +{maxEnchantmentLevel}"); + UmmUiRenderer.RenderLabelRow($"This would result in {itemToCraft.Name} having an equivalent enhancement bonus of more than +{maxEnchantmentLevel}"); } } } @@ -1619,10 +1619,10 @@ private static void RenderCastSpellNTimes(UnitEntityData caster, RecipeBasedItem if (upgradeItem != null) { equipment = upgradeItem.Blueprint as BlueprintItemEquipment; if (equipment == null || equipment.Ability != null && equipment.SpendCharges && !equipment.RestoreChargesOnRest) { - UmmUiRenderer.RenderLabel($"{upgradeItem.Name} cannot cast a spell N times a day (this is unexpected - please let the mod author know)"); + UmmUiRenderer.RenderLabelRow($"{upgradeItem.Name} cannot cast a spell N times a day (this is unexpected - please let the mod author know)"); return; } else if (equipment.Ability != null && !equipment.Ability.IsSpell) { - UmmUiRenderer.RenderLabel($"{equipment.Ability.Name} is not a spell, so cannot be upgraded."); + UmmUiRenderer.RenderLabelRow($"{equipment.Ability.Name} is not a spell, so cannot be upgraded."); return; } } @@ -1646,7 +1646,7 @@ private static void RenderCastSpellNTimes(UnitEntityData caster, RecipeBasedItem .OrderBy(spell => spell.Name) .ToArray(); if (!spellOptions.Any()) { - UmmUiRenderer.RenderLabel($"There are no level {spellLevel} {spellbook.Blueprint.Name} spells"); + UmmUiRenderer.RenderLabelRow($"There are no level {spellLevel} {spellbook.Blueprint.Name} spells"); return; } @@ -1669,7 +1669,7 @@ private static void RenderCastSpellNTimes(UnitEntityData caster, RecipeBasedItem .OrderBy(item => item.Name) .ToArray(); if (itemBlueprints.Length == 0) { - UmmUiRenderer.RenderLabel("You are not wielding any items that can cast spells."); + UmmUiRenderer.RenderLabelRow("You are not wielding any items that can cast spells."); return; } var itemNames = itemBlueprints.Select(item => item.Name).ToArray(); @@ -1677,13 +1677,13 @@ private static void RenderCastSpellNTimes(UnitEntityData caster, RecipeBasedItem var selectedItemBlueprint = itemBlueprints[itemIndex]; ability = selectedItemBlueprint.Ability; spellLevel = selectedItemBlueprint.SpellLevel; - UmmUiRenderer.RenderLabel($"Spell: {ability.Name}"); + UmmUiRenderer.RenderLabelRow($"Spell: {ability.Name}"); } } else { ability = equipment.Ability; spellLevel = equipment.SpellLevel; GameLogContext.Count = equipment.Charges; - UmmUiRenderer.RenderLabel($"Current: {L10NFormat("craftMagicItems-label-cast-spell-n-times-details", ability.Name, equipment.CasterLevel)}"); + UmmUiRenderer.RenderLabelRow($"Current: {L10NFormat("craftMagicItems-label-cast-spell-n-times-details", ability.Name, equipment.CasterLevel)}"); GameLogContext.Clear(); } @@ -1694,7 +1694,7 @@ private static void RenderCastSpellNTimes(UnitEntityData caster, RecipeBasedItem var maxCastsPerDay = equipment == null ? 10 : ((equipment.Charges + 10) / 10) * 10; selectedCastsPerDay = UmmUiRenderer.RenderIntSlider("Casts per day: ", selectedCastsPerDay, equipment == null ? 1 : equipment.Charges, maxCastsPerDay); if (equipment != null && ability == equipment.Ability && selectedCasterLevel == equipment.CasterLevel && selectedCastsPerDay == equipment.Charges) { - UmmUiRenderer.RenderLabel($"No changes made to {equipment.Name}"); + UmmUiRenderer.RenderLabelRow($"No changes made to {equipment.Name}"); return; } @@ -1727,7 +1727,7 @@ private static void RenderCastSpellNTimes(UnitEntityData caster, RecipeBasedItem // Render craft button GameLogContext.Count = selectedCastsPerDay; - UmmUiRenderer.RenderLabel(L10NFormat("craftMagicItems-label-cast-spell-n-times-details", ability.Name, selectedCasterLevel)); + UmmUiRenderer.RenderLabelRow(L10NFormat("craftMagicItems-label-cast-spell-n-times-details", ability.Name, selectedCasterLevel)); GameLogContext.Clear(); var recipe = new RecipeData { PrerequisiteSpells = new[] {ability}, @@ -1774,7 +1774,7 @@ private static int RenderCraftingSkillInformation(UnitEntityData crafter, StatTy CrafterPrerequisiteType[] crafterPrerequisites = null, bool render = true) { if (render) { - UmmUiRenderer.RenderLabel($"Base Crafting DC: {dc}"); + UmmUiRenderer.RenderLabelRow($"Base Crafting DC: {dc}"); } // ReSharper disable once UnusedVariable var missing = CheckSpellPrerequisites(prerequisiteSpells, anyPrerequisite, crafter.Descriptor, false, out var missingSpells, @@ -1789,11 +1789,11 @@ private static int RenderCraftingSkillInformation(UnitEntityData crafter, StatTy casterLevelShortfall = 0; } if (missing > 0 && render) { - UmmUiRenderer.RenderLabel( + UmmUiRenderer.RenderLabelRow( $"{crafter.CharacterName} is unable to meet {missing} of the prerequisites, raising the DC by {MissingPrerequisiteDCModifier * missing}"); } if (casterLevelShortfall > 0 && render) { - UmmUiRenderer.RenderLabel(L10NFormat("craftMagicItems-logMessage-low-caster-level", casterLevel, MissingPrerequisiteDCModifier * casterLevelShortfall)); + UmmUiRenderer.RenderLabelRow(L10NFormat("craftMagicItems-logMessage-low-caster-level", casterLevel, MissingPrerequisiteDCModifier * casterLevelShortfall)); } // Rob's ruling... if you're below the prerequisite caster level, you're considered to be missing a prerequisite for each // level you fall short. @@ -1802,18 +1802,18 @@ private static int RenderCraftingSkillInformation(UnitEntityData crafter, StatTy if (oppositionSchool != SpellSchool.None) { dc += OppositionSchoolDCModifier; if (render) { - UmmUiRenderer.RenderLabel(L10NFormat("craftMagicItems-logMessage-opposition-school", LocalizedTexts.Instance.SpellSchoolNames.GetText(oppositionSchool), + UmmUiRenderer.RenderLabelRow(L10NFormat("craftMagicItems-logMessage-opposition-school", LocalizedTexts.Instance.SpellSchoolNames.GetText(oppositionSchool), OppositionSchoolDCModifier)); } } var skillCheck = 10 + crafter.Stats.GetStat(skill).ModifiedValue; if (render) { - UmmUiRenderer.RenderLabel(L10NFormat("craftMagicItems-logMessage-made-progress-check", LocalizedTexts.Instance.Stats.GetText(skill), skillCheck, dc)); + UmmUiRenderer.RenderLabelRow(L10NFormat("craftMagicItems-logMessage-made-progress-check", LocalizedTexts.Instance.Stats.GetText(skill), skillCheck, dc)); } var skillMargin = skillCheck - dc; if (skillMargin < 0 && render) { - UmmUiRenderer.RenderLabel(ModSettings.CraftingTakesNoTime + UmmUiRenderer.RenderLabelRow(ModSettings.CraftingTakesNoTime ? $"This project would be too hard for {crafter.CharacterName} if \"Crafting Takes No Time\" cheat was disabled." : $"Warning: This project will be too hard for {crafter.CharacterName}"); } @@ -1934,7 +1934,7 @@ private static void RenderCraftMundaneItemsSection() { } if (!(selectedCraftingData is RecipeBasedItemCraftingData craftingData)) { - UmmUiRenderer.RenderLabel("Unable to find mundane crafting recipe."); + UmmUiRenderer.RenderLabelRow("Unable to find mundane crafting recipe."); return; } @@ -1942,7 +1942,7 @@ private static void RenderCraftMundaneItemsSection() { if (upgradingBlueprint != null) { baseBlueprint = upgradingBlueprint; - UmmUiRenderer.RenderLabel($"Applying upgrades to {baseBlueprint.Name}"); + UmmUiRenderer.RenderLabelRow($"Applying upgrades to {baseBlueprint.Name}"); } else { // Choose mundane item of selected type to create var blueprints = craftingData.NewItemBaseIDs @@ -1952,14 +1952,14 @@ private static void RenderCraftMundaneItemsSection() { .ToArray(); var blueprintNames = blueprints.Select(item => item.Name).ToArray(); if (blueprintNames.Length == 0) { - UmmUiRenderer.RenderLabel("No known items of that type."); + UmmUiRenderer.RenderLabelRow("No known items of that type."); return; } var selectedUpgradeItemIndex = DrawSelectionUserInterfaceElements("Item: ", blueprintNames, 5, ref selectedCustomName); baseBlueprint = blueprints[selectedUpgradeItemIndex]; // See existing item details and enchantments. - UmmUiRenderer.RenderLabel(baseBlueprint.Description); + UmmUiRenderer.RenderLabelRow(baseBlueprint.Description); } // Assume only one slot type per crafting data @@ -1977,9 +1977,9 @@ private static void RenderCraftMundaneItemsSection() { var selectedRecipe = availableRecipes.Any() ? availableRecipes[selectedRecipeIndex] : null; var selectedEnchantment = selectedRecipe?.Enchantments.Length == 1 ? selectedRecipe.Enchantments[0] : null; if (selectedRecipe != null && selectedRecipe.Material != 0) { - UmmUiRenderer.RenderLabel(GetWeaponMaterialDescription(selectedRecipe.Material)); + UmmUiRenderer.RenderLabelRow(GetWeaponMaterialDescription(selectedRecipe.Material)); } else if (selectedEnchantment != null && !string.IsNullOrEmpty(selectedEnchantment.Description)) { - UmmUiRenderer.RenderLabel(selectedEnchantment.Description); + UmmUiRenderer.RenderLabelRow(selectedEnchantment.Description); } var dc = craftingData.MundaneEnhancementsStackable @@ -2042,7 +2042,7 @@ private static void RenderCraftMundaneItemsSection() { } if (!itemToCraft) { - UmmUiRenderer.RenderLabel($"Error: null custom item from looking up blueprint ID {itemGuid}"); + UmmUiRenderer.RenderLabelRow($"Error: null custom item from looking up blueprint ID {itemGuid}"); } else { if (upgradingBlueprint != null && GUILayout.Button($"Cancel {baseBlueprint.Name}", GUILayout.ExpandWidth(false))) { upgradingBlueprint = null; @@ -2059,7 +2059,7 @@ private static void RenderCraftMundaneItemsSection() { } } - UmmUiRenderer.RenderLabel($"Current Money: {Game.Instance.Player.Money}"); + UmmUiRenderer.RenderLabelRow($"Current Money: {Game.Instance.Player.Money}"); } private static string GetWeaponMaterialDescription(PhysicalDamageMaterial material) { @@ -2113,11 +2113,11 @@ private static void RenderProjectsSection() { var timer = GetCraftingTimerComponentForCaster(caster.Descriptor); if (timer == null || timer.CraftingProjects.Count == 0) { - UmmUiRenderer.RenderLabel($"{caster.CharacterName} is not currently working on any crafting projects."); + UmmUiRenderer.RenderLabelRow($"{caster.CharacterName} is not currently working on any crafting projects."); return; } - UmmUiRenderer.RenderLabel($"{caster.CharacterName} currently has {timer.CraftingProjects.Count} crafting projects in progress."); + UmmUiRenderer.RenderLabelRow($"{caster.CharacterName} currently has {timer.CraftingProjects.Count} crafting projects in progress."); var firstItem = true; foreach (var project in timer.CraftingProjects.ToArray()) { GUILayout.BeginHorizontal(); @@ -2134,8 +2134,8 @@ private static void RenderProjectsSection() { timer.CraftingProjects.Insert(0, project); } GUILayout.EndHorizontal(); - UmmUiRenderer.RenderLabel($" {BuildItemDescription(project.ResultItem).Replace("\n", "\n ")}"); - UmmUiRenderer.RenderLabel($" {project.LastMessage}"); + UmmUiRenderer.RenderLabelRow($" {BuildItemDescription(project.ResultItem).Replace("\n", "\n ")}"); + UmmUiRenderer.RenderLabelRow($" {project.LastMessage}"); } } @@ -2150,11 +2150,11 @@ private static void RenderFeatReassignmentSection() { .Where(data => data.FeatGuid != null && !CharacterHasFeat(caster, data.FeatGuid) && data.MinimumCasterLevel <= casterLevel) .ToArray(); if (missingFeats.Length == 0) { - UmmUiRenderer.RenderLabel($"{caster.CharacterName} does not currently qualify for any crafting feats."); + UmmUiRenderer.RenderLabelRow($"{caster.CharacterName} does not currently qualify for any crafting feats."); return; } - UmmUiRenderer.RenderLabel("Use this section to reassign previous feat choices for this character to crafting feats. Warning: This is a one-way assignment!"); + UmmUiRenderer.RenderLabelRow("Use this section to reassign previous feat choices for this character to crafting feats. Warning: This is a one-way assignment!"); var featOptions = missingFeats.Select(data => new L10NString(data.NameId).ToString()).ToArray(); var selectedFeatToLearn = DrawSelectionUserInterfaceElements("Feat to learn", featOptions, 6); var learnFeatData = missingFeats[selectedFeatToLearn]; @@ -2225,7 +2225,7 @@ private static UnitEntityData GetSelectedCrafter(bool render) { .ToArray(); if (characters.Length == 0) { if (render) { - UmmUiRenderer.RenderLabel("No living characters available."); + UmmUiRenderer.RenderLabelRow("No living characters available."); } return null; @@ -2517,7 +2517,7 @@ private static void AttemptSpellBasedCraftItemAndRender(UnitEntityData caster, S if (itemBlueprintList == null && craftingData.NewItemBaseIDs == null) { var message = L10NFormat("craftMagicItems-label-no-item-exists", new L10NString(craftingData.NamePrefixId), spellBlueprint.Name); - UmmUiRenderer.RenderLabel(message, false); + UmmUiRenderer.RenderLabel(message); return; } @@ -2533,7 +2533,7 @@ private static void AttemptSpellBasedCraftItemAndRender(UnitEntityData caster, S //if the player cannot afford the time (not enough gold), alert them if (!canAfford) { - UmmUiRenderer.RenderLabel(label, false); + UmmUiRenderer.RenderLabel(label); } // ... otherwise let them spend their money else if (GUILayout.Button(label, GUILayout.ExpandWidth(false))) diff --git a/CraftMagicItems/UI/UmmUiRenderer.cs b/CraftMagicItems/UI/UmmUiRenderer.cs index 871c909..f37777c 100644 --- a/CraftMagicItems/UI/UmmUiRenderer.cs +++ b/CraftMagicItems/UI/UmmUiRenderer.cs @@ -83,27 +83,26 @@ public static bool RenderToggleSection(string label, bool value) /// Renders a Label control as its own line in Unity Mod Manager /// Text to be displayed - public static void RenderLabel(string label) + public static void RenderLabelRow(string label) { - RenderLabel(label, true); + GUILayout.BeginHorizontal(); + GUILayout.Label(label); + GUILayout.EndHorizontal(); } /// Renders a Label control as its own line in Unity Mod Manager /// Text to be displayed - /// Should the label be rendered on its own horizontal line? - public static void RenderLabel(string label, bool onItsOwnLine) + public static void RenderLabel(string label) { - if (onItsOwnLine) - { - GUILayout.BeginHorizontal(); - } - GUILayout.Label(label); + } - if (onItsOwnLine) - { - GUILayout.EndHorizontal(); - } + /// Renders a Label control as its own line in Unity Mod Manager + /// Text to be displayed + /// Should the label be expanded + public static void RenderLabel(string label, bool expandWidth) + { + GUILayout.Label(label, GUILayout.ExpandWidth(expandWidth)); } /// Renders a selection of to Unity Mod Manager @@ -127,5 +126,17 @@ public static int RenderSelection(string label, string[] options, int optionsInd return newIndex; } + + /// In the rendered Unity Mod Manager UI, sets the location of the UI control to the next horizontal alignment + public static void RenderHorizontalStart() + { + GUILayout.BeginHorizontal(); + } + + /// In the rendered Unity Mod Manager UI, sets the end of a horizontal alignment + public static void RenderHorizontalEnd() + { + GUILayout.EndHorizontal(); + } } } \ No newline at end of file From e984a29e02656a8f818563baf51702e6218b04d2 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Wed, 1 Jul 2020 14:22:42 -0500 Subject: [PATCH 029/132] Adding in a Float slider --- CraftMagicItems/UI/UmmUiRenderer.cs | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/CraftMagicItems/UI/UmmUiRenderer.cs b/CraftMagicItems/UI/UmmUiRenderer.cs index f37777c..c38e3f4 100644 --- a/CraftMagicItems/UI/UmmUiRenderer.cs +++ b/CraftMagicItems/UI/UmmUiRenderer.cs @@ -57,13 +57,26 @@ public static string RenderCustomNameField(string defaultValue, string selectedC public static int RenderIntSlider(string label, int value, int min, int max) { value = Mathf.Clamp(value, min, max); + var newValue = RenderFloatSlider(label, value, min, max); + + return Mathf.RoundToInt(newValue); + } + + /// Renders an integer selection slider + /// Label for the slider + /// Initial value + /// Minimum possible value + /// Maximum possible value + /// Returns the value selected by the user, clamped and rounded after rendering controls to the screen + public static float RenderFloatSlider(string label, float value, float min, float max) + { GUILayout.BeginHorizontal(); GUILayout.Label(label, GUILayout.ExpandWidth(false)); - value = Mathf.RoundToInt(GUILayout.HorizontalSlider(value, min, max, GUILayout.Width(300))); - GUILayout.Label($"{value}", GUILayout.ExpandWidth(false)); + var newValue = GUILayout.HorizontalSlider(value, min, max, GUILayout.Width(300)); + GUILayout.Label(newValue.ToString(), GUILayout.ExpandWidth(false)); GUILayout.EndHorizontal(); - return value; + return newValue; } /// Renders a toggle-able section selection in Unity Mod Manager for the user to show/hide From ac2ee6ce4ddfd88b157f029b0468ce1e020d8781 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Wed, 1 Jul 2020 15:15:01 -0500 Subject: [PATCH 030/132] Refactoring all of the UI stuff for the Cheat section into an interface that we can override or whatever to make unit testable --- CraftMagicItems/CraftMagicItems.csproj | 2 + CraftMagicItems/Main.cs | 3 +- .../UI/Sections/CheatSectionRenderer.cs | 65 +++++++++++++---- .../Sections/CheatSectionRendererFactory.cs | 35 +++++++++ .../UI/Sections/ICheatSectionRenderer.cs | 72 +++++++++++++++++++ .../UI/UserInterfaceEventHandlingLogic.cs | 44 +++++------- 6 files changed, 177 insertions(+), 44 deletions(-) create mode 100644 CraftMagicItems/UI/Sections/CheatSectionRendererFactory.cs create mode 100644 CraftMagicItems/UI/Sections/ICheatSectionRenderer.cs diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index 86630b5..b7fb684 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -100,6 +100,8 @@ + + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 61f04cb..68f4366 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -7,6 +7,7 @@ using System.Runtime.Serialization; using System.Text.RegularExpressions; using CraftMagicItems.UI; +using CraftMagicItems.UI.Sections; using Kingmaker; #if PATCH21 using Kingmaker.Assets.UI.Context; @@ -400,7 +401,7 @@ private static void OnGui(UnityModManager.ModEntry modEntry) { if (UmmUiRenderer.RenderToggleSection("Cheats", currentSection == OpenSection.CheatsSection)) { currentSection = OpenSection.CheatsSection; - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(ModSettings, CustomPriceLabel, CraftingPriceStrings, GetSelectionIndex, SetSelectionIndex); + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(CheatSectionRendererFactory.GetCheatSectionRenderer(), ModSettings, CustomPriceLabel, CraftingPriceStrings); } GUILayout.EndVertical(); diff --git a/CraftMagicItems/UI/Sections/CheatSectionRenderer.cs b/CraftMagicItems/UI/Sections/CheatSectionRenderer.cs index dc24f3a..2f142d2 100644 --- a/CraftMagicItems/UI/Sections/CheatSectionRenderer.cs +++ b/CraftMagicItems/UI/Sections/CheatSectionRenderer.cs @@ -1,12 +1,7 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace CraftMagicItems.UI.Sections +namespace CraftMagicItems.UI.Sections { - public class CheatSectionRenderer + /// User Interface renderer into Unity Mod Manager for the cheats section + public class CheatSectionRenderer : ICheatSectionRenderer { /// Renders a checkbox indicating whether crafting costs any gold and returns its current UI value /// Current value @@ -16,61 +11,101 @@ public bool Evaluate_CraftingCostsNoGold(bool currentSetting) return UmmUiRenderer.RenderCheckbox("Crafting costs no gold and no material components.", currentSetting); } - public int Evaluate_CraftingCostSelection(string priceLabel, string[] craftingPriceStrings, Action SetSelectionIndex) + /// Renders a selection to Unity Mod Manager for the options for custom crafting price adjustments and returns the currently selected value + /// Label to display for the selection UI control + /// Collection of options that the user can select from + /// The index of the selection that Unity currently has registered + public int Evaluate_CraftingCostSelection(string priceLabel, string[] craftingPriceStrings) { - throw new NotImplementedException("TODO"); + return Main.DrawSelectionUserInterfaceElements(priceLabel, craftingPriceStrings, 4); } - public int Evaluate_CustomCraftingCostSlider(int currentSetting) + /// Renders a slider to Unity Mod Manager for the custom crafting % cost. + /// Current setting to display on the control + /// The selection that the UI currently has registered + public float Evaluate_CustomCraftingCostSlider(float currentSetting) { - throw new NotImplementedException("TODO"); + float result = UmmUiRenderer.RenderFloatSlider("Custom Cost Factor: ", currentSetting * 100, 0, 500); + return result / 100; } + /// Renders a warning that price disparity between custom items crafts to non-custom items will have a selling cost disparity between crafting and sale. public void RenderOnly_WarningAboutCustomItemVanillaItemCostDisparity() { - throw new NotImplementedException("TODO"); + UmmUiRenderer.RenderLabelRow( + "Note: The sale price of custom crafted items will also be scaled by this factor, but vanilla items crafted by this mod" + + " will continue to use Owlcat's sale price, creating a price difference between the cost of crafting and sale price."); } + /// Renders a checkbox indicating whether crafting should ignore feats + /// Current value + /// The value of the UI checkbox public bool Evaluate_IgnoreCraftingFeats(bool currentSetting) { return UmmUiRenderer.RenderCheckbox("Crafting does not require characters to take crafting feats.", currentSetting); } + /// Renders a checkbox indicating whether crafting should take time to complete or not + /// Current value + /// The value of the UI checkbox public bool Evaluate_CraftingTakesNoTime(bool currentSetting) { return UmmUiRenderer.RenderCheckbox("Crafting takes no time to complete.", currentSetting); } + /// Renders a checkbox indicating whether crafting should take a non-standard rate or time + /// Current value + /// The value of the UI checkbox public bool Evaluate_CustomCraftRate(bool currentSetting) { return UmmUiRenderer.RenderCheckbox("Craft at a non-standard rate.", currentSetting); } + /// Renders a slider to Unity Mod Manager for the custom magic crafting rate. + /// Current setting to display on the control + /// The selection that the UI currently has registered public int Evaluate_MagicCraftingRateSlider(int currentSetting) { - throw new NotImplementedException("TODO"); + var maxMagicRate = ((currentSetting + 1000) / 1000) * 1000; + return UmmUiRenderer.RenderIntSlider("Magic Item Crafting Rate", currentSetting, 1, maxMagicRate); } + /// Renders a slider to Unity Mod Manager for the custom mundane crafting rate. + /// Current setting to display on the control + /// The selection that the UI currently has registered public int Evaluate_MundaneCraftingRateSlider(int currentSetting) { - throw new NotImplementedException("TODO"); + var maxMundaneRate = ((currentSetting + 10) / 10) * 10; + return UmmUiRenderer.RenderIntSlider("Mundane Item Crafting Rate", currentSetting, 1, maxMundaneRate); } + /// Renders a checkbox indicating whether missing caster levels should combine into a single prerequisite (compared to the default of 1 prerequisite per missing level) + /// Current value + /// The value of the UI checkbox public bool Evaluate_CasterLevelIsSinglePrerequisite(bool currentSetting) { return UmmUiRenderer.RenderCheckbox("When crafting, a Caster Level less than the prerequisite counts as a single missing prerequisite.", currentSetting); } + /// Renders a checkbox indicating whether characters should craft at full rate while travelling (compared to 25% rate while travelling) + /// Current value + /// The value of the UI checkbox public bool Evaluate_CraftAtFullSpeedWhileAdventuring(bool currentSetting) { return UmmUiRenderer.RenderCheckbox("Characters craft at full speed while adventuring (instead of 25% speed).", currentSetting); } + /// Renders a checkbox indicating whether weapons and armor should be allowed to exceed the +10 enchantment value + /// Current value + /// The value of the UI checkbox public bool Evaluate_IgnorePlusTenItemMaximum(bool currentSetting) { return UmmUiRenderer.RenderCheckbox("Ignore the rule that limits arms and armor to a maximum of +10 equivalent.", currentSetting); } + /// Renders a checkbox indicating whether crafting feats can ignore caster level prerequisites + /// Current value + /// The value of the UI checkbox public bool Evaluate_IgnoreFeatCasterLevelRestriction(bool currentSetting) { return UmmUiRenderer.RenderCheckbox("Ignore the crafting feat Caster Level prerequisites when learning feats.", currentSetting); diff --git a/CraftMagicItems/UI/Sections/CheatSectionRendererFactory.cs b/CraftMagicItems/UI/Sections/CheatSectionRendererFactory.cs new file mode 100644 index 0000000..f31d08c --- /dev/null +++ b/CraftMagicItems/UI/Sections/CheatSectionRendererFactory.cs @@ -0,0 +1,35 @@ +using System; + +namespace CraftMagicItems.UI.Sections +{ + /// Factory for + public static class CheatSectionRendererFactory + { + private static Func construction; + + /// Static constructor + static CheatSectionRendererFactory() + { + Reset(); + } + + /// Resets the constructed instance to + public static void Reset() + { + SetConstructor(() => { return new CheatSectionRenderer(); }); + } + + /// Sets the constructed instance to + public static void SetConstructor(Func constructor) + { + construction = constructor; + } + + /// Constructs an instance of + /// An instance of + public static ICheatSectionRenderer GetCheatSectionRenderer() + { + return construction(); + } + } +} \ No newline at end of file diff --git a/CraftMagicItems/UI/Sections/ICheatSectionRenderer.cs b/CraftMagicItems/UI/Sections/ICheatSectionRenderer.cs new file mode 100644 index 0000000..c39b7ed --- /dev/null +++ b/CraftMagicItems/UI/Sections/ICheatSectionRenderer.cs @@ -0,0 +1,72 @@ +using System; + +namespace CraftMagicItems.UI.Sections +{ + /// Interface defining the I/O around the user interface for the cheats section and its returned user input values + public interface ICheatSectionRenderer + { + /// Renders a checkbox indicating whether missing caster levels should combine into a single prerequisite (compared to the default of 1 prerequisite per missing level) + /// Current value + /// The value of the UI checkbox + bool Evaluate_CasterLevelIsSinglePrerequisite(bool currentSetting); + + /// Renders a checkbox indicating whether characters should craft at full rate while travelling (compared to 25% rate while travelling) + /// Current value + /// The value of the UI checkbox + bool Evaluate_CraftAtFullSpeedWhileAdventuring(bool currentSetting); + + /// Renders a selection for the options for custom crafting price adjustments and returns the currently selected value + /// Label to display for the selection UI control + /// Collection of options that the user can select from + /// The index of the current selection in the UI control + int Evaluate_CraftingCostSelection(string priceLabel, string[] craftingPriceStrings); + + /// Renders a checkbox indicating whether crafting costs any gold and returns its current UI value + /// Current value + /// The value of the UI checkbox + bool Evaluate_CraftingCostsNoGold(bool currentSetting); + + /// Renders a checkbox indicating whether crafting should take time to complete or not + /// Current value + /// The value of the UI checkbox + bool Evaluate_CraftingTakesNoTime(bool currentSetting); + + /// Renders a slider to Unity Mod Manager for the custom crafting % cost. + /// Current setting to display on the control + /// The selection that the UI currently has registered + float Evaluate_CustomCraftingCostSlider(float currentSetting); + + /// Renders a checkbox indicating whether crafting should take a non-standard rate or time + /// Current value + /// The value of the UI checkbox + bool Evaluate_CustomCraftRate(bool currentSetting); + + /// Renders a checkbox indicating whether crafting should ignore feats + /// Current value + /// The value of the UI checkbox + bool Evaluate_IgnoreCraftingFeats(bool currentSetting); + + /// Renders a checkbox indicating whether crafting feats can ignore caster level prerequisites + /// Current value + /// The value of the UI checkbox + bool Evaluate_IgnoreFeatCasterLevelRestriction(bool currentSetting); + + /// Renders a checkbox indicating whether weapons and armor should be allowed to exceed the +10 enchantment value + /// Current value + /// The value of the UI checkbox + bool Evaluate_IgnorePlusTenItemMaximum(bool currentSetting); + + /// Renders a slider to Unity Mod Manager for the custom magic crafting rate. + /// Current setting to display on the control + /// The selection that the UI currently has registered + int Evaluate_MagicCraftingRateSlider(int currentSetting); + + /// Renders a slider to Unity Mod Manager for the custom mundane crafting rate. + /// Current setting to display on the control + /// The selection that the UI currently has registered + int Evaluate_MundaneCraftingRateSlider(int currentSetting); + + /// Renders a warning that price disparity between custom items crafts to non-custom items will have a selling cost disparity between crafting and sale. + void RenderOnly_WarningAboutCustomItemVanillaItemCostDisparity(); + } +} \ No newline at end of file diff --git a/CraftMagicItems/UI/UserInterfaceEventHandlingLogic.cs b/CraftMagicItems/UI/UserInterfaceEventHandlingLogic.cs index 6dfa50a..ce1758f 100644 --- a/CraftMagicItems/UI/UserInterfaceEventHandlingLogic.cs +++ b/CraftMagicItems/UI/UserInterfaceEventHandlingLogic.cs @@ -1,6 +1,5 @@ using System; -using System.Globalization; -using UnityEngine; +using CraftMagicItems.UI.Sections; namespace CraftMagicItems.UI { @@ -11,47 +10,37 @@ public static class UserInterfaceEventHandlingLogic /// to default to and to read from /// Text to render for the price /// Collection of containing the display text for various pricing guidelines - /// that retrieves the index for - /// callback that updates the currently selected index from the UI - public static void RenderCheatsSectionAndUpdateSettings(Settings modSettings, string priceLabel, string[] craftingPriceStrings, - Func GetSelectionIndex, Action SetSelectionIndex) + public static void RenderCheatsSectionAndUpdateSettings(ICheatSectionRenderer renderer, Settings modSettings, string priceLabel, string[] craftingPriceStrings) { - modSettings.CraftingCostsNoGold = UmmUiRenderer.RenderCheckbox("Crafting costs no gold and no material components.", modSettings.CraftingCostsNoGold); + modSettings.CraftingCostsNoGold = renderer.Evaluate_CraftingCostsNoGold(modSettings.CraftingCostsNoGold); if (!modSettings.CraftingCostsNoGold) { - var selectedCustomPriceScaleIndex = Main.DrawSelectionUserInterfaceElements(priceLabel, craftingPriceStrings, 4); + var selectedCustomPriceScaleIndex = renderer.Evaluate_CraftingCostSelection(priceLabel, craftingPriceStrings); if (selectedCustomPriceScaleIndex == 2) //if user selected "Custom" { - GUILayout.BeginHorizontal(); - GUILayout.Label("Custom Cost Factor: ", GUILayout.ExpandWidth(false)); - modSettings.CraftingPriceScale = GUILayout.HorizontalSlider(modSettings.CraftingPriceScale * 100, 0, 500, GUILayout.Width(300)) / 100; - GUILayout.Label(Mathf.Round(modSettings.CraftingPriceScale * 100).ToString(CultureInfo.InvariantCulture)); - GUILayout.EndHorizontal(); + modSettings.CraftingPriceScale = renderer.Evaluate_CustomCraftingCostSlider(modSettings.CraftingPriceScale); } else { + //index 0 = 100%; index 1 = 200% modSettings.CraftingPriceScale = 1 + selectedCustomPriceScaleIndex; } if (selectedCustomPriceScaleIndex != 0) { - UmmUiRenderer.RenderLabel( - "Note: The sale price of custom crafted items will also be scaled by this factor, but vanilla items crafted by this mod" + - " will continue to use Owlcat's sale price, creating a price difference between the cost of crafting and sale price."); + renderer.RenderOnly_WarningAboutCustomItemVanillaItemCostDisparity(); } } - modSettings.IgnoreCraftingFeats = UmmUiRenderer.RenderCheckbox("Crafting does not require characters to take crafting feats.", modSettings.IgnoreCraftingFeats); - modSettings.CraftingTakesNoTime = UmmUiRenderer.RenderCheckbox("Crafting takes no time to complete.", modSettings.CraftingTakesNoTime); + modSettings.IgnoreCraftingFeats = renderer.Evaluate_IgnoreCraftingFeats(modSettings.IgnoreCraftingFeats); + modSettings.CraftingTakesNoTime = renderer.Evaluate_CraftingTakesNoTime(modSettings.CraftingTakesNoTime); if (!modSettings.CraftingTakesNoTime) { - modSettings.CustomCraftRate = UmmUiRenderer.RenderCheckbox("Craft at a non-standard rate.", modSettings.CustomCraftRate); + modSettings.CustomCraftRate = renderer.Evaluate_CustomCraftRate(modSettings.CustomCraftRate); if (modSettings.CustomCraftRate) { - var maxMagicRate = ((modSettings.MagicCraftingRate + 1000) / 1000) * 1000; - modSettings.MagicCraftingRate = UmmUiRenderer.RenderIntSlider("Magic Item Crafting Rate", modSettings.MagicCraftingRate, 1, maxMagicRate); - var maxMundaneRate = ((modSettings.MundaneCraftingRate + 10) / 10) * 10; - modSettings.MundaneCraftingRate = UmmUiRenderer.RenderIntSlider("Mundane Item Crafting Rate", modSettings.MundaneCraftingRate, 1, maxMundaneRate); + modSettings.MagicCraftingRate = renderer.Evaluate_MagicCraftingRateSlider(modSettings.MagicCraftingRate); + modSettings.MundaneCraftingRate = renderer.Evaluate_MundaneCraftingRateSlider(modSettings.MundaneCraftingRate); } else { @@ -60,11 +49,10 @@ public static void RenderCheatsSectionAndUpdateSettings(Settings modSettings, st } } - modSettings.CasterLevelIsSinglePrerequisite = UmmUiRenderer.RenderCheckbox("When crafting, a Caster Level less than the prerequisite counts as a single missing prerequisite.", - modSettings.CasterLevelIsSinglePrerequisite); - modSettings.CraftAtFullSpeedWhileAdventuring = UmmUiRenderer.RenderCheckbox("Characters craft at full speed while adventuring (instead of 25% speed).", modSettings.CraftAtFullSpeedWhileAdventuring); - modSettings.IgnorePlusTenItemMaximum = UmmUiRenderer.RenderCheckbox("Ignore the rule that limits arms and armor to a maximum of +10 equivalent.", modSettings.IgnorePlusTenItemMaximum); - modSettings.IgnoreFeatCasterLevelRestriction = UmmUiRenderer.RenderCheckbox("Ignore the crafting feat Caster Level prerequisites when learning feats.", modSettings.IgnoreFeatCasterLevelRestriction); + modSettings.CasterLevelIsSinglePrerequisite = renderer.Evaluate_CasterLevelIsSinglePrerequisite(modSettings.CasterLevelIsSinglePrerequisite); + modSettings.CraftAtFullSpeedWhileAdventuring = renderer.Evaluate_CraftAtFullSpeedWhileAdventuring(modSettings.CraftAtFullSpeedWhileAdventuring); + modSettings.IgnorePlusTenItemMaximum = renderer.Evaluate_IgnorePlusTenItemMaximum(modSettings.IgnorePlusTenItemMaximum); + modSettings.IgnoreFeatCasterLevelRestriction = renderer.Evaluate_IgnoreFeatCasterLevelRestriction(modSettings.IgnoreFeatCasterLevelRestriction); } } } \ No newline at end of file From 150e2cdd547c7a761e21764cbdfc0d5dc90c3089 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Wed, 1 Jul 2020 15:31:13 -0500 Subject: [PATCH 031/132] Moving UmmUiRenderer into UnityModManager namespace --- CraftMagicItems/CraftMagicItems.csproj | 3 +-- CraftMagicItems/Main.cs | 1 + CraftMagicItems/UI/Sections/CheatSectionRenderer.cs | 4 +++- CraftMagicItems/UI/{ => UnityModManager}/UmmUiRenderer.cs | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) rename CraftMagicItems/UI/{ => UnityModManager}/UmmUiRenderer.cs (99%) diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index b7fb684..40ee673 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -102,7 +102,7 @@ - + @@ -122,7 +122,6 @@ - diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 68f4366..69b67cf 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -8,6 +8,7 @@ using System.Text.RegularExpressions; using CraftMagicItems.UI; using CraftMagicItems.UI.Sections; +using CraftMagicItems.UI.UnityModManager; using Kingmaker; #if PATCH21 using Kingmaker.Assets.UI.Context; diff --git a/CraftMagicItems/UI/Sections/CheatSectionRenderer.cs b/CraftMagicItems/UI/Sections/CheatSectionRenderer.cs index 2f142d2..d83f61f 100644 --- a/CraftMagicItems/UI/Sections/CheatSectionRenderer.cs +++ b/CraftMagicItems/UI/Sections/CheatSectionRenderer.cs @@ -1,4 +1,6 @@ -namespace CraftMagicItems.UI.Sections +using CraftMagicItems.UI.UnityModManager; + +namespace CraftMagicItems.UI.Sections { /// User Interface renderer into Unity Mod Manager for the cheats section public class CheatSectionRenderer : ICheatSectionRenderer diff --git a/CraftMagicItems/UI/UmmUiRenderer.cs b/CraftMagicItems/UI/UnityModManager/UmmUiRenderer.cs similarity index 99% rename from CraftMagicItems/UI/UmmUiRenderer.cs rename to CraftMagicItems/UI/UnityModManager/UmmUiRenderer.cs index c38e3f4..50c8c5e 100644 --- a/CraftMagicItems/UI/UmmUiRenderer.cs +++ b/CraftMagicItems/UI/UnityModManager/UmmUiRenderer.cs @@ -1,6 +1,6 @@ using UnityEngine; -namespace CraftMagicItems.UI +namespace CraftMagicItems.UI.UnityModManager { /// Class that handles the Unity Mod Manager UI rendering public class UmmUiRenderer From c8a0ec1aa259902b6657044059a89d0f0b15c32d Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Wed, 1 Jul 2020 15:31:30 -0500 Subject: [PATCH 032/132] Adding in unit tests over CheatSectionRendererFactory --- .../CraftMagicItemsTests.csproj | 1 + .../UI/Sections/CheatSectionFactoryTests.cs | 64 +++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 CraftMagicItemsTests/UI/Sections/CheatSectionFactoryTests.cs diff --git a/CraftMagicItemsTests/CraftMagicItemsTests.csproj b/CraftMagicItemsTests/CraftMagicItemsTests.csproj index 1a6b216..7a6ab3a 100644 --- a/CraftMagicItemsTests/CraftMagicItemsTests.csproj +++ b/CraftMagicItemsTests/CraftMagicItemsTests.csproj @@ -84,6 +84,7 @@ + diff --git a/CraftMagicItemsTests/UI/Sections/CheatSectionFactoryTests.cs b/CraftMagicItemsTests/UI/Sections/CheatSectionFactoryTests.cs new file mode 100644 index 0000000..59f9480 --- /dev/null +++ b/CraftMagicItemsTests/UI/Sections/CheatSectionFactoryTests.cs @@ -0,0 +1,64 @@ +using System; +using CraftMagicItems.UI.Sections; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; + +namespace CraftMagicItemsTests.UI.Sections +{ + /// Test class for + [TestClass] + public class CheatSectionRendererFactoryTests + { + [TestMethod] + public void CheatSectionRendererFactory_Defaults_CheatSectionRenderer() + { + //control + CheatSectionRendererFactory.Reset(); + + //invocation + var instance = CheatSectionRendererFactory.GetCheatSectionRenderer(); + + //validation + Assert.AreEqual(typeof(CheatSectionRenderer), instance.GetType(), $"Expected an instance of {nameof(CheatSectionRenderer)} to be returned."); + } + + [TestMethod] + public void SetConstructor_Works() + { + //control + Func mockConstructor = () => { return new Mock().Object; }; + CheatSectionRendererFactory.SetConstructor(mockConstructor); + + //invocation + var instance = CheatSectionRendererFactory.GetCheatSectionRenderer(); + + //validation + Assert.AreNotEqual(typeof(CheatSectionRenderer), instance.GetType(), $"Expected an instance of {nameof(CheatSectionRenderer)} to be returned."); + } + + [TestMethod] + public void GetCheatSectionRenderer_Works() + { + //invocation + var instance = CheatSectionRendererFactory.GetCheatSectionRenderer(); + + //validation + Assert.IsNotNull(instance, $"Expected an instance of {nameof(ICheatSectionRenderer)} to be returned."); + } + + [TestMethod] + public void Reset_Works() + { + //control + Func mockConstructor = () => { return new Mock().Object; }; + CheatSectionRendererFactory.SetConstructor(mockConstructor); + CheatSectionRendererFactory.Reset(); + + //invocation + var instance = CheatSectionRendererFactory.GetCheatSectionRenderer(); + + //validation + Assert.AreEqual(typeof(CheatSectionRenderer), instance.GetType(), $"Expected an instance of {nameof(CheatSectionRenderer)} to be returned."); + } + } +} \ No newline at end of file From 15b286214294e6dab4f097588962b44496c573dc Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Wed, 1 Jul 2020 16:36:09 -0500 Subject: [PATCH 033/132] Adding in missing param XML comment --- CraftMagicItems/UI/UserInterfaceEventHandlingLogic.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/CraftMagicItems/UI/UserInterfaceEventHandlingLogic.cs b/CraftMagicItems/UI/UserInterfaceEventHandlingLogic.cs index ce1758f..ddb3d51 100644 --- a/CraftMagicItems/UI/UserInterfaceEventHandlingLogic.cs +++ b/CraftMagicItems/UI/UserInterfaceEventHandlingLogic.cs @@ -7,6 +7,7 @@ namespace CraftMagicItems.UI public static class UserInterfaceEventHandlingLogic { /// Renders the Cheats section and retrieves the values specified by its rendered UI + /// instance used to render controls and return current values /// to default to and to read from /// Text to render for the price /// Collection of containing the display text for various pricing guidelines From 66ddb5a888de22c1f9c20a58522943b21ca7c21c Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Wed, 1 Jul 2020 16:51:48 -0500 Subject: [PATCH 034/132] Adding in unit tests over RenderCheatsSectionAndUpdateSettings --- .../CraftMagicItemsTests.csproj | 1 + .../UserInterfaceEventHandlingLogicTests.cs | 545 ++++++++++++++++++ 2 files changed, 546 insertions(+) create mode 100644 CraftMagicItemsTests/UI/UserInterfaceEventHandlingLogicTests.cs diff --git a/CraftMagicItemsTests/CraftMagicItemsTests.csproj b/CraftMagicItemsTests/CraftMagicItemsTests.csproj index 7a6ab3a..c61ff07 100644 --- a/CraftMagicItemsTests/CraftMagicItemsTests.csproj +++ b/CraftMagicItemsTests/CraftMagicItemsTests.csproj @@ -85,6 +85,7 @@ + diff --git a/CraftMagicItemsTests/UI/UserInterfaceEventHandlingLogicTests.cs b/CraftMagicItemsTests/UI/UserInterfaceEventHandlingLogicTests.cs new file mode 100644 index 0000000..ae9c5aa --- /dev/null +++ b/CraftMagicItemsTests/UI/UserInterfaceEventHandlingLogicTests.cs @@ -0,0 +1,545 @@ +using System; +using CraftMagicItems; +using CraftMagicItems.UI; +using CraftMagicItems.UI.Sections; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; + +namespace CraftMagicItemsTests.UI +{ + /// Test class for + [TestClass] + public class UserInterfaceEventHandlingLogicTests + { + [TestMethod] + public void RenderCheatsSectionAndUpdateSettings_Reads_CraftingCostsNoGold() + { + string priceLabel = "irrelevant"; + string[] priceOptions = new[] { String.Empty, String.Empty, String.Empty }; + + /************ + * Test 1 * + ************/ + //control + Settings settings = new Settings { CraftingCostsNoGold = false }; + Mock renderer = new Mock(); + renderer.Setup(r => r.Evaluate_CraftingCostsNoGold(It.IsAny())).Returns(true); + + //invocation + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + + //validation + Assert.AreEqual(true, settings.CraftingCostsNoGold); + + /************ + * Test 2 * + ************/ + //control + settings = new Settings { CraftingCostsNoGold = true }; + renderer = new Mock(); + renderer.Setup(r => r.Evaluate_CraftingCostsNoGold(It.IsAny())).Returns(false); + + //invocation + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + + //validation + Assert.AreEqual(false, settings.CraftingCostsNoGold); + } + + [TestMethod] + public void RenderCheatsSectionAndUpdateSettings_DoesNotRead_CraftingPriceScale_When_CraftingCostsNoGold() + { + string priceLabel = "irrelevant"; + string[] priceOptions = new[] { String.Empty, String.Empty, String.Empty }; + int initial = -10000000; + + //control + Settings settings = new Settings + { + CraftingPriceScale = initial, + }; + Mock renderer = new Mock(); + renderer.Setup(r => r.Evaluate_CraftingCostsNoGold(It.IsAny())).Returns(true); + + //invocation + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + + //validation + Assert.AreEqual(initial, settings.CraftingPriceScale); + } + + [TestMethod] + public void RenderCheatsSectionAndUpdateSettings_DoesNotInvoke_WarningAboutCustomItemVanillaItemCostDisparity_When_CraftingCostsNoGold() + { + string priceLabel = "irrelevant"; + string[] priceOptions = new[] { String.Empty, String.Empty, String.Empty }; + + //control + bool invokedWarning = false; + Action setInvoked = () => { invokedWarning = true; }; + + Mock renderer = new Mock(); + renderer.Setup(r => r.Evaluate_CraftingCostsNoGold(It.IsAny())).Returns(true); + renderer.Setup(r => r.RenderOnly_WarningAboutCustomItemVanillaItemCostDisparity()).Callback(setInvoked); + + //invocation + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, new Settings(), priceLabel, priceOptions); + + //validation + Assert.AreEqual(false, invokedWarning); + } + + [TestMethod] + public void RenderCheatsSectionAndUpdateSettings_Reads_CraftingCostSelection() + { + string priceLabel = "irrelevant"; + string[] priceOptions = new[] { String.Empty, String.Empty, String.Empty }; + + //control + bool invokedWarning = false; + Action setInvoked = () => { invokedWarning = true; }; + + Mock renderer = new Mock(); + renderer.Setup(r => r.Evaluate_CraftingCostsNoGold(It.IsAny())).Returns(false); + renderer.Setup(r => r.Evaluate_CraftingCostSelection(It.IsAny(), It.IsAny())).Callback(setInvoked); + + //invocation + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, new Settings(), priceLabel, priceOptions); + + //validation + Assert.AreEqual(true, invokedWarning); + } + + [TestMethod] + public void RenderCheatsSectionAndUpdateSettings_Reads_CustomCraftingCostSlider() + { + string priceLabel = "irrelevant"; + string[] priceOptions = new[] { String.Empty, String.Empty, String.Empty }; + + //control + Settings settings = new Settings { CraftingPriceScale = -4 }; + Mock renderer = new Mock(); + renderer.Setup(r => r.Evaluate_CraftingCostsNoGold(It.IsAny())).Returns(false); + renderer.Setup(r => r.Evaluate_CraftingCostSelection(It.IsAny(), It.IsAny())).Returns(2); + renderer.Setup(r => r.Evaluate_CustomCraftingCostSlider(It.IsAny())).Returns(4000); + + //invocation + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + + //validation + Assert.AreEqual(4000, settings.CraftingPriceScale); + } + + [TestMethod] + public void RenderCheatsSectionAndUpdateSettings_Reads_CraftingPriceScale_From_CraftingCostSelection() + { + string priceLabel = "irrelevant"; + string[] priceOptions = new[] { String.Empty, String.Empty, String.Empty }; + + /************ + * Test 1 * + ************/ + //control + Settings settings = new Settings { CraftingPriceScale = -4 }; + Mock renderer = new Mock(); + renderer.Setup(r => r.Evaluate_CraftingCostsNoGold(It.IsAny())).Returns(false); + renderer.Setup(r => r.Evaluate_CraftingCostSelection(It.IsAny(), It.IsAny())).Returns(1); + + //invocation + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + + //validation + Assert.AreEqual(2, settings.CraftingPriceScale); + + /************ + * Test 2 * + ************/ + //control + settings = new Settings { CraftingPriceScale = -4 }; + renderer = new Mock(); + renderer.Setup(r => r.Evaluate_CraftingCostsNoGold(It.IsAny())).Returns(false); + renderer.Setup(r => r.Evaluate_CraftingCostSelection(It.IsAny(), It.IsAny())).Returns(0); + + //invocation + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + + //validation + Assert.AreEqual(1, settings.CraftingPriceScale); + } + + [TestMethod] + public void RenderCheatsSectionAndUpdateSettings_Invokes_WarningAboutCustomItemVanillaItemCostDisparity_When_CraftingCostsGold() + { + string priceLabel = "irrelevant"; + string[] priceOptions = new[] { String.Empty, String.Empty, String.Empty }; + + //control + bool invokedWarning = false; + Action setInvoked = () => { invokedWarning = true; }; + + Mock renderer = new Mock(); + renderer.Setup(r => r.Evaluate_CraftingCostsNoGold(It.IsAny())).Returns(false); + renderer.Setup(r => r.Evaluate_CraftingCostSelection(It.IsAny(), It.IsAny())).Returns(-1); + renderer.Setup(r => r.RenderOnly_WarningAboutCustomItemVanillaItemCostDisparity()).Callback(setInvoked); + + //invocation + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, new Settings(), priceLabel, priceOptions); + + //validation + Assert.AreEqual(true, invokedWarning); + } + + [TestMethod] + public void RenderCheatsSectionAndUpdateSettings_Reads_IgnoreCraftingFeats() + { + string priceLabel = "irrelevant"; + string[] priceOptions = new[] { String.Empty, String.Empty, String.Empty }; + + /************ + * Test 1 * + ************/ + //control + Settings settings = new Settings { IgnoreCraftingFeats = false }; + Mock renderer = new Mock(); + renderer.Setup(r => r.Evaluate_IgnoreCraftingFeats(It.IsAny())).Returns(true); + + //invocation + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + + //validation + Assert.AreEqual(true, settings.IgnoreCraftingFeats); + + /************ + * Test 2 * + ************/ + //control + settings = new Settings { IgnoreCraftingFeats = true }; + renderer = new Mock(); + renderer.Setup(r => r.Evaluate_IgnoreCraftingFeats(It.IsAny())).Returns(false); + + //invocation + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + + //validation + Assert.AreEqual(false, settings.IgnoreCraftingFeats); + } + + [TestMethod] + public void RenderCheatsSectionAndUpdateSettings_Reads_CraftingTakesNoTime() + { + string priceLabel = "irrelevant"; + string[] priceOptions = new[] { String.Empty, String.Empty, String.Empty }; + + /************ + * Test 1 * + ************/ + //control + Settings settings = new Settings { CraftingTakesNoTime = false }; + Mock renderer = new Mock(); + renderer.Setup(r => r.Evaluate_CraftingTakesNoTime(It.IsAny())).Returns(true); + + //invocation + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + + //validation + Assert.AreEqual(true, settings.CraftingTakesNoTime); + + /************ + * Test 2 * + ************/ + //control + settings = new Settings { CraftingTakesNoTime = true }; + renderer = new Mock(); + renderer.Setup(r => r.Evaluate_CraftingTakesNoTime(It.IsAny())).Returns(false); + + //invocation + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + + //validation + Assert.AreEqual(false, settings.CraftingTakesNoTime); + } + + [TestMethod] + public void RenderCheatsSectionAndUpdateSettings_DoesNotRead_CustomCraftRate_When_CraftingTakesNoTime() + { + string priceLabel = "irrelevant"; + string[] priceOptions = new[] { String.Empty, String.Empty, String.Empty }; + int initialMagicCraftRate = -10000000; + int initialMundaneCraftRate = 9348458; + bool initialCustomCraftRate = true; + + //control + Settings settings = new Settings + { + MagicCraftingRate = initialMagicCraftRate, + MundaneCraftingRate = initialMundaneCraftRate, + CustomCraftRate = initialCustomCraftRate, + }; + Mock renderer = new Mock(); + renderer.Setup(r => r.Evaluate_CraftingTakesNoTime(It.IsAny())).Returns(true); + + //invocation + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + + //validation + Assert.AreEqual(initialMagicCraftRate, settings.MagicCraftingRate); + Assert.AreEqual(initialMundaneCraftRate, settings.MundaneCraftingRate); + Assert.AreEqual(initialCustomCraftRate, settings.CustomCraftRate); + } + + [TestMethod] + public void RenderCheatsSectionAndUpdateSettings_Reads_CustomCraftRate() + { + string priceLabel = "irrelevant"; + string[] priceOptions = new[] { String.Empty, String.Empty, String.Empty }; + + /************ + * Test 1 * + ************/ + //control + Settings settings = new Settings { CustomCraftRate = false }; + Mock renderer = new Mock(); + renderer.Setup(r => r.Evaluate_CraftingTakesNoTime(It.IsAny())).Returns(false); + renderer.Setup(r => r.Evaluate_CustomCraftRate(It.IsAny())).Returns(true); + + //invocation + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + + //validation + Assert.AreEqual(true, settings.CustomCraftRate); + + /************ + * Test 2 * + ************/ + //control + settings = new Settings { CustomCraftRate = true }; + renderer = new Mock(); + renderer.Setup(r => r.Evaluate_CraftingTakesNoTime(It.IsAny())).Returns(false); + renderer.Setup(r => r.Evaluate_CustomCraftRate(It.IsAny())).Returns(false); + + //invocation + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + + //validation + Assert.AreEqual(false, settings.CustomCraftRate); + } + + [TestMethod] + public void RenderCheatsSectionAndUpdateSettings_Reads_MagicCraftingRate() + { + string priceLabel = "irrelevant"; + string[] priceOptions = new[] { String.Empty, String.Empty, String.Empty }; + + //control + Settings settings = new Settings { MagicCraftingRate = -8 }; + Mock renderer = new Mock(); + renderer.Setup(r => r.Evaluate_CraftingTakesNoTime(It.IsAny())).Returns(false); + renderer.Setup(r => r.Evaluate_CustomCraftRate(It.IsAny())).Returns(true); + renderer.Setup(r => r.Evaluate_MagicCraftingRateSlider(It.IsAny())).Returns(7); + + //invocation + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + + //validation + Assert.AreEqual(7, settings.MagicCraftingRate); + } + + [TestMethod] + public void RenderCheatsSectionAndUpdateSettings_Reads_MundaneCraftingRate() + { + string priceLabel = "irrelevant"; + string[] priceOptions = new[] { String.Empty, String.Empty, String.Empty }; + + //control + Settings settings = new Settings { MundaneCraftingRate = -23412345 }; + Mock renderer = new Mock(); + renderer.Setup(r => r.Evaluate_CraftingTakesNoTime(It.IsAny())).Returns(false); + renderer.Setup(r => r.Evaluate_CustomCraftRate(It.IsAny())).Returns(true); + renderer.Setup(r => r.Evaluate_MundaneCraftingRateSlider(It.IsAny())).Returns(12); + + //invocation + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + + //validation + Assert.AreEqual(12, settings.MundaneCraftingRate); + } + + [TestMethod] + public void RenderCheatsSectionAndUpdateSettings_Defaults_MagicCraftingRate() + { + string priceLabel = "irrelevant"; + string[] priceOptions = new[] { String.Empty, String.Empty, String.Empty }; + + //control + Settings settings = new Settings { MagicCraftingRate = -8 }; + Mock renderer = new Mock(); + renderer.Setup(r => r.Evaluate_CraftingTakesNoTime(It.IsAny())).Returns(false); + renderer.Setup(r => r.Evaluate_CustomCraftRate(It.IsAny())).Returns(false); + + //invocation + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + + //validation + Assert.AreEqual(Settings.MagicCraftingProgressPerDay, settings.MagicCraftingRate); + } + + [TestMethod] + public void RenderCheatsSectionAndUpdateSettings_Defaults_MundaneCraftingRate() + { + string priceLabel = "irrelevant"; + string[] priceOptions = new[] { String.Empty, String.Empty, String.Empty }; + + //control + Settings settings = new Settings { MundaneCraftingRate = -23412345 }; + Mock renderer = new Mock(); + renderer.Setup(r => r.Evaluate_CraftingTakesNoTime(It.IsAny())).Returns(false); + renderer.Setup(r => r.Evaluate_CustomCraftRate(It.IsAny())).Returns(false); + + //invocation + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + + //validation + Assert.AreEqual(Settings.MundaneCraftingProgressPerDay, settings.MundaneCraftingRate); + } + + [TestMethod] + public void RenderCheatsSectionAndUpdateSettings_Reads_CasterLevelIsSinglePrerequisite() + { + string priceLabel = "irrelevant"; + string[] priceOptions = new[] { String.Empty, String.Empty, String.Empty }; + + /************ + * Test 1 * + ************/ + //control + Settings settings = new Settings { CasterLevelIsSinglePrerequisite = false }; + Mock renderer = new Mock(); + renderer.Setup(r => r.Evaluate_CasterLevelIsSinglePrerequisite(It.IsAny())).Returns(true); + + //invocation + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + + //validation + Assert.AreEqual(true, settings.CasterLevelIsSinglePrerequisite); + + /************ + * Test 2 * + ************/ + //control + settings = new Settings { CasterLevelIsSinglePrerequisite = true }; + renderer = new Mock(); + renderer.Setup(r => r.Evaluate_CasterLevelIsSinglePrerequisite(It.IsAny())).Returns(false); + + //invocation + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + + //validation + Assert.AreEqual(false, settings.CasterLevelIsSinglePrerequisite); + } + + [TestMethod] + public void RenderCheatsSectionAndUpdateSettings_Reads_CraftAtFullSpeedWhileAdventuring() + { + string priceLabel = "irrelevant"; + string[] priceOptions = new[] { String.Empty, String.Empty, String.Empty }; + + /************ + * Test 1 * + ************/ + //control + Settings settings = new Settings { CraftAtFullSpeedWhileAdventuring = false }; + Mock renderer = new Mock(); + renderer.Setup(r => r.Evaluate_CraftAtFullSpeedWhileAdventuring(It.IsAny())).Returns(true); + + //invocation + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + + //validation + Assert.AreEqual(true, settings.CraftAtFullSpeedWhileAdventuring); + + /************ + * Test 2 * + ************/ + //control + settings = new Settings { CraftAtFullSpeedWhileAdventuring = true }; + renderer = new Mock(); + renderer.Setup(r => r.Evaluate_CraftAtFullSpeedWhileAdventuring(It.IsAny())).Returns(false); + + //invocation + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + + //validation + Assert.AreEqual(false, settings.CraftAtFullSpeedWhileAdventuring); + } + + [TestMethod] + public void RenderCheatsSectionAndUpdateSettings_Reads_IgnorePlusTenItemMaximum() + { + string priceLabel = "irrelevant"; + string[] priceOptions = new[] { String.Empty, String.Empty, String.Empty }; + + /************ + * Test 1 * + ************/ + //control + Settings settings = new Settings { IgnorePlusTenItemMaximum = false }; + Mock renderer = new Mock(); + renderer.Setup(r => r.Evaluate_IgnorePlusTenItemMaximum(It.IsAny())).Returns(true); + + //invocation + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + + //validation + Assert.AreEqual(true, settings.IgnorePlusTenItemMaximum); + + /************ + * Test 2 * + ************/ + //control + settings = new Settings { IgnorePlusTenItemMaximum = true }; + renderer = new Mock(); + renderer.Setup(r => r.Evaluate_IgnorePlusTenItemMaximum(It.IsAny())).Returns(false); + + //invocation + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + + //validation + Assert.AreEqual(false, settings.IgnorePlusTenItemMaximum); + } + + [TestMethod] + public void RenderCheatsSectionAndUpdateSettings_Reads_IgnoreFeatCasterLevelRestriction() + { + string priceLabel = "irrelevant"; + string[] priceOptions = new[] { String.Empty, String.Empty, String.Empty }; + + /************ + * Test 1 * + ************/ + //control + Settings settings = new Settings { IgnoreFeatCasterLevelRestriction = false }; + Mock renderer = new Mock(); + renderer.Setup(r => r.Evaluate_IgnoreFeatCasterLevelRestriction(It.IsAny())).Returns(true); + + //invocation + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + + //validation + Assert.AreEqual(true, settings.IgnoreFeatCasterLevelRestriction); + + /************ + * Test 2 * + ************/ + //control + settings = new Settings { IgnoreFeatCasterLevelRestriction = true }; + renderer = new Mock(); + renderer.Setup(r => r.Evaluate_IgnoreFeatCasterLevelRestriction(It.IsAny())).Returns(false); + + //invocation + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + + //validation + Assert.AreEqual(false, settings.IgnoreFeatCasterLevelRestriction); + } + } +} \ No newline at end of file From 6f7a27c13de8a2ba6c95ef9d1c57b4ef9c88017d Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Thu, 2 Jul 2020 10:07:27 -0500 Subject: [PATCH 035/132] Adding Button controls to the UmmUiRenderer --- .../UI/UnityModManager/UmmUiRenderer.cs | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/CraftMagicItems/UI/UnityModManager/UmmUiRenderer.cs b/CraftMagicItems/UI/UnityModManager/UmmUiRenderer.cs index 50c8c5e..738ce3a 100644 --- a/CraftMagicItems/UI/UnityModManager/UmmUiRenderer.cs +++ b/CraftMagicItems/UI/UnityModManager/UmmUiRenderer.cs @@ -103,14 +103,14 @@ public static void RenderLabelRow(string label) GUILayout.EndHorizontal(); } - /// Renders a Label control as its own line in Unity Mod Manager + /// Renders a Label control in Unity Mod Manager /// Text to be displayed public static void RenderLabel(string label) { GUILayout.Label(label); } - /// Renders a Label control as its own line in Unity Mod Manager + /// Renders a Label control in Unity Mod Manager /// Text to be displayed /// Should the label be expanded public static void RenderLabel(string label, bool expandWidth) @@ -118,6 +118,23 @@ public static void RenderLabel(string label, bool expandWidth) GUILayout.Label(label, GUILayout.ExpandWidth(expandWidth)); } + /// Renders a button control in Unity Mod Manager + /// Text to be displayed on the control + /// True if the button is clicked, otherwise false + public static bool RenderButton(string label) + { + return GUILayout.Button(label); + } + + /// Renders a button control in Unity Mod Manager + /// Text to be displayed on the control + /// Should the control be expanded + /// True if the button is clicked, otherwise false + public static bool RenderButton(string label, bool expandWidth) + { + return GUILayout.Button(label, GUILayout.ExpandWidth(expandWidth)); + } + /// Renders a selection of to Unity Mod Manager /// Label for the selection /// Options for the selection From 126df0c416faf1c422d98baa57b8bcf8f98febd7 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Thu, 2 Jul 2020 11:07:19 -0500 Subject: [PATCH 036/132] Adding a section renderer for feat reassignment --- CraftMagicItems/CraftMagicItems.csproj | 1 + .../FeatReassignmentSectionRenderer.cs | 47 +++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 CraftMagicItems/UI/Sections/FeatReassignmentSectionRenderer.cs diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index 40ee673..61f3899 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -101,6 +101,7 @@ + diff --git a/CraftMagicItems/UI/Sections/FeatReassignmentSectionRenderer.cs b/CraftMagicItems/UI/Sections/FeatReassignmentSectionRenderer.cs new file mode 100644 index 0000000..d9020aa --- /dev/null +++ b/CraftMagicItems/UI/Sections/FeatReassignmentSectionRenderer.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using CraftMagicItems.UI.UnityModManager; + +namespace CraftMagicItems.UI.Sections +{ + public class FeatReassignmentSectionRenderer + { + /// Renders a warning that the current character does not qualify for any crafting feats + /// Name of character currently being evaluated + public void RenderOnly_Warning_NoCraftingFeatQualifications(string characterName) + { + UmmUiRenderer.RenderLabelRow($"{characterName} does not currently qualify for any crafting feats."); + } + + /// Renders a message describing how to use the section + public void RenderOnly_UsageExplanation() + { + UmmUiRenderer.RenderLabelRow("Use this section to reassign previous feat choices for this character to crafting feats. Warning: This is a one-way assignment!"); + } + + /// Renders a selection to Unity Mod Manager for the options for selecting a missing casting feat + /// Collection of feat names that are missing from the currently selected character + /// The selected index of the feats + public int Evaluate_MissingFeatSelection(string[] featOptions) + { + return Main.DrawSelectionUserInterfaceElements("Feat to learn", featOptions, 6); + } + + /// Renders a label and button on their own line for selecting a current feat to be replaced by the selected replacement crafting feat + /// Name of the existing feat to potentially replace + /// Name of the feat to potentially replace + /// True if the button is clicked, otherwise false + public bool Evaluate_LearnFeatButton(string existingFeat, string replacementFeat) + { + UmmUiRenderer.RenderHorizontalStart(); + UmmUiRenderer.RenderLabel($"Feat: {existingFeat}", false); + var selection = UmmUiRenderer.RenderButton($"<- {replacementFeat}", false); + UmmUiRenderer.RenderHorizontalEnd(); + + return selection; + } + } +} \ No newline at end of file From 892db875c987f3a5ee0f7424c2912ead10a4f381 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Thu, 2 Jul 2020 11:17:40 -0500 Subject: [PATCH 037/132] Moving RenderFeatReassignmentSection into UserInterfaceEnventHandlingLogic --- CraftMagicItems/Main.cs | 68 +---------------- .../FeatReassignmentSectionRenderer.cs | 10 +-- .../UI/UserInterfaceEventHandlingLogic.cs | 74 +++++++++++++++++++ 3 files changed, 81 insertions(+), 71 deletions(-) diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 69b67cf..32e621f 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -91,7 +91,7 @@ public static class Main { "Custom" }; - private static readonly FeatureGroup[] CraftingFeatGroups = {FeatureGroup.Feat, FeatureGroup.WizardFeat}; + public static readonly FeatureGroup[] CraftingFeatGroups = {FeatureGroup.Feat, FeatureGroup.WizardFeat}; private const string MasterworkGuid = "6b38844e2bffbac48b63036b66e735be"; public const string MithralArmorEnchantmentGuid = "7b95a819181574a4799d93939aa99aff"; private const string OversizedGuid = "d8e1ebc1062d8cc42abff78783856b0d"; @@ -396,7 +396,7 @@ private static void OnGui(UnityModManager.ModEntry modEntry) { if (UmmUiRenderer.RenderToggleSection("Feat Reassignment", currentSection == OpenSection.FeatsSection)) { currentSection = OpenSection.FeatsSection; - RenderFeatReassignmentSection(); + UserInterfaceEventHandlingLogic.RenderFeatReassignmentSection(new FeatReassignmentSectionRenderer()); } if (UmmUiRenderer.RenderToggleSection("Cheats", currentSection == OpenSection.CheatsSection)) @@ -2141,66 +2141,6 @@ private static void RenderProjectsSection() { } } - private static void RenderFeatReassignmentSection() { - var caster = GetSelectedCrafter(false); - if (caster == null) { - return; - } - - var casterLevel = CharacterCasterLevel(caster.Descriptor); - var missingFeats = ItemCraftingData - .Where(data => data.FeatGuid != null && !CharacterHasFeat(caster, data.FeatGuid) && data.MinimumCasterLevel <= casterLevel) - .ToArray(); - if (missingFeats.Length == 0) { - UmmUiRenderer.RenderLabelRow($"{caster.CharacterName} does not currently qualify for any crafting feats."); - return; - } - - UmmUiRenderer.RenderLabelRow("Use this section to reassign previous feat choices for this character to crafting feats. Warning: This is a one-way assignment!"); - var featOptions = missingFeats.Select(data => new L10NString(data.NameId).ToString()).ToArray(); - var selectedFeatToLearn = DrawSelectionUserInterfaceElements("Feat to learn", featOptions, 6); - var learnFeatData = missingFeats[selectedFeatToLearn]; - var learnFeat = ResourcesLibrary.TryGetBlueprint(learnFeatData.FeatGuid); - if (learnFeat == null) { - throw new Exception($"Unable to find feat with guid {learnFeatData.FeatGuid}"); - } - - var removedFeatIndex = 0; - foreach (var feature in caster.Descriptor.Progression.Features) { - if (!feature.Blueprint.HideInUI && feature.Blueprint.HasGroup(CraftingFeatGroups) - && (feature.SourceProgression != null || feature.SourceRace != null)) { - GUILayout.BeginHorizontal(); - GUILayout.Label($"Feat: {feature.Name}", GUILayout.ExpandWidth(false)); - if (GUILayout.Button($"<- {learnFeat.Name}", GUILayout.ExpandWidth(false))) { - var currentRank = feature.Rank; - caster.Descriptor.Progression.ReplaceFeature(feature.Blueprint, learnFeat); - if (currentRank == 1) { - foreach (var addFact in feature.SelectComponents((AddFacts addFacts) => true)) { - addFact.OnFactDeactivate(); - } - - caster.Descriptor.Progression.Features.RemoveFact(feature); - } - - var addedFeature = caster.Descriptor.Progression.Features.AddFeature(learnFeat); - addedFeature.Source = feature.Source; - var mFacts = caster.Descriptor.Progression.Features.RawFacts; - if (removedFeatIndex < mFacts.Count) { - // Move the new feat to the place in the list originally occupied by the removed one. - mFacts.Remove(addedFeature); - mFacts.Insert(removedFeatIndex, addedFeature); - } - - ActionBarManager.Instance.HandleAbilityRemoved(null); - } - - GUILayout.EndHorizontal(); - } - - removedFeatIndex++; - } - } - private static bool IsPlayerSomewhereSafe() { if (Game.Instance.CurrentlyLoadedArea != null && SafeBlueprintAreaGuids.Contains(Game.Instance.CurrentlyLoadedArea.AssetGuid)) { return true; @@ -2215,7 +2155,7 @@ private static bool IsPlayerInCapital() { (Game.Instance.CurrentMode == GameModeType.Kingdom && KingdomTimelineManager.CanAdvanceTime()); } - private static UnitEntityData GetSelectedCrafter(bool render) { + public static UnitEntityData GetSelectedCrafter(bool render) { currentCaster = null; // Only allow remote companions if the player is in the capital. var remote = IsPlayerInCapital(); @@ -2358,7 +2298,7 @@ private static IEnumerable FindItemBlueprintForEnchantme return EnchantmentIdToItem.ContainsKey(assetGuid) ? EnchantmentIdToItem[assetGuid] : null; } - private static bool CharacterHasFeat(UnitEntityData caster, string featGuid) { + public static bool CharacterHasFeat(UnitEntityData caster, string featGuid) { return caster.Descriptor.Progression.Features.Enumerable.Any(feat => feat.Blueprint.AssetGuid == featGuid); } diff --git a/CraftMagicItems/UI/Sections/FeatReassignmentSectionRenderer.cs b/CraftMagicItems/UI/Sections/FeatReassignmentSectionRenderer.cs index d9020aa..adb7875 100644 --- a/CraftMagicItems/UI/Sections/FeatReassignmentSectionRenderer.cs +++ b/CraftMagicItems/UI/Sections/FeatReassignmentSectionRenderer.cs @@ -1,12 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using CraftMagicItems.UI.UnityModManager; +using CraftMagicItems.UI.UnityModManager; namespace CraftMagicItems.UI.Sections { + /// User Interface renderer into Unity Mod Manager for the feat reassignment section public class FeatReassignmentSectionRenderer { /// Renders a warning that the current character does not qualify for any crafting feats @@ -31,7 +27,7 @@ public int Evaluate_MissingFeatSelection(string[] featOptions) } /// Renders a label and button on their own line for selecting a current feat to be replaced by the selected replacement crafting feat - /// Name of the existing feat to potentially replace + /// Name of the existing feat to potentially be replaced /// Name of the feat to potentially replace /// True if the button is clicked, otherwise false public bool Evaluate_LearnFeatButton(string existingFeat, string replacementFeat) diff --git a/CraftMagicItems/UI/UserInterfaceEventHandlingLogic.cs b/CraftMagicItems/UI/UserInterfaceEventHandlingLogic.cs index ddb3d51..f406b15 100644 --- a/CraftMagicItems/UI/UserInterfaceEventHandlingLogic.cs +++ b/CraftMagicItems/UI/UserInterfaceEventHandlingLogic.cs @@ -1,5 +1,10 @@ using System; +using System.Linq; using CraftMagicItems.UI.Sections; +using Kingmaker.Blueprints; +using Kingmaker.Blueprints.Classes; +using Kingmaker.UI.ActionBar; +using Kingmaker.UnitLogic.FactLogic; namespace CraftMagicItems.UI { @@ -55,5 +60,74 @@ public static void RenderCheatsSectionAndUpdateSettings(ICheatSectionRenderer re modSettings.IgnorePlusTenItemMaximum = renderer.Evaluate_IgnorePlusTenItemMaximum(modSettings.IgnorePlusTenItemMaximum); modSettings.IgnoreFeatCasterLevelRestriction = renderer.Evaluate_IgnoreFeatCasterLevelRestriction(modSettings.IgnoreFeatCasterLevelRestriction); } + + /// Renders the section for feat reassignment and handles user selections + /// instance that handles rendering of controls + public static void RenderFeatReassignmentSection(FeatReassignmentSectionRenderer renderer) + { + var caster = Main.GetSelectedCrafter(false); + if (caster == null) + { + return; + } + + var casterLevel = Main.CharacterCasterLevel(caster.Descriptor); + var missingFeats = Main.ItemCraftingData + .Where(data => data.FeatGuid != null && !Main.CharacterHasFeat(caster, data.FeatGuid) && data.MinimumCasterLevel <= casterLevel) + .ToArray(); + if (missingFeats.Length == 0) + { + renderer.RenderOnly_Warning_NoCraftingFeatQualifications(caster.CharacterName); + return; + } + + renderer.RenderOnly_UsageExplanation(); + var featOptions = missingFeats.Select(data => new L10NString(data.NameId).ToString()).ToArray(); + var selectedFeatToLearn = renderer.Evaluate_MissingFeatSelection(featOptions); + var learnFeatData = missingFeats[selectedFeatToLearn]; + var learnFeat = ResourcesLibrary.TryGetBlueprint(learnFeatData.FeatGuid); + if (learnFeat == null) + { + throw new Exception($"Unable to find feat with guid {learnFeatData.FeatGuid}"); + } + + var removedFeatIndex = 0; + foreach (var feature in caster.Descriptor.Progression.Features) + { + if (!feature.Blueprint.HideInUI && feature.Blueprint.HasGroup(Main.CraftingFeatGroups) + && (feature.SourceProgression != null || feature.SourceRace != null)) + { + if (renderer.Evaluate_LearnFeatButton(feature.Name, learnFeat.Name)) + { + var currentRank = feature.Rank; + caster.Descriptor.Progression.ReplaceFeature(feature.Blueprint, learnFeat); + if (currentRank == 1) + { + foreach (var addFact in feature.SelectComponents((AddFacts addFacts) => true)) + { + addFact.OnFactDeactivate(); + } + + caster.Descriptor.Progression.Features.RemoveFact(feature); + } + + var addedFeature = caster.Descriptor.Progression.Features.AddFeature(learnFeat); + addedFeature.Source = feature.Source; + + var mFacts = caster.Descriptor.Progression.Features.RawFacts; + if (removedFeatIndex < mFacts.Count) + { + // Move the new feat to the place in the list originally occupied by the removed one. + mFacts.Remove(addedFeature); + mFacts.Insert(removedFeatIndex, addedFeature); + } + + ActionBarManager.Instance.HandleAbilityRemoved(null); + } + } + + removedFeatIndex++; + } + } } } \ No newline at end of file From a193c4582b9b89ce9509663f5f0774395d8d1432 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Thu, 2 Jul 2020 11:24:14 -0500 Subject: [PATCH 038/132] Extracting an interface from FeatReassignmentSectionRenderer --- CraftMagicItems/CraftMagicItems.csproj | 1 + .../FeatReassignmentSectionRenderer.cs | 2 +- .../IFeatReassignmentSectionRenderer.cs | 24 +++++++++++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 CraftMagicItems/UI/Sections/IFeatReassignmentSectionRenderer.cs diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index 61f3899..7685d7a 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -103,6 +103,7 @@ + diff --git a/CraftMagicItems/UI/Sections/FeatReassignmentSectionRenderer.cs b/CraftMagicItems/UI/Sections/FeatReassignmentSectionRenderer.cs index adb7875..3a838ed 100644 --- a/CraftMagicItems/UI/Sections/FeatReassignmentSectionRenderer.cs +++ b/CraftMagicItems/UI/Sections/FeatReassignmentSectionRenderer.cs @@ -3,7 +3,7 @@ namespace CraftMagicItems.UI.Sections { /// User Interface renderer into Unity Mod Manager for the feat reassignment section - public class FeatReassignmentSectionRenderer + public class FeatReassignmentSectionRenderer : IFeatReassignmentSectionRenderer { /// Renders a warning that the current character does not qualify for any crafting feats /// Name of character currently being evaluated diff --git a/CraftMagicItems/UI/Sections/IFeatReassignmentSectionRenderer.cs b/CraftMagicItems/UI/Sections/IFeatReassignmentSectionRenderer.cs new file mode 100644 index 0000000..fa46fbb --- /dev/null +++ b/CraftMagicItems/UI/Sections/IFeatReassignmentSectionRenderer.cs @@ -0,0 +1,24 @@ +namespace CraftMagicItems.UI.Sections +{ + /// Interface defining the I/O around the user interface for the feat reassignment section and its returned user input + public interface IFeatReassignmentSectionRenderer + { + /// Renders a label and button for selecting a current feat to be replaced by the selected replacement crafting feat + /// Name of the existing feat to potentially be replaced + /// Name of the feat to potentially replace + /// True if the button is clicked, otherwise false + bool Evaluate_LearnFeatButton(string existingFeat, string replacementFeat); + + /// Renders a selection for the options for selecting a missing casting feat + /// Collection of feat names that are missing from the currently selected character + /// The selected index of the feats + int Evaluate_MissingFeatSelection(string[] featOptions); + + /// Renders a message describing how to use the section + void RenderOnly_UsageExplanation(); + + /// Renders a warning that the current character does not qualify for any crafting feats + /// Name of character currently being evaluated + void RenderOnly_Warning_NoCraftingFeatQualifications(string characterName); + } +} \ No newline at end of file From 7dd6bdea6510aa8207ca57fcde8dd6b0bafa21db Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Thu, 2 Jul 2020 11:26:02 -0500 Subject: [PATCH 039/132] Adding a factory for IFeatReassignmentSectionRenderer --- CraftMagicItems/CraftMagicItems.csproj | 1 + .../FeatReassignmentSectionRendererFactory.cs | 35 +++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 CraftMagicItems/UI/Sections/FeatReassignmentSectionRendererFactory.cs diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index 7685d7a..c77853b 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -100,6 +100,7 @@ + diff --git a/CraftMagicItems/UI/Sections/FeatReassignmentSectionRendererFactory.cs b/CraftMagicItems/UI/Sections/FeatReassignmentSectionRendererFactory.cs new file mode 100644 index 0000000..d4e5622 --- /dev/null +++ b/CraftMagicItems/UI/Sections/FeatReassignmentSectionRendererFactory.cs @@ -0,0 +1,35 @@ +using System; + +namespace CraftMagicItems.UI.Sections +{ + /// Factory for + public static class FeatReassignmentSectionRendererFactory + { + private static Func construction; + + /// Static constructor + static FeatReassignmentSectionRendererFactory() + { + Reset(); + } + + /// Resets the constructed instance to + public static void Reset() + { + SetConstructor(() => { return new FeatReassignmentSectionRenderer(); }); + } + + /// Sets the constructed instance to + public static void SetConstructor(Func constructor) + { + construction = constructor; + } + + /// Constructs an instance of + /// An instance of + public static IFeatReassignmentSectionRenderer GetFeatReassignmentSectionRenderer() + { + return construction(); + } + } +} \ No newline at end of file From e88eb7efc2e74320149b67e9a3de35da561d267e Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Thu, 2 Jul 2020 11:27:19 -0500 Subject: [PATCH 040/132] Flipping over to interface instance and factory instantiation --- CraftMagicItems/Main.cs | 2 +- CraftMagicItems/UI/UserInterfaceEventHandlingLogic.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 32e621f..4137701 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -396,7 +396,7 @@ private static void OnGui(UnityModManager.ModEntry modEntry) { if (UmmUiRenderer.RenderToggleSection("Feat Reassignment", currentSection == OpenSection.FeatsSection)) { currentSection = OpenSection.FeatsSection; - UserInterfaceEventHandlingLogic.RenderFeatReassignmentSection(new FeatReassignmentSectionRenderer()); + UserInterfaceEventHandlingLogic.RenderFeatReassignmentSection(FeatReassignmentSectionRendererFactory.GetFeatReassignmentSectionRenderer()); } if (UmmUiRenderer.RenderToggleSection("Cheats", currentSection == OpenSection.CheatsSection)) diff --git a/CraftMagicItems/UI/UserInterfaceEventHandlingLogic.cs b/CraftMagicItems/UI/UserInterfaceEventHandlingLogic.cs index f406b15..6365d86 100644 --- a/CraftMagicItems/UI/UserInterfaceEventHandlingLogic.cs +++ b/CraftMagicItems/UI/UserInterfaceEventHandlingLogic.cs @@ -62,8 +62,8 @@ public static void RenderCheatsSectionAndUpdateSettings(ICheatSectionRenderer re } /// Renders the section for feat reassignment and handles user selections - /// instance that handles rendering of controls - public static void RenderFeatReassignmentSection(FeatReassignmentSectionRenderer renderer) + /// instance that handles rendering of controls + public static void RenderFeatReassignmentSection(IFeatReassignmentSectionRenderer renderer) { var caster = Main.GetSelectedCrafter(false); if (caster == null) From 87f1b00ef8ac48ecb3bb263bdaa935fc561b7f56 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Thu, 2 Jul 2020 11:29:21 -0500 Subject: [PATCH 041/132] Adding in unit tests over FeatReassignmentSectionRendererFactory --- .../CraftMagicItemsTests.csproj | 3 +- ...cs => CheatSectionRendererFactoryTests.cs} | 0 ...ReassignmentSectionRendererFactoryTests.cs | 64 +++++++++++++++++++ 3 files changed, 66 insertions(+), 1 deletion(-) rename CraftMagicItemsTests/UI/Sections/{CheatSectionFactoryTests.cs => CheatSectionRendererFactoryTests.cs} (100%) create mode 100644 CraftMagicItemsTests/UI/Sections/FeatReassignmentSectionRendererFactoryTests.cs diff --git a/CraftMagicItemsTests/CraftMagicItemsTests.csproj b/CraftMagicItemsTests/CraftMagicItemsTests.csproj index c61ff07..2b95793 100644 --- a/CraftMagicItemsTests/CraftMagicItemsTests.csproj +++ b/CraftMagicItemsTests/CraftMagicItemsTests.csproj @@ -84,7 +84,8 @@ - + + diff --git a/CraftMagicItemsTests/UI/Sections/CheatSectionFactoryTests.cs b/CraftMagicItemsTests/UI/Sections/CheatSectionRendererFactoryTests.cs similarity index 100% rename from CraftMagicItemsTests/UI/Sections/CheatSectionFactoryTests.cs rename to CraftMagicItemsTests/UI/Sections/CheatSectionRendererFactoryTests.cs diff --git a/CraftMagicItemsTests/UI/Sections/FeatReassignmentSectionRendererFactoryTests.cs b/CraftMagicItemsTests/UI/Sections/FeatReassignmentSectionRendererFactoryTests.cs new file mode 100644 index 0000000..f64c088 --- /dev/null +++ b/CraftMagicItemsTests/UI/Sections/FeatReassignmentSectionRendererFactoryTests.cs @@ -0,0 +1,64 @@ +using System; +using CraftMagicItems.UI.Sections; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; + +namespace CraftMagicItemsTests.UI.Sections +{ + /// Test class for + [TestClass] + public class FeatReassignmentSectionRendererFactoryTests + { + [TestMethod] + public void FeatReassignmentSectionRendererFactory_Defaults_FeatReassignmentSectionRenderer() + { + //control + FeatReassignmentSectionRendererFactory.Reset(); + + //invocation + var instance = FeatReassignmentSectionRendererFactory.GetFeatReassignmentSectionRenderer(); + + //validation + Assert.AreEqual(typeof(FeatReassignmentSectionRenderer), instance.GetType(), $"Expected an instance of {nameof(FeatReassignmentSectionRenderer)} to be returned."); + } + + [TestMethod] + public void SetConstructor_Works() + { + //control + Func mockConstructor = () => { return new Mock().Object; }; + FeatReassignmentSectionRendererFactory.SetConstructor(mockConstructor); + + //invocation + var instance = FeatReassignmentSectionRendererFactory.GetFeatReassignmentSectionRenderer(); + + //validation + Assert.AreNotEqual(typeof(FeatReassignmentSectionRenderer), instance.GetType(), $"Expected an instance of {nameof(FeatReassignmentSectionRenderer)} to be returned."); + } + + [TestMethod] + public void GetFeatReassignmentSectionRenderer_Works() + { + //invocation + var instance = FeatReassignmentSectionRendererFactory.GetFeatReassignmentSectionRenderer(); + + //validation + Assert.IsNotNull(instance, $"Expected an instance of {nameof(IFeatReassignmentSectionRenderer)} to be returned."); + } + + [TestMethod] + public void Reset_Works() + { + //control + Func mockConstructor = () => { return new Mock().Object; }; + FeatReassignmentSectionRendererFactory.SetConstructor(mockConstructor); + FeatReassignmentSectionRendererFactory.Reset(); + + //invocation + var instance = FeatReassignmentSectionRendererFactory.GetFeatReassignmentSectionRenderer(); + + //validation + Assert.AreEqual(typeof(FeatReassignmentSectionRenderer), instance.GetType(), $"Expected an instance of {nameof(FeatReassignmentSectionRenderer)} to be returned."); + } + } +} \ No newline at end of file From b2af0b5099542089b9807c57ae29a328d247ca46 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Thu, 2 Jul 2020 11:31:57 -0500 Subject: [PATCH 042/132] Encapsulating tests by method being tested. collapsable, more readable in the Test Explorer. --- .../UserInterfaceEventHandlingLogicTests.cs | 988 +++++++++--------- 1 file changed, 496 insertions(+), 492 deletions(-) diff --git a/CraftMagicItemsTests/UI/UserInterfaceEventHandlingLogicTests.cs b/CraftMagicItemsTests/UI/UserInterfaceEventHandlingLogicTests.cs index ae9c5aa..c4c9f02 100644 --- a/CraftMagicItemsTests/UI/UserInterfaceEventHandlingLogicTests.cs +++ b/CraftMagicItemsTests/UI/UserInterfaceEventHandlingLogicTests.cs @@ -8,538 +8,542 @@ namespace CraftMagicItemsTests.UI { /// Test class for - [TestClass] public class UserInterfaceEventHandlingLogicTests { - [TestMethod] - public void RenderCheatsSectionAndUpdateSettings_Reads_CraftingCostsNoGold() + /// Test class for + [TestClass] + public class RenderCheatsSectionAndUpdateSettingsTests { - string priceLabel = "irrelevant"; - string[] priceOptions = new[] { String.Empty, String.Empty, String.Empty }; - - /************ - * Test 1 * - ************/ - //control - Settings settings = new Settings { CraftingCostsNoGold = false }; - Mock renderer = new Mock(); - renderer.Setup(r => r.Evaluate_CraftingCostsNoGold(It.IsAny())).Returns(true); - - //invocation - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); - - //validation - Assert.AreEqual(true, settings.CraftingCostsNoGold); - - /************ - * Test 2 * - ************/ - //control - settings = new Settings { CraftingCostsNoGold = true }; - renderer = new Mock(); - renderer.Setup(r => r.Evaluate_CraftingCostsNoGold(It.IsAny())).Returns(false); - - //invocation - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); - - //validation - Assert.AreEqual(false, settings.CraftingCostsNoGold); - } - - [TestMethod] - public void RenderCheatsSectionAndUpdateSettings_DoesNotRead_CraftingPriceScale_When_CraftingCostsNoGold() - { - string priceLabel = "irrelevant"; - string[] priceOptions = new[] { String.Empty, String.Empty, String.Empty }; - int initial = -10000000; - - //control - Settings settings = new Settings + [TestMethod] + public void Reads_CraftingCostsNoGold() { - CraftingPriceScale = initial, - }; - Mock renderer = new Mock(); - renderer.Setup(r => r.Evaluate_CraftingCostsNoGold(It.IsAny())).Returns(true); - - //invocation - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); - - //validation - Assert.AreEqual(initial, settings.CraftingPriceScale); - } - - [TestMethod] - public void RenderCheatsSectionAndUpdateSettings_DoesNotInvoke_WarningAboutCustomItemVanillaItemCostDisparity_When_CraftingCostsNoGold() - { - string priceLabel = "irrelevant"; - string[] priceOptions = new[] { String.Empty, String.Empty, String.Empty }; - - //control - bool invokedWarning = false; - Action setInvoked = () => { invokedWarning = true; }; - - Mock renderer = new Mock(); - renderer.Setup(r => r.Evaluate_CraftingCostsNoGold(It.IsAny())).Returns(true); - renderer.Setup(r => r.RenderOnly_WarningAboutCustomItemVanillaItemCostDisparity()).Callback(setInvoked); - - //invocation - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, new Settings(), priceLabel, priceOptions); - - //validation - Assert.AreEqual(false, invokedWarning); - } - - [TestMethod] - public void RenderCheatsSectionAndUpdateSettings_Reads_CraftingCostSelection() - { - string priceLabel = "irrelevant"; - string[] priceOptions = new[] { String.Empty, String.Empty, String.Empty }; - - //control - bool invokedWarning = false; - Action setInvoked = () => { invokedWarning = true; }; - - Mock renderer = new Mock(); - renderer.Setup(r => r.Evaluate_CraftingCostsNoGold(It.IsAny())).Returns(false); - renderer.Setup(r => r.Evaluate_CraftingCostSelection(It.IsAny(), It.IsAny())).Callback(setInvoked); - - //invocation - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, new Settings(), priceLabel, priceOptions); + string priceLabel = "irrelevant"; + string[] priceOptions = new[] { String.Empty, String.Empty, String.Empty }; + + /************ + * Test 1 * + ************/ + //control + Settings settings = new Settings { CraftingCostsNoGold = false }; + Mock renderer = new Mock(); + renderer.Setup(r => r.Evaluate_CraftingCostsNoGold(It.IsAny())).Returns(true); + + //invocation + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + + //validation + Assert.AreEqual(true, settings.CraftingCostsNoGold); + + /************ + * Test 2 * + ************/ + //control + settings = new Settings { CraftingCostsNoGold = true }; + renderer = new Mock(); + renderer.Setup(r => r.Evaluate_CraftingCostsNoGold(It.IsAny())).Returns(false); + + //invocation + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + + //validation + Assert.AreEqual(false, settings.CraftingCostsNoGold); + } + + [TestMethod] + public void DoesNotRead_CraftingPriceScale_When_CraftingCostsNoGold() + { + string priceLabel = "irrelevant"; + string[] priceOptions = new[] { String.Empty, String.Empty, String.Empty }; + int initial = -10000000; + + //control + Settings settings = new Settings + { + CraftingPriceScale = initial, + }; + Mock renderer = new Mock(); + renderer.Setup(r => r.Evaluate_CraftingCostsNoGold(It.IsAny())).Returns(true); + + //invocation + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + + //validation + Assert.AreEqual(initial, settings.CraftingPriceScale); + } + + [TestMethod] + public void DoesNotInvoke_WarningAboutCustomItemVanillaItemCostDisparity_When_CraftingCostsNoGold() + { + string priceLabel = "irrelevant"; + string[] priceOptions = new[] { String.Empty, String.Empty, String.Empty }; - //validation - Assert.AreEqual(true, invokedWarning); - } + //control + bool invokedWarning = false; + Action setInvoked = () => { invokedWarning = true; }; - [TestMethod] - public void RenderCheatsSectionAndUpdateSettings_Reads_CustomCraftingCostSlider() - { - string priceLabel = "irrelevant"; - string[] priceOptions = new[] { String.Empty, String.Empty, String.Empty }; + Mock renderer = new Mock(); + renderer.Setup(r => r.Evaluate_CraftingCostsNoGold(It.IsAny())).Returns(true); + renderer.Setup(r => r.RenderOnly_WarningAboutCustomItemVanillaItemCostDisparity()).Callback(setInvoked); - //control - Settings settings = new Settings { CraftingPriceScale = -4 }; - Mock renderer = new Mock(); - renderer.Setup(r => r.Evaluate_CraftingCostsNoGold(It.IsAny())).Returns(false); - renderer.Setup(r => r.Evaluate_CraftingCostSelection(It.IsAny(), It.IsAny())).Returns(2); - renderer.Setup(r => r.Evaluate_CustomCraftingCostSlider(It.IsAny())).Returns(4000); + //invocation + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, new Settings(), priceLabel, priceOptions); - //invocation - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + //validation + Assert.AreEqual(false, invokedWarning); + } - //validation - Assert.AreEqual(4000, settings.CraftingPriceScale); - } + [TestMethod] + public void Reads_CraftingCostSelection() + { + string priceLabel = "irrelevant"; + string[] priceOptions = new[] { String.Empty, String.Empty, String.Empty }; - [TestMethod] - public void RenderCheatsSectionAndUpdateSettings_Reads_CraftingPriceScale_From_CraftingCostSelection() - { - string priceLabel = "irrelevant"; - string[] priceOptions = new[] { String.Empty, String.Empty, String.Empty }; - - /************ - * Test 1 * - ************/ - //control - Settings settings = new Settings { CraftingPriceScale = -4 }; - Mock renderer = new Mock(); - renderer.Setup(r => r.Evaluate_CraftingCostsNoGold(It.IsAny())).Returns(false); - renderer.Setup(r => r.Evaluate_CraftingCostSelection(It.IsAny(), It.IsAny())).Returns(1); - - //invocation - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); - - //validation - Assert.AreEqual(2, settings.CraftingPriceScale); - - /************ - * Test 2 * - ************/ - //control - settings = new Settings { CraftingPriceScale = -4 }; - renderer = new Mock(); - renderer.Setup(r => r.Evaluate_CraftingCostsNoGold(It.IsAny())).Returns(false); - renderer.Setup(r => r.Evaluate_CraftingCostSelection(It.IsAny(), It.IsAny())).Returns(0); - - //invocation - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); - - //validation - Assert.AreEqual(1, settings.CraftingPriceScale); - } + //control + bool invokedWarning = false; + Action setInvoked = () => { invokedWarning = true; }; - [TestMethod] - public void RenderCheatsSectionAndUpdateSettings_Invokes_WarningAboutCustomItemVanillaItemCostDisparity_When_CraftingCostsGold() - { - string priceLabel = "irrelevant"; - string[] priceOptions = new[] { String.Empty, String.Empty, String.Empty }; + Mock renderer = new Mock(); + renderer.Setup(r => r.Evaluate_CraftingCostsNoGold(It.IsAny())).Returns(false); + renderer.Setup(r => r.Evaluate_CraftingCostSelection(It.IsAny(), It.IsAny())).Callback(setInvoked); - //control - bool invokedWarning = false; - Action setInvoked = () => { invokedWarning = true; }; + //invocation + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, new Settings(), priceLabel, priceOptions); - Mock renderer = new Mock(); - renderer.Setup(r => r.Evaluate_CraftingCostsNoGold(It.IsAny())).Returns(false); - renderer.Setup(r => r.Evaluate_CraftingCostSelection(It.IsAny(), It.IsAny())).Returns(-1); - renderer.Setup(r => r.RenderOnly_WarningAboutCustomItemVanillaItemCostDisparity()).Callback(setInvoked); + //validation + Assert.AreEqual(true, invokedWarning); + } - //invocation - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, new Settings(), priceLabel, priceOptions); + [TestMethod] + public void Reads_CustomCraftingCostSlider() + { + string priceLabel = "irrelevant"; + string[] priceOptions = new[] { String.Empty, String.Empty, String.Empty }; - //validation - Assert.AreEqual(true, invokedWarning); - } + //control + Settings settings = new Settings { CraftingPriceScale = -4 }; + Mock renderer = new Mock(); + renderer.Setup(r => r.Evaluate_CraftingCostsNoGold(It.IsAny())).Returns(false); + renderer.Setup(r => r.Evaluate_CraftingCostSelection(It.IsAny(), It.IsAny())).Returns(2); + renderer.Setup(r => r.Evaluate_CustomCraftingCostSlider(It.IsAny())).Returns(4000); - [TestMethod] - public void RenderCheatsSectionAndUpdateSettings_Reads_IgnoreCraftingFeats() - { - string priceLabel = "irrelevant"; - string[] priceOptions = new[] { String.Empty, String.Empty, String.Empty }; - - /************ - * Test 1 * - ************/ - //control - Settings settings = new Settings { IgnoreCraftingFeats = false }; - Mock renderer = new Mock(); - renderer.Setup(r => r.Evaluate_IgnoreCraftingFeats(It.IsAny())).Returns(true); - - //invocation - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); - - //validation - Assert.AreEqual(true, settings.IgnoreCraftingFeats); - - /************ - * Test 2 * - ************/ - //control - settings = new Settings { IgnoreCraftingFeats = true }; - renderer = new Mock(); - renderer.Setup(r => r.Evaluate_IgnoreCraftingFeats(It.IsAny())).Returns(false); - - //invocation - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); - - //validation - Assert.AreEqual(false, settings.IgnoreCraftingFeats); - } + //invocation + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); - [TestMethod] - public void RenderCheatsSectionAndUpdateSettings_Reads_CraftingTakesNoTime() - { - string priceLabel = "irrelevant"; - string[] priceOptions = new[] { String.Empty, String.Empty, String.Empty }; - - /************ - * Test 1 * - ************/ - //control - Settings settings = new Settings { CraftingTakesNoTime = false }; - Mock renderer = new Mock(); - renderer.Setup(r => r.Evaluate_CraftingTakesNoTime(It.IsAny())).Returns(true); - - //invocation - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); - - //validation - Assert.AreEqual(true, settings.CraftingTakesNoTime); - - /************ - * Test 2 * - ************/ - //control - settings = new Settings { CraftingTakesNoTime = true }; - renderer = new Mock(); - renderer.Setup(r => r.Evaluate_CraftingTakesNoTime(It.IsAny())).Returns(false); - - //invocation - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); - - //validation - Assert.AreEqual(false, settings.CraftingTakesNoTime); - } + //validation + Assert.AreEqual(4000, settings.CraftingPriceScale); + } - [TestMethod] - public void RenderCheatsSectionAndUpdateSettings_DoesNotRead_CustomCraftRate_When_CraftingTakesNoTime() - { - string priceLabel = "irrelevant"; - string[] priceOptions = new[] { String.Empty, String.Empty, String.Empty }; - int initialMagicCraftRate = -10000000; - int initialMundaneCraftRate = 9348458; - bool initialCustomCraftRate = true; - - //control - Settings settings = new Settings + [TestMethod] + public void Reads_CraftingPriceScale_From_CraftingCostSelection() { - MagicCraftingRate = initialMagicCraftRate, - MundaneCraftingRate = initialMundaneCraftRate, - CustomCraftRate = initialCustomCraftRate, - }; - Mock renderer = new Mock(); - renderer.Setup(r => r.Evaluate_CraftingTakesNoTime(It.IsAny())).Returns(true); - - //invocation - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); - - //validation - Assert.AreEqual(initialMagicCraftRate, settings.MagicCraftingRate); - Assert.AreEqual(initialMundaneCraftRate, settings.MundaneCraftingRate); - Assert.AreEqual(initialCustomCraftRate, settings.CustomCraftRate); - } + string priceLabel = "irrelevant"; + string[] priceOptions = new[] { String.Empty, String.Empty, String.Empty }; + + /************ + * Test 1 * + ************/ + //control + Settings settings = new Settings { CraftingPriceScale = -4 }; + Mock renderer = new Mock(); + renderer.Setup(r => r.Evaluate_CraftingCostsNoGold(It.IsAny())).Returns(false); + renderer.Setup(r => r.Evaluate_CraftingCostSelection(It.IsAny(), It.IsAny())).Returns(1); + + //invocation + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + + //validation + Assert.AreEqual(2, settings.CraftingPriceScale); + + /************ + * Test 2 * + ************/ + //control + settings = new Settings { CraftingPriceScale = -4 }; + renderer = new Mock(); + renderer.Setup(r => r.Evaluate_CraftingCostsNoGold(It.IsAny())).Returns(false); + renderer.Setup(r => r.Evaluate_CraftingCostSelection(It.IsAny(), It.IsAny())).Returns(0); + + //invocation + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + + //validation + Assert.AreEqual(1, settings.CraftingPriceScale); + } + + [TestMethod] + public void Invokes_WarningAboutCustomItemVanillaItemCostDisparity_When_CraftingCostsGold() + { + string priceLabel = "irrelevant"; + string[] priceOptions = new[] { String.Empty, String.Empty, String.Empty }; - [TestMethod] - public void RenderCheatsSectionAndUpdateSettings_Reads_CustomCraftRate() - { - string priceLabel = "irrelevant"; - string[] priceOptions = new[] { String.Empty, String.Empty, String.Empty }; - - /************ - * Test 1 * - ************/ - //control - Settings settings = new Settings { CustomCraftRate = false }; - Mock renderer = new Mock(); - renderer.Setup(r => r.Evaluate_CraftingTakesNoTime(It.IsAny())).Returns(false); - renderer.Setup(r => r.Evaluate_CustomCraftRate(It.IsAny())).Returns(true); - - //invocation - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); - - //validation - Assert.AreEqual(true, settings.CustomCraftRate); - - /************ - * Test 2 * - ************/ - //control - settings = new Settings { CustomCraftRate = true }; - renderer = new Mock(); - renderer.Setup(r => r.Evaluate_CraftingTakesNoTime(It.IsAny())).Returns(false); - renderer.Setup(r => r.Evaluate_CustomCraftRate(It.IsAny())).Returns(false); - - //invocation - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); - - //validation - Assert.AreEqual(false, settings.CustomCraftRate); - } + //control + bool invokedWarning = false; + Action setInvoked = () => { invokedWarning = true; }; - [TestMethod] - public void RenderCheatsSectionAndUpdateSettings_Reads_MagicCraftingRate() - { - string priceLabel = "irrelevant"; - string[] priceOptions = new[] { String.Empty, String.Empty, String.Empty }; + Mock renderer = new Mock(); + renderer.Setup(r => r.Evaluate_CraftingCostsNoGold(It.IsAny())).Returns(false); + renderer.Setup(r => r.Evaluate_CraftingCostSelection(It.IsAny(), It.IsAny())).Returns(-1); + renderer.Setup(r => r.RenderOnly_WarningAboutCustomItemVanillaItemCostDisparity()).Callback(setInvoked); - //control - Settings settings = new Settings { MagicCraftingRate = -8 }; - Mock renderer = new Mock(); - renderer.Setup(r => r.Evaluate_CraftingTakesNoTime(It.IsAny())).Returns(false); - renderer.Setup(r => r.Evaluate_CustomCraftRate(It.IsAny())).Returns(true); - renderer.Setup(r => r.Evaluate_MagicCraftingRateSlider(It.IsAny())).Returns(7); + //invocation + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, new Settings(), priceLabel, priceOptions); - //invocation - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + //validation + Assert.AreEqual(true, invokedWarning); + } - //validation - Assert.AreEqual(7, settings.MagicCraftingRate); - } + [TestMethod] + public void Reads_IgnoreCraftingFeats() + { + string priceLabel = "irrelevant"; + string[] priceOptions = new[] { String.Empty, String.Empty, String.Empty }; + + /************ + * Test 1 * + ************/ + //control + Settings settings = new Settings { IgnoreCraftingFeats = false }; + Mock renderer = new Mock(); + renderer.Setup(r => r.Evaluate_IgnoreCraftingFeats(It.IsAny())).Returns(true); + + //invocation + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + + //validation + Assert.AreEqual(true, settings.IgnoreCraftingFeats); + + /************ + * Test 2 * + ************/ + //control + settings = new Settings { IgnoreCraftingFeats = true }; + renderer = new Mock(); + renderer.Setup(r => r.Evaluate_IgnoreCraftingFeats(It.IsAny())).Returns(false); + + //invocation + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + + //validation + Assert.AreEqual(false, settings.IgnoreCraftingFeats); + } + + [TestMethod] + public void Reads_CraftingTakesNoTime() + { + string priceLabel = "irrelevant"; + string[] priceOptions = new[] { String.Empty, String.Empty, String.Empty }; + + /************ + * Test 1 * + ************/ + //control + Settings settings = new Settings { CraftingTakesNoTime = false }; + Mock renderer = new Mock(); + renderer.Setup(r => r.Evaluate_CraftingTakesNoTime(It.IsAny())).Returns(true); + + //invocation + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + + //validation + Assert.AreEqual(true, settings.CraftingTakesNoTime); + + /************ + * Test 2 * + ************/ + //control + settings = new Settings { CraftingTakesNoTime = true }; + renderer = new Mock(); + renderer.Setup(r => r.Evaluate_CraftingTakesNoTime(It.IsAny())).Returns(false); + + //invocation + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + + //validation + Assert.AreEqual(false, settings.CraftingTakesNoTime); + } + + [TestMethod] + public void DoesNotRead_CustomCraftRate_When_CraftingTakesNoTime() + { + string priceLabel = "irrelevant"; + string[] priceOptions = new[] { String.Empty, String.Empty, String.Empty }; + int initialMagicCraftRate = -10000000; + int initialMundaneCraftRate = 9348458; + bool initialCustomCraftRate = true; + + //control + Settings settings = new Settings + { + MagicCraftingRate = initialMagicCraftRate, + MundaneCraftingRate = initialMundaneCraftRate, + CustomCraftRate = initialCustomCraftRate, + }; + Mock renderer = new Mock(); + renderer.Setup(r => r.Evaluate_CraftingTakesNoTime(It.IsAny())).Returns(true); + + //invocation + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + + //validation + Assert.AreEqual(initialMagicCraftRate, settings.MagicCraftingRate); + Assert.AreEqual(initialMundaneCraftRate, settings.MundaneCraftingRate); + Assert.AreEqual(initialCustomCraftRate, settings.CustomCraftRate); + } + + [TestMethod] + public void Reads_CustomCraftRate() + { + string priceLabel = "irrelevant"; + string[] priceOptions = new[] { String.Empty, String.Empty, String.Empty }; + + /************ + * Test 1 * + ************/ + //control + Settings settings = new Settings { CustomCraftRate = false }; + Mock renderer = new Mock(); + renderer.Setup(r => r.Evaluate_CraftingTakesNoTime(It.IsAny())).Returns(false); + renderer.Setup(r => r.Evaluate_CustomCraftRate(It.IsAny())).Returns(true); + + //invocation + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + + //validation + Assert.AreEqual(true, settings.CustomCraftRate); + + /************ + * Test 2 * + ************/ + //control + settings = new Settings { CustomCraftRate = true }; + renderer = new Mock(); + renderer.Setup(r => r.Evaluate_CraftingTakesNoTime(It.IsAny())).Returns(false); + renderer.Setup(r => r.Evaluate_CustomCraftRate(It.IsAny())).Returns(false); + + //invocation + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + + //validation + Assert.AreEqual(false, settings.CustomCraftRate); + } + + [TestMethod] + public void Reads_MagicCraftingRate() + { + string priceLabel = "irrelevant"; + string[] priceOptions = new[] { String.Empty, String.Empty, String.Empty }; - [TestMethod] - public void RenderCheatsSectionAndUpdateSettings_Reads_MundaneCraftingRate() - { - string priceLabel = "irrelevant"; - string[] priceOptions = new[] { String.Empty, String.Empty, String.Empty }; + //control + Settings settings = new Settings { MagicCraftingRate = -8 }; + Mock renderer = new Mock(); + renderer.Setup(r => r.Evaluate_CraftingTakesNoTime(It.IsAny())).Returns(false); + renderer.Setup(r => r.Evaluate_CustomCraftRate(It.IsAny())).Returns(true); + renderer.Setup(r => r.Evaluate_MagicCraftingRateSlider(It.IsAny())).Returns(7); - //control - Settings settings = new Settings { MundaneCraftingRate = -23412345 }; - Mock renderer = new Mock(); - renderer.Setup(r => r.Evaluate_CraftingTakesNoTime(It.IsAny())).Returns(false); - renderer.Setup(r => r.Evaluate_CustomCraftRate(It.IsAny())).Returns(true); - renderer.Setup(r => r.Evaluate_MundaneCraftingRateSlider(It.IsAny())).Returns(12); + //invocation + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); - //invocation - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + //validation + Assert.AreEqual(7, settings.MagicCraftingRate); + } - //validation - Assert.AreEqual(12, settings.MundaneCraftingRate); - } + [TestMethod] + public void Reads_MundaneCraftingRate() + { + string priceLabel = "irrelevant"; + string[] priceOptions = new[] { String.Empty, String.Empty, String.Empty }; - [TestMethod] - public void RenderCheatsSectionAndUpdateSettings_Defaults_MagicCraftingRate() - { - string priceLabel = "irrelevant"; - string[] priceOptions = new[] { String.Empty, String.Empty, String.Empty }; + //control + Settings settings = new Settings { MundaneCraftingRate = -23412345 }; + Mock renderer = new Mock(); + renderer.Setup(r => r.Evaluate_CraftingTakesNoTime(It.IsAny())).Returns(false); + renderer.Setup(r => r.Evaluate_CustomCraftRate(It.IsAny())).Returns(true); + renderer.Setup(r => r.Evaluate_MundaneCraftingRateSlider(It.IsAny())).Returns(12); - //control - Settings settings = new Settings { MagicCraftingRate = -8 }; - Mock renderer = new Mock(); - renderer.Setup(r => r.Evaluate_CraftingTakesNoTime(It.IsAny())).Returns(false); - renderer.Setup(r => r.Evaluate_CustomCraftRate(It.IsAny())).Returns(false); + //invocation + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); - //invocation - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + //validation + Assert.AreEqual(12, settings.MundaneCraftingRate); + } - //validation - Assert.AreEqual(Settings.MagicCraftingProgressPerDay, settings.MagicCraftingRate); - } + [TestMethod] + public void Defaults_MagicCraftingRate() + { + string priceLabel = "irrelevant"; + string[] priceOptions = new[] { String.Empty, String.Empty, String.Empty }; - [TestMethod] - public void RenderCheatsSectionAndUpdateSettings_Defaults_MundaneCraftingRate() - { - string priceLabel = "irrelevant"; - string[] priceOptions = new[] { String.Empty, String.Empty, String.Empty }; + //control + Settings settings = new Settings { MagicCraftingRate = -8 }; + Mock renderer = new Mock(); + renderer.Setup(r => r.Evaluate_CraftingTakesNoTime(It.IsAny())).Returns(false); + renderer.Setup(r => r.Evaluate_CustomCraftRate(It.IsAny())).Returns(false); - //control - Settings settings = new Settings { MundaneCraftingRate = -23412345 }; - Mock renderer = new Mock(); - renderer.Setup(r => r.Evaluate_CraftingTakesNoTime(It.IsAny())).Returns(false); - renderer.Setup(r => r.Evaluate_CustomCraftRate(It.IsAny())).Returns(false); + //invocation + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); - //invocation - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + //validation + Assert.AreEqual(Settings.MagicCraftingProgressPerDay, settings.MagicCraftingRate); + } - //validation - Assert.AreEqual(Settings.MundaneCraftingProgressPerDay, settings.MundaneCraftingRate); - } + [TestMethod] + public void Defaults_MundaneCraftingRate() + { + string priceLabel = "irrelevant"; + string[] priceOptions = new[] { String.Empty, String.Empty, String.Empty }; - [TestMethod] - public void RenderCheatsSectionAndUpdateSettings_Reads_CasterLevelIsSinglePrerequisite() - { - string priceLabel = "irrelevant"; - string[] priceOptions = new[] { String.Empty, String.Empty, String.Empty }; - - /************ - * Test 1 * - ************/ - //control - Settings settings = new Settings { CasterLevelIsSinglePrerequisite = false }; - Mock renderer = new Mock(); - renderer.Setup(r => r.Evaluate_CasterLevelIsSinglePrerequisite(It.IsAny())).Returns(true); - - //invocation - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); - - //validation - Assert.AreEqual(true, settings.CasterLevelIsSinglePrerequisite); - - /************ - * Test 2 * - ************/ - //control - settings = new Settings { CasterLevelIsSinglePrerequisite = true }; - renderer = new Mock(); - renderer.Setup(r => r.Evaluate_CasterLevelIsSinglePrerequisite(It.IsAny())).Returns(false); - - //invocation - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); - - //validation - Assert.AreEqual(false, settings.CasterLevelIsSinglePrerequisite); - } + //control + Settings settings = new Settings { MundaneCraftingRate = -23412345 }; + Mock renderer = new Mock(); + renderer.Setup(r => r.Evaluate_CraftingTakesNoTime(It.IsAny())).Returns(false); + renderer.Setup(r => r.Evaluate_CustomCraftRate(It.IsAny())).Returns(false); - [TestMethod] - public void RenderCheatsSectionAndUpdateSettings_Reads_CraftAtFullSpeedWhileAdventuring() - { - string priceLabel = "irrelevant"; - string[] priceOptions = new[] { String.Empty, String.Empty, String.Empty }; - - /************ - * Test 1 * - ************/ - //control - Settings settings = new Settings { CraftAtFullSpeedWhileAdventuring = false }; - Mock renderer = new Mock(); - renderer.Setup(r => r.Evaluate_CraftAtFullSpeedWhileAdventuring(It.IsAny())).Returns(true); - - //invocation - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); - - //validation - Assert.AreEqual(true, settings.CraftAtFullSpeedWhileAdventuring); - - /************ - * Test 2 * - ************/ - //control - settings = new Settings { CraftAtFullSpeedWhileAdventuring = true }; - renderer = new Mock(); - renderer.Setup(r => r.Evaluate_CraftAtFullSpeedWhileAdventuring(It.IsAny())).Returns(false); - - //invocation - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); - - //validation - Assert.AreEqual(false, settings.CraftAtFullSpeedWhileAdventuring); - } + //invocation + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); - [TestMethod] - public void RenderCheatsSectionAndUpdateSettings_Reads_IgnorePlusTenItemMaximum() - { - string priceLabel = "irrelevant"; - string[] priceOptions = new[] { String.Empty, String.Empty, String.Empty }; - - /************ - * Test 1 * - ************/ - //control - Settings settings = new Settings { IgnorePlusTenItemMaximum = false }; - Mock renderer = new Mock(); - renderer.Setup(r => r.Evaluate_IgnorePlusTenItemMaximum(It.IsAny())).Returns(true); - - //invocation - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); - - //validation - Assert.AreEqual(true, settings.IgnorePlusTenItemMaximum); - - /************ - * Test 2 * - ************/ - //control - settings = new Settings { IgnorePlusTenItemMaximum = true }; - renderer = new Mock(); - renderer.Setup(r => r.Evaluate_IgnorePlusTenItemMaximum(It.IsAny())).Returns(false); - - //invocation - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); - - //validation - Assert.AreEqual(false, settings.IgnorePlusTenItemMaximum); - } + //validation + Assert.AreEqual(Settings.MundaneCraftingProgressPerDay, settings.MundaneCraftingRate); + } - [TestMethod] - public void RenderCheatsSectionAndUpdateSettings_Reads_IgnoreFeatCasterLevelRestriction() - { - string priceLabel = "irrelevant"; - string[] priceOptions = new[] { String.Empty, String.Empty, String.Empty }; - - /************ - * Test 1 * - ************/ - //control - Settings settings = new Settings { IgnoreFeatCasterLevelRestriction = false }; - Mock renderer = new Mock(); - renderer.Setup(r => r.Evaluate_IgnoreFeatCasterLevelRestriction(It.IsAny())).Returns(true); - - //invocation - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); - - //validation - Assert.AreEqual(true, settings.IgnoreFeatCasterLevelRestriction); - - /************ - * Test 2 * - ************/ - //control - settings = new Settings { IgnoreFeatCasterLevelRestriction = true }; - renderer = new Mock(); - renderer.Setup(r => r.Evaluate_IgnoreFeatCasterLevelRestriction(It.IsAny())).Returns(false); - - //invocation - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); - - //validation - Assert.AreEqual(false, settings.IgnoreFeatCasterLevelRestriction); + [TestMethod] + public void Reads_CasterLevelIsSinglePrerequisite() + { + string priceLabel = "irrelevant"; + string[] priceOptions = new[] { String.Empty, String.Empty, String.Empty }; + + /************ + * Test 1 * + ************/ + //control + Settings settings = new Settings { CasterLevelIsSinglePrerequisite = false }; + Mock renderer = new Mock(); + renderer.Setup(r => r.Evaluate_CasterLevelIsSinglePrerequisite(It.IsAny())).Returns(true); + + //invocation + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + + //validation + Assert.AreEqual(true, settings.CasterLevelIsSinglePrerequisite); + + /************ + * Test 2 * + ************/ + //control + settings = new Settings { CasterLevelIsSinglePrerequisite = true }; + renderer = new Mock(); + renderer.Setup(r => r.Evaluate_CasterLevelIsSinglePrerequisite(It.IsAny())).Returns(false); + + //invocation + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + + //validation + Assert.AreEqual(false, settings.CasterLevelIsSinglePrerequisite); + } + + [TestMethod] + public void Reads_CraftAtFullSpeedWhileAdventuring() + { + string priceLabel = "irrelevant"; + string[] priceOptions = new[] { String.Empty, String.Empty, String.Empty }; + + /************ + * Test 1 * + ************/ + //control + Settings settings = new Settings { CraftAtFullSpeedWhileAdventuring = false }; + Mock renderer = new Mock(); + renderer.Setup(r => r.Evaluate_CraftAtFullSpeedWhileAdventuring(It.IsAny())).Returns(true); + + //invocation + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + + //validation + Assert.AreEqual(true, settings.CraftAtFullSpeedWhileAdventuring); + + /************ + * Test 2 * + ************/ + //control + settings = new Settings { CraftAtFullSpeedWhileAdventuring = true }; + renderer = new Mock(); + renderer.Setup(r => r.Evaluate_CraftAtFullSpeedWhileAdventuring(It.IsAny())).Returns(false); + + //invocation + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + + //validation + Assert.AreEqual(false, settings.CraftAtFullSpeedWhileAdventuring); + } + + [TestMethod] + public void Reads_IgnorePlusTenItemMaximum() + { + string priceLabel = "irrelevant"; + string[] priceOptions = new[] { String.Empty, String.Empty, String.Empty }; + + /************ + * Test 1 * + ************/ + //control + Settings settings = new Settings { IgnorePlusTenItemMaximum = false }; + Mock renderer = new Mock(); + renderer.Setup(r => r.Evaluate_IgnorePlusTenItemMaximum(It.IsAny())).Returns(true); + + //invocation + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + + //validation + Assert.AreEqual(true, settings.IgnorePlusTenItemMaximum); + + /************ + * Test 2 * + ************/ + //control + settings = new Settings { IgnorePlusTenItemMaximum = true }; + renderer = new Mock(); + renderer.Setup(r => r.Evaluate_IgnorePlusTenItemMaximum(It.IsAny())).Returns(false); + + //invocation + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + + //validation + Assert.AreEqual(false, settings.IgnorePlusTenItemMaximum); + } + + [TestMethod] + public void Reads_IgnoreFeatCasterLevelRestriction() + { + string priceLabel = "irrelevant"; + string[] priceOptions = new[] { String.Empty, String.Empty, String.Empty }; + + /************ + * Test 1 * + ************/ + //control + Settings settings = new Settings { IgnoreFeatCasterLevelRestriction = false }; + Mock renderer = new Mock(); + renderer.Setup(r => r.Evaluate_IgnoreFeatCasterLevelRestriction(It.IsAny())).Returns(true); + + //invocation + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + + //validation + Assert.AreEqual(true, settings.IgnoreFeatCasterLevelRestriction); + + /************ + * Test 2 * + ************/ + //control + settings = new Settings { IgnoreFeatCasterLevelRestriction = true }; + renderer = new Mock(); + renderer.Setup(r => r.Evaluate_IgnoreFeatCasterLevelRestriction(It.IsAny())).Returns(false); + + //invocation + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + + //validation + Assert.AreEqual(false, settings.IgnoreFeatCasterLevelRestriction); + } } } } \ No newline at end of file From 7bddf9a9d90168f5dd600d6e1d6f457b0bdbdb1c Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Thu, 2 Jul 2020 11:49:10 -0500 Subject: [PATCH 043/132] Extracting constants for item qualitoes into its own constants class --- .../Constants/ItemQualityBlueprints.cs | 21 +++++++++++++++ CraftMagicItems/CraftMagicItems.csproj | 1 + .../CraftMagicItemsBlueprintPatcher.cs | 3 ++- CraftMagicItems/Main.cs | 26 ++++++++----------- 4 files changed, 35 insertions(+), 16 deletions(-) create mode 100644 CraftMagicItems/Constants/ItemQualityBlueprints.cs diff --git a/CraftMagicItems/Constants/ItemQualityBlueprints.cs b/CraftMagicItems/Constants/ItemQualityBlueprints.cs new file mode 100644 index 0000000..28858cb --- /dev/null +++ b/CraftMagicItems/Constants/ItemQualityBlueprints.cs @@ -0,0 +1,21 @@ +namespace CraftMagicItems.Constants +{ + /// Class containing constants for identifying blueprint unique identifiers for items + public static class ItemQualityBlueprints + { + /// Blueprint guid for masterwork quality items + public const string MasterworkGuid = "6b38844e2bffbac48b63036b66e735be"; + + /// Blueprint for mithral item special material/enchantment + public const string MithralArmorEnchantmentGuid = "7b95a819181574a4799d93939aa99aff"; + + /// Blueprint guid for oversized items + public const string OversizedGuid = "d8e1ebc1062d8cc42abff78783856b0d"; + + /// Blueprint guid for light shields + public const string WeaponLightShieldGuid = "1fd965e522502fe479fdd423cca07684"; + + /// Blueprint guid for heavy shields + public const string WeaponHeavyShieldGuid = "be9b6408e6101cb4997a8996484baf19"; + } +} \ No newline at end of file diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index c77853b..a9a5b88 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -93,6 +93,7 @@ + diff --git a/CraftMagicItems/CraftMagicItemsBlueprintPatcher.cs b/CraftMagicItems/CraftMagicItemsBlueprintPatcher.cs index bdc401a..828be51 100644 --- a/CraftMagicItems/CraftMagicItemsBlueprintPatcher.cs +++ b/CraftMagicItems/CraftMagicItemsBlueprintPatcher.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Reflection; using System.Text.RegularExpressions; +using CraftMagicItems.Constants; using Kingmaker.Blueprints; using Kingmaker.Blueprints.Classes; using Kingmaker.Blueprints.Items; @@ -533,7 +534,7 @@ private string ApplyRecipeItemBlueprintPatch(BlueprintItemEquipment blueprint, M skipped.Add(enchantment); } enchantmentsForDescription.Add(enchantment); - if (blueprint is BlueprintItemArmor && guid == Main.MithralArmorEnchantmentGuid) { + if (blueprint is BlueprintItemArmor && guid == ItemQualityBlueprints.MithralArmorEnchantmentGuid) { // Mithral equipment has half weight accessors.SetBlueprintItemWeight(blueprint) = blueprint.Weight / 2; } diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 4137701..5379076 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -6,6 +6,7 @@ using System.Reflection.Emit; using System.Runtime.Serialization; using System.Text.RegularExpressions; +using CraftMagicItems.Constants; using CraftMagicItems.UI; using CraftMagicItems.UI.Sections; using CraftMagicItems.UI.UnityModManager; @@ -92,9 +93,6 @@ public static class Main { }; public static readonly FeatureGroup[] CraftingFeatGroups = {FeatureGroup.Feat, FeatureGroup.WizardFeat}; - private const string MasterworkGuid = "6b38844e2bffbac48b63036b66e735be"; - public const string MithralArmorEnchantmentGuid = "7b95a819181574a4799d93939aa99aff"; - private const string OversizedGuid = "d8e1ebc1062d8cc42abff78783856b0d"; private const string AlchemistProgressionGuid = "efd55ff9be2fda34981f5b9c83afe4f1"; private const string AlchemistGrenadierArchetypeGuid = "6af888a7800b3e949a40f558ff204aae"; private const string ScrollSavantArchetypeGuid = "f43c78692a4e10d43a38bd6aedf53c1b"; @@ -103,8 +101,6 @@ public static class Main { private const string ShieldMasterGuid = "dbec636d84482944f87435bd31522fcc"; private const string TwoWeaponFightingBasicMechanicsGuid = "6948b379c0562714d9f6d58ccbfa8faa"; private const string LongshankBaneGuid = "92a1f5db1a03c5b468828c25dd375806"; - private const string WeaponLightShieldGuid = "1fd965e522502fe479fdd423cca07684"; - private const string WeaponHeavyShieldGuid = "be9b6408e6101cb4997a8996484baf19"; private static readonly string[] SafeBlueprintAreaGuids = { "141f6999dada5a842a46bb3f029c287a", // Dire Narlmarches village @@ -889,7 +885,7 @@ private static string FindSupersededEnchantmentId(BlueprintItem blueprint, strin // Special case - enchanting a masterwork item supersedes the masterwork quality if (IsMasterwork(blueprint)) { - return MasterworkGuid; + return ItemQualityBlueprints.MasterworkGuid; } } @@ -988,11 +984,11 @@ private static string BuildCommaList(this IEnumerable list, bool or) { } private static bool IsMasterwork(BlueprintItem blueprint) { - return GetEnchantments(blueprint).Any(enchantment => enchantment.AssetGuid == MasterworkGuid); + return GetEnchantments(blueprint).Any(enchantment => enchantment.AssetGuid == ItemQualityBlueprints.MasterworkGuid); } private static bool IsOversized(BlueprintItem blueprint) { - return GetEnchantments(blueprint).Any(enchantment => enchantment.AssetGuid.StartsWith(OversizedGuid) && !enchantment.GetComponent()); + return GetEnchantments(blueprint).Any(enchantment => enchantment.AssetGuid.StartsWith(ItemQualityBlueprints.OversizedGuid) && !enchantment.GetComponent()); } // Use instead of UIUtility.IsMagicItem. @@ -2023,7 +2019,7 @@ private static void RenderCraftMundaneItemsSection() { if (shield.WeaponComponent != null) { PhysicalDamageMaterial material = selectedRecipe.Material; if ((shield.WeaponComponent.DamageType.Physical.Form & PhysicalDamageForm.Bludgeoning) != 0 - && selectedEnchantment != null && selectedEnchantment.AssetGuid == MithralArmorEnchantmentGuid) { + && selectedEnchantment != null && selectedEnchantment.AssetGuid == ItemQualityBlueprints.MithralArmorEnchantmentGuid) { material = PhysicalDamageMaterial.Silver; } var weaponEnchantments = selectedEnchantment != null && selectedRecipe.Restrictions.Contains(ItemRestrictions.ShieldWeapon) ? @@ -2584,7 +2580,7 @@ private static void RenderRecipeBasedCraftItemControl(UnitEntityData caster, Ite var blueprintCost = standardBlueprint.Cost; var blueprintWeight = standardBlueprint.Weight; foreach (var enchantment in itemBlueprint.Enchantments) { - if (enchantment.AssetGuid.StartsWith(OversizedGuid)) { + if (enchantment.AssetGuid.StartsWith(ItemQualityBlueprints.OversizedGuid)) { var weaponBaseSizeChange = enchantment.GetComponent(); if (weaponBaseSizeChange != null) { var sizeCategoryChange = weaponBaseSizeChange.SizeCategoryChange; @@ -2883,9 +2879,9 @@ public static int RulesRecipeItemCost(BlueprintItem blueprint, int baseCost = -1 var mithralArmorEnchantmentGuid = false; var cost = 0; foreach (var enchantment in blueprint.Enchantments) { - if (enchantment.AssetGuid == MithralArmorEnchantmentGuid) { + if (enchantment.AssetGuid == ItemQualityBlueprints.MithralArmorEnchantmentGuid) { mithralArmorEnchantmentGuid = true; - } else if (enchantment.AssetGuid.StartsWith(OversizedGuid)) { + } else if (enchantment.AssetGuid.StartsWith(ItemQualityBlueprints.OversizedGuid)) { var weaponBaseSizeChange = enchantment.GetComponent(); if (weaponBaseSizeChange != null) { var sizeCategoryChange = weaponBaseSizeChange.SizeCategoryChange; @@ -3269,9 +3265,9 @@ private static void PatchBlueprints() { } } - var lightShield = ResourcesLibrary.TryGetBlueprint(WeaponLightShieldGuid); + var lightShield = ResourcesLibrary.TryGetBlueprint(ItemQualityBlueprints.WeaponLightShieldGuid); Accessors.SetBlueprintItemBaseDamage(lightShield) = new DiceFormula(1, DiceType.D3); - var heavyShield = ResourcesLibrary.TryGetBlueprint(WeaponHeavyShieldGuid); + var heavyShield = ResourcesLibrary.TryGetBlueprint(ItemQualityBlueprints.WeaponHeavyShieldGuid); Accessors.SetBlueprintItemBaseDamage(heavyShield) = new DiceFormula(1, DiceType.D4); for (int i = 0; i < ItemEnchantmentGuids.Length; i += 2) { @@ -4628,7 +4624,7 @@ private static bool Prefix(ref bool __result) { private static class UnitViewHandSlotDataWeaponScalePatch { private static void Postfix(UnitViewHandSlotData __instance, ref float __result) { if (__instance.VisibleItem is ItemEntityWeapon weapon && !weapon.Blueprint.AssetGuid.Contains(",visual=")) { - var enchantment = GetEnchantments(weapon.Blueprint).FirstOrDefault(e => e.AssetGuid.StartsWith(OversizedGuid)); + var enchantment = GetEnchantments(weapon.Blueprint).FirstOrDefault(e => e.AssetGuid.StartsWith(ItemQualityBlueprints.OversizedGuid)); if (enchantment != null) { var component = enchantment.GetComponent(); if (component != null) { From e7fefec41e95312b2439356c512cad4ed53ef232 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Thu, 2 Jul 2020 12:07:35 -0500 Subject: [PATCH 044/132] Extracting specific enchantment unique identifiers into their own constants class --- .../Constants/EnchantmentBlueprints.cs | 27 +++++++++++++++++++ CraftMagicItems/CraftMagicItems.csproj | 1 + CraftMagicItems/Main.cs | 18 +++---------- 3 files changed, 32 insertions(+), 14 deletions(-) create mode 100644 CraftMagicItems/Constants/EnchantmentBlueprints.cs diff --git a/CraftMagicItems/Constants/EnchantmentBlueprints.cs b/CraftMagicItems/Constants/EnchantmentBlueprints.cs new file mode 100644 index 0000000..22b643f --- /dev/null +++ b/CraftMagicItems/Constants/EnchantmentBlueprints.cs @@ -0,0 +1,27 @@ +namespace CraftMagicItems.Constants +{ + /// Class containing constants for identifying blueprint unique identifiers for item enchantments + public static class EnchantmentBlueprints + { + /// Unique identifier for the Longshank-bane weapon enchantment + public const string LongshankBaneGuid = "92a1f5db1a03c5b468828c25dd375806"; + + /// Collection of unique identifiers for basic(?) enchantments + /// + /// Array is broken up into sets of 2, the first of each set is the "source" and the second a "destination" + /// + /// It looks like the first one is the "weapon" enhancement, and the second is the "unarmed" equivalent. Must be for the Anulet of Mighty Fists? + /// + /// TODO: flip this over into a Struct of Weapon/Amulet so that what is happening is clearer. + /// + public static readonly string[] ItemEnchantmentGuids = + { + "d42fc23b92c640846ac137dc26e000d4", "da7d830b3f75749458c2e51524805560", // Enchantment +1 + "eb2faccc4c9487d43b3575d7e77ff3f5", "49f9befa0e77cd5428ca3b28fd66a54e", // Enchantment +2 + "80bb8a737579e35498177e1e3c75899b", "bae627dfb77c2b048900f154719ca07b", // Enchantment +3 + "783d7d496da6ac44f9511011fc5f1979", "a4016a5d78384a94581497d0d135d98b", // Enchantment +4 + "bdba267e951851449af552aa9f9e3992", "c3ad7f708c573b24082dde91b081ca5f", // Enchantment +5 + "a36ad92c51789b44fa8a1c5c116a1328", "90316f5801dbe4748a66816a7c00380c", // Agile + }; + } +} \ No newline at end of file diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index a9a5b88..7b67c96 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -93,6 +93,7 @@ + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 5379076..224fb2d 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -100,7 +100,6 @@ public static class Main { private const string ChannelEnergyFeatureGuid = "a79013ff4bcd4864cb669622a29ddafb"; private const string ShieldMasterGuid = "dbec636d84482944f87435bd31522fcc"; private const string TwoWeaponFightingBasicMechanicsGuid = "6948b379c0562714d9f6d58ccbfa8faa"; - private const string LongshankBaneGuid = "92a1f5db1a03c5b468828c25dd375806"; private static readonly string[] SafeBlueprintAreaGuids = { "141f6999dada5a842a46bb3f029c287a", // Dire Narlmarches village @@ -117,15 +116,6 @@ public static class Main { "c49315fe499f0e5468af6f19242499a2", // Tenebrous Depths start hub (Roguelike) }; - private static readonly string[] ItemEnchantmentGuids = { - "d42fc23b92c640846ac137dc26e000d4", "da7d830b3f75749458c2e51524805560", // Enchantment +1 - "eb2faccc4c9487d43b3575d7e77ff3f5", "49f9befa0e77cd5428ca3b28fd66a54e", // Enchantment +2 - "80bb8a737579e35498177e1e3c75899b", "bae627dfb77c2b048900f154719ca07b", // Enchantment +3 - "783d7d496da6ac44f9511011fc5f1979", "a4016a5d78384a94581497d0d135d98b", // Enchantment +4 - "bdba267e951851449af552aa9f9e3992", "c3ad7f708c573b24082dde91b081ca5f", // Enchantment +5 - "a36ad92c51789b44fa8a1c5c116a1328", "90316f5801dbe4748a66816a7c00380c", // Agile - }; - private const string CustomPriceLabel = "Crafting Cost: "; private static readonly LocalizedString CasterLevelLocalized = new L10NString("dfb34498-61df-49b1-af18-0a84ce47fc98"); private static readonly LocalizedString CharacterUsedItemLocalized = new L10NString("be7942ed-3af1-4fc7-b20b-41966d2f80b7"); @@ -3270,14 +3260,14 @@ private static void PatchBlueprints() { var heavyShield = ResourcesLibrary.TryGetBlueprint(ItemQualityBlueprints.WeaponHeavyShieldGuid); Accessors.SetBlueprintItemBaseDamage(heavyShield) = new DiceFormula(1, DiceType.D4); - for (int i = 0; i < ItemEnchantmentGuids.Length; i += 2) { - var source = ResourcesLibrary.TryGetBlueprint(ItemEnchantmentGuids[i]); - var dest = ResourcesLibrary.TryGetBlueprint(ItemEnchantmentGuids[i + 1]); + for (int i = 0; i < EnchantmentBlueprints.ItemEnchantmentGuids.Length; i += 2) { + var source = ResourcesLibrary.TryGetBlueprint(EnchantmentBlueprints.ItemEnchantmentGuids[i]); + var dest = ResourcesLibrary.TryGetBlueprint(EnchantmentBlueprints.ItemEnchantmentGuids[i + 1]); Accessors.SetBlueprintItemEnchantmentEnchantName(dest) = Accessors.GetBlueprintItemEnchantmentEnchantName(source); Accessors.SetBlueprintItemEnchantmentDescription(dest) = Accessors.GetBlueprintItemEnchantmentDescription(source); } - var longshankBane = ResourcesLibrary.TryGetBlueprint(LongshankBaneGuid); + var longshankBane = ResourcesLibrary.TryGetBlueprint(EnchantmentBlueprints.LongshankBaneGuid); if (longshankBane.ComponentsArray.Length >= 2 && longshankBane.ComponentsArray[1] is WeaponConditionalDamageDice conditional) { for (int i = 0; i < conditional.Conditions.Conditions.Length; i++) { if (conditional.Conditions.Conditions[i] is Kingmaker.Designers.EventConditionActionSystem.Conditions.HasFact condition) { From d3c28c37876d35ab65e333308ce6fd9d8d6e835e Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Thu, 2 Jul 2020 14:32:03 -0500 Subject: [PATCH 045/132] Adding in a struct to replace/convert the `EnchantmentBlueprints.ItemEnchantmentGuids` string array --- .../Constants/UnarmedStrikeEnchantment.cs | 15 +++++++++++++++ CraftMagicItems/CraftMagicItems.csproj | 1 + 2 files changed, 16 insertions(+) create mode 100644 CraftMagicItems/Constants/UnarmedStrikeEnchantment.cs diff --git a/CraftMagicItems/Constants/UnarmedStrikeEnchantment.cs b/CraftMagicItems/Constants/UnarmedStrikeEnchantment.cs new file mode 100644 index 0000000..25a6622 --- /dev/null +++ b/CraftMagicItems/Constants/UnarmedStrikeEnchantment.cs @@ -0,0 +1,15 @@ +namespace CraftMagicItems.Constants +{ + /// Structure defining enchantments that exist for both melee and unarmed strikes + public struct UnarmedStrikeEnchantment + { + /// Used for descriptive purposes rather than a comment in code that someone might delete + public string Description; + + /// The unique identifier of the weapon enchantment blueprint to copy data from + public string WeaponEnchantmentGuid; + + /// The unique identifier of the unarmed strike blueprint to copy data into + public string UnarmedEnchantmentGuid; + } +} \ No newline at end of file diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index 7b67c96..f543754 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -95,6 +95,7 @@ + From 245956dba1dee744218e0b0c38c63db38657877d Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Thu, 2 Jul 2020 14:39:59 -0500 Subject: [PATCH 046/132] Converting EnchantmentBlueprints.ItemEnchantmentGuids into a struct reference and updating code references to it --- .../Constants/EnchantmentBlueprints.cs | 53 +++++++++++++------ CraftMagicItems/Main.cs | 6 +-- 2 files changed, 41 insertions(+), 18 deletions(-) diff --git a/CraftMagicItems/Constants/EnchantmentBlueprints.cs b/CraftMagicItems/Constants/EnchantmentBlueprints.cs index 22b643f..20442dc 100644 --- a/CraftMagicItems/Constants/EnchantmentBlueprints.cs +++ b/CraftMagicItems/Constants/EnchantmentBlueprints.cs @@ -6,22 +6,45 @@ public static class EnchantmentBlueprints /// Unique identifier for the Longshank-bane weapon enchantment public const string LongshankBaneGuid = "92a1f5db1a03c5b468828c25dd375806"; - /// Collection of unique identifiers for basic(?) enchantments - /// - /// Array is broken up into sets of 2, the first of each set is the "source" and the second a "destination" - /// - /// It looks like the first one is the "weapon" enhancement, and the second is the "unarmed" equivalent. Must be for the Anulet of Mighty Fists? - /// - /// TODO: flip this over into a Struct of Weapon/Amulet so that what is happening is clearer. - /// - public static readonly string[] ItemEnchantmentGuids = + /// Collection of unique identifiers for enchantments applicable to both melee weapons and Amulet of Mighty Fists + public static readonly UnarmedStrikeEnchantment[] ItemEnchantmentGuids = { - "d42fc23b92c640846ac137dc26e000d4", "da7d830b3f75749458c2e51524805560", // Enchantment +1 - "eb2faccc4c9487d43b3575d7e77ff3f5", "49f9befa0e77cd5428ca3b28fd66a54e", // Enchantment +2 - "80bb8a737579e35498177e1e3c75899b", "bae627dfb77c2b048900f154719ca07b", // Enchantment +3 - "783d7d496da6ac44f9511011fc5f1979", "a4016a5d78384a94581497d0d135d98b", // Enchantment +4 - "bdba267e951851449af552aa9f9e3992", "c3ad7f708c573b24082dde91b081ca5f", // Enchantment +5 - "a36ad92c51789b44fa8a1c5c116a1328", "90316f5801dbe4748a66816a7c00380c", // Agile + new UnarmedStrikeEnchantment + { + WeaponEnchantmentGuid = "d42fc23b92c640846ac137dc26e000d4", + UnarmedEnchantmentGuid = "da7d830b3f75749458c2e51524805560", + Description = "Enchantment +1" + }, + new UnarmedStrikeEnchantment + { + WeaponEnchantmentGuid = "eb2faccc4c9487d43b3575d7e77ff3f5", + UnarmedEnchantmentGuid = "49f9befa0e77cd5428ca3b28fd66a54e", + Description = "Enchantment +2" + }, + new UnarmedStrikeEnchantment + { + WeaponEnchantmentGuid = "80bb8a737579e35498177e1e3c75899b", + UnarmedEnchantmentGuid = "bae627dfb77c2b048900f154719ca07b", + Description = "Enchantment +3" + }, + new UnarmedStrikeEnchantment + { + WeaponEnchantmentGuid = "783d7d496da6ac44f9511011fc5f1979", + UnarmedEnchantmentGuid = "a4016a5d78384a94581497d0d135d98b", + Description = "Enchantment +4" + }, + new UnarmedStrikeEnchantment + { + WeaponEnchantmentGuid = "bdba267e951851449af552aa9f9e3992", + UnarmedEnchantmentGuid = "c3ad7f708c573b24082dde91b081ca5f", + Description = "Enchantment +5" + }, + new UnarmedStrikeEnchantment + { + WeaponEnchantmentGuid = "a36ad92c51789b44fa8a1c5c116a1328", + UnarmedEnchantmentGuid = "90316f5801dbe4748a66816a7c00380c", + Description = "Agile" + }, }; } } \ No newline at end of file diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 224fb2d..f77b7cc 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -3260,9 +3260,9 @@ private static void PatchBlueprints() { var heavyShield = ResourcesLibrary.TryGetBlueprint(ItemQualityBlueprints.WeaponHeavyShieldGuid); Accessors.SetBlueprintItemBaseDamage(heavyShield) = new DiceFormula(1, DiceType.D4); - for (int i = 0; i < EnchantmentBlueprints.ItemEnchantmentGuids.Length; i += 2) { - var source = ResourcesLibrary.TryGetBlueprint(EnchantmentBlueprints.ItemEnchantmentGuids[i]); - var dest = ResourcesLibrary.TryGetBlueprint(EnchantmentBlueprints.ItemEnchantmentGuids[i + 1]); + for (int i = 0; i < EnchantmentBlueprints.ItemEnchantmentGuids.Length; i++) { + var source = ResourcesLibrary.TryGetBlueprint(EnchantmentBlueprints.ItemEnchantmentGuids[i].WeaponEnchantmentGuid); + var dest = ResourcesLibrary.TryGetBlueprint(EnchantmentBlueprints.ItemEnchantmentGuids[i].UnarmedEnchantmentGuid); Accessors.SetBlueprintItemEnchantmentEnchantName(dest) = Accessors.GetBlueprintItemEnchantmentEnchantName(source); Accessors.SetBlueprintItemEnchantmentDescription(dest) = Accessors.GetBlueprintItemEnchantmentDescription(source); } From 04d3cfa2f5510923337221585e1a22799e8e44ed Mon Sep 17 00:00:00 2001 From: Benjamin Fennema Date: Sat, 12 Dec 2020 14:12:07 -0800 Subject: [PATCH 047/132] Fix undersized and oversized can be used with two hands flag Signed-off-by: Benjamin Fennema --- CraftMagicItems/WeaponBaseSizeChange.cs | 28 ++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/CraftMagicItems/WeaponBaseSizeChange.cs b/CraftMagicItems/WeaponBaseSizeChange.cs index 480b366..195869c 100644 --- a/CraftMagicItems/WeaponBaseSizeChange.cs +++ b/CraftMagicItems/WeaponBaseSizeChange.cs @@ -2,7 +2,6 @@ using Kingmaker.Blueprints.Items.Weapons; using Kingmaker.EntitySystem.Stats; using Kingmaker.RuleSystem; -using Object = UnityEngine.Object; namespace CraftMagicItems { [ComponentName("Weapon Base Size Change")] @@ -14,7 +13,6 @@ public class WeaponBaseSizeChange : GameLogicComponent { public int SizeCategoryChange; [HarmonyLib.HarmonyPatch(typeof(BlueprintItemWeapon), "BaseDamage", HarmonyLib.MethodType.Getter)] - // ReSharper disable once UnusedMember.Local private static class BlueprintItemWeaponBaseDamage { private static void Postfix(BlueprintItemWeapon __instance, ref DiceFormula __result) { foreach (var enchantment in __instance.Enchantments) { @@ -28,7 +26,6 @@ private static void Postfix(BlueprintItemWeapon __instance, ref DiceFormula __re } [HarmonyLib.HarmonyPatch(typeof(BlueprintItemWeapon), "AttackBonusStat", HarmonyLib.MethodType.Getter)] - // ReSharper disable once UnusedMember.Local private static class BlueprintItemWeaponAttackBonusStat { private static void Postfix(BlueprintItemWeapon __instance, ref StatType __result) { foreach (var enchantment in __instance.Enchantments) { @@ -43,9 +40,7 @@ private static void Postfix(BlueprintItemWeapon __instance, ref StatType __resul } } - [HarmonyLib.HarmonyPatch(typeof(BlueprintItemWeapon), "IsTwoHanded", HarmonyLib.MethodType.Getter)] - // ReSharper disable once UnusedMember.Local private static class BlueprintItemWeaponIsTwoHanded { private static void Postfix(BlueprintItemWeapon __instance, ref bool __result) { foreach (var enchantment in __instance.Enchantments) { @@ -63,7 +58,6 @@ private static void Postfix(BlueprintItemWeapon __instance, ref bool __result) { } [HarmonyLib.HarmonyPatch(typeof(BlueprintItemWeapon), "IsLight", HarmonyLib.MethodType.Getter)] - // ReSharper disable once UnusedMember.Local private static class BlueprintItemWeaponIsLight { private static void Postfix(BlueprintItemWeapon __instance, ref bool __result) { foreach (var enchantment in __instance.Enchantments) { @@ -80,8 +74,28 @@ private static void Postfix(BlueprintItemWeapon __instance, ref bool __result) { } } + [HarmonyLib.HarmonyPatch(typeof(BlueprintItemWeapon), "IsOneHandedWhichCanBeUsedWithTwoHands", HarmonyLib.MethodType.Getter)] + private static class BlueprintItemWeaponIsOneHandedWhichCanBeUsedWithTwoHands { + private static void Postfix(BlueprintItemWeapon __instance, ref bool __result) { + if (__instance.Type.AttackType == AttackType.Melee && !__instance.Type.IsNatural && !__instance.Type.IsUnarmed) { + foreach (var enchantment in __instance.Enchantments) { + var component = enchantment.GetComponent(); + if (component != null) { + if (component.SizeCategoryChange == 1 && __instance.Type.IsLight) { + __result = true; + } else if ((component.SizeCategoryChange == -1 && __instance.Type.IsTwoHanded)) { + __result = true; + } else { + __result = false; + } + break; + } + } + } + } + } + [HarmonyLib.HarmonyPatch(typeof(BlueprintItemWeapon), "SubtypeName", HarmonyLib.MethodType.Getter)] - // ReSharper disable once UnusedMember.Local private static class BlueprintItemWeaponSubtypeName { private static void Postfix(BlueprintItemWeapon __instance, ref string __result) { foreach (var enchantment in __instance.Enchantments) { From cdae0ff791698aab6138bd5fe10e6f4b7c244cb1 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Fri, 3 Jul 2020 10:29:28 -0500 Subject: [PATCH 048/132] Extracting unique identifiers for classes and archetyes into their own constants class --- CraftMagicItems/Constants/ClassBlueprints.cs | 15 +++++++++++++++ CraftMagicItems/CraftMagicItems.csproj | 1 + CraftMagicItems/Main.cs | 9 +++------ 3 files changed, 19 insertions(+), 6 deletions(-) create mode 100644 CraftMagicItems/Constants/ClassBlueprints.cs diff --git a/CraftMagicItems/Constants/ClassBlueprints.cs b/CraftMagicItems/Constants/ClassBlueprints.cs new file mode 100644 index 0000000..0aa0f26 --- /dev/null +++ b/CraftMagicItems/Constants/ClassBlueprints.cs @@ -0,0 +1,15 @@ +namespace CraftMagicItems.Constants +{ + /// Contains constants for class/archetype blueprint unique identifiers + public static class ClassBlueprints + { + /// Blueprint for Alchemist class progression + public const string AlchemistProgressionGuid = "efd55ff9be2fda34981f5b9c83afe4f1"; + + /// Blueprint for Alchemist Grenadier archetype + public const string AlchemistGrenadierArchetypeGuid = "6af888a7800b3e949a40f558ff204aae"; + + /// Blueprint for Wizard Scroll Savant archetype + public const string ScrollSavantArchetypeGuid = "f43c78692a4e10d43a38bd6aedf53c1b"; + } +} \ No newline at end of file diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index f543754..dc3ec09 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -93,6 +93,7 @@ + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index f77b7cc..a761d5c 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -93,9 +93,6 @@ public static class Main { }; public static readonly FeatureGroup[] CraftingFeatGroups = {FeatureGroup.Feat, FeatureGroup.WizardFeat}; - private const string AlchemistProgressionGuid = "efd55ff9be2fda34981f5b9c83afe4f1"; - private const string AlchemistGrenadierArchetypeGuid = "6af888a7800b3e949a40f558ff204aae"; - private const string ScrollSavantArchetypeGuid = "f43c78692a4e10d43a38bd6aedf53c1b"; private const string MartialWeaponProficiencies = "203992ef5b35c864390b4e4a1e200629"; private const string ChannelEnergyFeatureGuid = "a79013ff4bcd4864cb669622a29ddafb"; private const string ShieldMasterGuid = "dbec636d84482944f87435bd31522fcc"; @@ -3111,8 +3108,8 @@ private static void AddAllCraftingFeats() { // Alchemists get Brew Potion as a bonus 1st level feat, except for Grenadier archetype alchemists. var brewPotionData = ItemCraftingData.First(data => data.Name == "Potion"); var brewPotion = ResourcesLibrary.TryGetBlueprint(brewPotionData.FeatGuid); - var alchemistProgression = ResourcesLibrary.TryGetBlueprint(AlchemistProgressionGuid); - var grenadierArchetype = ResourcesLibrary.TryGetBlueprint(AlchemistGrenadierArchetypeGuid); + var alchemistProgression = ResourcesLibrary.TryGetBlueprint(ClassBlueprints.AlchemistProgressionGuid); + var grenadierArchetype = ResourcesLibrary.TryGetBlueprint(ClassBlueprints.AlchemistGrenadierArchetypeGuid); if (brewPotion != null && alchemistProgression != null && grenadierArchetype != null) { var firstLevelIndex = alchemistProgression.LevelEntries.FindIndex((levelEntry) => (levelEntry.Level == 1)); alchemistProgression.LevelEntries[firstLevelIndex].Features.Add(brewPotion); @@ -3132,7 +3129,7 @@ private static void AddAllCraftingFeats() { // Scroll Savant should get Scribe Scroll as a bonus 1st level feat. var scribeScrollData = ItemCraftingData.First(data => data.Name == "Scroll"); var scribeScroll = ResourcesLibrary.TryGetBlueprint(scribeScrollData.FeatGuid); - var scrollSavantArchetype = ResourcesLibrary.TryGetBlueprint(ScrollSavantArchetypeGuid); + var scrollSavantArchetype = ResourcesLibrary.TryGetBlueprint(ClassBlueprints.ScrollSavantArchetypeGuid); if (scribeScroll != null && scrollSavantArchetype != null) { var firstLevelAdd = scrollSavantArchetype.AddFeatures.First((levelEntry) => (levelEntry.Level == 1)); firstLevelAdd.Features.Add(scribeScroll); From 32e20424394dc76e580a4557bff5e5322e6ad2f8 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Fri, 3 Jul 2020 10:53:39 -0500 Subject: [PATCH 049/132] Extracting feat, ability, and feature GUIDs into constants class --- CraftMagicItems/Constants/Features.cs | 30 +++++++++++++++++++ CraftMagicItems/CraftMagicItems.csproj | 1 + CraftMagicItems/Main.cs | 14 ++++----- .../UI/UserInterfaceEventHandlingLogic.cs | 3 +- 4 files changed, 38 insertions(+), 10 deletions(-) create mode 100644 CraftMagicItems/Constants/Features.cs diff --git a/CraftMagicItems/Constants/Features.cs b/CraftMagicItems/Constants/Features.cs new file mode 100644 index 0000000..3f2f5a1 --- /dev/null +++ b/CraftMagicItems/Constants/Features.cs @@ -0,0 +1,30 @@ +using Kingmaker.Blueprints.Classes; + +namespace CraftMagicItems.Constants +{ + /// Class containing constants for abilities and Feats + public static class Features + { + /// Collection of that crafting feats can be added to + public static readonly FeatureGroup[] CraftingFeatGroups = { FeatureGroup.Feat, FeatureGroup.WizardFeat }; + + /// Blueprint unique identifier for the items that are "martial" weapons + /// Used to determine which mundane items are martial under special conditions + public const string MartialWeaponProficiencies = "203992ef5b35c864390b4e4a1e200629"; + + /// Blueprint unique identifier for the Channel Energy ability applied to a character + public const string ChannelEnergyFeatureGuid = "a79013ff4bcd4864cb669622a29ddafb"; + + /// Blueprint unique identifier for the Shield Master feat + public const string ShieldMasterGuid = "dbec636d84482944f87435bd31522fcc"; + + /// Blueprint unique identifier for the Prodigious Two-Weapon Fighting feat + public const string ProdigiousTwoWeaponFightingGuid = "ddba046d03074037be18ad33ea462028"; + + /// Blueprint unique identifiers for applied class features that have bonded item features added by the mod + public static readonly string[] BondedItemFeatures = { + "2fb5e65bd57caa943b45ee32d825e9b9", + "aa34ca4f3cd5e5d49b2475fcfdf56b24" + }; + } +} \ No newline at end of file diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index dc3ec09..ae9f006 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -95,6 +95,7 @@ + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index a761d5c..b715d71 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -92,10 +92,6 @@ public static class Main { "Custom" }; - public static readonly FeatureGroup[] CraftingFeatGroups = {FeatureGroup.Feat, FeatureGroup.WizardFeat}; - private const string MartialWeaponProficiencies = "203992ef5b35c864390b4e4a1e200629"; - private const string ChannelEnergyFeatureGuid = "a79013ff4bcd4864cb669622a29ddafb"; - private const string ShieldMasterGuid = "dbec636d84482944f87435bd31522fcc"; private const string TwoWeaponFightingBasicMechanicsGuid = "6948b379c0562714d9f6d58ccbfa8faa"; private static readonly string[] SafeBlueprintAreaGuids = { @@ -490,7 +486,7 @@ private static void RenderCraftMagicItemsSection() { //can the character have a bonded item? var hasBondedItemFeature = - caster.Descriptor.Progression.Features.Enumerable.Any(feature => BondedItemFeatures.Contains(feature.Blueprint.AssetGuid)); + caster.Descriptor.Progression.Features.Enumerable.Any(feature => Features.BondedItemFeatures.Contains(feature.Blueprint.AssetGuid)); var bondedItemData = GetBondedItemComponentForCaster(caster.Descriptor); if (!hasBondedItemFeature && bondedItemData && bondedItemData.ownerItem != null) { @@ -1865,7 +1861,7 @@ private static int CalculateBaseMundaneCraftingDC(RecipeBasedItemCraftingData cr return dc + shield.ArmorComponent.ArmorBonus; case BlueprintItemWeapon weapon: if (weapon.Category.HasSubCategory(WeaponSubCategory.Exotic)) { - var martialWeaponProficiencies = ResourcesLibrary.TryGetBlueprint(MartialWeaponProficiencies); + var martialWeaponProficiencies = ResourcesLibrary.TryGetBlueprint(Features.MartialWeaponProficiencies); if (martialWeaponProficiencies != null && martialWeaponProficiencies.GetComponents() .Any(addProficiencies => addProficiencies.RaceRestriction != null && addProficiencies.RaceRestriction == crafter.Progression.Race @@ -3077,7 +3073,7 @@ private static void AddCraftingFeats(ObjectIDGenerator idGenerator, BlueprintPro foreach (var levelEntry in progression.LevelEntries) { foreach (var featureBase in levelEntry.Features) { var selection = featureBase as BlueprintFeatureSelection; - if (selection != null && (CraftingFeatGroups.Contains(selection.Group) || CraftingFeatGroups.Contains(selection.Group2))) { + if (selection != null && (Features.CraftingFeatGroups.Contains(selection.Group) || Features.CraftingFeatGroups.Contains(selection.Group2))) { // Use ObjectIDGenerator to detect which shared lists we've added the feats to. idGenerator.GetId(selection.AllFeatures, out var firstTime); if (firstTime) { @@ -3239,7 +3235,7 @@ public void OnEventDidTrigger(RuleCalculateAttackBonusWithoutTarget evt) { } } private static void PatchBlueprints() { - var shieldMaster = ResourcesLibrary.TryGetBlueprint(ShieldMasterGuid); + var shieldMaster = ResourcesLibrary.TryGetBlueprint(Features.ShieldMasterGuid); var twoWeaponFighting = ResourcesLibrary.TryGetBlueprint(TwoWeaponFightingBasicMechanicsGuid); TwoWeaponFightingAttackPenaltyOnEventAboutToTriggerPatch.ShieldMaster = shieldMaster; Accessors.SetBlueprintUnitFactDisplayName(twoWeaponFighting) = new L10NString("e32ce256-78dc-4fd0-bf15-21f9ebdf9921"); @@ -3602,7 +3598,7 @@ private static List GetMissingCrafterPrerequisites(Craf || prerequisite == CrafterPrerequisiteType.AlignmentChaotic && (caster.Alignment.Value.ToMask() & AlignmentMaskType.Chaotic) == 0 || prerequisite == CrafterPrerequisiteType.AlignmentEvil && (caster.Alignment.Value.ToMask() & AlignmentMaskType.Evil) == 0 || prerequisite == CrafterPrerequisiteType.FeatureChannelEnergy && - caster.GetFeature(ResourcesLibrary.TryGetBlueprint(ChannelEnergyFeatureGuid)) == null + caster.GetFeature(ResourcesLibrary.TryGetBlueprint(Features.ChannelEnergyFeatureGuid)) == null )); } diff --git a/CraftMagicItems/UI/UserInterfaceEventHandlingLogic.cs b/CraftMagicItems/UI/UserInterfaceEventHandlingLogic.cs index 6365d86..db200ba 100644 --- a/CraftMagicItems/UI/UserInterfaceEventHandlingLogic.cs +++ b/CraftMagicItems/UI/UserInterfaceEventHandlingLogic.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using CraftMagicItems.Constants; using CraftMagicItems.UI.Sections; using Kingmaker.Blueprints; using Kingmaker.Blueprints.Classes; @@ -94,7 +95,7 @@ public static void RenderFeatReassignmentSection(IFeatReassignmentSectionRendere var removedFeatIndex = 0; foreach (var feature in caster.Descriptor.Progression.Features) { - if (!feature.Blueprint.HideInUI && feature.Blueprint.HasGroup(Main.CraftingFeatGroups) + if (!feature.Blueprint.HideInUI && feature.Blueprint.HasGroup(Features.CraftingFeatGroups) && (feature.SourceProgression != null || feature.SourceRace != null)) { if (renderer.Evaluate_LearnFeatButton(feature.Name, learnFeat.Name)) From ce93e95ab4f259067fb08ce0152cf24fa68614ae Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Wed, 15 Jul 2020 17:17:23 -0500 Subject: [PATCH 050/132] Extracting the area blueprint GUIDs to their own file --- CraftMagicItems/Constants/Areas.cs | 24 ++++++++++++++++++++++++ CraftMagicItems/CraftMagicItems.csproj | 1 + CraftMagicItems/Main.cs | 17 +---------------- 3 files changed, 26 insertions(+), 16 deletions(-) create mode 100644 CraftMagicItems/Constants/Areas.cs diff --git a/CraftMagicItems/Constants/Areas.cs b/CraftMagicItems/Constants/Areas.cs new file mode 100644 index 0000000..fde61de --- /dev/null +++ b/CraftMagicItems/Constants/Areas.cs @@ -0,0 +1,24 @@ +namespace CraftMagicItems.Constants +{ + /// Class containing unique identifiers for area blueprints + public static class Areas + { + /// Collection of blueprint unique identifiers for "safe" areas + public static readonly string[] SafeBlueprintAreaGuids = + { + "141f6999dada5a842a46bb3f029c287a", // Dire Narlmarches village + "e0852647faf68a641a0a5ec5436fc0bf", // Dunsward village + "537acbd9039b6a04f915dfc21572affb", // Glenebon village + "3ddf191773e8c2f448d31289b8d654bf", // Kamelands village + "f1b76870cc69e6a479c767cbe3c8a8ca", // North Narlmarches village + "ea3788bcdf33d884baffc34440ff620f", // Outskirts village + "e7292eab463c4924c8f14548545e25b7", // Silverstep village + "7c4a954c65e8d7146a6edc00c498a582", // South Narlmarches village + "7d9616b3807840c47ba3b2ab380c55a0", // Tors of Levenies village + "653811192a3fcd148816384a9492bd08", // Secluded Lodge + "fd1b6fa9f788ca24e86bd922a10da080", // Tenebrous Depths start hub + "c49315fe499f0e5468af6f19242499a2", // Tenebrous Depths start hub (Roguelike) + // TODO: camp outside the capital? + }; + } +} \ No newline at end of file diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index ae9f006..07bb0ae 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -93,6 +93,7 @@ + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index b715d71..2d524a4 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -94,21 +94,6 @@ public static class Main { private const string TwoWeaponFightingBasicMechanicsGuid = "6948b379c0562714d9f6d58ccbfa8faa"; - private static readonly string[] SafeBlueprintAreaGuids = { - "141f6999dada5a842a46bb3f029c287a", // Dire Narlmarches village - "e0852647faf68a641a0a5ec5436fc0bf", // Dunsward village - "537acbd9039b6a04f915dfc21572affb", // Glenebon village - "3ddf191773e8c2f448d31289b8d654bf", // Kamelands village - "f1b76870cc69e6a479c767cbe3c8a8ca", // North Narlmarches village - "ea3788bcdf33d884baffc34440ff620f", // Outskirts village - "e7292eab463c4924c8f14548545e25b7", // Silverstep village - "7c4a954c65e8d7146a6edc00c498a582", // South Narlmarches village - "7d9616b3807840c47ba3b2ab380c55a0", // Tors of Levenies village - "653811192a3fcd148816384a9492bd08", // Secluded Lodge - "fd1b6fa9f788ca24e86bd922a10da080", // Tenebrous Depths start hub - "c49315fe499f0e5468af6f19242499a2", // Tenebrous Depths start hub (Roguelike) - }; - private const string CustomPriceLabel = "Crafting Cost: "; private static readonly LocalizedString CasterLevelLocalized = new L10NString("dfb34498-61df-49b1-af18-0a84ce47fc98"); private static readonly LocalizedString CharacterUsedItemLocalized = new L10NString("be7942ed-3af1-4fc7-b20b-41966d2f80b7"); @@ -2121,7 +2106,7 @@ private static void RenderProjectsSection() { } private static bool IsPlayerSomewhereSafe() { - if (Game.Instance.CurrentlyLoadedArea != null && SafeBlueprintAreaGuids.Contains(Game.Instance.CurrentlyLoadedArea.AssetGuid)) { + if (Game.Instance.CurrentlyLoadedArea != null && Areas.SafeBlueprintAreaGuids.Contains(Game.Instance.CurrentlyLoadedArea.AssetGuid)) { return true; } // Otherwise, check if they're in the capital. From ce2fad2f59c00da6fb959906b9293a8589005ace Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Thu, 16 Jul 2020 13:18:55 -0500 Subject: [PATCH 051/132] Extracting game mechanics Guids to their own class --- CraftMagicItems/Constants/MechanicsBlueprints.cs | 9 +++++++++ CraftMagicItems/CraftMagicItems.csproj | 1 + CraftMagicItems/Main.cs | 4 +--- 3 files changed, 11 insertions(+), 3 deletions(-) create mode 100644 CraftMagicItems/Constants/MechanicsBlueprints.cs diff --git a/CraftMagicItems/Constants/MechanicsBlueprints.cs b/CraftMagicItems/Constants/MechanicsBlueprints.cs new file mode 100644 index 0000000..f9c5f7c --- /dev/null +++ b/CraftMagicItems/Constants/MechanicsBlueprints.cs @@ -0,0 +1,9 @@ +namespace CraftMagicItems.Constants +{ + /// Class containing constants for game mechanics + public static class MechanicsBlueprints + { + /// Constant for Two-weapon fighting game mechanics + public const string TwoWeaponFightingBasicMechanicsGuid = "6948b379c0562714d9f6d58ccbfa8faa"; + } +} \ No newline at end of file diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index 07bb0ae..16568b4 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -98,6 +98,7 @@ + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 2d524a4..1be99b2 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -92,8 +92,6 @@ public static class Main { "Custom" }; - private const string TwoWeaponFightingBasicMechanicsGuid = "6948b379c0562714d9f6d58ccbfa8faa"; - private const string CustomPriceLabel = "Crafting Cost: "; private static readonly LocalizedString CasterLevelLocalized = new L10NString("dfb34498-61df-49b1-af18-0a84ce47fc98"); private static readonly LocalizedString CharacterUsedItemLocalized = new L10NString("be7942ed-3af1-4fc7-b20b-41966d2f80b7"); @@ -3221,7 +3219,7 @@ public void OnEventDidTrigger(RuleCalculateAttackBonusWithoutTarget evt) { } private static void PatchBlueprints() { var shieldMaster = ResourcesLibrary.TryGetBlueprint(Features.ShieldMasterGuid); - var twoWeaponFighting = ResourcesLibrary.TryGetBlueprint(TwoWeaponFightingBasicMechanicsGuid); + var twoWeaponFighting = ResourcesLibrary.TryGetBlueprint(MechanicsBlueprints.TwoWeaponFightingBasicMechanicsGuid); TwoWeaponFightingAttackPenaltyOnEventAboutToTriggerPatch.ShieldMaster = shieldMaster; Accessors.SetBlueprintUnitFactDisplayName(twoWeaponFighting) = new L10NString("e32ce256-78dc-4fd0-bf15-21f9ebdf9921"); From 2a6706615460cb305f4f9c69f9467fdeecfee016 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Thu, 16 Jul 2020 13:20:00 -0500 Subject: [PATCH 052/132] Removing bonded item features from Main --- CraftMagicItems/Main.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 1be99b2..c347a56 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -114,11 +114,6 @@ public static class Main { ItemsFilter.ItemType.Neck }; - private static readonly string[] BondedItemFeatures = { - "2fb5e65bd57caa943b45ee32d825e9b9", - "aa34ca4f3cd5e5d49b2475fcfdf56b24" - }; - private enum ItemLocationFilter { All, From e463359516c094917800290ac44f1b5edc610446 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Thu, 16 Jul 2020 13:29:59 -0500 Subject: [PATCH 053/132] Moving the data into the empty Data dir already in the solution --- CraftMagicItems/AfterBuild.bat | 4 +-- CraftMagicItems/CraftMagicItems.csproj | 26 ++++++++++++++++++- .../Data}/ArmsArmorRecipes.json | 0 .../Data}/CommonRecipes.json | 0 {Data => CraftMagicItems/Data}/ItemTypes.json | 0 {Data => CraftMagicItems/Data}/LootItems.json | 0 .../Data}/MundaneRecipes.json | 0 .../Data}/RingRecipes.json | 0 .../Data}/RodRecipes.json | 0 .../Data}/WondrousRecipes.json | 0 10 files changed, 27 insertions(+), 3 deletions(-) rename {Data => CraftMagicItems/Data}/ArmsArmorRecipes.json (100%) rename {Data => CraftMagicItems/Data}/CommonRecipes.json (100%) rename {Data => CraftMagicItems/Data}/ItemTypes.json (100%) rename {Data => CraftMagicItems/Data}/LootItems.json (100%) rename {Data => CraftMagicItems/Data}/MundaneRecipes.json (100%) rename {Data => CraftMagicItems/Data}/RingRecipes.json (100%) rename {Data => CraftMagicItems/Data}/RodRecipes.json (100%) rename {Data => CraftMagicItems/Data}/WondrousRecipes.json (100%) diff --git a/CraftMagicItems/AfterBuild.bat b/CraftMagicItems/AfterBuild.bat index 22c7b99..68323d6 100644 --- a/CraftMagicItems/AfterBuild.bat +++ b/CraftMagicItems/AfterBuild.bat @@ -6,10 +6,10 @@ xcopy CraftMagicItems.dll CraftMagicItems || goto :error xcopy ..\..\..\Info.json CraftMagicItems || goto :error xcopy /E /I ..\..\..\L10n CraftMagicItems\L10n || goto :error xcopy /E /I ..\..\..\Icons CraftMagicItems\Icons || goto :error -xcopy /E /I ..\..\..\Data CraftMagicItems\Data || goto :error +xcopy /E /I Data CraftMagicItems\Data || goto :error "C:\Program Files\7-Zip\7z.exe" a CraftMagicItems.zip CraftMagicItems || goto :error -"C:\Program Files\7-Zip\7z.exe" a CraftMagicItems-Source.zip ..\..\*.cs ..\..\..\L10n ..\..\..\Icons ..\..\..\Data || goto :error +"C:\Program Files\7-Zip\7z.exe" a CraftMagicItems-Source.zip ..\..\*.cs ..\..\..\L10n ..\..\..\Icons Data || goto :error goto :EOF diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index 16568b4..3c9b335 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -130,8 +130,32 @@ + - + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + diff --git a/Data/ArmsArmorRecipes.json b/CraftMagicItems/Data/ArmsArmorRecipes.json similarity index 100% rename from Data/ArmsArmorRecipes.json rename to CraftMagicItems/Data/ArmsArmorRecipes.json diff --git a/Data/CommonRecipes.json b/CraftMagicItems/Data/CommonRecipes.json similarity index 100% rename from Data/CommonRecipes.json rename to CraftMagicItems/Data/CommonRecipes.json diff --git a/Data/ItemTypes.json b/CraftMagicItems/Data/ItemTypes.json similarity index 100% rename from Data/ItemTypes.json rename to CraftMagicItems/Data/ItemTypes.json diff --git a/Data/LootItems.json b/CraftMagicItems/Data/LootItems.json similarity index 100% rename from Data/LootItems.json rename to CraftMagicItems/Data/LootItems.json diff --git a/Data/MundaneRecipes.json b/CraftMagicItems/Data/MundaneRecipes.json similarity index 100% rename from Data/MundaneRecipes.json rename to CraftMagicItems/Data/MundaneRecipes.json diff --git a/Data/RingRecipes.json b/CraftMagicItems/Data/RingRecipes.json similarity index 100% rename from Data/RingRecipes.json rename to CraftMagicItems/Data/RingRecipes.json diff --git a/Data/RodRecipes.json b/CraftMagicItems/Data/RodRecipes.json similarity index 100% rename from Data/RodRecipes.json rename to CraftMagicItems/Data/RodRecipes.json diff --git a/Data/WondrousRecipes.json b/CraftMagicItems/Data/WondrousRecipes.json similarity index 100% rename from Data/WondrousRecipes.json rename to CraftMagicItems/Data/WondrousRecipes.json From 9035924fee188edef46930805924a715dce1e125 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Thu, 16 Jul 2020 13:32:13 -0500 Subject: [PATCH 054/132] =?UTF-8?q?Today=20I=20learned=20that=20L10n=20bei?= =?UTF-8?q?ng=20short=20for=20localization=20and=20is=20actually=20a=20thi?= =?UTF-8?q?ng=E2=84=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CraftMagicItems/L10n.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/CraftMagicItems/L10n.cs b/CraftMagicItems/L10n.cs index 7ab82a6..3d78289 100644 --- a/CraftMagicItems/L10n.cs +++ b/CraftMagicItems/L10n.cs @@ -15,6 +15,7 @@ public class L10NData { [JsonProperty] public string Value; } + /// Localization is sometimes written in English as l10n, where 10 is the number of letters in the English word between 'l' and 'n' class L10n { private static readonly Dictionary ModifiedL10NStrings = new Dictionary(); From 0c664da4492b85eacf41c9cb19513b76bd65a599 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Thu, 16 Jul 2020 13:50:17 -0500 Subject: [PATCH 055/132] Extracting item costs from Main into their own constants class --- CraftMagicItems/Constants/DefaultCosts.cs | 24 +++++++++++++++++++++++ CraftMagicItems/CraftMagicItems.csproj | 1 + CraftMagicItems/Main.cs | 19 +++++++----------- 3 files changed, 32 insertions(+), 12 deletions(-) create mode 100644 CraftMagicItems/Constants/DefaultCosts.cs diff --git a/CraftMagicItems/Constants/DefaultCosts.cs b/CraftMagicItems/Constants/DefaultCosts.cs new file mode 100644 index 0000000..803b5ae --- /dev/null +++ b/CraftMagicItems/Constants/DefaultCosts.cs @@ -0,0 +1,24 @@ +namespace CraftMagicItems.Constants +{ + /// Default costs (in Gold/GP) of items and such + public static class DefaultCosts + { + /// Cost of applying masterwork to armor + public const int ArmorMasterworkCost = 150; + + /// Cost of applying masterwork to a weapon + public const int WeaponMasterworkCost = 300; + + /// Cost of applying adamantine + public const int Adamantine = 3000; + + /// Default cost for adding a +N enchantment to a weapon + public const int WeaponPlusCost = 2000; + + /// Default cost for adding a +N enchantment to an armor + public const int ArmorPlusCost = 1000; + + /// Default cost for adding a +N enchantment to an Amulet of Mighty Fists + public const int UnarmedPlusCost = 4000; + } +} \ No newline at end of file diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index 3c9b335..c2f1e5e 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -95,6 +95,7 @@ + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index c347a56..f3cca55 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -79,11 +79,6 @@ public static class Main { private const int MissingPrerequisiteDCModifier = 5; private const int OppositionSchoolDCModifier = 4; private const int AdventuringProgressPenalty = 4; - private const int WeaponMasterworkCost = 300; - private const int WeaponPlusCost = 2000; - private const int ArmorMasterworkCost = 150; - private const int ArmorPlusCost = 1000; - private const int UnarmedPlusCost = 4000; private const string BondedItemRitual = "bondedItemRitual"; private static readonly string[] CraftingPriceStrings = { @@ -2795,17 +2790,17 @@ private static int GetEnchantmentCost(string enchantmentId, BlueprintItem bluepr private static int GetSpecialMaterialCost(PhysicalDamageMaterial material, BlueprintItemWeapon weapon, int baseCost, float weight) { switch (material) { case PhysicalDamageMaterial.Adamantite: - return 3000 - WeaponMasterworkCost; // Cost of masterwork is subsumed by the cost of adamantite + return DefaultCosts.Adamantine - DefaultCosts.WeaponMasterworkCost; // Cost of masterwork is subsumed by the cost of adamantite case PhysicalDamageMaterial.ColdIron: var enhancementLevel = ItemPlusEquivalent(weapon); // Cold Iron weapons cost double, excluding the masterwork component and 2000 extra for enchanting the first +1 // double weapon - return baseCost + (enhancementLevel > 0 ? WeaponPlusCost : 0); + return baseCost + (enhancementLevel > 0 ? DefaultCosts.WeaponPlusCost : 0); case PhysicalDamageMaterial.Silver: // PhysicalDamageMaterial.Silver is really Mithral. Non-armor Mithral items cost 500 gp per pound of the original, non-Mithral item, which // translates to 1000 gp per pound of Mithral. See https://paizo.com/paizo/faq/v5748nruor1fm#v5748eaic9r9u // Only charge for weight on the primary half - return (int)(500 * weight) - WeaponMasterworkCost; // Cost of masterwork is subsumed by the cost of mithral + return (int)(500 * weight) - DefaultCosts.WeaponMasterworkCost; // Cost of masterwork is subsumed by the cost of mithral default: return 0; } @@ -2876,18 +2871,18 @@ public static int RulesRecipeItemCost(BlueprintItem blueprint, int baseCost = -1 var enhancementLevel = ItemPlusEquivalent(blueprint); if (blueprint is BlueprintItemWeapon weapon) { if (enhancementLevel > 0) { - cost += WeaponMasterworkCost; + cost += DefaultCosts.WeaponMasterworkCost; } if (weapon.DamageType.Physical.Material != 0) { cost += GetSpecialMaterialCost(weapon.DamageType.Physical.Material, weapon, baseCost, weight); } } else if (blueprint is BlueprintItemArmor) { if (enhancementLevel > 0 && !mithralArmorEnchantmentGuid) { - cost += ArmorMasterworkCost; + cost += DefaultCosts.ArmorMasterworkCost; } } - var factor = blueprint is BlueprintItemWeapon ? WeaponPlusCost : ArmorPlusCost; + var factor = blueprint is BlueprintItemWeapon ? DefaultCosts.WeaponPlusCost : DefaultCosts.ArmorPlusCost; cost += enhancementLevel * enhancementLevel * factor; if (blueprint is BlueprintItemWeapon doubleWeapon && doubleWeapon.Double) { return baseCost + cost + RulesRecipeItemCost(doubleWeapon.SecondWeapon, 0, 0.0f); @@ -2897,7 +2892,7 @@ public static int RulesRecipeItemCost(BlueprintItem blueprint, int baseCost = -1 if (ItemPlusEquivalent(blueprint) > 0) { var enhancementLevel = ItemPlusEquivalent(blueprint); - var enhancementCost = enhancementLevel * enhancementLevel * UnarmedPlusCost; + var enhancementCost = enhancementLevel * enhancementLevel * DefaultCosts.UnarmedPlusCost; cost += enhancementCost; if (mostExpensiveEnchantmentCost < enhancementCost) { mostExpensiveEnchantmentCost = enhancementCost; From d5fe71eccbf16f545ab0efdf61b8862fd5f85c1d Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Thu, 16 Jul 2020 13:52:26 -0500 Subject: [PATCH 056/132] Adding in a comment about the cost for special materials --- CraftMagicItems/Main.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index f3cca55..a3d33f5 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -2800,6 +2800,15 @@ private static int GetSpecialMaterialCost(PhysicalDamageMaterial material, Bluep // PhysicalDamageMaterial.Silver is really Mithral. Non-armor Mithral items cost 500 gp per pound of the original, non-Mithral item, which // translates to 1000 gp per pound of Mithral. See https://paizo.com/paizo/faq/v5748nruor1fm#v5748eaic9r9u // Only charge for weight on the primary half + + // TODO: this is wrong and only applies to weapons and wondrous items. + // see: https://www.d20pfsrd.com/equipment/special-materials/#Mithral + // Type of Item Item Cost Modifier + // Light armor +1,000 gp + // Medium armor +4,000 gp + // Heavy armor +9,000 gp + // Shield +1,000 gp + // Other items +500 gp/lb. return (int)(500 * weight) - DefaultCosts.WeaponMasterworkCost; // Cost of masterwork is subsumed by the cost of mithral default: return 0; From eee6e4f6cb8401d8e27175afac241db9121d4750 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Thu, 16 Jul 2020 13:58:40 -0500 Subject: [PATCH 057/132] Adding mithral price per pound constant --- CraftMagicItems/Constants/DefaultCosts.cs | 3 +++ CraftMagicItems/Main.cs | 5 +++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CraftMagicItems/Constants/DefaultCosts.cs b/CraftMagicItems/Constants/DefaultCosts.cs index 803b5ae..0373cca 100644 --- a/CraftMagicItems/Constants/DefaultCosts.cs +++ b/CraftMagicItems/Constants/DefaultCosts.cs @@ -12,6 +12,9 @@ public static class DefaultCosts /// Cost of applying adamantine public const int Adamantine = 3000; + /// Cost of applying mithral per pound + public const int MithralPerPound = 500; + /// Default cost for adding a +N enchantment to a weapon public const int WeaponPlusCost = 2000; diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index a3d33f5..ad8a5a6 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -2797,7 +2797,8 @@ private static int GetSpecialMaterialCost(PhysicalDamageMaterial material, Bluep // double weapon return baseCost + (enhancementLevel > 0 ? DefaultCosts.WeaponPlusCost : 0); case PhysicalDamageMaterial.Silver: - // PhysicalDamageMaterial.Silver is really Mithral. Non-armor Mithral items cost 500 gp per pound of the original, non-Mithral item, which + // PhysicalDamageMaterial.Silver is really Mithral. + // Non-armor Mithral items cost 500 gp per pound of the original, non-Mithral item, which // translates to 1000 gp per pound of Mithral. See https://paizo.com/paizo/faq/v5748nruor1fm#v5748eaic9r9u // Only charge for weight on the primary half @@ -2809,7 +2810,7 @@ private static int GetSpecialMaterialCost(PhysicalDamageMaterial material, Bluep // Heavy armor +9,000 gp // Shield +1,000 gp // Other items +500 gp/lb. - return (int)(500 * weight) - DefaultCosts.WeaponMasterworkCost; // Cost of masterwork is subsumed by the cost of mithral + return (int)(DefaultCosts.MithralPerPound * weight) - DefaultCosts.WeaponMasterworkCost; // Cost of masterwork is subsumed by the cost of mithral default: return 0; } From 573fc355336e5f9993742bac75164dc34d0a6ae9 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Thu, 16 Jul 2020 14:37:56 -0500 Subject: [PATCH 058/132] Extracting the localized string constants into their own class (and also remark exactly WHAT the string is supposed to be) --- CraftMagicItems/Constants/DifficultyClass.cs | 15 +++++ .../Constants/LocalizedStringBlueprints.cs | 20 +++++++ CraftMagicItems/CraftMagicItems.csproj | 2 + CraftMagicItems/Main.cs | 55 ++++++++----------- 4 files changed, 61 insertions(+), 31 deletions(-) create mode 100644 CraftMagicItems/Constants/DifficultyClass.cs create mode 100644 CraftMagicItems/Constants/LocalizedStringBlueprints.cs diff --git a/CraftMagicItems/Constants/DifficultyClass.cs b/CraftMagicItems/Constants/DifficultyClass.cs new file mode 100644 index 0000000..30b2c85 --- /dev/null +++ b/CraftMagicItems/Constants/DifficultyClass.cs @@ -0,0 +1,15 @@ +namespace CraftMagicItems.Constants +{ + /// Constants class containing modifiers and penalties to crafting DC + public static class DifficultyClass + { + /// Additional DC modifier for missing prerequisites to crafting + public const int MissingPrerequisiteDCModifier = 5; + + /// Additional DC modifier for a required spell from a Wizard's opposition school of magic + public const int OppositionSchoolDCModifier = 4; + + /// Penalty to progress that can be made while adventuring + public const int AdventuringProgressPenalty = 4; + } +} \ No newline at end of file diff --git a/CraftMagicItems/Constants/LocalizedStringBlueprints.cs b/CraftMagicItems/Constants/LocalizedStringBlueprints.cs new file mode 100644 index 0000000..d42e63d --- /dev/null +++ b/CraftMagicItems/Constants/LocalizedStringBlueprints.cs @@ -0,0 +1,20 @@ +using Kingmaker.Localization; + +namespace CraftMagicItems.Constants +{ + /// Constants class for references to localized strings + public static class LocalizedStringBlueprints + { + /// Localized "Caster Level" reference + public static readonly LocalizedString CasterLevelLocalized = new L10NString("dfb34498-61df-49b1-af18-0a84ce47fc98"); + + /// Localized "<b></b> uses item: <b></b>." reference + public static readonly LocalizedString CharacterUsedItemLocalized = new L10NString("be7942ed-3af1-4fc7-b20b-41966d2f80b7"); + + /// Localized "Shield Bash" reference + public static readonly LocalizedString ShieldBashLocalized = new L10NString("314ff56d-e93b-4915-8ca4-24a7670ad436"); + + /// Localized "Qualities" reference + public static readonly LocalizedString QualitiesLocalized = new L10NString("0f84fde9-14ca-4e2f-9c82-b2522039dbff"); + } +} \ No newline at end of file diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index c2f1e5e..e85c3c4 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -96,9 +96,11 @@ + + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index ad8a5a6..a046ff8 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -76,9 +76,6 @@ namespace CraftMagicItems { public static class Main { - private const int MissingPrerequisiteDCModifier = 5; - private const int OppositionSchoolDCModifier = 4; - private const int AdventuringProgressPenalty = 4; private const string BondedItemRitual = "bondedItemRitual"; private static readonly string[] CraftingPriceStrings = { @@ -88,10 +85,6 @@ public static class Main { }; private const string CustomPriceLabel = "Crafting Cost: "; - private static readonly LocalizedString CasterLevelLocalized = new L10NString("dfb34498-61df-49b1-af18-0a84ce47fc98"); - private static readonly LocalizedString CharacterUsedItemLocalized = new L10NString("be7942ed-3af1-4fc7-b20b-41966d2f80b7"); - private static readonly LocalizedString ShieldBashLocalized = new L10NString("314ff56d-e93b-4915-8ca4-24a7670ad436"); - private static readonly LocalizedString QualitiesLocalized = new L10NString("0f84fde9-14ca-4e2f-9c82-b2522039dbff"); private static readonly WeaponCategory[] AmmunitionWeaponCategories = { WeaponCategory.Longbow, @@ -1354,7 +1347,7 @@ private static void RenderRecipeBasedCrafting(UnitEntityData caster, RecipeBased GUILayout.BeginHorizontal(); GUILayout.Label("Prerequisites: ", GUILayout.ExpandWidth(false)); - var prerequisites = $"{CasterLevelLocalized} {casterLevel}"; + var prerequisites = $"{LocalizedStringBlueprints.CasterLevelLocalized} {casterLevel}"; if (selectedRecipe.PrerequisiteSpells != null && selectedRecipe.PrerequisiteSpells.Length > 0) { prerequisites += $"; {selectedRecipe.PrerequisiteSpells.Select(ability => ability.Name).BuildCommaList(selectedRecipe.AnyPrerequisite)}"; } @@ -1543,13 +1536,13 @@ private static string BuildItemDescription(ItemEntity item) { qualities = GetEnchantmentNames(shield.ArmorComponent); var weaponQualities = shield.WeaponComponent == null ? null : GetEnchantmentNames(shield.WeaponComponent); if (!string.IsNullOrEmpty(weaponQualities)) { - qualities = $"{qualities}{(string.IsNullOrEmpty(qualities) ? "" : ", ")}{ShieldBashLocalized}: {weaponQualities}"; + qualities = $"{qualities}{(string.IsNullOrEmpty(qualities) ? "" : ", ")}{LocalizedStringBlueprints.ShieldBashLocalized}: {weaponQualities}"; } } else { qualities = GetEnchantmentNames(item.Blueprint); } if (!string.IsNullOrEmpty(qualities)) { - description += $"{(string.IsNullOrEmpty(description) ? "" : "\n")}{QualitiesLocalized}: {qualities}"; + description += $"{(string.IsNullOrEmpty(description) ? "" : "\n")}{LocalizedStringBlueprints.QualitiesLocalized}: {qualities}"; } } return description; @@ -1744,20 +1737,20 @@ private static int RenderCraftingSkillInformation(UnitEntityData crafter, StatTy } if (missing > 0 && render) { UmmUiRenderer.RenderLabelRow( - $"{crafter.CharacterName} is unable to meet {missing} of the prerequisites, raising the DC by {MissingPrerequisiteDCModifier * missing}"); + $"{crafter.CharacterName} is unable to meet {missing} of the prerequisites, raising the DC by {DifficultyClass.MissingPrerequisiteDCModifier * missing}"); } if (casterLevelShortfall > 0 && render) { - UmmUiRenderer.RenderLabelRow(L10NFormat("craftMagicItems-logMessage-low-caster-level", casterLevel, MissingPrerequisiteDCModifier * casterLevelShortfall)); + UmmUiRenderer.RenderLabelRow(L10NFormat("craftMagicItems-logMessage-low-caster-level", casterLevel, DifficultyClass.MissingPrerequisiteDCModifier * casterLevelShortfall)); } // Rob's ruling... if you're below the prerequisite caster level, you're considered to be missing a prerequisite for each // level you fall short. - dc += MissingPrerequisiteDCModifier * (missing + casterLevelShortfall); + dc += DifficultyClass.MissingPrerequisiteDCModifier * (missing + casterLevelShortfall); var oppositionSchool = CheckForOppositionSchool(crafter.Descriptor, prerequisiteSpells); if (oppositionSchool != SpellSchool.None) { - dc += OppositionSchoolDCModifier; + dc += DifficultyClass.OppositionSchoolDCModifier; if (render) { UmmUiRenderer.RenderLabelRow(L10NFormat("craftMagicItems-logMessage-opposition-school", LocalizedTexts.Instance.SpellSchoolNames.GetText(oppositionSchool), - OppositionSchoolDCModifier)); + DifficultyClass.OppositionSchoolDCModifier)); } } var skillCheck = 10 + crafter.Stats.GetStat(skill).ModifiedValue; @@ -2393,7 +2386,7 @@ private static void CalculateProjectEstimate(CraftingProjectData project) { if (ModSettings.CraftAtFullSpeedWhileAdventuring) { project.AddMessage(new L10NString("craftMagicItems-time-estimate-single-rate")); } else { - var progressPerDayAdventuring = (int) (progressRate * (1 + (float) skillMargin / 5) / AdventuringProgressPenalty); + var progressPerDayAdventuring = (int) (progressRate * (1 + (float) skillMargin / 5) / DifficultyClass.AdventuringProgressPenalty); var adventuringDayCount = (project.TargetCost + progressPerDayAdventuring - 1) / progressPerDayAdventuring; project.AddMessage(adventuringDayCount == 1 ? new L10NString("craftMagicItems-time-estimate-one-day") @@ -3566,7 +3559,7 @@ private static int CheckCrafterPrerequisites(CraftingProjectData project, UnitDe var missing = GetMissingCrafterPrerequisites(project.CrafterPrerequisites, caster); foreach (var prerequisite in missing) { AddBattleLogMessage(L10NFormat("craftMagicItems-logMessage-missing-crafter-prerequisite", - new L10NString($"craftMagicItems-crafter-prerequisite-{prerequisite}"), MissingPrerequisiteDCModifier)); + new L10NString($"craftMagicItems-crafter-prerequisite-{prerequisite}"), DifficultyClass.MissingPrerequisiteDCModifier)); } return missing.Count; @@ -3686,32 +3679,32 @@ private static void WorkOnProjects(UnitDescriptor caster, bool returningToCapita } AddBattleLogMessage(L10NFormat("craftMagicItems-logMessage-missing-spell", missingSpellNames, - MissingPrerequisiteDCModifier * missing)); + DifficultyClass.MissingPrerequisiteDCModifier * missing)); } var missing2 = CheckFeatPrerequisites(project, caster, out var missingFeats); if (missing2 > 0) { var missingFeatNames = missingFeats.Select(ability => ability.Name).BuildCommaList(project.AnyPrerequisite); AddBattleLogMessage(L10NFormat("craftMagicItems-logMessage-missing-feat", missingFeatNames, - MissingPrerequisiteDCModifier * missing2)); + DifficultyClass.MissingPrerequisiteDCModifier * missing2)); } missing += missing2; missing += CheckCrafterPrerequisites(project, caster); - dc += MissingPrerequisiteDCModifier * missing; + dc += DifficultyClass.MissingPrerequisiteDCModifier * missing; var casterLevel = CharacterCasterLevel(caster); if (casterLevel < project.CasterLevel) { // Rob's ruling... if you're below the prerequisite caster level, you're considered to be missing a prerequisite for each // level you fall short, unless ModSettings.CasterLevelIsSinglePrerequisite is true. var casterLevelPenalty = ModSettings.CasterLevelIsSinglePrerequisite - ? MissingPrerequisiteDCModifier - : MissingPrerequisiteDCModifier * (project.CasterLevel - casterLevel); + ? DifficultyClass.MissingPrerequisiteDCModifier + : DifficultyClass.MissingPrerequisiteDCModifier * (project.CasterLevel - casterLevel); dc += casterLevelPenalty; AddBattleLogMessage(L10NFormat("craftMagicItems-logMessage-low-caster-level", project.CasterLevel, casterLevelPenalty)); } var oppositionSchool = CheckForOppositionSchool(caster, project.SpellPrerequisites); if (oppositionSchool != SpellSchool.None) { - dc += OppositionSchoolDCModifier; + dc += DifficultyClass.OppositionSchoolDCModifier; AddBattleLogMessage(L10NFormat("craftMagicItems-logMessage-opposition-school", - LocalizedTexts.Instance.SpellSchoolNames.GetText(oppositionSchool), OppositionSchoolDCModifier)); + LocalizedTexts.Instance.SpellSchoolNames.GetText(oppositionSchool), DifficultyClass.OppositionSchoolDCModifier)); } var skillCheck = 10 + caster.Stats.GetStat(craftingSkill).ModifiedValue; @@ -3725,7 +3718,7 @@ private static void WorkOnProjects(UnitDescriptor caster, bool returningToCapita // Cleared the last hurdle, so caster is going to make progress on this project. // You only work at 1/4 speed if you're crafting while adventuring. - var adventuringPenalty = !isAdventuring || ModSettings.CraftAtFullSpeedWhileAdventuring ? 1 : AdventuringProgressPenalty; + var adventuringPenalty = !isAdventuring || ModSettings.CraftAtFullSpeedWhileAdventuring ? 1 : DifficultyClass.AdventuringProgressPenalty; // Each 1 by which the skill check exceeds the DC increases the crafting rate by 20% of the base progressRate var progressPerDay = (int) (progressRate * (1 + (float) (skillCheck - dc) / 5) / adventuringPenalty); var daysUntilProjectFinished = (int) Math.Ceiling(1.0 * (project.TargetCost - project.Progress) / progressPerDay); @@ -3745,14 +3738,14 @@ private static void WorkOnProjects(UnitDescriptor caster, bool returningToCapita if (itemSpell == null) { // We've run out of items that can cast the spell...crafting progress is going to slow, if not stop. progressGold -= progressPerDay * (daysCrafting - day); - skillCheck -= MissingPrerequisiteDCModifier; + skillCheck -= DifficultyClass.MissingPrerequisiteDCModifier; if (craftingData.PrerequisitesMandatory || project.PrerequisitesMandatory) { AddBattleLogMessage(L10NFormat("craftMagicItems-logMessage-missing-prerequisite", project.ResultItem.Name, spell.Name)); daysCrafting = day; break; } - AddBattleLogMessage(L10NFormat("craftMagicItems-logMessage-missing-spell", spell.Name, MissingPrerequisiteDCModifier)); + AddBattleLogMessage(L10NFormat("craftMagicItems-logMessage-missing-spell", spell.Name, DifficultyClass.MissingPrerequisiteDCModifier)); if (skillCheck < dc) { // Can no longer make progress AddBattleLogMessage(L10NFormat("craftMagicItems-logMessage-dc-too-high", project.ResultItem.Name, @@ -3772,7 +3765,7 @@ private static void WorkOnProjects(UnitDescriptor caster, bool returningToCapita GameLogContext.SourceUnit = caster.Unit; GameLogContext.Text = itemSpell.SourceItem.Name; - AddBattleLogMessage(CharacterUsedItemLocalized); + AddBattleLogMessage(LocalizedStringBlueprints.CharacterUsedItemLocalized); GameLogContext.Clear(); itemSpell.SourceItem.SpendCharges(caster); } @@ -4143,16 +4136,16 @@ private static void Postfix(ItemEntity item, TooltipData data, ref string __resu TooltipData tmp = new TooltipData(); string result = Accessors.CallUIUtilityItemFillEnchantmentDescription(shield.WeaponComponent, tmp); if (!string.IsNullOrEmpty(result)) { - __result += $"{ShieldBashLocalized}\n"; + __result += $"{LocalizedStringBlueprints.ShieldBashLocalized}\n"; __result += result; } data.Texts[TooltipElement.AttackType] = tmp.Texts[TooltipElement.AttackType]; data.Texts[TooltipElement.ProficiencyGroup] = tmp.Texts[TooltipElement.ProficiencyGroup]; if (tmp.Texts.ContainsKey(TooltipElement.Qualities) && !string.IsNullOrEmpty(tmp.Texts[TooltipElement.Qualities])) { if (data.Texts.ContainsKey(TooltipElement.Qualities)) { - data.Texts[TooltipElement.Qualities] += $", {ShieldBashLocalized}: {tmp.Texts[TooltipElement.Qualities]}"; + data.Texts[TooltipElement.Qualities] += $", {LocalizedStringBlueprints.ShieldBashLocalized}: {tmp.Texts[TooltipElement.Qualities]}"; } else { - data.Texts[TooltipElement.Qualities] = $"{ShieldBashLocalized}: {tmp.Texts[TooltipElement.Qualities]}"; + data.Texts[TooltipElement.Qualities] = $"{LocalizedStringBlueprints.ShieldBashLocalized}: {tmp.Texts[TooltipElement.Qualities]}"; } } data.Texts[TooltipElement.Damage] = tmp.Texts[TooltipElement.Damage]; From 17a1d372ea53f464a1c797276694db73e051f76a Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Thu, 16 Jul 2020 15:35:41 -0500 Subject: [PATCH 059/132] Moving IkPatches (?) into their own constants, namespace, and file --- .../Constants/VisualAdjustmentPatches.cs | 22 +++++++++++++++ CraftMagicItems/CraftMagicItems.csproj | 2 ++ CraftMagicItems/Main.cs | 27 +++---------------- CraftMagicItems/Patches/IkPatch.cs | 23 ++++++++++++++++ 4 files changed, 51 insertions(+), 23 deletions(-) create mode 100644 CraftMagicItems/Constants/VisualAdjustmentPatches.cs create mode 100644 CraftMagicItems/Patches/IkPatch.cs diff --git a/CraftMagicItems/Constants/VisualAdjustmentPatches.cs b/CraftMagicItems/Constants/VisualAdjustmentPatches.cs new file mode 100644 index 0000000..86635ad --- /dev/null +++ b/CraftMagicItems/Constants/VisualAdjustmentPatches.cs @@ -0,0 +1,22 @@ +using CraftMagicItems.Patches; + +namespace CraftMagicItems.Constants +{ + /// Constants class containing definitions + public static class VisualAdjustmentPatches + { + /// Collection of patches for visual adjustments to displayed items + public static readonly IkPatch[] IkPatchList = + { + new IkPatch("a6f7e3dc443ff114ba68b4648fd33e9f", 0.00f, -0.10f, 0.01f), // dueling sword + new IkPatch("13fa38737d46c9e4abc7f4d74aaa59c3", 0.00f, -0.36f, 0.00f), // tongi + new IkPatch("1af5621e2ae551e42bd1dd6744d98639", 0.00f, -0.07f, 0.00f), // falcata + new IkPatch("d516765b3c2904e4a939749526a52a9a", 0.00f, -0.15f, 0.00f), // estoc + new IkPatch("2ece38f30500f454b8569136221e55b0", 0.00f, -0.08f, 0.00f), // rapier + new IkPatch("a492410f3d65f744c892faf09daad84a", 0.00f, -0.20f, 0.00f), // heavy pick + new IkPatch("6ff66364e0a2c89469c2e52ebb46365e", 0.00f, -0.10f, 0.00f), // trident + new IkPatch("d5a167f0f0208dd439ec7481e8989e21", 0.00f, -0.08f, 0.00f), // heavy mace + new IkPatch("8fefb7e0da38b06408f185e29372c703", -0.14f, 0.00f, 0.00f), // heavy flail + }; + } +} \ No newline at end of file diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index e85c3c4..84dffc8 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -103,6 +103,8 @@ + + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index a046ff8..34491ef 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -7,6 +7,7 @@ using System.Runtime.Serialization; using System.Text.RegularExpressions; using CraftMagicItems.Constants; +using CraftMagicItems.Patches; using CraftMagicItems.UI; using CraftMagicItems.UI.Sections; using CraftMagicItems.UI.UnityModManager; @@ -110,15 +111,6 @@ private enum ItemLocationFilter Stash } - struct IkPatch { - public IkPatch(string uuid, float x, float y, float z) { - m_uuid = uuid; - m_x = x; m_y = y; m_z = z; - } - public string m_uuid; - public float m_x, m_y, m_z; - } - struct MethodPatch { public MethodPatch(MethodBase original, HarmonyLib.HarmonyMethod prefix = null, HarmonyLib.HarmonyMethod postfix = null) { m_original = original; @@ -158,18 +150,6 @@ public bool MatchPostfix(MethodBase method) { postfix: new HarmonyLib.HarmonyMethod(typeof(PlayerPostLoadPatch).GetMethod("Postfix", BindingFlags.NonPublic | BindingFlags.Static))), }; - private static readonly IkPatch[] IkPatchList = { - new IkPatch("a6f7e3dc443ff114ba68b4648fd33e9f", 0.00f, -0.10f, 0.01f), // dueling sword - new IkPatch("13fa38737d46c9e4abc7f4d74aaa59c3", 0.00f, -0.36f, 0.00f), // tongi - new IkPatch("1af5621e2ae551e42bd1dd6744d98639", 0.00f, -0.07f, 0.00f), // falcata - new IkPatch("d516765b3c2904e4a939749526a52a9a", 0.00f, -0.15f, 0.00f), // estoc - new IkPatch("2ece38f30500f454b8569136221e55b0", 0.00f, -0.08f, 0.00f), // rapier - new IkPatch("a492410f3d65f744c892faf09daad84a", 0.00f, -0.20f, 0.00f), // heavy pick - new IkPatch("6ff66364e0a2c89469c2e52ebb46365e", 0.00f, -0.10f, 0.00f), // trident - new IkPatch("d5a167f0f0208dd439ec7481e8989e21", 0.00f, -0.08f, 0.00f), // heavy mace - new IkPatch("8fefb7e0da38b06408f185e29372c703", -0.14f, 0.00f, 0.00f), // heavy flail - }; - public static UnityModManager.ModEntry ModEntry; public static Settings ModSettings; public static CraftMagicItemsAccessors Accessors; @@ -3254,10 +3234,11 @@ private static void PatchBlueprints() { } private static void PatchIk() { - foreach (var patch in IkPatchList) { + foreach (var patch in VisualAdjustmentPatches.IkPatchList) { var weapon = ResourcesLibrary.TryGetBlueprint(patch.m_uuid); if (weapon != null) { - var model = weapon.VisualParameters.Model; var equipmentOffsets = model.GetComponent(); + var model = weapon.VisualParameters.Model; + var equipmentOffsets = model.GetComponent(); var locator = new GameObject(); locator.transform.SetParent(model.transform); locator.transform.localPosition = new Vector3(patch.m_x, patch.m_y, patch.m_z); diff --git a/CraftMagicItems/Patches/IkPatch.cs b/CraftMagicItems/Patches/IkPatch.cs new file mode 100644 index 0000000..46998d3 --- /dev/null +++ b/CraftMagicItems/Patches/IkPatch.cs @@ -0,0 +1,23 @@ +namespace CraftMagicItems.Patches +{ + /// Structure defining visual X/Y/Z-axis adjustments to weapons to patch the existing game + public struct IkPatch + { + /// Constructor + /// Unique ID of the blueprint to patch + /// X-axis adjustment + /// Y-axis adjustment + /// Z-axis adjustment + public IkPatch(string uuid, float x, float y, float z) + { + m_uuid = uuid; + m_x = x; + m_y = y; + m_z = z; + } + + public string m_uuid; + + public float m_x, m_y, m_z; + } +} From 83a5322c8b87de29d21acf296c687dc794d0886b Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Thu, 16 Jul 2020 15:38:41 -0500 Subject: [PATCH 060/132] Renaming _public_ fields in IkPatch --- CraftMagicItems/Main.cs | 4 ++-- CraftMagicItems/Patches/IkPatch.cs | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 34491ef..80ba1d9 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -3235,13 +3235,13 @@ private static void PatchBlueprints() { private static void PatchIk() { foreach (var patch in VisualAdjustmentPatches.IkPatchList) { - var weapon = ResourcesLibrary.TryGetBlueprint(patch.m_uuid); + var weapon = ResourcesLibrary.TryGetBlueprint(patch.BlueprintId); if (weapon != null) { var model = weapon.VisualParameters.Model; var equipmentOffsets = model.GetComponent(); var locator = new GameObject(); locator.transform.SetParent(model.transform); - locator.transform.localPosition = new Vector3(patch.m_x, patch.m_y, patch.m_z); + locator.transform.localPosition = new Vector3(patch.X, patch.Y, patch.Z); locator.transform.localEulerAngles = new Vector3(0.0f, 0.0f, 0.0f); locator.transform.localScale = new Vector3(1.0f, 1.0f, 1.0f); equipmentOffsets.IkTargetLeftHand = locator.transform; diff --git a/CraftMagicItems/Patches/IkPatch.cs b/CraftMagicItems/Patches/IkPatch.cs index 46998d3..dc4deea 100644 --- a/CraftMagicItems/Patches/IkPatch.cs +++ b/CraftMagicItems/Patches/IkPatch.cs @@ -10,14 +10,14 @@ public struct IkPatch /// Z-axis adjustment public IkPatch(string uuid, float x, float y, float z) { - m_uuid = uuid; - m_x = x; - m_y = y; - m_z = z; + BlueprintId = uuid; + X = x; + Y = y; + Z = z; } - public string m_uuid; + public string BlueprintId; - public float m_x, m_y, m_z; + public float X, Y, Z; } } From 8a21b33448e99c282c317e4ab23adc6090112c11 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Thu, 16 Jul 2020 16:18:46 -0500 Subject: [PATCH 061/132] Adding in a few unit tests to ensure that appropriate blueprints are exposed --- .../Constants/VisualAdjustmentPatchesTests.cs | 72 +++++++++++++++++++ .../CraftMagicItemsTests.csproj | 1 + 2 files changed, 73 insertions(+) create mode 100644 CraftMagicItemsTests/Constants/VisualAdjustmentPatchesTests.cs diff --git a/CraftMagicItemsTests/Constants/VisualAdjustmentPatchesTests.cs b/CraftMagicItemsTests/Constants/VisualAdjustmentPatchesTests.cs new file mode 100644 index 0000000..faa8d1b --- /dev/null +++ b/CraftMagicItemsTests/Constants/VisualAdjustmentPatchesTests.cs @@ -0,0 +1,72 @@ +using System.Collections.Generic; +using System.Linq; +using CraftMagicItems.Constants; +using CraftMagicItems.Patches; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace CraftMagicItemsTests.Constants +{ + [TestClass] + public class VisualAdjustmentPatchesTests + { + [TestMethod] + public void IkPatchList_Exposes_DuelingSword() + { + AssertBlueprintInIkPatchCollection("a6f7e3dc443ff114ba68b4648fd33e9f", VisualAdjustmentPatches.IkPatchList); + } + + [TestMethod] + public void IkPatchList_Exposes_Tongi() + { + AssertBlueprintInIkPatchCollection("13fa38737d46c9e4abc7f4d74aaa59c3", VisualAdjustmentPatches.IkPatchList); + } + + [TestMethod] + public void IkPatchList_Exposes_Falcata() + { + AssertBlueprintInIkPatchCollection("1af5621e2ae551e42bd1dd6744d98639", VisualAdjustmentPatches.IkPatchList); + } + + [TestMethod] + public void IkPatchList_Exposes_Estoc() + { + AssertBlueprintInIkPatchCollection("d516765b3c2904e4a939749526a52a9a", VisualAdjustmentPatches.IkPatchList); + } + + [TestMethod] + public void IkPatchList_Exposes_Rapier() + { + AssertBlueprintInIkPatchCollection("2ece38f30500f454b8569136221e55b0", VisualAdjustmentPatches.IkPatchList); + } + + [TestMethod] + public void IkPatchList_Exposes_HeavyPick() + { + AssertBlueprintInIkPatchCollection("a492410f3d65f744c892faf09daad84a", VisualAdjustmentPatches.IkPatchList); + } + + [TestMethod] + public void IkPatchList_Exposes_Trident() + { + AssertBlueprintInIkPatchCollection("6ff66364e0a2c89469c2e52ebb46365e", VisualAdjustmentPatches.IkPatchList); + } + + [TestMethod] + public void IkPatchList_Exposes_HeavyMace() + { + AssertBlueprintInIkPatchCollection("d5a167f0f0208dd439ec7481e8989e21", VisualAdjustmentPatches.IkPatchList); + } + + [TestMethod] + public void IkPatchList_Exposes_HeavyFlail() + { + AssertBlueprintInIkPatchCollection("8fefb7e0da38b06408f185e29372c703", VisualAdjustmentPatches.IkPatchList); + } + + private void AssertBlueprintInIkPatchCollection(string uuid, IEnumerable collection) + { + IkPatch instance = collection.Single(patch => patch.BlueprintId == uuid); + Assert.AreNotEqual(default, instance); + } + } +} \ No newline at end of file diff --git a/CraftMagicItemsTests/CraftMagicItemsTests.csproj b/CraftMagicItemsTests/CraftMagicItemsTests.csproj index 2b95793..86e91eb 100644 --- a/CraftMagicItemsTests/CraftMagicItemsTests.csproj +++ b/CraftMagicItemsTests/CraftMagicItemsTests.csproj @@ -81,6 +81,7 @@ + From 58ede238929cbc5d12f1c0a0b24315847db4562c Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Thu, 16 Jul 2020 15:50:34 -0500 Subject: [PATCH 062/132] Refactoring the returned IkPatch entities into something a bit better isolated/tracked and not just identified by comments in code. --- .../Constants/VisualAdjustmentPatches.cs | 58 +++++++++++++++---- CraftMagicItems/Main.cs | 1 - 2 files changed, 46 insertions(+), 13 deletions(-) diff --git a/CraftMagicItems/Constants/VisualAdjustmentPatches.cs b/CraftMagicItems/Constants/VisualAdjustmentPatches.cs index 86635ad..c3718fd 100644 --- a/CraftMagicItems/Constants/VisualAdjustmentPatches.cs +++ b/CraftMagicItems/Constants/VisualAdjustmentPatches.cs @@ -1,4 +1,5 @@ -using CraftMagicItems.Patches; +using System.Collections.Generic; +using CraftMagicItems.Patches; namespace CraftMagicItems.Constants { @@ -6,17 +7,50 @@ namespace CraftMagicItems.Constants public static class VisualAdjustmentPatches { /// Collection of patches for visual adjustments to displayed items - public static readonly IkPatch[] IkPatchList = + public static IEnumerable IkPatchList { - new IkPatch("a6f7e3dc443ff114ba68b4648fd33e9f", 0.00f, -0.10f, 0.01f), // dueling sword - new IkPatch("13fa38737d46c9e4abc7f4d74aaa59c3", 0.00f, -0.36f, 0.00f), // tongi - new IkPatch("1af5621e2ae551e42bd1dd6744d98639", 0.00f, -0.07f, 0.00f), // falcata - new IkPatch("d516765b3c2904e4a939749526a52a9a", 0.00f, -0.15f, 0.00f), // estoc - new IkPatch("2ece38f30500f454b8569136221e55b0", 0.00f, -0.08f, 0.00f), // rapier - new IkPatch("a492410f3d65f744c892faf09daad84a", 0.00f, -0.20f, 0.00f), // heavy pick - new IkPatch("6ff66364e0a2c89469c2e52ebb46365e", 0.00f, -0.10f, 0.00f), // trident - new IkPatch("d5a167f0f0208dd439ec7481e8989e21", 0.00f, -0.08f, 0.00f), // heavy mace - new IkPatch("8fefb7e0da38b06408f185e29372c703", -0.14f, 0.00f, 0.00f), // heavy flail - }; + get + { + return new[] + { + DuelingSword, + Tongi, + Falcata, + Estoc, + Rapier, + HeavyPick, + Trident, + HeavyMace, + HeavyFlail, + }; + } + } + + /// Dueling sword patch + private static readonly IkPatch DuelingSword = new IkPatch("a6f7e3dc443ff114ba68b4648fd33e9f", 0.00f, -0.10f, 0.01f); + + /// Tongi patch + private static readonly IkPatch Tongi = new IkPatch("13fa38737d46c9e4abc7f4d74aaa59c3", 0.00f, -0.36f, 0.00f); + + /// Falcata patch + private static readonly IkPatch Falcata = new IkPatch("1af5621e2ae551e42bd1dd6744d98639", 0.00f, -0.07f, 0.00f); + + /// Estoc patch + private static readonly IkPatch Estoc = new IkPatch("d516765b3c2904e4a939749526a52a9a", 0.00f, -0.15f, 0.00f); + + /// Rapier patch + private static readonly IkPatch Rapier = new IkPatch("2ece38f30500f454b8569136221e55b0", 0.00f, -0.08f, 0.00f); + + /// Heavy pick patch + private static readonly IkPatch HeavyPick = new IkPatch("a492410f3d65f744c892faf09daad84a", 0.00f, -0.20f, 0.00f); + + /// Trident patch + private static readonly IkPatch Trident = new IkPatch("6ff66364e0a2c89469c2e52ebb46365e", 0.00f, -0.10f, 0.00f); + + /// Heavy mace patch + private static readonly IkPatch HeavyMace = new IkPatch("d5a167f0f0208dd439ec7481e8989e21", 0.00f, -0.08f, 0.00f); + + /// Heavy flail patch + private static readonly IkPatch HeavyFlail = new IkPatch("8fefb7e0da38b06408f185e29372c703", -0.14f, 0.00f, 0.00f); } } \ No newline at end of file diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 80ba1d9..a10f640 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -7,7 +7,6 @@ using System.Runtime.Serialization; using System.Text.RegularExpressions; using CraftMagicItems.Constants; -using CraftMagicItems.Patches; using CraftMagicItems.UI; using CraftMagicItems.UI.Sections; using CraftMagicItems.UI.UnityModManager; From fd4f9dd5b7653ca1c985bda666395ffd4ed64b29 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Thu, 16 Jul 2020 16:36:53 -0500 Subject: [PATCH 063/132] Moving `PatchIk` out into its own class and be a bit clearer on what the method does. --- .../Constants/VisualAdjustmentPatches.cs | 2 +- CraftMagicItems/CraftMagicItems.csproj | 1 + CraftMagicItems/Main.cs | 19 ++--------- .../Patches/LeftHandVisualDisplayPatcher.cs | 34 +++++++++++++++++++ .../Constants/VisualAdjustmentPatchesTests.cs | 18 +++++----- 5 files changed, 47 insertions(+), 27 deletions(-) create mode 100644 CraftMagicItems/Patches/LeftHandVisualDisplayPatcher.cs diff --git a/CraftMagicItems/Constants/VisualAdjustmentPatches.cs b/CraftMagicItems/Constants/VisualAdjustmentPatches.cs index c3718fd..bec9e54 100644 --- a/CraftMagicItems/Constants/VisualAdjustmentPatches.cs +++ b/CraftMagicItems/Constants/VisualAdjustmentPatches.cs @@ -7,7 +7,7 @@ namespace CraftMagicItems.Constants public static class VisualAdjustmentPatches { /// Collection of patches for visual adjustments to displayed items - public static IEnumerable IkPatchList + public static IEnumerable LeftHandedWeaponPatchList { get { diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index 84dffc8..9400405 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -105,6 +105,7 @@ + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index a10f640..92ed58c 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -7,6 +7,7 @@ using System.Runtime.Serialization; using System.Text.RegularExpressions; using CraftMagicItems.Constants; +using CraftMagicItems.Patches; using CraftMagicItems.UI; using CraftMagicItems.UI.Sections; using CraftMagicItems.UI.UnityModManager; @@ -3232,26 +3233,10 @@ private static void PatchBlueprints() { } } - private static void PatchIk() { - foreach (var patch in VisualAdjustmentPatches.IkPatchList) { - var weapon = ResourcesLibrary.TryGetBlueprint(patch.BlueprintId); - if (weapon != null) { - var model = weapon.VisualParameters.Model; - var equipmentOffsets = model.GetComponent(); - var locator = new GameObject(); - locator.transform.SetParent(model.transform); - locator.transform.localPosition = new Vector3(patch.X, patch.Y, patch.Z); - locator.transform.localEulerAngles = new Vector3(0.0f, 0.0f, 0.0f); - locator.transform.localScale = new Vector3(1.0f, 1.0f, 1.0f); - equipmentOffsets.IkTargetLeftHand = locator.transform; - } - } - } - private static void InitialiseMod() { if (modEnabled) { PatchBlueprints(); - PatchIk(); + LeftHandVisualDisplayPatcher.PatchLeftHandedWeaponModels(); InitialiseCraftingData(); AddAllCraftingFeats(); } diff --git a/CraftMagicItems/Patches/LeftHandVisualDisplayPatcher.cs b/CraftMagicItems/Patches/LeftHandVisualDisplayPatcher.cs new file mode 100644 index 0000000..00828ab --- /dev/null +++ b/CraftMagicItems/Patches/LeftHandVisualDisplayPatcher.cs @@ -0,0 +1,34 @@ +using CraftMagicItems.Constants; +using Kingmaker.Blueprints; +using Kingmaker.Blueprints.Items.Weapons; +using Kingmaker.View.Equipment; +using UnityEngine; + +namespace CraftMagicItems.Patches +{ + /// Class that patches the visual display for left-handed Ik Targets + public static class LeftHandVisualDisplayPatcher + { + /// Patches the for left-handed characters + public static void PatchLeftHandedWeaponModels() + { + foreach (var patch in VisualAdjustmentPatches.LeftHandedWeaponPatchList) + { + var weapon = ResourcesLibrary.TryGetBlueprint(patch.BlueprintId); + if (weapon != null) + { + var model = weapon.VisualParameters.Model; + var equipmentOffsets = model.GetComponent(); + + var locator = new GameObject(); + locator.transform.SetParent(model.transform); + locator.transform.localPosition = new Vector3(patch.X, patch.Y, patch.Z); + locator.transform.localEulerAngles = new Vector3(0.0f, 0.0f, 0.0f); + locator.transform.localScale = new Vector3(1.0f, 1.0f, 1.0f); + + equipmentOffsets.IkTargetLeftHand = locator.transform; + } + } + } + } +} \ No newline at end of file diff --git a/CraftMagicItemsTests/Constants/VisualAdjustmentPatchesTests.cs b/CraftMagicItemsTests/Constants/VisualAdjustmentPatchesTests.cs index faa8d1b..14854c8 100644 --- a/CraftMagicItemsTests/Constants/VisualAdjustmentPatchesTests.cs +++ b/CraftMagicItemsTests/Constants/VisualAdjustmentPatchesTests.cs @@ -12,55 +12,55 @@ public class VisualAdjustmentPatchesTests [TestMethod] public void IkPatchList_Exposes_DuelingSword() { - AssertBlueprintInIkPatchCollection("a6f7e3dc443ff114ba68b4648fd33e9f", VisualAdjustmentPatches.IkPatchList); + AssertBlueprintInIkPatchCollection("a6f7e3dc443ff114ba68b4648fd33e9f", VisualAdjustmentPatches.LeftHandedWeaponPatchList); } [TestMethod] public void IkPatchList_Exposes_Tongi() { - AssertBlueprintInIkPatchCollection("13fa38737d46c9e4abc7f4d74aaa59c3", VisualAdjustmentPatches.IkPatchList); + AssertBlueprintInIkPatchCollection("13fa38737d46c9e4abc7f4d74aaa59c3", VisualAdjustmentPatches.LeftHandedWeaponPatchList); } [TestMethod] public void IkPatchList_Exposes_Falcata() { - AssertBlueprintInIkPatchCollection("1af5621e2ae551e42bd1dd6744d98639", VisualAdjustmentPatches.IkPatchList); + AssertBlueprintInIkPatchCollection("1af5621e2ae551e42bd1dd6744d98639", VisualAdjustmentPatches.LeftHandedWeaponPatchList); } [TestMethod] public void IkPatchList_Exposes_Estoc() { - AssertBlueprintInIkPatchCollection("d516765b3c2904e4a939749526a52a9a", VisualAdjustmentPatches.IkPatchList); + AssertBlueprintInIkPatchCollection("d516765b3c2904e4a939749526a52a9a", VisualAdjustmentPatches.LeftHandedWeaponPatchList); } [TestMethod] public void IkPatchList_Exposes_Rapier() { - AssertBlueprintInIkPatchCollection("2ece38f30500f454b8569136221e55b0", VisualAdjustmentPatches.IkPatchList); + AssertBlueprintInIkPatchCollection("2ece38f30500f454b8569136221e55b0", VisualAdjustmentPatches.LeftHandedWeaponPatchList); } [TestMethod] public void IkPatchList_Exposes_HeavyPick() { - AssertBlueprintInIkPatchCollection("a492410f3d65f744c892faf09daad84a", VisualAdjustmentPatches.IkPatchList); + AssertBlueprintInIkPatchCollection("a492410f3d65f744c892faf09daad84a", VisualAdjustmentPatches.LeftHandedWeaponPatchList); } [TestMethod] public void IkPatchList_Exposes_Trident() { - AssertBlueprintInIkPatchCollection("6ff66364e0a2c89469c2e52ebb46365e", VisualAdjustmentPatches.IkPatchList); + AssertBlueprintInIkPatchCollection("6ff66364e0a2c89469c2e52ebb46365e", VisualAdjustmentPatches.LeftHandedWeaponPatchList); } [TestMethod] public void IkPatchList_Exposes_HeavyMace() { - AssertBlueprintInIkPatchCollection("d5a167f0f0208dd439ec7481e8989e21", VisualAdjustmentPatches.IkPatchList); + AssertBlueprintInIkPatchCollection("d5a167f0f0208dd439ec7481e8989e21", VisualAdjustmentPatches.LeftHandedWeaponPatchList); } [TestMethod] public void IkPatchList_Exposes_HeavyFlail() { - AssertBlueprintInIkPatchCollection("8fefb7e0da38b06408f185e29372c703", VisualAdjustmentPatches.IkPatchList); + AssertBlueprintInIkPatchCollection("8fefb7e0da38b06408f185e29372c703", VisualAdjustmentPatches.LeftHandedWeaponPatchList); } private void AssertBlueprintInIkPatchCollection(string uuid, IEnumerable collection) From 5680b3ee54b8bca96d7a42144ad47a27ae8c0658 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Thu, 16 Jul 2020 16:44:14 -0500 Subject: [PATCH 064/132] Moving `CraftingPriceStrings` into UserInterfaceEventHandlingLogic --- CraftMagicItems/Main.cs | 8 +-- .../UI/UserInterfaceEventHandlingLogic.cs | 13 ++++- .../UserInterfaceEventHandlingLogicTests.cs | 56 +++++++++---------- 3 files changed, 39 insertions(+), 38 deletions(-) diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 92ed58c..5e5ac5e 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -79,12 +79,6 @@ namespace CraftMagicItems { public static class Main { private const string BondedItemRitual = "bondedItemRitual"; - private static readonly string[] CraftingPriceStrings = { - "100% (Owlcat prices)", - "200% (Tabletop prices)", - "Custom" - }; - private const string CustomPriceLabel = "Crafting Cost: "; private static readonly WeaponCategory[] AmmunitionWeaponCategories = { @@ -327,7 +321,7 @@ private static void OnGui(UnityModManager.ModEntry modEntry) { if (UmmUiRenderer.RenderToggleSection("Cheats", currentSection == OpenSection.CheatsSection)) { currentSection = OpenSection.CheatsSection; - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(CheatSectionRendererFactory.GetCheatSectionRenderer(), ModSettings, CustomPriceLabel, CraftingPriceStrings); + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(CheatSectionRendererFactory.GetCheatSectionRenderer(), ModSettings, CustomPriceLabel); } GUILayout.EndVertical(); diff --git a/CraftMagicItems/UI/UserInterfaceEventHandlingLogic.cs b/CraftMagicItems/UI/UserInterfaceEventHandlingLogic.cs index db200ba..17fc4f8 100644 --- a/CraftMagicItems/UI/UserInterfaceEventHandlingLogic.cs +++ b/CraftMagicItems/UI/UserInterfaceEventHandlingLogic.cs @@ -12,17 +12,24 @@ namespace CraftMagicItems.UI /// Class that performs User Interface rendering and takes the results freom such and performs logical operations based on the UI state public static class UserInterfaceEventHandlingLogic { + /// Collection of containing the display text for various pricing guidelines + private static readonly string[] CraftingPriceStrings = + { + "100% (Owlcat prices)", + "200% (Tabletop prices)", + "Custom" + }; + /// Renders the Cheats section and retrieves the values specified by its rendered UI /// instance used to render controls and return current values /// to default to and to read from /// Text to render for the price - /// Collection of containing the display text for various pricing guidelines - public static void RenderCheatsSectionAndUpdateSettings(ICheatSectionRenderer renderer, Settings modSettings, string priceLabel, string[] craftingPriceStrings) + public static void RenderCheatsSectionAndUpdateSettings(ICheatSectionRenderer renderer, Settings modSettings, string priceLabel) { modSettings.CraftingCostsNoGold = renderer.Evaluate_CraftingCostsNoGold(modSettings.CraftingCostsNoGold); if (!modSettings.CraftingCostsNoGold) { - var selectedCustomPriceScaleIndex = renderer.Evaluate_CraftingCostSelection(priceLabel, craftingPriceStrings); + var selectedCustomPriceScaleIndex = renderer.Evaluate_CraftingCostSelection(priceLabel, CraftingPriceStrings); if (selectedCustomPriceScaleIndex == 2) //if user selected "Custom" { modSettings.CraftingPriceScale = renderer.Evaluate_CustomCraftingCostSlider(modSettings.CraftingPriceScale); diff --git a/CraftMagicItemsTests/UI/UserInterfaceEventHandlingLogicTests.cs b/CraftMagicItemsTests/UI/UserInterfaceEventHandlingLogicTests.cs index c4c9f02..8f52136 100644 --- a/CraftMagicItemsTests/UI/UserInterfaceEventHandlingLogicTests.cs +++ b/CraftMagicItemsTests/UI/UserInterfaceEventHandlingLogicTests.cs @@ -29,7 +29,7 @@ public void Reads_CraftingCostsNoGold() renderer.Setup(r => r.Evaluate_CraftingCostsNoGold(It.IsAny())).Returns(true); //invocation - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel); //validation Assert.AreEqual(true, settings.CraftingCostsNoGold); @@ -43,7 +43,7 @@ public void Reads_CraftingCostsNoGold() renderer.Setup(r => r.Evaluate_CraftingCostsNoGold(It.IsAny())).Returns(false); //invocation - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel); //validation Assert.AreEqual(false, settings.CraftingCostsNoGold); @@ -65,7 +65,7 @@ public void DoesNotRead_CraftingPriceScale_When_CraftingCostsNoGold() renderer.Setup(r => r.Evaluate_CraftingCostsNoGold(It.IsAny())).Returns(true); //invocation - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel); //validation Assert.AreEqual(initial, settings.CraftingPriceScale); @@ -86,7 +86,7 @@ public void DoesNotInvoke_WarningAboutCustomItemVanillaItemCostDisparity_When_Cr renderer.Setup(r => r.RenderOnly_WarningAboutCustomItemVanillaItemCostDisparity()).Callback(setInvoked); //invocation - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, new Settings(), priceLabel, priceOptions); + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, new Settings(), priceLabel); //validation Assert.AreEqual(false, invokedWarning); @@ -107,7 +107,7 @@ public void Reads_CraftingCostSelection() renderer.Setup(r => r.Evaluate_CraftingCostSelection(It.IsAny(), It.IsAny())).Callback(setInvoked); //invocation - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, new Settings(), priceLabel, priceOptions); + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, new Settings(), priceLabel); //validation Assert.AreEqual(true, invokedWarning); @@ -127,7 +127,7 @@ public void Reads_CustomCraftingCostSlider() renderer.Setup(r => r.Evaluate_CustomCraftingCostSlider(It.IsAny())).Returns(4000); //invocation - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel); //validation Assert.AreEqual(4000, settings.CraftingPriceScale); @@ -149,7 +149,7 @@ public void Reads_CraftingPriceScale_From_CraftingCostSelection() renderer.Setup(r => r.Evaluate_CraftingCostSelection(It.IsAny(), It.IsAny())).Returns(1); //invocation - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel); //validation Assert.AreEqual(2, settings.CraftingPriceScale); @@ -164,7 +164,7 @@ public void Reads_CraftingPriceScale_From_CraftingCostSelection() renderer.Setup(r => r.Evaluate_CraftingCostSelection(It.IsAny(), It.IsAny())).Returns(0); //invocation - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel); //validation Assert.AreEqual(1, settings.CraftingPriceScale); @@ -186,7 +186,7 @@ public void Invokes_WarningAboutCustomItemVanillaItemCostDisparity_When_Crafting renderer.Setup(r => r.RenderOnly_WarningAboutCustomItemVanillaItemCostDisparity()).Callback(setInvoked); //invocation - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, new Settings(), priceLabel, priceOptions); + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, new Settings(), priceLabel); //validation Assert.AreEqual(true, invokedWarning); @@ -207,7 +207,7 @@ public void Reads_IgnoreCraftingFeats() renderer.Setup(r => r.Evaluate_IgnoreCraftingFeats(It.IsAny())).Returns(true); //invocation - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel); //validation Assert.AreEqual(true, settings.IgnoreCraftingFeats); @@ -221,7 +221,7 @@ public void Reads_IgnoreCraftingFeats() renderer.Setup(r => r.Evaluate_IgnoreCraftingFeats(It.IsAny())).Returns(false); //invocation - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel); //validation Assert.AreEqual(false, settings.IgnoreCraftingFeats); @@ -242,7 +242,7 @@ public void Reads_CraftingTakesNoTime() renderer.Setup(r => r.Evaluate_CraftingTakesNoTime(It.IsAny())).Returns(true); //invocation - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel); //validation Assert.AreEqual(true, settings.CraftingTakesNoTime); @@ -256,7 +256,7 @@ public void Reads_CraftingTakesNoTime() renderer.Setup(r => r.Evaluate_CraftingTakesNoTime(It.IsAny())).Returns(false); //invocation - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel); //validation Assert.AreEqual(false, settings.CraftingTakesNoTime); @@ -282,7 +282,7 @@ public void DoesNotRead_CustomCraftRate_When_CraftingTakesNoTime() renderer.Setup(r => r.Evaluate_CraftingTakesNoTime(It.IsAny())).Returns(true); //invocation - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel); //validation Assert.AreEqual(initialMagicCraftRate, settings.MagicCraftingRate); @@ -306,7 +306,7 @@ public void Reads_CustomCraftRate() renderer.Setup(r => r.Evaluate_CustomCraftRate(It.IsAny())).Returns(true); //invocation - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel); //validation Assert.AreEqual(true, settings.CustomCraftRate); @@ -321,7 +321,7 @@ public void Reads_CustomCraftRate() renderer.Setup(r => r.Evaluate_CustomCraftRate(It.IsAny())).Returns(false); //invocation - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel); //validation Assert.AreEqual(false, settings.CustomCraftRate); @@ -341,7 +341,7 @@ public void Reads_MagicCraftingRate() renderer.Setup(r => r.Evaluate_MagicCraftingRateSlider(It.IsAny())).Returns(7); //invocation - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel); //validation Assert.AreEqual(7, settings.MagicCraftingRate); @@ -361,7 +361,7 @@ public void Reads_MundaneCraftingRate() renderer.Setup(r => r.Evaluate_MundaneCraftingRateSlider(It.IsAny())).Returns(12); //invocation - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel); //validation Assert.AreEqual(12, settings.MundaneCraftingRate); @@ -380,7 +380,7 @@ public void Defaults_MagicCraftingRate() renderer.Setup(r => r.Evaluate_CustomCraftRate(It.IsAny())).Returns(false); //invocation - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel); //validation Assert.AreEqual(Settings.MagicCraftingProgressPerDay, settings.MagicCraftingRate); @@ -399,7 +399,7 @@ public void Defaults_MundaneCraftingRate() renderer.Setup(r => r.Evaluate_CustomCraftRate(It.IsAny())).Returns(false); //invocation - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel); //validation Assert.AreEqual(Settings.MundaneCraftingProgressPerDay, settings.MundaneCraftingRate); @@ -420,7 +420,7 @@ public void Reads_CasterLevelIsSinglePrerequisite() renderer.Setup(r => r.Evaluate_CasterLevelIsSinglePrerequisite(It.IsAny())).Returns(true); //invocation - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel); //validation Assert.AreEqual(true, settings.CasterLevelIsSinglePrerequisite); @@ -434,7 +434,7 @@ public void Reads_CasterLevelIsSinglePrerequisite() renderer.Setup(r => r.Evaluate_CasterLevelIsSinglePrerequisite(It.IsAny())).Returns(false); //invocation - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel); //validation Assert.AreEqual(false, settings.CasterLevelIsSinglePrerequisite); @@ -455,7 +455,7 @@ public void Reads_CraftAtFullSpeedWhileAdventuring() renderer.Setup(r => r.Evaluate_CraftAtFullSpeedWhileAdventuring(It.IsAny())).Returns(true); //invocation - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel); //validation Assert.AreEqual(true, settings.CraftAtFullSpeedWhileAdventuring); @@ -469,7 +469,7 @@ public void Reads_CraftAtFullSpeedWhileAdventuring() renderer.Setup(r => r.Evaluate_CraftAtFullSpeedWhileAdventuring(It.IsAny())).Returns(false); //invocation - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel); //validation Assert.AreEqual(false, settings.CraftAtFullSpeedWhileAdventuring); @@ -490,7 +490,7 @@ public void Reads_IgnorePlusTenItemMaximum() renderer.Setup(r => r.Evaluate_IgnorePlusTenItemMaximum(It.IsAny())).Returns(true); //invocation - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel); //validation Assert.AreEqual(true, settings.IgnorePlusTenItemMaximum); @@ -504,7 +504,7 @@ public void Reads_IgnorePlusTenItemMaximum() renderer.Setup(r => r.Evaluate_IgnorePlusTenItemMaximum(It.IsAny())).Returns(false); //invocation - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel); //validation Assert.AreEqual(false, settings.IgnorePlusTenItemMaximum); @@ -525,7 +525,7 @@ public void Reads_IgnoreFeatCasterLevelRestriction() renderer.Setup(r => r.Evaluate_IgnoreFeatCasterLevelRestriction(It.IsAny())).Returns(true); //invocation - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel); //validation Assert.AreEqual(true, settings.IgnoreFeatCasterLevelRestriction); @@ -539,7 +539,7 @@ public void Reads_IgnoreFeatCasterLevelRestriction() renderer.Setup(r => r.Evaluate_IgnoreFeatCasterLevelRestriction(It.IsAny())).Returns(false); //invocation - UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel, priceOptions); + UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(renderer.Object, settings, priceLabel); //validation Assert.AreEqual(false, settings.IgnoreFeatCasterLevelRestriction); From 8cdd04d44a80717a560273b71612ef95efd7d489 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Fri, 17 Jul 2020 11:21:07 -0500 Subject: [PATCH 065/132] Extracting "selected" and "current" static variables out into an instantiable `Selections` class so that we can inject the settings for unit tests later on down the road --- CraftMagicItems/Config/Selections.cs | 54 +++++++ CraftMagicItems/CraftMagicItems.csproj | 1 + CraftMagicItems/Main.cs | 214 ++++++++++++------------- 3 files changed, 160 insertions(+), 109 deletions(-) create mode 100644 CraftMagicItems/Config/Selections.cs diff --git a/CraftMagicItems/Config/Selections.cs b/CraftMagicItems/Config/Selections.cs new file mode 100644 index 0000000..8dc9297 --- /dev/null +++ b/CraftMagicItems/Config/Selections.cs @@ -0,0 +1,54 @@ +using System.Collections.Generic; +using CraftMagicItems.UI; +using Kingmaker.Blueprints.Items; +using Kingmaker.Blueprints.Items.Equipment; +using Kingmaker.EntitySystem.Entities; + +namespace CraftMagicItems.Config +{ + /// Class containing the selections from the UI + public class Selections + { + /// Currently selected index for a given setting + /// + /// TODO: This should probably be broken up for ease of maintenance + /// + public readonly Dictionary SelectedIndex = new Dictionary(); + + /// + /// The currently-selected caster level with which to create an item (wand, scroll, potion, item effect, etc.) + /// i.e.: "cast {spell} as an Nth level caster" + /// + public int SelectedCasterLevel; + + /// Flag indicating whether to ONLY display currently prepared/available spells + public bool SelectedShowPreparedSpells; + + /// Is a double weapon's second side selected? + public bool SelectedDoubleWeaponSecondEnd; + + /// Is a shield's weapon (bash, spikes, etc.) seleted? + public bool SelectedShieldWeapon; + + /// Selected times that an item can cast a given spell per day + public int SelectedCastsPerDay; + + /// Currently selected base item blueprint + public BlueprintItemEquipment SelectedBaseBlueprint; + + /// User-entered custom name for the item + public string SelectedCustomName; + + /// Has the user selected to bond with a new item + public bool SelectedBondWithNewObject; + + /// Currently selected crafter/caster + public UnitEntityData CurrentCaster; + + /// Currently-selected crafting section to render + public OpenSection CurrentSection = OpenSection.CraftMagicItemsSection; + + /// Blueprint that is currently being upgraded + public BlueprintItem UpgradingBlueprint; + } +} \ No newline at end of file diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index 9400405..c2e3cae 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -93,6 +93,7 @@ + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 5e5ac5e..24dadd7 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -6,6 +6,7 @@ using System.Reflection.Emit; using System.Runtime.Serialization; using System.Text.RegularExpressions; +using CraftMagicItems.Config; using CraftMagicItems.Constants; using CraftMagicItems.Patches; using CraftMagicItems.UI; @@ -144,8 +145,13 @@ public bool MatchPostfix(MethodBase method) { postfix: new HarmonyLib.HarmonyMethod(typeof(PlayerPostLoadPatch).GetMethod("Postfix", BindingFlags.NonPublic | BindingFlags.Static))), }; - public static UnityModManager.ModEntry ModEntry; + /// UI selections made within Unity Mod Manager + public static Selections Selections; + + /// Settings that are saved and managed by Unity Mod Manager public static Settings ModSettings; + + public static UnityModManager.ModEntry ModEntry; public static CraftMagicItemsAccessors Accessors; public static ItemCraftingData[] ItemCraftingData; public static CustomLootItem[] CustomLootItems; @@ -158,18 +164,6 @@ public bool MatchPostfix(MethodBase method) { private static bool modEnabled = true; private static HarmonyLib.Harmony harmonyInstance; private static CraftMagicItemsBlueprintPatcher blueprintPatcher; - private static OpenSection currentSection = OpenSection.CraftMagicItemsSection; - private static readonly Dictionary SelectedIndex = new Dictionary(); - private static int selectedCasterLevel; - private static bool selectedShowPreparedSpells; - private static bool selectedDoubleWeaponSecondEnd; - private static bool selectedShieldWeapon; - private static int selectedCastsPerDay; - private static BlueprintItemEquipment selectedBaseBlueprint; - private static string selectedCustomName; - private static BlueprintItem upgradingBlueprint; - private static bool selectedBondWithNewObject; - private static UnitEntityData currentCaster; private static readonly Dictionary>> SpellIdToItem = new Dictionary>>(); @@ -234,9 +228,10 @@ private static void UnpatchAllExcept(params MethodPatch[] exceptMethods) { // ReSharper disable once UnusedMember.Local private static void Load(UnityModManager.ModEntry modEntry) { try { + Selections = new Selections(); ModEntry = modEntry; ModSettings = UnityModManager.ModSettings.Load(modEntry); - SelectedIndex[CustomPriceLabel] = Mathf.Abs(ModSettings.CraftingPriceScale - 1f) < 0.001 ? 0 : + Selections.SelectedIndex[CustomPriceLabel] = Mathf.Abs(ModSettings.CraftingPriceScale - 1f) < 0.001 ? 0 : Mathf.Abs(ModSettings.CraftingPriceScale - 2f) < 0.001 ? 1 : 2; modEnabled = modEntry.Active; modEntry.OnSaveGUI = OnSaveGui; @@ -294,33 +289,33 @@ private static void OnGui(UnityModManager.ModEntry modEntry) { GetSelectedCrafter(true); //render toggleable views in the main functionality of the mod - if (UmmUiRenderer.RenderToggleSection("Craft Magic Items", currentSection == OpenSection.CraftMagicItemsSection)) + if (UmmUiRenderer.RenderToggleSection("Craft Magic Items", Selections.CurrentSection == OpenSection.CraftMagicItemsSection)) { - currentSection = OpenSection.CraftMagicItemsSection; + Selections.CurrentSection = OpenSection.CraftMagicItemsSection; RenderCraftMagicItemsSection(); } - if (UmmUiRenderer.RenderToggleSection("Craft Mundane Items", currentSection == OpenSection.CraftMundaneItemsSection)) + if (UmmUiRenderer.RenderToggleSection("Craft Mundane Items", Selections.CurrentSection == OpenSection.CraftMundaneItemsSection)) { - currentSection = OpenSection.CraftMundaneItemsSection; + Selections.CurrentSection = OpenSection.CraftMundaneItemsSection; RenderCraftMundaneItemsSection(); } - if (UmmUiRenderer.RenderToggleSection("Work in Progress", currentSection == OpenSection.ProjectsSection)) + if (UmmUiRenderer.RenderToggleSection("Work in Progress", Selections.CurrentSection == OpenSection.ProjectsSection)) { - currentSection = OpenSection.ProjectsSection; + Selections.CurrentSection = OpenSection.ProjectsSection; RenderProjectsSection(); } - if (UmmUiRenderer.RenderToggleSection("Feat Reassignment", currentSection == OpenSection.FeatsSection)) + if (UmmUiRenderer.RenderToggleSection("Feat Reassignment", Selections.CurrentSection == OpenSection.FeatsSection)) { - currentSection = OpenSection.FeatsSection; + Selections.CurrentSection = OpenSection.FeatsSection; UserInterfaceEventHandlingLogic.RenderFeatReassignmentSection(FeatReassignmentSectionRendererFactory.GetFeatReassignmentSectionRenderer()); } - if (UmmUiRenderer.RenderToggleSection("Cheats", currentSection == OpenSection.CheatsSection)) + if (UmmUiRenderer.RenderToggleSection("Cheats", Selections.CurrentSection == OpenSection.CheatsSection)) { - currentSection = OpenSection.CheatsSection; + Selections.CurrentSection = OpenSection.CheatsSection; UserInterfaceEventHandlingLogic.RenderCheatsSectionAndUpdateSettings(CheatSectionRendererFactory.GetCheatSectionRenderer(), ModSettings, CustomPriceLabel); } @@ -342,7 +337,7 @@ public static string L10NFormat(UnitEntityData sourceUnit, string key, params ob } private static string L10NFormat(string key, params object[] args) { - return L10NFormat(currentCaster ?? GetSelectedCrafter(false), key, args); + return L10NFormat(Selections.CurrentCaster ?? GetSelectedCrafter(false), key, args); } public static T ReadJsonFile(string fileName, params JsonConverter[] converters) { @@ -456,7 +451,7 @@ private static void RenderCraftMagicItemsSection() { .PrependConditional(hasBondedItemFeature, new L10NString("craftMagicItems-bonded-object-name")).ToArray(); //render whatever the user has selected - var selectedItemTypeIndex = DrawSelectionUserInterfaceElements("Crafting: ", itemTypeNames, 6, ref selectedCustomName, false); + var selectedItemTypeIndex = DrawSelectionUserInterfaceElements("Crafting: ", itemTypeNames, 6, ref Selections.SelectedCustomName, false); //render options for actual selection if (hasBondedItemFeature && selectedItemTypeIndex == 0) { @@ -525,13 +520,13 @@ private static void RenderBondedItemCrafting(UnitEntityData caster) { } var bondedComponent = GetBondedItemComponentForCaster(caster.Descriptor); var characterCasterLevel = CharacterCasterLevel(caster.Descriptor); - if (bondedComponent == null || bondedComponent.ownerItem == null || selectedBondWithNewObject) { - if (selectedBondWithNewObject) { + if (bondedComponent == null || bondedComponent.ownerItem == null || Selections.SelectedBondWithNewObject) { + if (Selections.SelectedBondWithNewObject) { UmmUiRenderer.RenderLabelRow("You may bond with a different object by performing a special ritual that costs 200 gp per caster level. This ritual takes 8 " + "hours to complete. Items replaced in this way do not possess any of the additional enchantments of the previous bonded item, " + "and the previous bonded item loses any enchantments you added via your bond."); if (GUILayout.Button("Cancel bonding to a new object")) { - selectedBondWithNewObject = false; + Selections.SelectedBondWithNewObject = false; } } UmmUiRenderer.RenderLabelRow( @@ -559,7 +554,7 @@ private static void RenderBondedItemCrafting(UnitEntityData caster) { var itemNames = items.Select(item => item.Name).ToArray(); var selectedUpgradeItemIndex = DrawSelectionUserInterfaceElements("Item: ", itemNames, 5); var selectedItem = items[selectedUpgradeItemIndex]; - var goldCost = !selectedBondWithNewObject || ModSettings.CraftingCostsNoGold ? 0 : 200 * characterCasterLevel; + var goldCost = !Selections.SelectedBondWithNewObject || ModSettings.CraftingCostsNoGold ? 0 : 200 * characterCasterLevel; var canAfford = BuildCostString(out var cost, null, goldCost); var label = $"Make {selectedItem.Name} your bonded item{(goldCost == 0 ? "" : " for " + cost)}"; if (!canAfford) { @@ -569,15 +564,15 @@ private static void RenderBondedItemCrafting(UnitEntityData caster) { Game.Instance.UI.Common.UISound.Play(UISoundType.LootCollectGold); Game.Instance.Player.SpendMoney(goldCost); } - if (selectedBondWithNewObject) { - selectedBondWithNewObject = false; + if (Selections.SelectedBondWithNewObject) { + Selections.SelectedBondWithNewObject = false; if (!ModSettings.CraftingTakesNoTime) { // Create project AddBattleLogMessage(L10NFormat("craftMagicItems-logMessage-begin-ritual-bonded-item", cost, selectedItem.Name)); var project = new CraftingProjectData(caster, ModSettings.MagicCraftingRate, goldCost, 0, selectedItem, BondedItemRitual); AddNewProject(caster.Descriptor, project); CalculateProjectEstimate(project); - currentSection = OpenSection.ProjectsSection; + Selections.CurrentSection = OpenSection.ProjectsSection; return; } } @@ -587,7 +582,7 @@ private static void RenderBondedItemCrafting(UnitEntityData caster) { GUILayout.BeginHorizontal(); GUILayout.Label($"Your bonded item: {bondedComponent.ownerItem.Name}"); if (GUILayout.Button("Bond with a different item", GUILayout.ExpandWidth(false))) { - selectedBondWithNewObject = true; + Selections.SelectedBondWithNewObject = true; } GUILayout.EndHorizontal(); var craftingData = GetBondedItemCraftingData(bondedComponent); @@ -622,18 +617,18 @@ private static void RenderSpellBasedCrafting(UnitEntityData caster, SpellBasedIt var spellLevel = DrawSelectionUserInterfaceElements("Spell level: ", spellLevelNames, 10); if (spellLevel > 0 && !spellbook.Blueprint.Spontaneous) { if (ModSettings.CraftingTakesNoTime) { - selectedShowPreparedSpells = true; + Selections.SelectedShowPreparedSpells = true; } else { GUILayout.BeginHorizontal(); - selectedShowPreparedSpells = GUILayout.Toggle(selectedShowPreparedSpells, " Show prepared spells only"); + Selections.SelectedShowPreparedSpells = GUILayout.Toggle(Selections.SelectedShowPreparedSpells, " Show prepared spells only"); GUILayout.EndHorizontal(); } } else { - selectedShowPreparedSpells = false; + Selections.SelectedShowPreparedSpells = false; } List spellOptions; - if (selectedShowPreparedSpells) { + if (Selections.SelectedShowPreparedSpells) { // Prepared spellcaster spellOptions = spellbook.GetMemorizedSpells(spellLevel).Where(slot => slot.Available).Select(slot => slot.Spell).ToList(); } else { @@ -655,15 +650,15 @@ private static void RenderSpellBasedCrafting(UnitEntityData caster, SpellBasedIt var minCasterLevel = Math.Max(1, 2 * spellLevel - 1); var maxCasterLevel = CharacterCasterLevel(caster.Descriptor, spellbook); if (minCasterLevel < maxCasterLevel) { - selectedCasterLevel = UmmUiRenderer.RenderIntSlider("Caster level: ", selectedCasterLevel, minCasterLevel, maxCasterLevel); + Selections.SelectedCasterLevel = UmmUiRenderer.RenderIntSlider("Caster level: ", Selections.SelectedCasterLevel, minCasterLevel, maxCasterLevel); } else { - selectedCasterLevel = minCasterLevel; - UmmUiRenderer.RenderLabelRow($"Caster level: {selectedCasterLevel}"); + Selections.SelectedCasterLevel = minCasterLevel; + UmmUiRenderer.RenderLabelRow($"Caster level: {Selections.SelectedCasterLevel}"); } - RenderCraftingSkillInformation(caster, StatType.SkillKnowledgeArcana, 5 + selectedCasterLevel, selectedCasterLevel); + RenderCraftingSkillInformation(caster, StatType.SkillKnowledgeArcana, 5 + Selections.SelectedCasterLevel, Selections.SelectedCasterLevel); - if (selectedShowPreparedSpells && spellbook.GetSpontaneousConversionSpells(spellLevel).Any()) { + if (Selections.SelectedShowPreparedSpells && spellbook.GetSpontaneousConversionSpells(spellLevel).Any()) { var firstSpell = spellbook.Blueprint.Spontaneous ? spellbook.GetKnownSpells(spellLevel).First(spell => true) : spellbook.GetMemorizedSpells(spellLevel).FirstOrDefault(slot => slot.Available)?.Spell; @@ -688,12 +683,12 @@ private static void RenderSpellBasedCrafting(UnitEntityData caster, SpellBasedIt // Spells with choices (e.g. Protection from Alignment, which can be Protection from Evil, Good, Chaos or Law) foreach (var variant in spell.Blueprint.Variants) { - AttemptSpellBasedCraftItemAndRender(caster, craftingData, spell, variant, spellLevel, selectedCasterLevel); + AttemptSpellBasedCraftItemAndRender(caster, craftingData, spell, variant, spellLevel, Selections.SelectedCasterLevel); } } else { - AttemptSpellBasedCraftItemAndRender(caster, craftingData, spell, spell.Blueprint, spellLevel, selectedCasterLevel); + AttemptSpellBasedCraftItemAndRender(caster, craftingData, spell, spell.Blueprint, spellLevel, Selections.SelectedCasterLevel); } } } @@ -1143,12 +1138,12 @@ private static void RenderRecipeBasedCrafting(UnitEntityData caster, RecipeBased var selectedItemSlotIndex = 0; if (craftingData.Slots.Length > 1) { var names = craftingData.Slots.Select(slot => new L10NString(GetSlotStringKey(slot, craftingData.SlotRestrictions)).ToString()).ToArray(); - selectedItemSlotIndex = DrawSelectionUserInterfaceElements("Item type", names, 10, ref selectedCustomName); + selectedItemSlotIndex = DrawSelectionUserInterfaceElements("Item type", names, 10, ref Selections.SelectedCustomName); } var locationFilter = ItemLocationFilter.All; var locationNames = Enum.GetNames(typeof(ItemLocationFilter)); - locationFilter = (ItemLocationFilter)DrawSelectionUserInterfaceElements("Item location", locationNames, locationNames.Length, ref selectedCustomName); + locationFilter = (ItemLocationFilter)DrawSelectionUserInterfaceElements("Item location", locationNames, locationNames.Length, ref Selections.SelectedCustomName); selectedSlot = craftingData.Slots[selectedItemSlotIndex]; var playerInCapital = IsPlayerInCapital(); @@ -1183,7 +1178,7 @@ private static void RenderRecipeBasedCrafting(UnitEntityData caster, RecipeBased return; } - var selectedUpgradeItemIndex = DrawSelectionUserInterfaceElements("Item: ", itemNames, 5, ref selectedCustomName); + var selectedUpgradeItemIndex = DrawSelectionUserInterfaceElements("Item: ", itemNames, 5, ref Selections.SelectedCustomName); // See existing item details and enchantments. var index = selectedUpgradeItemIndex - (canCreateNew ? 1 : 0); upgradeItem = index < 0 ? null : items[index]; @@ -1196,11 +1191,11 @@ private static void RenderRecipeBasedCrafting(UnitEntityData caster, RecipeBased if (upgradeItemDoubleWeapon != null && upgradeItemDoubleWeapon.Blueprint.Double) { GUILayout.BeginHorizontal(); GUILayout.Label($"{upgradeItem.Name} is a double weapon; enchanting ", GUILayout.ExpandWidth(false)); - var label = selectedDoubleWeaponSecondEnd ? "Secondary end" : "Primary end"; + var label = Selections.SelectedDoubleWeaponSecondEnd ? "Secondary end" : "Primary end"; if (GUILayout.Button(label, GUILayout.ExpandWidth(false))) { - selectedDoubleWeaponSecondEnd = !selectedDoubleWeaponSecondEnd; + Selections.SelectedDoubleWeaponSecondEnd = !Selections.SelectedDoubleWeaponSecondEnd; } - if (selectedDoubleWeaponSecondEnd) { + if (Selections.SelectedDoubleWeaponSecondEnd) { upgradeItem = upgradeItemDoubleWeapon.Second; } else { upgradeItemDoubleWeapon = null; @@ -1212,18 +1207,18 @@ private static void RenderRecipeBasedCrafting(UnitEntityData caster, RecipeBased if (upgradeItemShieldWeapon != null) { GUILayout.BeginHorizontal(); GUILayout.Label($"{upgradeItem.Name} is a shield; enchanting ", GUILayout.ExpandWidth(false)); - var label = selectedShieldWeapon ? "Shield Bash" : "Shield"; + var label = Selections.SelectedShieldWeapon ? "Shield Bash" : "Shield"; if (GUILayout.Button(label, GUILayout.ExpandWidth(false))) { - selectedShieldWeapon = !selectedShieldWeapon; + Selections.SelectedShieldWeapon = !Selections.SelectedShieldWeapon; } - if (selectedShieldWeapon) { + if (Selections.SelectedShieldWeapon) { upgradeItem = upgradeItemShieldWeapon; } else { upgradeItem = upgradeItemShieldArmor; } GUILayout.EndHorizontal(); } else { - selectedShieldWeapon = false; + Selections.SelectedShieldWeapon = false; } if (upgradeItemShield != null) { UmmUiRenderer.RenderLabelRow(BuildItemDescription(upgradeItemShield)); @@ -1245,7 +1240,7 @@ private static void RenderRecipeBasedCrafting(UnitEntityData caster, RecipeBased ? new string[0] : new[] {new L10NString("craftMagicItems-label-cast-spell-n-times").ToString()}) .ToArray(); - var selectedRecipeIndex = DrawSelectionUserInterfaceElements("Enchantment: ", recipeNames, 5, ref selectedCustomName); + var selectedRecipeIndex = DrawSelectionUserInterfaceElements("Enchantment: ", recipeNames, 5, ref Selections.SelectedCustomName); if (selectedRecipeIndex == availableRecipes.Length) { // Cast spell N times RenderCastSpellNTimes(caster, craftingData, upgradeItemShield ?? upgradeItem, selectedSlot); @@ -1259,7 +1254,7 @@ private static void RenderRecipeBasedCrafting(UnitEntityData caster, RecipeBased .OrderBy(recipe => recipe.NameId) .ToArray(); recipeNames = availableSubRecipes.Select(recipe => recipe.NameId).ToArray(); - var selectedSubRecipeIndex = DrawSelectionUserInterfaceElements(category + ": ", recipeNames, 5, ref selectedCustomName); + var selectedSubRecipeIndex = DrawSelectionUserInterfaceElements(category + ": ", recipeNames, 5, ref Selections.SelectedCustomName); selectedRecipe = availableSubRecipes[selectedSubRecipeIndex]; } @@ -1357,7 +1352,7 @@ private static void RenderRecipeBasedCrafting(UnitEntityData caster, RecipeBased DoesItemMatchAllEnchantments(blueprint, null, selectedEnchantment.AssetGuid, upgradeItemDoubleWeapon?.Blueprint as BlueprintItemEquipment, false) ); } else if (upgradeItemShield != null) { - if (selectedShieldWeapon) { + if (Selections.SelectedShieldWeapon) { matchingItem = allItemBlueprintsWithEnchantment?.FirstOrDefault(blueprint => DoesItemMatchAllEnchantments(blueprint, null, selectedEnchantment.AssetGuid, upgradeItemShield?.Blueprint as BlueprintItemEquipment, false) ); @@ -1391,8 +1386,8 @@ private static void RenderRecipeBasedCrafting(UnitEntityData caster, RecipeBased } else if (upgradeItem != null) { // Upgrading to a custom blueprint var name = upgradeItemShield?.Blueprint?.Name ?? upgradeItem.Blueprint.Name; - selectedCustomName = UmmUiRenderer.RenderCustomNameField(name, selectedCustomName); - name = selectedCustomName == name ? null : selectedCustomName; + Selections.SelectedCustomName = UmmUiRenderer.RenderCustomNameField(name, Selections.SelectedCustomName); + name = Selections.SelectedCustomName == name ? null : Selections.SelectedCustomName; IEnumerable enchantments; string supersededEnchantmentId; if (selectedRecipe.EnchantmentsCumulative) { @@ -1406,7 +1401,7 @@ private static void RenderRecipeBasedCrafting(UnitEntityData caster, RecipeBased if (upgradeItemShield != null) { upgradeItem = upgradeItemShield; } - if (selectedShieldWeapon) { + if (Selections.SelectedShieldWeapon) { itemGuid = blueprintPatcher.BuildCustomRecipeItemGuid(upgradeItemShieldWeapon.Blueprint.AssetGuid, enchantments, supersededEnchantmentId == null ? null : new[] {supersededEnchantmentId}); itemGuid = blueprintPatcher.BuildCustomRecipeItemGuid(upgradeItemShield.Blueprint.AssetGuid, Enumerable.Empty(), @@ -1425,8 +1420,8 @@ private static void RenderRecipeBasedCrafting(UnitEntityData caster, RecipeBased } else { // Crafting a new custom blueprint from scratch. SelectRandomApplicableBaseGuid(craftingData, selectedSlot); - var baseBlueprint = selectedBaseBlueprint; - selectedCustomName = UmmUiRenderer.RenderCustomNameField($"{selectedRecipe.NameId} {new L10NString(GetSlotStringKey(selectedSlot, craftingData.SlotRestrictions))}", selectedCustomName); + var baseBlueprint = Selections.SelectedBaseBlueprint; + Selections.SelectedCustomName = UmmUiRenderer.RenderCustomNameField($"{selectedRecipe.NameId} {new L10NString(GetSlotStringKey(selectedSlot, craftingData.SlotRestrictions))}", Selections.SelectedCustomName); var enchantmentsToRemove = GetEnchantments(baseBlueprint, selectedRecipe).Select(enchantment => enchantment.AssetGuid).ToArray(); IEnumerable enchantments; if (selectedRecipe.EnchantmentsCumulative) { @@ -1434,8 +1429,8 @@ private static void RenderRecipeBasedCrafting(UnitEntityData caster, RecipeBased } else { enchantments = new List { selectedEnchantment.AssetGuid }; } - itemGuid = blueprintPatcher.BuildCustomRecipeItemGuid(selectedBaseBlueprint.AssetGuid, enchantments, enchantmentsToRemove, - selectedCustomName ?? "[custom item]", "null", "null"); + itemGuid = blueprintPatcher.BuildCustomRecipeItemGuid(Selections.SelectedBaseBlueprint.AssetGuid, enchantments, enchantmentsToRemove, + Selections.SelectedCustomName ?? "[custom item]", "null", "null"); itemToCraft = ResourcesLibrary.TryGetBlueprint(itemGuid); } @@ -1523,14 +1518,14 @@ private static string BuildItemDescription(ItemEntity item) { } private static void SelectRandomApplicableBaseGuid(ItemCraftingData craftingData, ItemsFilter.ItemType selectedSlot) { - if (selectedBaseBlueprint != null) { - var baseBlueprint = selectedBaseBlueprint; + if (Selections.SelectedBaseBlueprint != null) { + var baseBlueprint = Selections.SelectedBaseBlueprint; if (!baseBlueprint || !DoesBlueprintMatchSlot(baseBlueprint, selectedSlot)) { - selectedBaseBlueprint = null; + Selections.SelectedBaseBlueprint = null; } } - selectedBaseBlueprint = selectedBaseBlueprint ?? RandomBaseBlueprintId(craftingData, + Selections.SelectedBaseBlueprint = Selections.SelectedBaseBlueprint ?? RandomBaseBlueprintId(craftingData, blueprint => DoesBlueprintMatchSlot(blueprint, selectedSlot)); } @@ -1554,12 +1549,12 @@ private static void RenderCastSpellNTimes(UnitEntityData caster, RecipeBasedItem // Choose a spellbook known to the caster var spellbooks = caster.Descriptor.Spellbooks.ToList(); var spellBookNames = spellbooks.Select(book => book.Blueprint.Name.ToString()).Concat(Enumerable.Repeat("From Items", 1)).ToArray(); - var selectedSpellbookIndex = DrawSelectionUserInterfaceElements("Source: ", spellBookNames, 10, ref selectedCustomName); + var selectedSpellbookIndex = DrawSelectionUserInterfaceElements("Source: ", spellBookNames, 10, ref Selections.SelectedCustomName); if (selectedSpellbookIndex < spellbooks.Count) { var spellbook = spellbooks[selectedSpellbookIndex]; // Choose a spell level var spellLevelNames = Enumerable.Range(0, spellbook.Blueprint.MaxSpellLevel + 1).Select(index => $"Level {index}").ToArray(); - spellLevel = DrawSelectionUserInterfaceElements("Spell level: ", spellLevelNames, 10, ref selectedCustomName); + spellLevel = DrawSelectionUserInterfaceElements("Spell level: ", spellLevelNames, 10, ref Selections.SelectedCustomName); var specialSpellLists = Accessors.GetSpellbookSpecialLists(spellbook); var spellOptions = spellbook.Blueprint.SpellList.GetSpells(spellLevel) .Concat(specialSpellLists.Aggregate(new List(), (allSpecial, spellList) => spellList.GetSpells(spellLevel))) @@ -1572,11 +1567,11 @@ private static void RenderCastSpellNTimes(UnitEntityData caster, RecipeBasedItem } var spellNames = spellOptions.Select(spell => spell.Name).ToArray(); - var selectedSpellIndex = DrawSelectionUserInterfaceElements("Spell: ", spellNames, 4, ref selectedCustomName); + var selectedSpellIndex = DrawSelectionUserInterfaceElements("Spell: ", spellNames, 4, ref Selections.SelectedCustomName); ability = spellOptions[selectedSpellIndex]; if (ability.HasVariants && ability.Variants != null) { var selectedVariantIndex = - DrawSelectionUserInterfaceElements("Variant: ", ability.Variants.Select(spell => spell.Name).ToArray(), 4, ref selectedCustomName); + DrawSelectionUserInterfaceElements("Variant: ", ability.Variants.Select(spell => spell.Name).ToArray(), 4, ref Selections.SelectedCustomName); ability = ability.Variants[selectedVariantIndex]; } } else { @@ -1594,7 +1589,7 @@ private static void RenderCastSpellNTimes(UnitEntityData caster, RecipeBasedItem return; } var itemNames = itemBlueprints.Select(item => item.Name).ToArray(); - var itemIndex = DrawSelectionUserInterfaceElements("Cast from item: ", itemNames, 5, ref selectedCustomName); + var itemIndex = DrawSelectionUserInterfaceElements("Cast from item: ", itemNames, 5, ref Selections.SelectedCustomName); var selectedItemBlueprint = itemBlueprints[itemIndex]; ability = selectedItemBlueprint.Ability; spellLevel = selectedItemBlueprint.SpellLevel; @@ -1610,51 +1605,51 @@ private static void RenderCastSpellNTimes(UnitEntityData caster, RecipeBasedItem // Choose a caster level var minCasterLevel = Math.Max(equipment == null ? 0 : equipment.CasterLevel, Math.Max(1, 2 * spellLevel - 1)); - selectedCasterLevel = UmmUiRenderer.RenderIntSlider("Caster level: ", selectedCasterLevel, minCasterLevel, 20); + Selections.SelectedCasterLevel = UmmUiRenderer.RenderIntSlider("Caster level: ", Selections.SelectedCasterLevel, minCasterLevel, 20); // Choose number of times per day var maxCastsPerDay = equipment == null ? 10 : ((equipment.Charges + 10) / 10) * 10; - selectedCastsPerDay = UmmUiRenderer.RenderIntSlider("Casts per day: ", selectedCastsPerDay, equipment == null ? 1 : equipment.Charges, maxCastsPerDay); - if (equipment != null && ability == equipment.Ability && selectedCasterLevel == equipment.CasterLevel && selectedCastsPerDay == equipment.Charges) { + Selections.SelectedCastsPerDay = UmmUiRenderer.RenderIntSlider("Casts per day: ", Selections.SelectedCastsPerDay, equipment == null ? 1 : equipment.Charges, maxCastsPerDay); + if (equipment != null && ability == equipment.Ability && Selections.SelectedCasterLevel == equipment.CasterLevel && Selections.SelectedCastsPerDay == equipment.Charges) { UmmUiRenderer.RenderLabelRow($"No changes made to {equipment.Name}"); return; } // Show skill info - RenderCraftingSkillInformation(caster, StatType.SkillKnowledgeArcana, 5 + selectedCasterLevel, selectedCasterLevel, new[] {ability}); + RenderCraftingSkillInformation(caster, StatType.SkillKnowledgeArcana, 5 + Selections.SelectedCasterLevel, Selections.SelectedCasterLevel, new[] {ability}); string itemGuid; if (upgradeItem == null) { // Option to rename item - selectedCustomName = UmmUiRenderer.RenderCustomNameField($"{ability.Name} {new L10NString(GetSlotStringKey(selectedSlot, craftingData.SlotRestrictions))}", selectedCustomName); + Selections.SelectedCustomName = UmmUiRenderer.RenderCustomNameField($"{ability.Name} {new L10NString(GetSlotStringKey(selectedSlot, craftingData.SlotRestrictions))}", Selections.SelectedCustomName); // Pick random base item SelectRandomApplicableBaseGuid(craftingData, selectedSlot); // Create customised item GUID - var baseBlueprint = selectedBaseBlueprint; + var baseBlueprint = Selections.SelectedBaseBlueprint; var enchantmentsToRemove = GetEnchantments(baseBlueprint).Select(enchantment => enchantment.AssetGuid).ToArray(); - itemGuid = blueprintPatcher.BuildCustomRecipeItemGuid(selectedBaseBlueprint.AssetGuid, new List(), enchantmentsToRemove, selectedCustomName, - ability.AssetGuid, "null", casterLevel: selectedCasterLevel, spellLevel: spellLevel, perDay: selectedCastsPerDay); + itemGuid = blueprintPatcher.BuildCustomRecipeItemGuid(Selections.SelectedBaseBlueprint.AssetGuid, new List(), enchantmentsToRemove, Selections.SelectedCustomName, + ability.AssetGuid, "null", casterLevel: Selections.SelectedCasterLevel, spellLevel: spellLevel, perDay: Selections.SelectedCastsPerDay); } else { // Option to rename item - selectedCustomName = UmmUiRenderer.RenderCustomNameField(upgradeItem.Blueprint.Name, selectedCustomName); + Selections.SelectedCustomName = UmmUiRenderer.RenderCustomNameField(upgradeItem.Blueprint.Name, Selections.SelectedCustomName); // Create customised item GUID itemGuid = blueprintPatcher.BuildCustomRecipeItemGuid(upgradeItem.Blueprint.AssetGuid, new List(), null, - selectedCustomName == upgradeItem.Blueprint.Name ? null : selectedCustomName, ability.AssetGuid, - casterLevel: selectedCasterLevel == equipment.CasterLevel ? -1 : selectedCasterLevel, + Selections.SelectedCustomName == upgradeItem.Blueprint.Name ? null : Selections.SelectedCustomName, ability.AssetGuid, + casterLevel: Selections.SelectedCasterLevel == equipment.CasterLevel ? -1 : Selections.SelectedCasterLevel, spellLevel: spellLevel == equipment.SpellLevel ? -1 : spellLevel, - perDay: selectedCastsPerDay == equipment.Charges ? -1 : selectedCastsPerDay); + perDay: Selections.SelectedCastsPerDay == equipment.Charges ? -1 : Selections.SelectedCastsPerDay); } var itemToCraft = ResourcesLibrary.TryGetBlueprint(itemGuid); // Render craft button - GameLogContext.Count = selectedCastsPerDay; - UmmUiRenderer.RenderLabelRow(L10NFormat("craftMagicItems-label-cast-spell-n-times-details", ability.Name, selectedCasterLevel)); + GameLogContext.Count = Selections.SelectedCastsPerDay; + UmmUiRenderer.RenderLabelRow(L10NFormat("craftMagicItems-label-cast-spell-n-times-details", ability.Name, Selections.SelectedCasterLevel)); GameLogContext.Clear(); var recipe = new RecipeData { PrerequisiteSpells = new[] {ability}, PrerequisitesMandatory = true }; - RenderRecipeBasedCraftItemControl(caster, craftingData, recipe, selectedCasterLevel, itemToCraft, upgradeItem); + RenderRecipeBasedCraftItemControl(caster, craftingData, recipe, Selections.SelectedCasterLevel, itemToCraft, upgradeItem); } public static int CharacterCasterLevel(UnitDescriptor character, Spellbook forSpellbook = null) { @@ -1839,15 +1834,15 @@ private static void RenderCraftMundaneItemsSection() { && (data.ParentNameId == null || SubCraftingData[data.ParentNameId][0] == data)) .ToArray(); var itemTypeNames = itemTypes.Select(data => new L10NString(data.ParentNameId ?? data.NameId).ToString()).ToArray(); - var selectedItemTypeIndex = upgradingBlueprint == null - ? DrawSelectionUserInterfaceElements("Mundane Crafting: ", itemTypeNames, 6, ref selectedCustomName) + var selectedItemTypeIndex = Selections.UpgradingBlueprint == null + ? DrawSelectionUserInterfaceElements("Mundane Crafting: ", itemTypeNames, 6, ref Selections.SelectedCustomName) : GetSelectionIndex("Mundane Crafting: "); var selectedCraftingData = itemTypes[selectedItemTypeIndex]; if (selectedCraftingData.ParentNameId != null) { itemTypeNames = SubCraftingData[selectedCraftingData.ParentNameId].Select(data => new L10NString(data.NameId).ToString()).ToArray(); var label = new L10NString(selectedCraftingData.ParentNameId) + ": "; - var selectedItemSubTypeIndex = upgradingBlueprint == null + var selectedItemSubTypeIndex = Selections.UpgradingBlueprint == null ? DrawSelectionUserInterfaceElements(label, itemTypeNames, 6) : GetSelectionIndex(label); @@ -1861,8 +1856,8 @@ private static void RenderCraftMundaneItemsSection() { BlueprintItem baseBlueprint; - if (upgradingBlueprint != null) { - baseBlueprint = upgradingBlueprint; + if (Selections.UpgradingBlueprint != null) { + baseBlueprint = Selections.UpgradingBlueprint; UmmUiRenderer.RenderLabelRow($"Applying upgrades to {baseBlueprint.Name}"); } else { // Choose mundane item of selected type to create @@ -1877,7 +1872,7 @@ private static void RenderCraftMundaneItemsSection() { return; } - var selectedUpgradeItemIndex = DrawSelectionUserInterfaceElements("Item: ", blueprintNames, 5, ref selectedCustomName); + var selectedUpgradeItemIndex = DrawSelectionUserInterfaceElements("Item: ", blueprintNames, 5, ref Selections.SelectedCustomName); baseBlueprint = blueprints[selectedUpgradeItemIndex]; // See existing item details and enchantments. UmmUiRenderer.RenderLabelRow(baseBlueprint.Description); @@ -1894,7 +1889,7 @@ private static void RenderCraftMundaneItemsSection() { .OrderBy(recipe => recipe.NameId) .ToArray(); var recipeNames = availableRecipes.Select(recipe => recipe.NameId).ToArray(); - var selectedRecipeIndex = DrawSelectionUserInterfaceElements("Craft: ", recipeNames, 6, ref selectedCustomName); + var selectedRecipeIndex = DrawSelectionUserInterfaceElements("Craft: ", recipeNames, 6, ref Selections.SelectedCustomName); var selectedRecipe = availableRecipes.Any() ? availableRecipes[selectedRecipeIndex] : null; var selectedEnchantment = selectedRecipe?.Enchantments.Length == 1 ? selectedRecipe.Enchantments[0] : null; if (selectedRecipe != null && selectedRecipe.Material != 0) { @@ -1965,13 +1960,13 @@ private static void RenderCraftMundaneItemsSection() { if (!itemToCraft) { UmmUiRenderer.RenderLabelRow($"Error: null custom item from looking up blueprint ID {itemGuid}"); } else { - if (upgradingBlueprint != null && GUILayout.Button($"Cancel {baseBlueprint.Name}", GUILayout.ExpandWidth(false))) { - upgradingBlueprint = null; + if (Selections.UpgradingBlueprint != null && GUILayout.Button($"Cancel {baseBlueprint.Name}", GUILayout.ExpandWidth(false))) { + Selections.UpgradingBlueprint = null; } if (craftingData.MundaneEnhancementsStackable) { if (upgradeName != null && GUILayout.Button($"Add {upgradeName} to {baseBlueprint.Name}", GUILayout.ExpandWidth(false))) { - upgradingBlueprint = itemToCraft; + Selections.UpgradingBlueprint = itemToCraft; } RenderRecipeBasedCraftItemControl(crafter, craftingData, null, 0, baseBlueprint); @@ -2075,7 +2070,7 @@ private static bool IsPlayerInCapital() { } public static UnitEntityData GetSelectedCrafter(bool render) { - currentCaster = null; + Selections.CurrentCaster = null; // Only allow remote companions if the player is in the capital. var remote = IsPlayerInCapital(); var characters = UIUtility.GetGroup(remote).Where(character => character != null @@ -2098,7 +2093,7 @@ public static UnitEntityData GetSelectedCrafter(bool render) { var partyNames = characters.Select(entity => $"{entity.CharacterName}" + $"{((GetCraftingTimerComponentForCaster(entity.Descriptor)?.CraftingProjects.Any() ?? false) ? "*" : "")}") .ToArray(); - selectedSpellcasterIndex = DrawSelectionUserInterfaceElements(label, partyNames, 8, ref upgradingBlueprint); + selectedSpellcasterIndex = DrawSelectionUserInterfaceElements(label, partyNames, 8, ref Selections.UpgradingBlueprint); } if (selectedSpellcasterIndex >= characters.Length) { selectedSpellcasterIndex = 0; @@ -2137,13 +2132,14 @@ public static int DrawSelectionUserInterfaceElements(string label, string[] o return newIndex; } - private static int GetSelectionIndex(string label) { - return SelectedIndex.ContainsKey(label) ? SelectedIndex[label] : 0; + private static int GetSelectionIndex(string label) + { + return Selections.SelectedIndex.ContainsKey(label) ? Selections.SelectedIndex[label] : 0; } private static void SetSelectionIndex(string label, int value) { - SelectedIndex[label] = value; + Selections.SelectedIndex[label] = value; } public static void AddItemBlueprintForSpell(UsableItemType itemType, BlueprintItemEquipment itemBlueprint) { @@ -2465,7 +2461,7 @@ private static void BeginCraftingSpellBasedItem(UnitEntityData caster, SpellBase new[] { spellBlueprint }); AddNewProject(caster.Descriptor, project); CalculateProjectEstimate(project); - currentSection = OpenSection.ProjectsSection; + Selections.CurrentSection = OpenSection.ProjectsSection; } } @@ -2573,13 +2569,13 @@ private static void RenderRecipeBasedCraftItemControl(UnitEntityData caster, Ite recipe?.AnyPrerequisite ?? false, upgradeItem, recipe?.CrafterPrerequisites ?? new CrafterPrerequisiteType[0]); AddNewProject(caster.Descriptor, project); CalculateProjectEstimate(project); - currentSection = OpenSection.ProjectsSection; + Selections.CurrentSection = OpenSection.ProjectsSection; } // Reset base blueprint for next item - selectedBaseBlueprint = null; + Selections.SelectedBaseBlueprint = null; // And stop upgrading the item, if relevant. - upgradingBlueprint = null; + Selections.UpgradingBlueprint = null; } } @@ -3545,7 +3541,7 @@ private static void WorkOnProjects(UnitDescriptor caster, bool returningToCapita return; } - currentCaster = caster.Unit; + Selections.CurrentCaster = caster.Unit; var withPlayer = Game.Instance.Player.PartyCharacters.Contains(caster.Unit); var playerInCapital = IsPlayerInCapital(); // Only update characters in the capital when the player is also there. From 57353ae4d7f671dc65250857b0d0cfaa0cf53245 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Fri, 17 Jul 2020 11:27:08 -0500 Subject: [PATCH 066/132] Moving the SelectionIndex getter and setter into the Selections class --- CraftMagicItems/Config/Selections.cs | 17 +++++++++++++++++ CraftMagicItems/Main.cs | 20 +++++--------------- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/CraftMagicItems/Config/Selections.cs b/CraftMagicItems/Config/Selections.cs index 8dc9297..25d83ea 100644 --- a/CraftMagicItems/Config/Selections.cs +++ b/CraftMagicItems/Config/Selections.cs @@ -50,5 +50,22 @@ public class Selections /// Blueprint that is currently being upgraded public BlueprintItem UpgradingBlueprint; + + + /// Retrieves the select index in matching the key of + /// Label used as a key to search on + /// The selected index value, or 0 if cannot be found + public int GetSelectionIndex(string label) + { + return SelectedIndex.ContainsKey(label) ? SelectedIndex[label] : 0; + } + + /// Sets the matching the key of + /// Label used as a key to search on + /// Value to assign + public void SetSelectionIndex(string label, int value) + { + SelectedIndex[label] = value; + } } } \ No newline at end of file diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 24dadd7..fa998b4 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -1836,7 +1836,7 @@ private static void RenderCraftMundaneItemsSection() { var itemTypeNames = itemTypes.Select(data => new L10NString(data.ParentNameId ?? data.NameId).ToString()).ToArray(); var selectedItemTypeIndex = Selections.UpgradingBlueprint == null ? DrawSelectionUserInterfaceElements("Mundane Crafting: ", itemTypeNames, 6, ref Selections.SelectedCustomName) - : GetSelectionIndex("Mundane Crafting: "); + : Selections.GetSelectionIndex("Mundane Crafting: "); var selectedCraftingData = itemTypes[selectedItemTypeIndex]; if (selectedCraftingData.ParentNameId != null) { @@ -1844,7 +1844,7 @@ private static void RenderCraftMundaneItemsSection() { var label = new L10NString(selectedCraftingData.ParentNameId) + ": "; var selectedItemSubTypeIndex = Selections.UpgradingBlueprint == null ? DrawSelectionUserInterfaceElements(label, itemTypeNames, 6) - : GetSelectionIndex(label); + : Selections.GetSelectionIndex(label); selectedCraftingData = SubCraftingData[selectedCraftingData.ParentNameId][selectedItemSubTypeIndex]; } @@ -2088,7 +2088,7 @@ public static UnitEntityData GetSelectedCrafter(bool render) { } const string label = "Crafter: "; - var selectedSpellcasterIndex = GetSelectionIndex(label); + var selectedSpellcasterIndex = Selections.GetSelectionIndex(label); if (render) { var partyNames = characters.Select(entity => $"{entity.CharacterName}" + $"{((GetCraftingTimerComponentForCaster(entity.Descriptor)?.CraftingProjects.Any() ?? false) ? "*" : "")}") @@ -2114,7 +2114,7 @@ public static int DrawSelectionUserInterfaceElements(string label, string[] opti public static int DrawSelectionUserInterfaceElements(string label, string[] options, int horizontalCount, ref T emptyOnChange, bool addSpace = true) { - var index = GetSelectionIndex(label); + var index = Selections.GetSelectionIndex(label); if (index >= options.Length) { index = 0; @@ -2127,21 +2127,11 @@ public static int DrawSelectionUserInterfaceElements(string label, string[] o emptyOnChange = default(T); } - SetSelectionIndex(label, newIndex); + Selections.SetSelectionIndex(label, newIndex); return newIndex; } - private static int GetSelectionIndex(string label) - { - return Selections.SelectedIndex.ContainsKey(label) ? Selections.SelectedIndex[label] : 0; - } - - private static void SetSelectionIndex(string label, int value) - { - Selections.SelectedIndex[label] = value; - } - public static void AddItemBlueprintForSpell(UsableItemType itemType, BlueprintItemEquipment itemBlueprint) { if (!SpellIdToItem.ContainsKey(itemType)) { SpellIdToItem.Add(itemType, new Dictionary>()); From ca9beb55544ba9ecfbfc62736300b45f5852997b Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Fri, 17 Jul 2020 16:49:29 -0500 Subject: [PATCH 067/132] Extracting the various dictionary data --- CraftMagicItems/Config/DictionaryData.cs | 47 ++++++++++ CraftMagicItems/CraftMagicItems.csproj | 1 + CraftMagicItems/Main.cs | 106 +++++++++++------------ 3 files changed, 98 insertions(+), 56 deletions(-) create mode 100644 CraftMagicItems/Config/DictionaryData.cs diff --git a/CraftMagicItems/Config/DictionaryData.cs b/CraftMagicItems/Config/DictionaryData.cs new file mode 100644 index 0000000..c8609ec --- /dev/null +++ b/CraftMagicItems/Config/DictionaryData.cs @@ -0,0 +1,47 @@ +using System.Collections.Generic; +using Kingmaker.Blueprints.Items; +using Kingmaker.Blueprints.Items.Equipment; +using Kingmaker.Enums.Damage; + +namespace CraftMagicItems.Config +{ + /// + /// Class containing all of the loaded dictionaries of data that are loaded + /// from config files or game resources + /// + public class DictionaryData + { + /// Collection of items that are related to spells + public readonly Dictionary>> SpellIdToItem; + + /// Crafting data loaded fron JSON files and its hierarchy + public readonly Dictionary> SubCraftingData; + + /// Collection of items matching type of blueprint (shield, armor, weapon) + public readonly Dictionary TypeToItem; + + /// Collection of various item blueprints, keyed on enchantment blueprint ID + public readonly Dictionary> EnchantmentIdToItem; + + /// Collection of various recipies, keyed on enchantment blueprint ID + public readonly Dictionary> EnchantmentIdToRecipe; + + /// Collection of various recipies, keyed on physical material + public readonly Dictionary> MaterialToRecipe; + + /// Collection of various enchantment costs, keyed on enchantment blueprint ID + public readonly Dictionary EnchantmentIdToCost; + + /// Default constructor + public DictionaryData() + { + SpellIdToItem = new Dictionary>>(); + SubCraftingData = new Dictionary>(); + TypeToItem = new Dictionary(); + EnchantmentIdToItem = new Dictionary>(); + EnchantmentIdToRecipe = new Dictionary>(); + MaterialToRecipe = new Dictionary>(); + EnchantmentIdToCost = new Dictionary(); + } + } +} \ No newline at end of file diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index c2e3cae..edd86ac 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -93,6 +93,7 @@ + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index fa998b4..ff02aa5 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -151,6 +151,9 @@ public bool MatchPostfix(MethodBase method) { /// Settings that are saved and managed by Unity Mod Manager public static Settings ModSettings; + /// Dictionaries that are loaded from configs and such + public static DictionaryData LoadedData; + public static UnityModManager.ModEntry ModEntry; public static CraftMagicItemsAccessors Accessors; public static ItemCraftingData[] ItemCraftingData; @@ -165,15 +168,6 @@ public bool MatchPostfix(MethodBase method) { private static HarmonyLib.Harmony harmonyInstance; private static CraftMagicItemsBlueprintPatcher blueprintPatcher; - private static readonly Dictionary>> SpellIdToItem = - new Dictionary>>(); - - private static readonly Dictionary> SubCraftingData = new Dictionary>(); - private static readonly Dictionary TypeToItem = new Dictionary(); - private static readonly Dictionary> EnchantmentIdToItem = new Dictionary>(); - private static readonly Dictionary> EnchantmentIdToRecipe = new Dictionary>(); - private static readonly Dictionary> MaterialToRecipe = new Dictionary>(); - private static readonly Dictionary EnchantmentIdToCost = new Dictionary(); private static readonly Dictionary ItemUpgradeProjects = new Dictionary(); private static readonly List ItemCreationProjects = new List(); @@ -770,17 +764,17 @@ private static string GetBlueprintItemType(BlueprintItem blueprint) { private static BlueprintItem GetStandardItem(BlueprintItem blueprint) { string assetGuid = GetBlueprintItemType(blueprint); - return !string.IsNullOrEmpty(assetGuid) && TypeToItem.ContainsKey(assetGuid) ? TypeToItem[assetGuid] : null; + return !string.IsNullOrEmpty(assetGuid) && LoadedData.TypeToItem.ContainsKey(assetGuid) ? LoadedData.TypeToItem[assetGuid] : null; } public static RecipeData FindSourceRecipe(string selectedEnchantmentId, BlueprintItem blueprint) { List recipes = null; - if (EnchantmentIdToRecipe.ContainsKey(selectedEnchantmentId)) { - recipes = EnchantmentIdToRecipe[selectedEnchantmentId]; + if (LoadedData.EnchantmentIdToRecipe.ContainsKey(selectedEnchantmentId)) { + recipes = LoadedData.EnchantmentIdToRecipe[selectedEnchantmentId]; } else { foreach (var material in blueprintPatcher.PhysicalDamageMaterialEnchantments.Keys) { if (blueprintPatcher.PhysicalDamageMaterialEnchantments[material] == selectedEnchantmentId) { - recipes = MaterialToRecipe[material]; + recipes = LoadedData.MaterialToRecipe[material]; } } } @@ -1831,7 +1825,7 @@ private static void RenderCraftMundaneItemsSection() { // Choose crafting data var itemTypes = ItemCraftingData .Where(data => data.NameId != null && data.FeatGuid == null - && (data.ParentNameId == null || SubCraftingData[data.ParentNameId][0] == data)) + && (data.ParentNameId == null || LoadedData.SubCraftingData[data.ParentNameId][0] == data)) .ToArray(); var itemTypeNames = itemTypes.Select(data => new L10NString(data.ParentNameId ?? data.NameId).ToString()).ToArray(); var selectedItemTypeIndex = Selections.UpgradingBlueprint == null @@ -1840,13 +1834,13 @@ private static void RenderCraftMundaneItemsSection() { var selectedCraftingData = itemTypes[selectedItemTypeIndex]; if (selectedCraftingData.ParentNameId != null) { - itemTypeNames = SubCraftingData[selectedCraftingData.ParentNameId].Select(data => new L10NString(data.NameId).ToString()).ToArray(); + itemTypeNames = LoadedData.SubCraftingData[selectedCraftingData.ParentNameId].Select(data => new L10NString(data.NameId).ToString()).ToArray(); var label = new L10NString(selectedCraftingData.ParentNameId) + ": "; var selectedItemSubTypeIndex = Selections.UpgradingBlueprint == null ? DrawSelectionUserInterfaceElements(label, itemTypeNames, 6) : Selections.GetSelectionIndex(label); - selectedCraftingData = SubCraftingData[selectedCraftingData.ParentNameId][selectedItemSubTypeIndex]; + selectedCraftingData = LoadedData.SubCraftingData[selectedCraftingData.ParentNameId][selectedItemSubTypeIndex]; } if (!(selectedCraftingData is RecipeBasedItemCraftingData craftingData)) { @@ -2133,19 +2127,19 @@ public static int DrawSelectionUserInterfaceElements(string label, string[] o } public static void AddItemBlueprintForSpell(UsableItemType itemType, BlueprintItemEquipment itemBlueprint) { - if (!SpellIdToItem.ContainsKey(itemType)) { - SpellIdToItem.Add(itemType, new Dictionary>()); + if (!LoadedData.SpellIdToItem.ContainsKey(itemType)) { + LoadedData.SpellIdToItem.Add(itemType, new Dictionary>()); } - if (!SpellIdToItem[itemType].ContainsKey(itemBlueprint.Ability.AssetGuid)) { - SpellIdToItem[itemType][itemBlueprint.Ability.AssetGuid] = new List(); + if (!LoadedData.SpellIdToItem[itemType].ContainsKey(itemBlueprint.Ability.AssetGuid)) { + LoadedData.SpellIdToItem[itemType][itemBlueprint.Ability.AssetGuid] = new List(); } - SpellIdToItem[itemType][itemBlueprint.Ability.AssetGuid].Add(itemBlueprint); + LoadedData.SpellIdToItem[itemType][itemBlueprint.Ability.AssetGuid].Add(itemBlueprint); } public static List FindItemBlueprintsForSpell(BlueprintScriptableObject spell, UsableItemType itemType) { - if (!SpellIdToItem.ContainsKey(itemType)) { + if (!LoadedData.SpellIdToItem.ContainsKey(itemType)) { #if PATCH21_BETA var allUsableItems = ResourcesLibrary.GetBlueprints(); #else @@ -2158,49 +2152,49 @@ public static List FindItemBlueprintsForSpell(BlueprintS } } - return SpellIdToItem[itemType].ContainsKey(spell.AssetGuid) ? SpellIdToItem[itemType][spell.AssetGuid] : null; + return LoadedData.SpellIdToItem[itemType].ContainsKey(spell.AssetGuid) ? LoadedData.SpellIdToItem[itemType][spell.AssetGuid] : null; } private static void AddItemForType(BlueprintItem blueprint) { string assetGuid = GetBlueprintItemType(blueprint); if (!string.IsNullOrEmpty(assetGuid)) { - TypeToItem.Add(assetGuid, blueprint); + LoadedData.TypeToItem.Add(assetGuid, blueprint); } } private static void AddItemIdForEnchantment(BlueprintItemEquipment itemBlueprint) { if (itemBlueprint != null) { foreach (var enchantment in GetEnchantments(itemBlueprint)) { - if (!EnchantmentIdToItem.ContainsKey(enchantment.AssetGuid)) { - EnchantmentIdToItem[enchantment.AssetGuid] = new List(); + if (!LoadedData.EnchantmentIdToItem.ContainsKey(enchantment.AssetGuid)) { + LoadedData.EnchantmentIdToItem[enchantment.AssetGuid] = new List(); } - EnchantmentIdToItem[enchantment.AssetGuid].Add(itemBlueprint); + LoadedData.EnchantmentIdToItem[enchantment.AssetGuid].Add(itemBlueprint); } } } private static void AddRecipeForEnchantment(string enchantmentId, RecipeData recipe) { - if (!EnchantmentIdToRecipe.ContainsKey(enchantmentId)) { - EnchantmentIdToRecipe.Add(enchantmentId, new List()); + if (!LoadedData.EnchantmentIdToRecipe.ContainsKey(enchantmentId)) { + LoadedData.EnchantmentIdToRecipe.Add(enchantmentId, new List()); } - if (!EnchantmentIdToRecipe[enchantmentId].Contains(recipe)) { - EnchantmentIdToRecipe[enchantmentId].Add(recipe); + if (!LoadedData.EnchantmentIdToRecipe[enchantmentId].Contains(recipe)) { + LoadedData.EnchantmentIdToRecipe[enchantmentId].Add(recipe); } } private static void AddRecipeForMaterial(PhysicalDamageMaterial material, RecipeData recipe) { - if (!MaterialToRecipe.ContainsKey(material)) { - MaterialToRecipe.Add(material, new List()); + if (!LoadedData.MaterialToRecipe.ContainsKey(material)) { + LoadedData.MaterialToRecipe.Add(material, new List()); } - if (!MaterialToRecipe[material].Contains(recipe)) { - MaterialToRecipe[material].Add(recipe); + if (!LoadedData.MaterialToRecipe[material].Contains(recipe)) { + LoadedData.MaterialToRecipe[material].Add(recipe); } } private static IEnumerable FindItemBlueprintForEnchantmentId(string assetGuid) { - return EnchantmentIdToItem.ContainsKey(assetGuid) ? EnchantmentIdToItem[assetGuid] : null; + return LoadedData.EnchantmentIdToItem.ContainsKey(assetGuid) ? LoadedData.EnchantmentIdToItem[assetGuid] : null; } public static bool CharacterHasFeat(UnitEntityData caster, string featGuid) { @@ -2684,7 +2678,7 @@ public static int ItemPlusEquivalent(BlueprintItem blueprint) { var enhancementLevel = 0; var cumulative = new Dictionary(); foreach (var enchantment in blueprint.Enchantments) { - if (EnchantmentIdToRecipe.ContainsKey(enchantment.AssetGuid)) { + if (LoadedData.EnchantmentIdToRecipe.ContainsKey(enchantment.AssetGuid)) { var recipe = FindSourceRecipe(enchantment.AssetGuid, blueprint); if (recipe != null && recipe.CostType == RecipeCostType.EnhancementLevelSquared) { var level = recipe.Enchantments.FindIndex(e => e == enchantment) + 1; @@ -2737,7 +2731,7 @@ private static int GetEnchantmentCost(string enchantmentId, BlueprintItem bluepr } } - return EnchantmentIdToCost.ContainsKey(enchantmentId) ? EnchantmentIdToCost[enchantmentId] : 0; + return LoadedData.EnchantmentIdToCost.ContainsKey(enchantmentId) ? LoadedData.EnchantmentIdToCost[enchantmentId] : 0; } private static int GetSpecialMaterialCost(PhysicalDamageMaterial material, BlueprintItemWeapon weapon, int baseCost, float weight) { @@ -2867,7 +2861,7 @@ public static int RulesRecipeItemCost(BlueprintItem blueprint, int baseCost = -1 } // Attempt to work out the cost of enchantments which aren't in recipes by checking if blueprint, which contains the enchantment, contains only other - // enchantments whose cost is know. + // enchantments whose cost is known. private static bool ReverseEngineerEnchantmentCost(BlueprintItemEquipment blueprint, string enchantmentId) { if (blueprint == null || blueprint.IsNotable || blueprint.Ability != null || blueprint.ActivatableAbility != null) { return false; @@ -2885,7 +2879,7 @@ private static bool ReverseEngineerEnchantmentCost(BlueprintItemEquipment bluepr continue; } - if (!EnchantmentIdToRecipe.ContainsKey(enchantment.AssetGuid) && !EnchantmentIdToCost.ContainsKey(enchantment.AssetGuid)) { + if (!LoadedData.EnchantmentIdToRecipe.ContainsKey(enchantment.AssetGuid) && !LoadedData.EnchantmentIdToCost.ContainsKey(enchantment.AssetGuid)) { return false; } @@ -2899,10 +2893,10 @@ private static bool ReverseEngineerEnchantmentCost(BlueprintItemEquipment bluepr var remainder = blueprint.Cost - 3 * costSum / 2; if (remainder >= mostExpensiveEnchantmentCost) { // enchantmentId is the most expensive enchantment - EnchantmentIdToCost[enchantmentId] = remainder; + LoadedData.EnchantmentIdToCost[enchantmentId] = remainder; } else { // mostExpensiveEnchantmentCost is the most expensive enchantment - EnchantmentIdToCost[enchantmentId] = (2 * remainder + mostExpensiveEnchantmentCost) / 3; + LoadedData.EnchantmentIdToCost[enchantmentId] = (2 * remainder + mostExpensiveEnchantmentCost) / 3; } return true; @@ -2964,11 +2958,11 @@ private static void InitialiseCraftingData() { } if (itemData.ParentNameId != null) { - if (!SubCraftingData.ContainsKey(itemData.ParentNameId)) { - SubCraftingData[itemData.ParentNameId] = new List(); + if (!LoadedData.SubCraftingData.ContainsKey(itemData.ParentNameId)) { + LoadedData.SubCraftingData[itemData.ParentNameId] = new List(); } - SubCraftingData[itemData.ParentNameId].Add(itemData); + LoadedData.SubCraftingData[itemData.ParentNameId].Add(itemData); } } @@ -2978,23 +2972,23 @@ private static void InitialiseCraftingData() { } var allNonRecipeEnchantmentsInItems = ResourcesLibrary.GetBlueprints() - .Where(enchantment => !EnchantmentIdToRecipe.ContainsKey(enchantment.AssetGuid) && EnchantmentIdToItem.ContainsKey(enchantment.AssetGuid)) + .Where(enchantment => !LoadedData.EnchantmentIdToRecipe.ContainsKey(enchantment.AssetGuid) && LoadedData.EnchantmentIdToItem.ContainsKey(enchantment.AssetGuid)) .ToArray(); // BlueprintEnchantment.EnchantmentCost seems to be full of nonsense values - attempt to set cost of each enchantment by using the prices of // items with enchantments. foreach (var enchantment in allNonRecipeEnchantmentsInItems) { - var itemsWithEnchantment = EnchantmentIdToItem[enchantment.AssetGuid]; + var itemsWithEnchantment = LoadedData.EnchantmentIdToItem[enchantment.AssetGuid]; foreach (var item in itemsWithEnchantment) { if (DoesItemMatchAllEnchantments(item, enchantment.AssetGuid)) { - EnchantmentIdToCost[enchantment.AssetGuid] = item.Cost; + LoadedData.EnchantmentIdToCost[enchantment.AssetGuid] = item.Cost; break; } } } foreach (var enchantment in allNonRecipeEnchantmentsInItems) { - if (!EnchantmentIdToCost.ContainsKey(enchantment.AssetGuid)) { - var itemsWithEnchantment = EnchantmentIdToItem[enchantment.AssetGuid]; + if (!LoadedData.EnchantmentIdToCost.ContainsKey(enchantment.AssetGuid)) { + var itemsWithEnchantment = LoadedData.EnchantmentIdToItem[enchantment.AssetGuid]; foreach (var item in itemsWithEnchantment) { if (ReverseEngineerEnchantmentCost(item, enchantment.AssetGuid)) { break; @@ -3244,12 +3238,12 @@ public static void ModEnabledChanged() { if (!modEnabled) { // Reset everything InitialiseMod initialises ItemCraftingData = null; - SubCraftingData.Clear(); - SpellIdToItem.Clear(); - TypeToItem.Clear(); - EnchantmentIdToItem.Clear(); - EnchantmentIdToCost.Clear(); - EnchantmentIdToRecipe.Clear(); + LoadedData.SubCraftingData.Clear(); + LoadedData.SpellIdToItem.Clear(); + LoadedData.TypeToItem.Clear(); + LoadedData.EnchantmentIdToItem.Clear(); + LoadedData.EnchantmentIdToCost.Clear(); + LoadedData.EnchantmentIdToRecipe.Clear(); UnpatchAllExcept(MethodPatchList); } else if (mainMenuStarted) { // If the mod is enabled and we're past the Start of main menu, (re-)initialise. From e579267640f690f706645677e7e384416092fe3d Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Fri, 17 Jul 2020 16:53:41 -0500 Subject: [PATCH 068/132] Moving a patcher over to the patching namespace --- CraftMagicItems/CraftMagicItems.csproj | 2 +- .../CraftMagicItemsBlueprintPatcher.cs | 495 ++++++++++++------ 2 files changed, 337 insertions(+), 160 deletions(-) rename CraftMagicItems/{ => Patches}/CraftMagicItemsBlueprintPatcher.cs (84%) diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index edd86ac..d626dd5 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -124,7 +124,7 @@ - + diff --git a/CraftMagicItems/CraftMagicItemsBlueprintPatcher.cs b/CraftMagicItems/Patches/CraftMagicItemsBlueprintPatcher.cs similarity index 84% rename from CraftMagicItems/CraftMagicItemsBlueprintPatcher.cs rename to CraftMagicItems/Patches/CraftMagicItemsBlueprintPatcher.cs index 828be51..4915ce6 100644 --- a/CraftMagicItems/CraftMagicItemsBlueprintPatcher.cs +++ b/CraftMagicItems/Patches/CraftMagicItemsBlueprintPatcher.cs @@ -31,8 +31,10 @@ using Object = UnityEngine.Object; #endif -namespace CraftMagicItems { - public class CraftMagicItemsBlueprintPatcher { +namespace CraftMagicItems.Patches +{ + public class CraftMagicItemsBlueprintPatcher + { public const string TimerBlueprintGuid = "52e4be2ba79c8c94d907bdbaf23ec15f#CraftMagicItems(timer)"; public const string BondedItemBuffBlueprintGuid = "1efa689e594ca82428b8fff1a739c9be#CraftMagicItems(bondedItem)"; @@ -75,7 +77,8 @@ public class CraftMagicItemsBlueprintPatcher { private readonly CraftMagicItemsAccessors accessors; - public CraftMagicItemsBlueprintPatcher(CraftMagicItemsAccessors accessors, bool modEnabled) { + public CraftMagicItemsBlueprintPatcher(CraftMagicItemsAccessors accessors, bool modEnabled) + { this.accessors = accessors; CustomBlueprintBuilder.Initialise(ApplyBlueprintPatch, modEnabled, new CustomBlueprintBuilder.Substitution { @@ -152,18 +155,22 @@ public CraftMagicItemsBlueprintPatcher(CraftMagicItemsAccessors accessors, bool }); } - public string BuildCustomSpellItemGuid(string originalGuid, int casterLevel, int spellLevel = -1, string spellId = null) { + public string BuildCustomSpellItemGuid(string originalGuid, int casterLevel, int spellLevel = -1, string spellId = null) + { // Check if GUID is already customised by this mod var match = BlueprintRegex.Match(originalGuid); - if (match.Success && match.Groups["casterLevel"].Success) { + if (match.Success && match.Groups["casterLevel"].Success) + { // Remove the existing customisation originalGuid = CustomBlueprintBuilder.AssetGuidWithoutMatch(originalGuid, match); // Use any values which aren't explicitly overridden - if (spellLevel == -1 && match.Groups["spellLevelMatch"].Success) { + if (spellLevel == -1 && match.Groups["spellLevelMatch"].Success) + { spellLevel = int.Parse(match.Groups["spellLevel"].Value); } - if (spellId == null && match.Groups["spellIdMatch"].Success) { + if (spellId == null && match.Groups["spellIdMatch"].Success) + { spellId = match.Groups["spellId"].Value; } } @@ -177,18 +184,23 @@ public string BuildCustomSpellItemGuid(string originalGuid, int casterLevel, int public string BuildCustomRecipeItemGuid(string originalGuid, IEnumerable enchantments, string[] remove = null, string name = null, string ability = null, string activatableAbility = null, int charges = -1, int weight = -1, PhysicalDamageMaterial material = 0, string visual = null, string animation = null, int casterLevel = -1, int spellLevel = -1, int perDay = -1, string nameId = null, string descriptionId = null, string secondEndGuid = null, - int priceAdjust = 0) { + int priceAdjust = 0) + { // Check if GUID is already customised by this mod var match = BlueprintRegex.Match(originalGuid); - if (match.Success && match.Groups["enchantments"].Success) { + if (match.Success && match.Groups["enchantments"].Success) + { var enchantmentsList = enchantments.Concat(match.Groups["enchantments"].Value.Split(';')) .Where(guid => guid.Length > 0).Distinct().ToList(); var removeList = match.Groups["remove"].Success ? (remove ?? Enumerable.Empty()).Concat(match.Groups["remove"].Value.Split(';')).Distinct().ToList() : remove?.ToList(); - if (removeList != null) { - foreach (var guid in removeList.ToArray()) { - if (enchantmentsList.Contains(guid)) { + if (removeList != null) + { + foreach (var guid in removeList.ToArray()) + { + if (enchantmentsList.Contains(guid)) + { enchantmentsList.Remove(guid); removeList.Remove(guid); } @@ -197,64 +209,79 @@ public string BuildCustomRecipeItemGuid(string originalGuid, IEnumerable enchantments = enchantmentsList; remove = removeList?.Count > 0 ? removeList.ToArray() : null; - if (name == null && match.Groups["name"].Success) { + if (name == null && match.Groups["name"].Success) + { name = match.Groups["name"].Value; nameId = null; } - if (ability == null && match.Groups["ability"].Success) { + if (ability == null && match.Groups["ability"].Success) + { ability = match.Groups["ability"].Value; } - if (activatableAbility == null && match.Groups["activatableAbility"].Success) { + if (activatableAbility == null && match.Groups["activatableAbility"].Success) + { activatableAbility = match.Groups["activatableAbility"].Value; } - if (charges == -1 && match.Groups["charges"].Success) { + if (charges == -1 && match.Groups["charges"].Success) + { perDay = int.Parse(match.Groups["charges"].Value); } - if (weight == -1 && match.Groups["weight"].Success) { + if (weight == -1 && match.Groups["weight"].Success) + { weight = int.Parse(match.Groups["weight"].Value); } - if (material == 0 && match.Groups["material"].Success) { + if (material == 0 && match.Groups["material"].Success) + { Enum.TryParse(match.Groups["material"].Value, out material); } - if (visual == null && match.Groups["visual"].Success) { + if (visual == null && match.Groups["visual"].Success) + { visual = match.Groups["visual"].Value; } - if (animation == null && match.Groups["animation"].Success) { + if (animation == null && match.Groups["animation"].Success) + { animation = match.Groups["animation"].Value; } - if (priceAdjust == 0 && match.Groups["priceAdjust"].Success) { + if (priceAdjust == 0 && match.Groups["priceAdjust"].Success) + { priceAdjust = int.Parse(match.Groups["priceAdjust"].Value); } - if (match.Groups["casterLevel"].Success) { + if (match.Groups["casterLevel"].Success) + { casterLevel = Math.Max(casterLevel, int.Parse(match.Groups["casterLevel"].Value)); } - if (match.Groups["spellLevel"].Success) { + if (match.Groups["spellLevel"].Success) + { spellLevel = Math.Max(spellLevel, int.Parse(match.Groups["spellLevel"].Value)); } - if (perDay == -1 && match.Groups["perDay"].Success) { + if (perDay == -1 && match.Groups["perDay"].Success) + { perDay = int.Parse(match.Groups["perDay"].Value); } - if (name == null && nameId == null && match.Groups["nameId"].Success) { + if (name == null && nameId == null && match.Groups["nameId"].Success) + { nameId = match.Groups["nameId"].Value; } - if (descriptionId == null && match.Groups["descriptionId"].Success) { + if (descriptionId == null && match.Groups["descriptionId"].Success) + { descriptionId = match.Groups["descriptionId"].Value; } - if (secondEndGuid == null && match.Groups["secondEnd"].Success) { + if (secondEndGuid == null && match.Groups["secondEnd"].Success) + { secondEndGuid = match.Groups["secondEnd"].Value; } @@ -282,9 +309,11 @@ public string BuildCustomRecipeItemGuid(string originalGuid, IEnumerable ")"; } - private string BuildCustomComponentsItemGuid(string originalGuid, string[] values, string nameId, string descriptionId) { + private string BuildCustomComponentsItemGuid(string originalGuid, string[] values, string nameId, string descriptionId) + { var components = ""; - for (var index = 0; index < values.Length; index += 3) { + for (var index = 0; index < values.Length; index += 3) + { components += $"{(index > 0 ? "," : "")}Component[{values[index]}]{values[index + 1]}={values[index + 2]}"; } @@ -292,12 +321,14 @@ private string BuildCustomComponentsItemGuid(string originalGuid, string[] value $"{originalGuid}{BlueprintPrefix}({components}{(nameId == null ? "" : $",nameId={nameId}")}{(descriptionId == null ? "" : $",descriptionId={descriptionId}")})"; } - private string BuildCustomFeatGuid(string originalGuid, string feat) { + private string BuildCustomFeatGuid(string originalGuid, string feat) + { return $"{originalGuid}{BlueprintPrefix}(feat={feat})"; } - private void ApplyBuffBlueprintPatch(BlueprintBuff blueprint, BlueprintComponent component, string nameId) { - blueprint.ComponentsArray = new[] {component}; + private void ApplyBuffBlueprintPatch(BlueprintBuff blueprint, BlueprintComponent component, string nameId) + { + blueprint.ComponentsArray = new[] { component }; accessors.SetBlueprintBuffFlags(blueprint, 2 + 8); // BlueprintBluff.Flags enum is private. Values are HiddenInUi = 2 + StayOnDeath = 8 blueprint.FxOnStart = new PrefabLink(); blueprint.FxOnRemove = new PrefabLink(); @@ -305,7 +336,8 @@ private void ApplyBuffBlueprintPatch(BlueprintBuff blueprint, BlueprintComponent accessors.SetBlueprintUnitFactDisplayName(blueprint) = new L10NString(nameId); } - private string ApplyTimerBlueprintPatch(BlueprintBuff blueprint) { + private string ApplyTimerBlueprintPatch(BlueprintBuff blueprint) + { #if PATCH21_BETA ApplyBuffBlueprintPatch(blueprint, SerializedScriptableObject.CreateInstance(), "craftMagicItems-timer-buff-name"); #else @@ -315,7 +347,8 @@ private string ApplyTimerBlueprintPatch(BlueprintBuff blueprint) { return TimerBlueprintGuid; } - private string ApplyBondedItemBlueprintPatch(BlueprintBuff blueprint) { + private string ApplyBondedItemBlueprintPatch(BlueprintBuff blueprint) + { #if PATCH21_BETA ApplyBuffBlueprintPatch(blueprint, SerializedScriptableObject.CreateInstance(), "craftMagicItems-bondedItem-buff-name"); #else @@ -324,7 +357,8 @@ private string ApplyBondedItemBlueprintPatch(BlueprintBuff blueprint) { return BondedItemBuffBlueprintGuid; } - private string ApplyFeatBlueprintPatch(BlueprintFeature blueprint, Match match) { + private string ApplyFeatBlueprintPatch(BlueprintFeature blueprint, Match match) + { var feat = match.Groups["feat"].Value; accessors.SetBlueprintUnitFactDisplayName(blueprint) = new L10NString($"craftMagicItems-feat-{feat}-displayName"); accessors.SetBlueprintUnitFactDescription(blueprint) = new L10NString($"craftMagicItems-feat-{feat}-description"); @@ -338,60 +372,70 @@ private string ApplyFeatBlueprintPatch(BlueprintFeature blueprint, Match match) var featGuid = BuildCustomFeatGuid(blueprint.AssetGuid, feat); var itemData = Main.ItemCraftingData.First(data => data.FeatGuid == featGuid); prerequisite.SetPrerequisiteCasterLevel(itemData.MinimumCasterLevel); - blueprint.ComponentsArray = new BlueprintComponent[] {prerequisite}; + blueprint.ComponentsArray = new BlueprintComponent[] { prerequisite }; return featGuid; } - private string ApplySpellItemBlueprintPatch(BlueprintItemEquipmentUsable blueprint, Match match) { + private string ApplySpellItemBlueprintPatch(BlueprintItemEquipmentUsable blueprint, Match match) + { var casterLevel = int.Parse(match.Groups["casterLevel"].Value); blueprint.CasterLevel = casterLevel; var spellLevel = -1; - if (match.Groups["spellLevelMatch"].Success) { + if (match.Groups["spellLevelMatch"].Success) + { spellLevel = int.Parse(match.Groups["spellLevel"].Value); blueprint.SpellLevel = spellLevel; } string spellId = null; - if (match.Groups["spellIdMatch"].Success) { + if (match.Groups["spellIdMatch"].Success) + { spellId = match.Groups["spellId"].Value; - blueprint.Ability = (BlueprintAbility) ResourcesLibrary.TryGetBlueprint(spellId); + blueprint.Ability = (BlueprintAbility)ResourcesLibrary.TryGetBlueprint(spellId); blueprint.DC = 0; } - if (blueprint.Ability != null && blueprint.Ability.LocalizedSavingThrow != null && blueprint.Ability.LocalizedSavingThrow.IsSet()) { + if (blueprint.Ability != null && blueprint.Ability.LocalizedSavingThrow != null && blueprint.Ability.LocalizedSavingThrow.IsSet()) + { blueprint.DC = 10 + blueprint.SpellLevel * 3 / 2; } accessors.SetBlueprintItemCost(blueprint) = 0; // Allow the game to auto-calculate the cost // Also store the new item blueprint in our spell-to-item lookup dictionary. var itemBlueprintsForSpell = Main.FindItemBlueprintsForSpell(blueprint.Ability, blueprint.Type); - if (itemBlueprintsForSpell == null || !itemBlueprintsForSpell.Contains(blueprint)) { + if (itemBlueprintsForSpell == null || !itemBlueprintsForSpell.Contains(blueprint)) + { Main.AddItemBlueprintForSpell(blueprint.Type, blueprint); } return BuildCustomSpellItemGuid(blueprint.AssetGuid, casterLevel, spellLevel, spellId); } - public static bool DoesBlueprintShowEnchantments(BlueprintItem blueprint) { - if (blueprint.ItemType == ItemsFilter.ItemType.Neck && Main.ItemPlusEquivalent(blueprint) > 0) { + public static bool DoesBlueprintShowEnchantments(BlueprintItem blueprint) + { + if (blueprint.ItemType == ItemsFilter.ItemType.Neck && Main.ItemPlusEquivalent(blueprint) > 0) + { return true; } return SlotsWhichShowEnchantments.Contains(blueprint.ItemType); } - private string ApplyRecipeItemBlueprintPatch(BlueprintItemEquipment blueprint, Match match) { + private string ApplyRecipeItemBlueprintPatch(BlueprintItemEquipment blueprint, Match match) + { var priceDelta = blueprint.Cost - Main.RulesRecipeItemCost(blueprint); string secondEndGuid = null; var removedIds = new List(); - if (match.Groups["remove"].Success) { + if (match.Groups["remove"].Success) + { removedIds = match.Groups["remove"].Value.Split(';').ToList(); } var enchantmentsValue = match.Groups["enchantments"].Value; var enchantmentIds = enchantmentsValue.Split(';').ToList(); - if (blueprint is BlueprintItemShield shield) { + if (blueprint is BlueprintItemShield shield) + { #if PATCH21_BETA var armorComponentClone = (BlueprintItemArmor)SerializedScriptableObject.Instantiate(shield.ArmorComponent); armorComponentClone.AssetGuid = shield.ArmorComponent.AssetGuid; @@ -400,15 +444,21 @@ private string ApplyRecipeItemBlueprintPatch(BlueprintItemEquipment blueprint, M var armorComponentClone = Object.Instantiate(shield.ArmorComponent); #endif ApplyRecipeItemBlueprintPatch(armorComponentClone, match); - if (match.Groups["secondEnd"].Success) { + if (match.Groups["secondEnd"].Success) + { secondEndGuid = match.Groups["secondEnd"].Value; - } else if (shield.WeaponComponent != null) { + } + else if (shield.WeaponComponent != null) + { var weaponEnchantmentIds = enchantmentIds; - if (weaponEnchantmentIds.Count > 0) { + if (weaponEnchantmentIds.Count > 0) + { enchantmentIds = new List(); var armorEnchantments = armorComponentClone.Enchantments; - foreach (var enchantment in armorEnchantments) { - if (weaponEnchantmentIds.Contains(enchantment.AssetGuid)) { + foreach (var enchantment in armorEnchantments) + { + if (weaponEnchantmentIds.Contains(enchantment.AssetGuid)) + { weaponEnchantmentIds.Remove(enchantment.AssetGuid); enchantmentIds.Add(enchantment.AssetGuid); } @@ -416,31 +466,41 @@ private string ApplyRecipeItemBlueprintPatch(BlueprintItemEquipment blueprint, M } var weaponRemovedIds = removedIds; - if (weaponRemovedIds.Count > 0) { + if (weaponRemovedIds.Count > 0) + { removedIds = new List(); var armorEnchantments = shield.ArmorComponent.Enchantments; - foreach (var enchantment in armorEnchantments) { - if (weaponRemovedIds.Contains(enchantment.AssetGuid)) { + foreach (var enchantment in armorEnchantments) + { + if (weaponRemovedIds.Contains(enchantment.AssetGuid)) + { weaponRemovedIds.Remove(enchantment.AssetGuid); removedIds.Add(enchantment.AssetGuid); } } } - if (weaponEnchantmentIds.Count > 0 || weaponRemovedIds.Count > 0) { + if (weaponEnchantmentIds.Count > 0 || weaponRemovedIds.Count > 0) + { PhysicalDamageMaterial weaponMaterial = 0; - if (match.Groups["material"].Success) { + if (match.Groups["material"].Success) + { Enum.TryParse(match.Groups["material"].Value, out weaponMaterial); } secondEndGuid = BuildCustomRecipeItemGuid(shield.WeaponComponent.AssetGuid, weaponEnchantmentIds, weaponRemovedIds.Count > 0 ? weaponRemovedIds.ToArray() : null, material: weaponMaterial); } } - if (secondEndGuid != null) { + + if (secondEndGuid != null) + { var weaponComponent = ResourcesLibrary.TryGetBlueprint(secondEndGuid); - if ((weaponComponent.DamageType.Physical.Form & PhysicalDamageForm.Piercing) != 0) { + if ((weaponComponent.DamageType.Physical.Form & PhysicalDamageForm.Piercing) != 0) + { accessors.SetBlueprintItemWeight(weaponComponent) = 5.0f; - } else { + } + else + { accessors.SetBlueprintItemWeight(weaponComponent) = 0.0f; } accessors.SetBlueprintItemShieldWeaponComponent(shield) = weaponComponent; @@ -454,15 +514,20 @@ private string ApplyRecipeItemBlueprintPatch(BlueprintItemEquipment blueprint, M // Copy Enchantments so we leave base blueprint alone var enchantmentsCopy = blueprint.Enchantments.ToList(); - if (!(blueprint is BlueprintItemShield)) { + if (!(blueprint is BlueprintItemShield)) + { accessors.SetBlueprintItemCachedEnchantments(blueprint) = enchantmentsCopy; } + // Remove enchantments first, to see if we end up with an item with no abilities. var removed = new List(); - if (match.Groups["remove"].Success) { - foreach (var guid in removedIds) { + if (match.Groups["remove"].Success) + { + foreach (var guid in removedIds) + { var enchantment = ResourcesLibrary.TryGetBlueprint(guid); - if (!enchantment) { + if (!enchantment) + { throw new Exception($"Failed to load enchantment {guid}"); } @@ -472,9 +537,11 @@ private string ApplyRecipeItemBlueprintPatch(BlueprintItemEquipment blueprint, M } string ability = null; - if (match.Groups["ability"].Success) { + if (match.Groups["ability"].Success) + { ability = match.Groups["ability"].Value; - if (blueprint.Ability != null) { + if (blueprint.Ability != null) + { replaceAbility = true; } blueprint.Ability = ability == "null" ? null : ResourcesLibrary.TryGetBlueprint(ability); @@ -483,9 +550,11 @@ private string ApplyRecipeItemBlueprintPatch(BlueprintItemEquipment blueprint, M } string activatableAbility = null; - if (match.Groups["activatableAbility"].Success) { + if (match.Groups["activatableAbility"].Success) + { activatableAbility = match.Groups["activatableAbility"].Value; - if (blueprint.ActivatableAbility != null) { + if (blueprint.ActivatableAbility != null) + { replaceAbility = true; } blueprint.ActivatableAbility = activatableAbility == "null" @@ -494,7 +563,8 @@ private string ApplyRecipeItemBlueprintPatch(BlueprintItemEquipment blueprint, M } int charges = -1; - if (match.Groups["charges"].Success) { + if (match.Groups["charges"].Success) + { charges = int.Parse(match.Groups["charges"].Value); blueprint.Charges = charges; blueprint.SpendCharges = true; @@ -503,17 +573,21 @@ private string ApplyRecipeItemBlueprintPatch(BlueprintItemEquipment blueprint, M } int weight = -1; - if (match.Groups["weight"].Success) { + if (match.Groups["weight"].Success) + { weight = int.Parse(match.Groups["weight"].Value); accessors.SetBlueprintItemWeight(blueprint) = weight * .01f; } var priceAdjust = 0; - if (match.Groups["priceAdjust"].Success) { + if (match.Groups["priceAdjust"].Success) + { priceAdjust = int.Parse(match.Groups["priceAdjust"].Value); priceDelta += priceAdjust; - } else if (!initiallyMundane && enchantmentsCopy.Count == 0 - && (blueprint.Ability == null || ability != null) && (blueprint.ActivatableAbility == null || activatableAbility != null)) { + } + else if (!initiallyMundane && enchantmentsCopy.Count == 0 + && (blueprint.Ability == null || ability != null) && (blueprint.ActivatableAbility == null || activatableAbility != null)) + { // We're down to a base item with no abilities - reset priceDelta. priceDelta = 0; } @@ -521,46 +595,59 @@ private string ApplyRecipeItemBlueprintPatch(BlueprintItemEquipment blueprint, M var skipped = new List(); var enchantmentsForDescription = new List(); int sizeCategoryChange = 0; - if (!string.IsNullOrEmpty(enchantmentsValue)) { - foreach (var guid in enchantmentIds) { + if (!string.IsNullOrEmpty(enchantmentsValue)) + { + foreach (var guid in enchantmentIds) + { var enchantment = ResourcesLibrary.TryGetBlueprint(guid); - if (!enchantment) { + if (!enchantment) + { throw new Exception($"Failed to load enchantment {guid}"); } var component = enchantment.GetComponent(); if (!string.IsNullOrEmpty(enchantment.Name) || - (component && component.Descriptor != ModifierDescriptor.ArmorEnhancement && component.Descriptor != ModifierDescriptor.ShieldEnhancement)) { + (component && component.Descriptor != ModifierDescriptor.ArmorEnhancement && component.Descriptor != ModifierDescriptor.ShieldEnhancement)) + { skipped.Add(enchantment); } enchantmentsForDescription.Add(enchantment); - if (blueprint is BlueprintItemArmor && guid == ItemQualityBlueprints.MithralArmorEnchantmentGuid) { + if (blueprint is BlueprintItemArmor && guid == ItemQualityBlueprints.MithralArmorEnchantmentGuid) + { // Mithral equipment has half weight accessors.SetBlueprintItemWeight(blueprint) = blueprint.Weight / 2; } - if (blueprint is BlueprintItemEquipmentHand) { + if (blueprint is BlueprintItemEquipmentHand) + { var weaponBaseSizeChange = enchantment.GetComponent(); - if (weaponBaseSizeChange != null) { + if (weaponBaseSizeChange != null) + { sizeCategoryChange = weaponBaseSizeChange.SizeCategoryChange; - if (sizeCategoryChange > 0) { + if (sizeCategoryChange > 0) + { accessors.SetBlueprintItemWeight(blueprint) = blueprint.Weight * 2; - } else if (sizeCategoryChange < 0) { + } + else if (sizeCategoryChange < 0) + { accessors.SetBlueprintItemWeight(blueprint) = blueprint.Weight / 2; } } } if (!(blueprint is BlueprintItemShield) && (Main.GetItemType(blueprint) != ItemsFilter.ItemType.Shield - || Main.FindSourceRecipe(guid, blueprint) != null)) { + || Main.FindSourceRecipe(guid, blueprint) != null)) + { enchantmentsCopy.Add(enchantment); } } } PhysicalDamageMaterial material = 0; - if (match.Groups["material"].Success) { + if (match.Groups["material"].Success) + { Enum.TryParse(match.Groups["material"].Value, out material); - if (blueprint is BlueprintItemWeapon weapon) { + if (blueprint is BlueprintItemWeapon weapon) + { accessors.SetBlueprintItemWeaponDamageType(weapon) = TraverseCloneAndSetField(weapon.DamageType, "Physical.Material", material.ToString()); accessors.SetBlueprintItemWeaponOverrideDamageType(weapon) = true; var materialGuid = PhysicalDamageMaterialEnchantments[material]; @@ -568,7 +655,8 @@ private string ApplyRecipeItemBlueprintPatch(BlueprintItemEquipment blueprint, M enchantmentsCopy.Add(enchantment); skipped.Add(enchantment); enchantmentsForDescription.Add(enchantment); - if (material == PhysicalDamageMaterial.Silver) { + if (material == PhysicalDamageMaterial.Silver) + { // PhysicalDamageMaterial.Silver is really Mithral, and Mithral equipment has half weight accessors.SetBlueprintItemWeight(blueprint) = blueprint.Weight / 2; } @@ -577,89 +665,111 @@ private string ApplyRecipeItemBlueprintPatch(BlueprintItemEquipment blueprint, M var equipmentHand = blueprint as BlueprintItemEquipmentHand; string visual = null; - if (match.Groups["visual"].Success) { + if (match.Groups["visual"].Success) + { visual = match.Groups["visual"].Value; // Copy icon from a different item var copyFromBlueprint = visual == "null" ? null : ResourcesLibrary.TryGetBlueprint(visual); var iconSprite = copyFromBlueprint == null ? null : copyFromBlueprint.Icon; accessors.SetBlueprintItemIcon(blueprint) = iconSprite; - if (equipmentHand != null && copyFromBlueprint is BlueprintItemEquipmentHand srcEquipmentHand) { + if (equipmentHand != null && copyFromBlueprint is BlueprintItemEquipmentHand srcEquipmentHand) + { accessors.SetBlueprintItemEquipmentHandVisualParameters(equipmentHand) = srcEquipmentHand.VisualParameters; - } else if (blueprint is BlueprintItemArmor armor && copyFromBlueprint is BlueprintItemArmor srcArmor) { + } + else if (blueprint is BlueprintItemArmor armor && copyFromBlueprint is BlueprintItemArmor srcArmor) + { accessors.SetBlueprintItemArmorVisualParameters(armor) = srcArmor.VisualParameters; } } string animation = null; - if (match.Groups["animation"].Success) { + if (match.Groups["animation"].Success) + { animation = match.Groups["animation"].Value; WeaponAnimationStyle weaponAnimation; - if (Enum.TryParse(animation, out weaponAnimation)) { - if (equipmentHand != null) { + if (Enum.TryParse(animation, out weaponAnimation)) + { + if (equipmentHand != null) + { accessors.SetBlueprintItemEquipmentWeaponAnimationStyle(equipmentHand.VisualParameters) = weaponAnimation; } } } var casterLevel = -1; - if (match.Groups["casterLevel"].Success) { + if (match.Groups["casterLevel"].Success) + { casterLevel = int.Parse(match.Groups["casterLevel"].Value); blueprint.CasterLevel = casterLevel; } var spellLevel = -1; - if (match.Groups["spellLevel"].Success) { + if (match.Groups["spellLevel"].Success) + { spellLevel = int.Parse(match.Groups["spellLevel"].Value); blueprint.SpellLevel = spellLevel; } var perDay = -1; - if (match.Groups["perDay"].Success) { + if (match.Groups["perDay"].Success) + { perDay = int.Parse(match.Groups["perDay"].Value); blueprint.Charges = perDay; blueprint.SpendCharges = true; blueprint.RestoreChargesOnRest = true; - if (blueprint.Ability.LocalizedSavingThrow != null && blueprint.Ability.LocalizedSavingThrow.IsSet()) { + if (blueprint.Ability.LocalizedSavingThrow != null && blueprint.Ability.LocalizedSavingThrow.IsSet()) + { blueprint.DC = 10 + blueprint.SpellLevel * 3 / 2; - } else { + } + else + { blueprint.DC = 0; } } string name = null; - if (match.Groups["name"].Success) { + if (match.Groups["name"].Success) + { name = match.Groups["name"].Value; accessors.SetBlueprintItemDisplayNameText(blueprint) = new FakeL10NString(name); } string nameId = null; - if (name == null && match.Groups["nameId"].Success) { + if (name == null && match.Groups["nameId"].Success) + { nameId = match.Groups["nameId"].Value; accessors.SetBlueprintItemDisplayNameText(blueprint) = new L10NString(nameId); } string descriptionId = null; - if (match.Groups["descriptionId"].Success) { + if (match.Groups["descriptionId"].Success) + { descriptionId = match.Groups["descriptionId"].Value; - if (descriptionId == "craftMagicItems-material-silver-weapon-description") { + if (descriptionId == "craftMagicItems-material-silver-weapon-description") + { // Backwards compatibility - remove custom silver weapon description descriptionId = null; } } - if (match.Groups["secondEnd"].Success && blueprint is BlueprintItemWeapon doubleWeapon) { + if (match.Groups["secondEnd"].Success && blueprint is BlueprintItemWeapon doubleWeapon) + { secondEndGuid = match.Groups["secondEnd"].Value; doubleWeapon.SecondWeapon = ResourcesLibrary.TryGetBlueprint(secondEndGuid); } - if (!DoesBlueprintShowEnchantments(blueprint)) { + if (!DoesBlueprintShowEnchantments(blueprint)) + { skipped.Clear(); } - if (descriptionId != null) { + if (descriptionId != null) + { accessors.SetBlueprintItemDescriptionText(blueprint) = new L10NString(descriptionId); accessors.SetBlueprintItemFlavorText(blueprint) = new L10NString(""); - } else if ((blueprint is BlueprintItemShield || Main.GetItemType(blueprint) != ItemsFilter.ItemType.Shield) - && (!DoesBlueprintShowEnchantments(blueprint) || enchantmentsForDescription.Count != skipped.Count || removed.Count > 0)) { + } + else if ((blueprint is BlueprintItemShield || Main.GetItemType(blueprint) != ItemsFilter.ItemType.Shield) + && (!DoesBlueprintShowEnchantments(blueprint) || enchantmentsForDescription.Count != skipped.Count || removed.Count > 0)) + { accessors.SetBlueprintItemDescriptionText(blueprint) = Main.BuildCustomRecipeItemDescription(blueprint, enchantmentsForDescription, skipped, removed, replaceAbility, ability, casterLevel, perDay); accessors.SetBlueprintItemFlavorText(blueprint) = new L10NString(""); @@ -670,32 +780,39 @@ private string ApplyRecipeItemBlueprintPatch(BlueprintItemEquipment blueprint, M activatableAbility, charges, weight, material, visual, animation, casterLevel, spellLevel, perDay, nameId, descriptionId, secondEndGuid, priceAdjust); } - private T CloneObject(T originalObject) { + private T CloneObject(T originalObject) + { var type = originalObject.GetType(); #if PATCH21_BETA - if (typeof(BlueprintScriptableObject).IsAssignableFrom(type)) { + if (typeof(BlueprintScriptableObject).IsAssignableFrom(type)) + { var srcBlueprint = originalObject as BlueprintScriptableObject; var dstBlueprint = (BlueprintScriptableObject)SerializedScriptableObject.Instantiate(originalObject as BlueprintScriptableObject); dstBlueprint.AssetGuid = srcBlueprint.AssetGuid; dstBlueprint.name = srcBlueprint.name + "(Clone)"; return (T)(object)dstBlueprint; - } else if (typeof(SerializedScriptableObject).IsAssignableFrom(type)) { + } + else if (typeof(SerializedScriptableObject).IsAssignableFrom(type)) + { return (T)(object)SerializedScriptableObject.Instantiate(originalObject as SerializedScriptableObject); } #else - if (typeof(ScriptableObject).IsAssignableFrom(type)) { + if (typeof(ScriptableObject).IsAssignableFrom(type)) + { return (T) (object) Object.Instantiate(originalObject as Object); } #endif var clone = (T) Activator.CreateInstance(type); #if PATCH21_BETA - for (; type != null && type != typeof(IDirectlySerializable); type = type.BaseType) { + for (; type != null && type != typeof(IDirectlySerializable); type = type.BaseType) #else - for (; type != null && type != typeof(Object); type = type.BaseType) { + for (; type != null && type != typeof(Object); type = type.BaseType) #endif + { var fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); - foreach (var field in fields) { + foreach (var field in fields) + { field.SetValue(clone, field.GetValue(originalObject)); } } @@ -703,11 +820,14 @@ private T CloneObject(T originalObject) { return clone; } - private T TraverseCloneAndSetField(T original, string field, string value) where T : class { - if (string.IsNullOrEmpty(field)) { + private T TraverseCloneAndSetField(T original, string field, string value) where T : class + { + if (string.IsNullOrEmpty(field)) + { value = value.Replace("#", ", "); var componentType = Type.GetType(value); - if (componentType == null) { + if (componentType == null) + { throw new Exception($"Failed to create object with type {value}"); } @@ -720,84 +840,117 @@ private T TraverseCloneAndSetField(T original, string field, string value) wh #endif : Activator.CreateInstance(componentType); - if (!(componentObject is T component)) { + if (!(componentObject is T component)) + { throw new Exception($"Failed to create expected instance with type {value}, " + $"result is {componentType.FullName}"); } return component; - } else { + } + else + { // Strip leading . off field - if (field.StartsWith(".")) { + if (field.StartsWith(".")) + { field = field.Substring(1); } } var clone = CloneObject(original); var fieldNameEnd = field.IndexOf('.'); - if (fieldNameEnd < 0) { + if (fieldNameEnd < 0) + { var fieldAccess = HarmonyLib.Traverse.Create(clone).Field(field); - if (!fieldAccess.FieldExists()) { + if (!fieldAccess.FieldExists()) + { throw new Exception( $"Field {field} does not exist on original of type {clone.GetType().FullName}, available fields: {string.Join(", ", HarmonyLib.Traverse.Create(clone).Fields())}"); } - if (value == "null") { + if (value == "null") + { fieldAccess.SetValue(null); - } else if (typeof(BlueprintScriptableObject).IsAssignableFrom(fieldAccess.GetValueType())) { + } + else if (typeof(BlueprintScriptableObject).IsAssignableFrom(fieldAccess.GetValueType())) + { fieldAccess.SetValue(ResourcesLibrary.TryGetBlueprint(value)); - } else if (fieldAccess.GetValueType() == typeof(LocalizedString)) { + } + else if (fieldAccess.GetValueType() == typeof(LocalizedString)) + { fieldAccess.SetValue(new L10NString(value)); - } else if (fieldAccess.GetValueType() == typeof(bool)) { + } + else if (fieldAccess.GetValueType() == typeof(bool)) + { fieldAccess.SetValue(value == "true"); - } else if (fieldAccess.GetValueType() == typeof(int)) { + } + else if (fieldAccess.GetValueType() == typeof(int)) + { fieldAccess.SetValue(int.Parse(value)); - } else if (fieldAccess.GetValueType().IsEnum) { + } + else if (fieldAccess.GetValueType().IsEnum) + { fieldAccess.SetValue(Enum.Parse(fieldAccess.GetValueType(), value)); - } else { + } + else + { fieldAccess.SetValue(value); } - } else { + } + else + { var thisField = field.Substring(0, fieldNameEnd); var remainingFields = field.Substring(fieldNameEnd + 1); var arrayPos = thisField.IndexOf('['); - if (arrayPos < 0) { + if (arrayPos < 0) + { var fieldAccess = HarmonyLib.Traverse.Create(clone).Field(thisField); - if (!fieldAccess.FieldExists()) { + if (!fieldAccess.FieldExists()) + { throw new Exception( $"Field {thisField} does not exist on original of type {clone.GetType().FullName}, available fields: {string.Join(", ", HarmonyLib.Traverse.Create(clone).Fields())}"); } - if (fieldAccess.GetValueType().IsArray) { + if (fieldAccess.GetValueType().IsArray) + { throw new Exception($"Field {thisField} is an array but overall access {field} did not index the array"); } fieldAccess.SetValue(TraverseCloneAndSetField(fieldAccess.GetValue(), remainingFields, value)); - } else { + } + else + { var index = int.Parse(new string(thisField.Skip(arrayPos + 1).TakeWhile(char.IsDigit).ToArray())); thisField = field.Substring(0, arrayPos); var fieldAccess = HarmonyLib.Traverse.Create(clone).Field(thisField); - if (!fieldAccess.FieldExists()) { + if (!fieldAccess.FieldExists()) + { throw new Exception( $"Field {thisField} does not exist on original of type {clone.GetType().FullName}, available fields: {string.Join(", ", HarmonyLib.Traverse.Create(clone).Fields())}"); } - if (!fieldAccess.GetValueType().IsArray) { + if (!fieldAccess.GetValueType().IsArray) + { throw new Exception( $"Field {thisField} is of type {fieldAccess.GetValueType().FullName} but overall access {field} used an array index"); } // TODO if I use fieldAccess.GetValue().ToArray() to make this universally applicable, the SetValue fails saying it can't // convert object[] to e.g. Condition[]. Hard-code to only support Condition for array for now. - if (fieldAccess.GetValueType() == typeof(Condition[])) { + if (fieldAccess.GetValueType() == typeof(Condition[])) + { var arrayClone = fieldAccess.GetValue().ToArray(); arrayClone[index] = TraverseCloneAndSetField(arrayClone[index], remainingFields, value); fieldAccess.SetValue(arrayClone); - } else if (fieldAccess.GetValueType() == typeof(GameAction[])) { + } + else if (fieldAccess.GetValueType() == typeof(GameAction[])) + { var arrayClone = fieldAccess.GetValue().ToArray(); arrayClone[index] = TraverseCloneAndSetField(arrayClone[index], remainingFields, value); fieldAccess.SetValue(arrayClone); - } else { + } + else + { throw new Exception( $"Field {thisField} is of unsupported array type {fieldAccess.GetValueType().FullName} ({field})"); } @@ -807,36 +960,43 @@ private T TraverseCloneAndSetField(T original, string field, string value) wh return clone; } - public void EnsureComponentNameUnique(BlueprintComponent component, BlueprintComponent[] existing) { + public void EnsureComponentNameUnique(BlueprintComponent component, BlueprintComponent[] existing) + { // According to Elmindra, components which are serialized need to have unique names in their array var name = component.name; var suffix = 0; - while (existing.Any(blueprint => blueprint.name == name)) { + while (existing.Any(blueprint => blueprint.name == name)) + { suffix++; name = $"{component.name}_{suffix}"; } component.name = name; } - private string ApplyItemEnchantmentBlueprintPatch(BlueprintScriptableObject blueprint, Match match) { + private string ApplyItemEnchantmentBlueprintPatch(BlueprintScriptableObject blueprint, Match match) + { var values = new List(); // Ensure Components array is not shared with base blueprint var componentsCopy = blueprint.ComponentsArray.ToArray(); var indexCaptures = match.Groups["index"].Captures; var fieldCaptures = match.Groups["field"].Captures; var valueCaptures = match.Groups["value"].Captures; - for (var index = 0; index < indexCaptures.Count; ++index) { + for (var index = 0; index < indexCaptures.Count; ++index) + { var componentIndex = int.Parse(indexCaptures[index].Value); var field = fieldCaptures[index].Value; var value = valueCaptures[index].Value; values.Add(indexCaptures[index].Value); values.Add(field); values.Add(value); - if (componentIndex >= componentsCopy.Length) { + if (componentIndex >= componentsCopy.Length) + { var component = TraverseCloneAndSetField(null, field, value); EnsureComponentNameUnique(component, componentsCopy); - componentsCopy = componentsCopy.Concat(new[] {component}).ToArray(); - } else { + componentsCopy = componentsCopy.Concat(new[] { component }).ToArray(); + } + else + { componentsCopy[componentIndex] = TraverseCloneAndSetField(componentsCopy[componentIndex], field, value); } } @@ -847,27 +1007,41 @@ private string ApplyItemEnchantmentBlueprintPatch(BlueprintScriptableObject blue var buff = blueprint as BlueprintBuff; var ability = blueprint as BlueprintAbility; string nameId = null; - if (match.Groups["nameId"].Success) { + if (match.Groups["nameId"].Success) + { nameId = match.Groups["nameId"].Value; - if (enchantment != null) { + if (enchantment != null) + { accessors.SetBlueprintItemEnchantmentEnchantName(enchantment) = new L10NString(nameId); - } else if (feature != null) { + } + else if (feature != null) + { accessors.SetBlueprintUnitFactDisplayName(feature) = new L10NString(nameId); - } else if (buff != null) { + } + else if (buff != null) + { accessors.SetBlueprintUnitFactDisplayName(buff) = new L10NString(nameId); } } string descriptionId = null; - if (match.Groups["descriptionId"].Success) { + if (match.Groups["descriptionId"].Success) + { descriptionId = match.Groups["descriptionId"].Value; - if (enchantment != null) { + if (enchantment != null) + { accessors.SetBlueprintItemEnchantmentDescription(enchantment) = new L10NString(descriptionId); - } else if (feature != null) { + } + else if (feature != null) + { accessors.SetBlueprintUnitFactDescription(feature) = new L10NString(descriptionId); - } else if (buff != null) { + } + else if (buff != null) + { accessors.SetBlueprintUnitFactDescription(buff) = new L10NString(descriptionId); - } else if (ability != null) { + } + else if (ability != null) + { accessors.SetBlueprintUnitFactDescription(ability) = new L10NString(descriptionId); } } @@ -878,8 +1052,10 @@ private string ApplyItemEnchantmentBlueprintPatch(BlueprintScriptableObject blue // Make our mod-specific updates to the blueprint based on the data stored in assetId. Return a string which // is the AssetGuid of the supplied blueprint plus our customization again, or null if we couldn't change the // blueprint. - private string ApplyBlueprintPatch(BlueprintScriptableObject blueprint, Match match) { - switch (blueprint) { + private string ApplyBlueprintPatch(BlueprintScriptableObject blueprint, Match match) + { + switch (blueprint) + { case BlueprintBuff buff when match.Groups["timer"].Success: return ApplyTimerBlueprintPatch(buff); case BlueprintBuff buff when match.Groups["bondedItem"].Success: @@ -898,9 +1074,10 @@ private string ApplyBlueprintPatch(BlueprintScriptableObject blueprint, Match ma return ApplyItemEnchantmentBlueprintPatch(buff, match); case BlueprintAbility ability when match.Groups["components"].Success: return ApplyItemEnchantmentBlueprintPatch(ability, match); - default: { - throw new Exception($"Match of assetId {match.Value} didn't match blueprint type {blueprint.GetType()}"); - } + default: + { + throw new Exception($"Match of assetId {match.Value} didn't match blueprint type {blueprint.GetType()}"); + } } } } From e33a7d3ab0666254c3bdc8d33107dd917d434ba7 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Fri, 17 Jul 2020 16:59:42 -0500 Subject: [PATCH 069/132] Moving loaded JSON data into the DictionaryData class (which needs to be renamed) --- CraftMagicItems/Config/DictionaryData.cs | 6 ++++ CraftMagicItems/Main.cs | 33 +++++++++---------- .../CraftMagicItemsBlueprintPatcher.cs | 2 +- .../UI/UserInterfaceEventHandlingLogic.cs | 2 +- 4 files changed, 24 insertions(+), 19 deletions(-) diff --git a/CraftMagicItems/Config/DictionaryData.cs b/CraftMagicItems/Config/DictionaryData.cs index c8609ec..fd6d239 100644 --- a/CraftMagicItems/Config/DictionaryData.cs +++ b/CraftMagicItems/Config/DictionaryData.cs @@ -32,6 +32,12 @@ public class DictionaryData /// Collection of various enchantment costs, keyed on enchantment blueprint ID public readonly Dictionary EnchantmentIdToCost; + /// Array of item crafting data read from JSON file + public ItemCraftingData[] ItemCraftingData; + + /// Array of custom loot data read from JSON file + public CustomLootItem[] CustomLootItems; + /// Default constructor public DictionaryData() { diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index ff02aa5..f4521b2 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -156,8 +156,6 @@ public bool MatchPostfix(MethodBase method) { public static UnityModManager.ModEntry ModEntry; public static CraftMagicItemsAccessors Accessors; - public static ItemCraftingData[] ItemCraftingData; - public static CustomLootItem[] CustomLootItems; #if PATCH21 public static readonly List PendingLogItems = new List(); #else @@ -432,7 +430,7 @@ private static void RenderCraftMagicItemsSection() { } //what crafting options are available (which feats are available for the selected character) - var itemTypes = ItemCraftingData + var itemTypes = LoadedData.ItemCraftingData .Where(data => data.FeatGuid != null && (ModSettings.IgnoreCraftingFeats || CharacterHasFeat(caster, data.FeatGuid))) .ToArray(); if (!Enumerable.Any(itemTypes) && !hasBondedItemFeature) { @@ -466,7 +464,7 @@ private static void RenderCraftMagicItemsSection() { private static RecipeBasedItemCraftingData GetBondedItemCraftingData(BondedItemComponent bondedComponent) { // Find crafting data relevant to the bonded item - return ItemCraftingData.OfType() + return LoadedData.ItemCraftingData.OfType() .First(data => data.Slots.Contains(bondedComponent.ownerItem.Blueprint.ItemType) && !IsMundaneCraftingData(data)); } @@ -1754,7 +1752,7 @@ private static void CancelCraftingProject(CraftingProjectData project) { Game.Instance.UI.Common.UISound.Play(UISoundType.LootCollectGold); var goldRefund = project.GoldSpent >= 0 ? project.GoldSpent : project.TargetCost; Game.Instance.Player.GainMoney(goldRefund); - var craftingData = ItemCraftingData.FirstOrDefault(data => data.Name == project.ItemType); + var craftingData = LoadedData.ItemCraftingData.FirstOrDefault(data => data.Name == project.ItemType); BuildCostString(out var cost, craftingData, goldRefund, project.SpellPrerequisites, project.ResultItem.Blueprint, project.UpgradeItem?.Blueprint); var factor = GetMaterialComponentMultiplier(craftingData, project.ResultItem.Blueprint, project.UpgradeItem?.Blueprint); if (factor > 0) { @@ -1823,7 +1821,7 @@ private static void RenderCraftMundaneItemsSection() { var crafter = GetSelectedCrafter(false); // Choose crafting data - var itemTypes = ItemCraftingData + var itemTypes = LoadedData.ItemCraftingData .Where(data => data.NameId != null && data.FeatGuid == null && (data.ParentNameId == null || LoadedData.SubCraftingData[data.ParentNameId][0] == data)) .ToArray(); @@ -2314,7 +2312,7 @@ private static void AddNewProject(UnitDescriptor casterDescriptor, CraftingProje } private static void CalculateProjectEstimate(CraftingProjectData project) { - var craftingData = ItemCraftingData.FirstOrDefault(data => data.Name == project.ItemType); + var craftingData = LoadedData.ItemCraftingData.FirstOrDefault(data => data.Name == project.ItemType); StatType craftingSkill; int dc; int progressRate; @@ -2908,9 +2906,9 @@ private static class MainMenuStartPatch { private static void InitialiseCraftingData() { // Read the crafting data now that ResourcesLibrary is loaded. - ItemCraftingData = ReadJsonFile($"{ModEntry.Path}/Data/ItemTypes.json", new CraftingTypeConverter()); + LoadedData.ItemCraftingData = ReadJsonFile($"{ModEntry.Path}/Data/ItemTypes.json", new CraftingTypeConverter()); // Initialise lookup tables. - foreach (var itemData in ItemCraftingData) { + foreach (var itemData in LoadedData.ItemCraftingData) { if (itemData is RecipeBasedItemCraftingData recipeBased) { recipeBased.Recipes = recipeBased.RecipeFileNames.Aggregate(Enumerable.Empty(), (all, fileName) => all.Concat(ReadJsonFile($"{ModEntry.Path}/Data/{fileName}")) @@ -2996,7 +2994,8 @@ private static void InitialiseCraftingData() { } } } - CustomLootItems = ReadJsonFile($"{ModEntry.Path}/Data/LootItems.json"); + + LoadedData.CustomLootItems = ReadJsonFile($"{ModEntry.Path}/Data/LootItems.json"); } private static void AddCraftingFeats(ObjectIDGenerator idGenerator, BlueprintProgression progression) { @@ -3007,7 +3006,7 @@ private static void AddCraftingFeats(ObjectIDGenerator idGenerator, BlueprintPro // Use ObjectIDGenerator to detect which shared lists we've added the feats to. idGenerator.GetId(selection.AllFeatures, out var firstTime); if (firstTime) { - foreach (var data in ItemCraftingData) { + foreach (var data in LoadedData.ItemCraftingData) { if (data.FeatGuid != null) { var featBlueprint = ResourcesLibrary.TryGetBlueprint(data.FeatGuid) as BlueprintFeature; var list = selection.AllFeatures.ToList(); @@ -3032,7 +3031,7 @@ private static void AddAllCraftingFeats() { } // Alchemists get Brew Potion as a bonus 1st level feat, except for Grenadier archetype alchemists. - var brewPotionData = ItemCraftingData.First(data => data.Name == "Potion"); + var brewPotionData = LoadedData.ItemCraftingData.First(data => data.Name == "Potion"); var brewPotion = ResourcesLibrary.TryGetBlueprint(brewPotionData.FeatGuid); var alchemistProgression = ResourcesLibrary.TryGetBlueprint(ClassBlueprints.AlchemistProgressionGuid); var grenadierArchetype = ResourcesLibrary.TryGetBlueprint(ClassBlueprints.AlchemistGrenadierArchetypeGuid); @@ -3053,7 +3052,7 @@ private static void AddAllCraftingFeats() { } // Scroll Savant should get Scribe Scroll as a bonus 1st level feat. - var scribeScrollData = ItemCraftingData.First(data => data.Name == "Scroll"); + var scribeScrollData = LoadedData.ItemCraftingData.First(data => data.Name == "Scroll"); var scribeScroll = ResourcesLibrary.TryGetBlueprint(scribeScrollData.FeatGuid); var scrollSavantArchetype = ResourcesLibrary.TryGetBlueprint(ClassBlueprints.ScrollSavantArchetypeGuid); if (scribeScroll != null && scrollSavantArchetype != null) { @@ -3237,7 +3236,7 @@ public static void ModEnabledChanged() { if (!modEnabled) { // Reset everything InitialiseMod initialises - ItemCraftingData = null; + LoadedData.ItemCraftingData = null; LoadedData.SubCraftingData.Clear(); LoadedData.SpellIdToItem.Clear(); LoadedData.TypeToItem.Clear(); @@ -3587,7 +3586,7 @@ private static void WorkOnProjects(UnitDescriptor caster, bool returningToCapita } } - var craftingData = ItemCraftingData.FirstOrDefault(data => data.Name == project.ItemType); + var craftingData = LoadedData.ItemCraftingData.FirstOrDefault(data => data.Name == project.ItemType); StatType craftingSkill; int dc; int progressRate; @@ -3826,7 +3825,7 @@ private static void AddToLootTables(BlueprintItem blueprint, string[] tableNames } private static void UpgradeSave(Version version) { - foreach (var lootItem in CustomLootItems) { + foreach (var lootItem in LoadedData.CustomLootItems) { var firstTime = (version == null || version.CompareTo(lootItem.AddInVersion) < 0); var item = ResourcesLibrary.TryGetBlueprint(lootItem.AssetGuid); if (item == null) { @@ -3856,7 +3855,7 @@ private static void Postfix() { foreach (var project in timer.CraftingProjects) { if (project.ItemBlueprint != null) { // Migrate all projects using ItemBlueprint to use ResultItem - var craftingData = ItemCraftingData.First(data => data.Name == project.ItemType); + var craftingData = LoadedData.ItemCraftingData.First(data => data.Name == project.ItemType); project.ResultItem = BuildItemEntity(project.ItemBlueprint, craftingData, character); project.ItemBlueprint = null; } diff --git a/CraftMagicItems/Patches/CraftMagicItemsBlueprintPatcher.cs b/CraftMagicItems/Patches/CraftMagicItemsBlueprintPatcher.cs index 4915ce6..478fff9 100644 --- a/CraftMagicItems/Patches/CraftMagicItemsBlueprintPatcher.cs +++ b/CraftMagicItems/Patches/CraftMagicItemsBlueprintPatcher.cs @@ -370,7 +370,7 @@ private string ApplyFeatBlueprintPatch(BlueprintFeature blueprint, Match match) var prerequisite = ScriptableObject.CreateInstance(); #endif var featGuid = BuildCustomFeatGuid(blueprint.AssetGuid, feat); - var itemData = Main.ItemCraftingData.First(data => data.FeatGuid == featGuid); + var itemData = Main.LoadedData.ItemCraftingData.First(data => data.FeatGuid == featGuid); prerequisite.SetPrerequisiteCasterLevel(itemData.MinimumCasterLevel); blueprint.ComponentsArray = new BlueprintComponent[] { prerequisite }; return featGuid; diff --git a/CraftMagicItems/UI/UserInterfaceEventHandlingLogic.cs b/CraftMagicItems/UI/UserInterfaceEventHandlingLogic.cs index 17fc4f8..b4c9b26 100644 --- a/CraftMagicItems/UI/UserInterfaceEventHandlingLogic.cs +++ b/CraftMagicItems/UI/UserInterfaceEventHandlingLogic.cs @@ -80,7 +80,7 @@ public static void RenderFeatReassignmentSection(IFeatReassignmentSectionRendere } var casterLevel = Main.CharacterCasterLevel(caster.Descriptor); - var missingFeats = Main.ItemCraftingData + var missingFeats = Main.LoadedData.ItemCraftingData .Where(data => data.FeatGuid != null && !Main.CharacterHasFeat(caster, data.FeatGuid) && data.MinimumCasterLevel <= casterLevel) .ToArray(); if (missingFeats.Length == 0) From 298601e5ea4b158216cc6feda932656ff3fb999e Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Fri, 17 Jul 2020 17:54:57 -0500 Subject: [PATCH 070/132] Moving Localization (L10N) files into Localization namespace. This will get me some traction to expose a helper in a logical location and tear things away from Main. --- CraftMagicItems/BondedItemComponent.cs | 1 + .../Constants/LocalizedStringBlueprints.cs | 3 +- CraftMagicItems/CraftMagicItems.csproj | 4 +- .../{ => Localization}/L10NString.cs | 51 ++++++++----- CraftMagicItems/{ => Localization}/L10n.cs | 71 +++++++++++++------ CraftMagicItems/Main.cs | 5 +- .../CraftMagicItemsBlueprintPatcher.cs | 1 + CraftMagicItems/PrerequisiteCasterLevel.cs | 1 + CraftMagicItems/QuiverAbility.cs | 1 + CraftMagicItems/SustenanceEnchantment.cs | 1 + .../UI/UserInterfaceEventHandlingLogic.cs | 1 + CraftMagicItems/WeaponBaseSizeChange.cs | 1 + CraftMagicItems/WildEnchantment.cs | 1 + 13 files changed, 97 insertions(+), 45 deletions(-) rename CraftMagicItems/{ => Localization}/L10NString.cs (70%) rename CraftMagicItems/{ => Localization}/L10n.cs (72%) diff --git a/CraftMagicItems/BondedItemComponent.cs b/CraftMagicItems/BondedItemComponent.cs index fb71567..617b36f 100644 --- a/CraftMagicItems/BondedItemComponent.cs +++ b/CraftMagicItems/BondedItemComponent.cs @@ -1,3 +1,4 @@ +using CraftMagicItems.Localization; using Kingmaker; using Kingmaker.Items; using Kingmaker.Items.Slots; diff --git a/CraftMagicItems/Constants/LocalizedStringBlueprints.cs b/CraftMagicItems/Constants/LocalizedStringBlueprints.cs index d42e63d..ba73161 100644 --- a/CraftMagicItems/Constants/LocalizedStringBlueprints.cs +++ b/CraftMagicItems/Constants/LocalizedStringBlueprints.cs @@ -1,4 +1,5 @@ -using Kingmaker.Localization; +using CraftMagicItems.Localization; +using Kingmaker.Localization; namespace CraftMagicItems.Constants { diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index d626dd5..dcfe7ae 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -129,8 +129,8 @@ - - + + diff --git a/CraftMagicItems/L10NString.cs b/CraftMagicItems/Localization/L10NString.cs similarity index 70% rename from CraftMagicItems/L10NString.cs rename to CraftMagicItems/Localization/L10NString.cs index b242d7a..c22f0e6 100644 --- a/CraftMagicItems/L10NString.cs +++ b/CraftMagicItems/Localization/L10NString.cs @@ -1,18 +1,24 @@ using System.Text.RegularExpressions; using Kingmaker.Localization; -namespace CraftMagicItems { - public class L10NString : LocalizedString { +namespace CraftMagicItems.Localization +{ + public class L10NString : LocalizedString + { private static readonly Regex StringModifier = new Regex(@"(?[-0-9a-f]+)/((?[^/]+)/(?[^/]*)/)+"); - public L10NString(string key) { - if (LocalizationManager.CurrentPack != null && !LocalizationManager.CurrentPack.Strings.ContainsKey(key)) { + public L10NString(string key) + { + if (LocalizationManager.CurrentPack != null && !LocalizationManager.CurrentPack.Strings.ContainsKey(key)) + { var match = StringModifier.Match(key); - if (match.Success) { + if (match.Success) + { // If we're modifying an existing string, we need to insert it into the language bundle up front. var result = new L10NString(match.Groups["key"].Value).ToString(); var count = match.Groups["find"].Captures.Count; - for (var index = 0; index < count; ++index) { + for (var index = 0; index < count; ++index) + { result = result.Replace(match.Groups["find"].Captures[index].Value, match.Groups["replace"].Captures[index].Value); } LocalizationManager.CurrentPack.Strings[key] = result; @@ -22,20 +28,25 @@ public L10NString(string key) { } } - public class FakeL10NString : LocalizedString { + public class FakeL10NString : LocalizedString + { private readonly string fakeValue; - public FakeL10NString(string fakeValue) { + public FakeL10NString(string fakeValue) + { this.fakeValue = fakeValue; HarmonyLib.Traverse.Create(this).Field("m_Key").SetValue(fakeValue); } [HarmonyLib.HarmonyPatch(typeof(LocalizedString), "LoadString")] // ReSharper disable once UnusedMember.Local - private static class LocalizedStringLoadStringPatch { + private static class LocalizedStringLoadStringPatch + { // ReSharper disable once UnusedMember.Local - private static bool Prefix(LocalizedString __instance, ref string __result) { - if (__instance is FakeL10NString fake) { + private static bool Prefix(LocalizedString __instance, ref string __result) + { + if (__instance is FakeL10NString fake) + { __result = fake.fakeValue; return false; } @@ -46,10 +57,13 @@ private static bool Prefix(LocalizedString __instance, ref string __result) { [HarmonyLib.HarmonyPatch(typeof(LocalizedString), "IsSet")] // ReSharper disable once UnusedMember.Local - private static class LocalizedStringIsSetPatch { + private static class LocalizedStringIsSetPatch + { // ReSharper disable once UnusedMember.Local - private static bool Prefix(LocalizedString __instance, ref bool __result) { - if (__instance is FakeL10NString fake) { + private static bool Prefix(LocalizedString __instance, ref bool __result) + { + if (__instance is FakeL10NString fake) + { __result = !string.IsNullOrEmpty(fake.fakeValue); return false; } @@ -60,10 +74,13 @@ private static bool Prefix(LocalizedString __instance, ref bool __result) { [HarmonyLib.HarmonyPatch(typeof(LocalizedString), "IsEmpty")] // ReSharper disable once UnusedMember.Local - private static class LocalizedStringIsEmptyPatch { + private static class LocalizedStringIsEmptyPatch + { // ReSharper disable once UnusedMember.Local - private static bool Prefix(LocalizedString __instance, ref bool __result) { - if (__instance is FakeL10NString fake) { + private static bool Prefix(LocalizedString __instance, ref bool __result) + { + if (__instance is FakeL10NString fake) + { __result = string.IsNullOrEmpty(fake.fakeValue); return false; } diff --git a/CraftMagicItems/L10n.cs b/CraftMagicItems/Localization/L10n.cs similarity index 72% rename from CraftMagicItems/L10n.cs rename to CraftMagicItems/Localization/L10n.cs index 3d78289..081ee0b 100644 --- a/CraftMagicItems/L10n.cs +++ b/CraftMagicItems/Localization/L10n.cs @@ -9,57 +9,74 @@ using Kingmaker.Localization; using Newtonsoft.Json; -namespace CraftMagicItems { - public class L10NData { +namespace CraftMagicItems.Localization +{ + public class L10NData + { [JsonProperty] public string Key; [JsonProperty] public string Value; } /// Localization is sometimes written in English as l10n, where 10 is the number of letters in the English word between 'l' and 'n' - class L10n { + class L10N + { private static readonly Dictionary ModifiedL10NStrings = new Dictionary(); private static bool initialLoad; private static bool enabled = true; - private static void LoadL10NStrings() { - if (LocalizationManager.CurrentPack == null) { + private static void LoadL10NStrings() + { + if (LocalizationManager.CurrentPack == null) + { return; } initialLoad = true; var currentLocale = LocalizationManager.CurrentLocale.ToString(); var fileName = $"{Main.ModEntry.Path}/L10n/Strings_{currentLocale}.json"; - if (!File.Exists(fileName)) { + if (!File.Exists(fileName)) + { Main.ModEntry.Logger.Warning($"Localised text for current local \"{currentLocale}\" not found, falling back on enGB."); currentLocale = "enGB"; fileName = $"{Main.ModEntry.Path}/L10n/Strings_{currentLocale}.json"; } - try { + try + { var allStrings = Main.ReadJsonFile(fileName); - foreach (var data in allStrings) { + foreach (var data in allStrings) + { var value = data.Value; - if (LocalizationManager.CurrentPack.Strings.ContainsKey(data.Key)) { + if (LocalizationManager.CurrentPack.Strings.ContainsKey(data.Key)) + { var original = LocalizationManager.CurrentPack.Strings[data.Key]; ModifiedL10NStrings.Add(data.Key, original); - if (value[0] == '+') { + if (value[0] == '+') + { value = original + value.Substring(1); } } LocalizationManager.CurrentPack.Strings[data.Key] = value; } - } catch (Exception e) { + } + catch (Exception e) + { Main.ModEntry.Logger.Warning($"Exception loading L10n data for locale {currentLocale}: {e}"); throw; } } - public static void SetEnabled(bool newEnabled) { - if (LocalizationManager.CurrentPack != null) { - if (!initialLoad) { + public static void SetEnabled(bool newEnabled) + { + if (LocalizationManager.CurrentPack != null) + { + if (!initialLoad) + { LoadL10NStrings(); } - if (enabled != newEnabled) { + + if (enabled != newEnabled) + { enabled = newEnabled; foreach (var key in ModifiedL10NStrings.Keys.ToArray()) { var swap = ModifiedL10NStrings[key]; @@ -71,18 +88,23 @@ public static void SetEnabled(bool newEnabled) { } [HarmonyLib.HarmonyPatch(typeof(LocalizationManager), "CurrentLocale", HarmonyLib.MethodType.Setter)] - private static class LocalizationManagerCurrentLocaleSetterPatch { + private static class LocalizationManagerCurrentLocaleSetterPatch + { // ReSharper disable once UnusedMember.Local - private static void Postfix() { + private static void Postfix() + { LoadL10NStrings(); } } [HarmonyLib.HarmonyPatch(typeof(MainMenu), "Start")] - private static class MainMenuStartPatch { - private static void Prefix() { + private static class MainMenuStartPatch + { + private static void Prefix() + { // Kingmaker Mod Loader doesn't appear to patch the game before LocalizationManager.CurrentLocale has been set. - if (!initialLoad) { + if (!initialLoad) + { LoadL10NStrings(); } } @@ -90,10 +112,13 @@ private static void Prefix() { #if PATCH21 [HarmonyLib.HarmonyPatch(typeof(MainMenuUiContext), "Initialize")] - private static class MainMenuUiContextInitializePatch { - private static void Prefix() { + private static class MainMenuUiContextInitializePatch + { + private static void Prefix() + { // Kingmaker Mod Loader doesn't appear to patch the game before LocalizationManager.CurrentLocale has been set. - if (!initialLoad) { + if (!initialLoad) + { LoadL10NStrings(); } } diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index f4521b2..3f7f9f3 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -8,6 +8,7 @@ using System.Text.RegularExpressions; using CraftMagicItems.Config; using CraftMagicItems.Constants; +using CraftMagicItems.Localization; using CraftMagicItems.Patches; using CraftMagicItems.UI; using CraftMagicItems.UI.Sections; @@ -3226,7 +3227,7 @@ public static void Postfix() { public static void ModEnabledChanged() { if (!mainMenuStarted && ResourcesLibrary.LibraryObject != null) { mainMenuStarted = true; - L10n.SetEnabled(true); + L10N.SetEnabled(true); SustenanceEnchantment.MainMenuStartPatch.Postfix(); WildEnchantment.MainMenuStartPatch.Postfix(); CreateQuiverAbility.MainMenuStartPatch.Postfix(); @@ -3249,7 +3250,7 @@ public static void ModEnabledChanged() { PatchAllOrdered(); InitialiseMod(); } - L10n.SetEnabled(modEnabled); + L10N.SetEnabled(modEnabled); } } diff --git a/CraftMagicItems/Patches/CraftMagicItemsBlueprintPatcher.cs b/CraftMagicItems/Patches/CraftMagicItemsBlueprintPatcher.cs index 478fff9..93c6faf 100644 --- a/CraftMagicItems/Patches/CraftMagicItemsBlueprintPatcher.cs +++ b/CraftMagicItems/Patches/CraftMagicItemsBlueprintPatcher.cs @@ -4,6 +4,7 @@ using System.Reflection; using System.Text.RegularExpressions; using CraftMagicItems.Constants; +using CraftMagicItems.Localization; using Kingmaker.Blueprints; using Kingmaker.Blueprints.Classes; using Kingmaker.Blueprints.Items; diff --git a/CraftMagicItems/PrerequisiteCasterLevel.cs b/CraftMagicItems/PrerequisiteCasterLevel.cs index 0bccb1b..0f3a09c 100644 --- a/CraftMagicItems/PrerequisiteCasterLevel.cs +++ b/CraftMagicItems/PrerequisiteCasterLevel.cs @@ -1,3 +1,4 @@ +using CraftMagicItems.Localization; using Kingmaker.Blueprints.Classes.Prerequisites; using Kingmaker.Localization; using Kingmaker.UnitLogic; diff --git a/CraftMagicItems/QuiverAbility.cs b/CraftMagicItems/QuiverAbility.cs index 6e64292..c19ed66 100644 --- a/CraftMagicItems/QuiverAbility.cs +++ b/CraftMagicItems/QuiverAbility.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Reflection; using System.Reflection.Emit; +using CraftMagicItems.Localization; using Kingmaker; #if PATCH21 using Kingmaker.Assets.UI.Context; diff --git a/CraftMagicItems/SustenanceEnchantment.cs b/CraftMagicItems/SustenanceEnchantment.cs index 819e591..1920641 100644 --- a/CraftMagicItems/SustenanceEnchantment.cs +++ b/CraftMagicItems/SustenanceEnchantment.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using CraftMagicItems.Localization; using Kingmaker; #if PATCH21 using Kingmaker.Assets.UI.Context; diff --git a/CraftMagicItems/UI/UserInterfaceEventHandlingLogic.cs b/CraftMagicItems/UI/UserInterfaceEventHandlingLogic.cs index b4c9b26..f37dc03 100644 --- a/CraftMagicItems/UI/UserInterfaceEventHandlingLogic.cs +++ b/CraftMagicItems/UI/UserInterfaceEventHandlingLogic.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using CraftMagicItems.Constants; +using CraftMagicItems.Localization; using CraftMagicItems.UI.Sections; using Kingmaker.Blueprints; using Kingmaker.Blueprints.Classes; diff --git a/CraftMagicItems/WeaponBaseSizeChange.cs b/CraftMagicItems/WeaponBaseSizeChange.cs index 195869c..8fb0708 100644 --- a/CraftMagicItems/WeaponBaseSizeChange.cs +++ b/CraftMagicItems/WeaponBaseSizeChange.cs @@ -1,3 +1,4 @@ +using CraftMagicItems.Localization; using Kingmaker.Blueprints; using Kingmaker.Blueprints.Items.Weapons; using Kingmaker.EntitySystem.Stats; diff --git a/CraftMagicItems/WildEnchantment.cs b/CraftMagicItems/WildEnchantment.cs index d9027b7..e66622e 100644 --- a/CraftMagicItems/WildEnchantment.cs +++ b/CraftMagicItems/WildEnchantment.cs @@ -1,3 +1,4 @@ +using CraftMagicItems.Localization; using Kingmaker; #if PATCH21 using Kingmaker.Assets.UI.Context; From c157e0155261d6f91b7565853c7d5723f1574b33 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Fri, 17 Jul 2020 18:03:01 -0500 Subject: [PATCH 071/132] Moving L10NFormat into a helper class (and renaming (for clarity?)) --- CraftMagicItems/BondedItemComponent.cs | 2 +- CraftMagicItems/CraftMagicItems.csproj | 1 + .../Localization/LocalizationHelper.cs | 33 ++++++ CraftMagicItems/Main.cs | 107 ++++++++---------- 4 files changed, 82 insertions(+), 61 deletions(-) create mode 100644 CraftMagicItems/Localization/LocalizationHelper.cs diff --git a/CraftMagicItems/BondedItemComponent.cs b/CraftMagicItems/BondedItemComponent.cs index 617b36f..bebb387 100644 --- a/CraftMagicItems/BondedItemComponent.cs +++ b/CraftMagicItems/BondedItemComponent.cs @@ -53,7 +53,7 @@ private static void Postfix(UnitUseAbility __instance) { var bondedComponent = Main.GetBondedItemComponentForCaster(caster.Descriptor); if (bondedComponent != null && bondedComponent.ownerItem != null && bondedComponent.ownerItem.Wielder != caster.Descriptor) { Main.AddBattleLogMessage( - Main.L10NFormat(caster, "craftMagicItems-logMessage-not-wielding-bonded-item", bondedComponent.ownerItem.Name), + LocalizationHelper.FormatLocalizedString(caster, "craftMagicItems-logMessage-not-wielding-bonded-item", bondedComponent.ownerItem.Name), new L10NString("craftMagicItems-bonded-item-glossary")); // Concentration checks have no way of overriding the DC, so contrive some fake damage to give a DC of 20 + spell level. var ruleDamage = new RuleDealDamage(caster, caster, null); diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index dcfe7ae..1186d92 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -106,6 +106,7 @@ + diff --git a/CraftMagicItems/Localization/LocalizationHelper.cs b/CraftMagicItems/Localization/LocalizationHelper.cs new file mode 100644 index 0000000..d24942d --- /dev/null +++ b/CraftMagicItems/Localization/LocalizationHelper.cs @@ -0,0 +1,33 @@ +using Kingmaker.EntitySystem.Entities; +using Kingmaker.UI.Log; + +namespace CraftMagicItems.Localization +{ + /// Class containing methods used for formatting localized strings + public static class LocalizationHelper + { + /// Formats the localized string specified in + /// specifying what is taking action (such as a crafter) + /// Key used to look up a localized string + /// Collection of arguments used in + /// A representation of the localized string + public static string FormatLocalizedString(UnitEntityData sourceUnit, string key, params object[] args) + { + // Set GameLogContext so the caster will be used when generating localized strings. + GameLogContext.SourceUnit = sourceUnit; + var template = new L10NString(key); + var result = string.Format(template.ToString(), args); + GameLogContext.Clear(); + return result; + } + + /// Adds the currently selected caster to the localized string specified in + /// Key used to look up a localized string + /// Arguments used in + /// A representation of the localized string + public static string FormatLocalizedString(string key, params object[] args) + { + return FormatLocalizedString(Main.Selections.CurrentCaster ?? Main.GetSelectedCrafter(false), key, args); + } + } +} \ No newline at end of file diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 3f7f9f3..6420023 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -320,19 +320,6 @@ private static void OnGui(UnityModManager.ModEntry modEntry) { } } - public static string L10NFormat(UnitEntityData sourceUnit, string key, params object[] args) { - // Set GameLogContext so the caster will be used when generating localized strings. - GameLogContext.SourceUnit = sourceUnit; - var template = new L10NString(key); - var result = string.Format(template.ToString(), args); - GameLogContext.Clear(); - return result; - } - - private static string L10NFormat(string key, params object[] args) { - return L10NFormat(Selections.CurrentCaster ?? GetSelectedCrafter(false), key, args); - } - public static T ReadJsonFile(string fileName, params JsonConverter[] converters) { try { var serializer = new JsonSerializer(); @@ -561,7 +548,7 @@ private static void RenderBondedItemCrafting(UnitEntityData caster) { Selections.SelectedBondWithNewObject = false; if (!ModSettings.CraftingTakesNoTime) { // Create project - AddBattleLogMessage(L10NFormat("craftMagicItems-logMessage-begin-ritual-bonded-item", cost, selectedItem.Name)); + AddBattleLogMessage(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-begin-ritual-bonded-item", cost, selectedItem.Name)); var project = new CraftingProjectData(caster, ModSettings.MagicCraftingRate, goldCost, 0, selectedItem, BondedItemRitual); AddNewProject(caster.Descriptor, project); CalculateProjectEstimate(project); @@ -891,7 +878,7 @@ private static string BuildCommaList(this IEnumerable list, bool or) { } var key = or ? "craftMagicItems-logMessage-comma-list-or" : "craftMagicItems-logMessage-comma-list-and"; - return L10NFormat(key, commaList, array[array.Length - 1]); + return LocalizationHelper.FormatLocalizedString(key, commaList, array[array.Length - 1]); } private static bool IsMasterwork(BlueprintItem blueprint) { @@ -1319,7 +1306,7 @@ private static void RenderRecipeBasedCrafting(UnitEntityData caster, RecipeBased } if (selectedRecipe.CrafterPrerequisites != null) { - prerequisites += "; " + L10NFormat("craftMagicItems-crafter-prerequisite-required", selectedRecipe.CrafterPrerequisites + prerequisites += "; " + LocalizationHelper.FormatLocalizedString("craftMagicItems-crafter-prerequisite-required", selectedRecipe.CrafterPrerequisites .Select(prerequisite => new L10NString($"craftMagicItems-crafter-prerequisite-{prerequisite}").ToString()) .BuildCommaList(false)); } @@ -1592,7 +1579,7 @@ private static void RenderCastSpellNTimes(UnitEntityData caster, RecipeBasedItem ability = equipment.Ability; spellLevel = equipment.SpellLevel; GameLogContext.Count = equipment.Charges; - UmmUiRenderer.RenderLabelRow($"Current: {L10NFormat("craftMagicItems-label-cast-spell-n-times-details", ability.Name, equipment.CasterLevel)}"); + UmmUiRenderer.RenderLabelRow($"Current: {LocalizationHelper.FormatLocalizedString("craftMagicItems-label-cast-spell-n-times-details", ability.Name, equipment.CasterLevel)}"); GameLogContext.Clear(); } @@ -1636,7 +1623,7 @@ private static void RenderCastSpellNTimes(UnitEntityData caster, RecipeBasedItem // Render craft button GameLogContext.Count = Selections.SelectedCastsPerDay; - UmmUiRenderer.RenderLabelRow(L10NFormat("craftMagicItems-label-cast-spell-n-times-details", ability.Name, Selections.SelectedCasterLevel)); + UmmUiRenderer.RenderLabelRow(LocalizationHelper.FormatLocalizedString("craftMagicItems-label-cast-spell-n-times-details", ability.Name, Selections.SelectedCasterLevel)); GameLogContext.Clear(); var recipe = new RecipeData { PrerequisiteSpells = new[] {ability}, @@ -1702,7 +1689,7 @@ private static int RenderCraftingSkillInformation(UnitEntityData crafter, StatTy $"{crafter.CharacterName} is unable to meet {missing} of the prerequisites, raising the DC by {DifficultyClass.MissingPrerequisiteDCModifier * missing}"); } if (casterLevelShortfall > 0 && render) { - UmmUiRenderer.RenderLabelRow(L10NFormat("craftMagicItems-logMessage-low-caster-level", casterLevel, DifficultyClass.MissingPrerequisiteDCModifier * casterLevelShortfall)); + UmmUiRenderer.RenderLabelRow(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-low-caster-level", casterLevel, DifficultyClass.MissingPrerequisiteDCModifier * casterLevelShortfall)); } // Rob's ruling... if you're below the prerequisite caster level, you're considered to be missing a prerequisite for each // level you fall short. @@ -1711,13 +1698,13 @@ private static int RenderCraftingSkillInformation(UnitEntityData crafter, StatTy if (oppositionSchool != SpellSchool.None) { dc += DifficultyClass.OppositionSchoolDCModifier; if (render) { - UmmUiRenderer.RenderLabelRow(L10NFormat("craftMagicItems-logMessage-opposition-school", LocalizedTexts.Instance.SpellSchoolNames.GetText(oppositionSchool), + UmmUiRenderer.RenderLabelRow(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-opposition-school", LocalizedTexts.Instance.SpellSchoolNames.GetText(oppositionSchool), DifficultyClass.OppositionSchoolDCModifier)); } } var skillCheck = 10 + crafter.Stats.GetStat(skill).ModifiedValue; if (render) { - UmmUiRenderer.RenderLabelRow(L10NFormat("craftMagicItems-logMessage-made-progress-check", LocalizedTexts.Instance.Stats.GetText(skill), skillCheck, dc)); + UmmUiRenderer.RenderLabelRow(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-made-progress-check", LocalizedTexts.Instance.Stats.GetText(skill), skillCheck, dc)); } var skillMargin = skillCheck - dc; @@ -1765,7 +1752,7 @@ private static void CancelCraftingProject(CraftingProjectData project) { } } - AddBattleLogMessage(L10NFormat("craftMagicItems-logMessage-crafting-cancelled", project.ResultItem.Name, cost)); + AddBattleLogMessage(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-crafting-cancelled", project.ResultItem.Name, cost)); } var timer = GetCraftingTimerComponentForCaster(project.Crafter.Descriptor); @@ -2270,7 +2257,7 @@ private static bool BuildCostString(out string cost, ItemCraftingData craftingDa } else { canAfford = (Game.Instance.Player.Money >= goldCost); var notAffordGold = canAfford ? "" : new L10NString("craftMagicItems-label-cost-gold-too-much"); - cost = L10NFormat("craftMagicItems-label-cost-gold", goldCost, notAffordGold); + cost = LocalizationHelper.FormatLocalizedString("craftMagicItems-label-cost-gold", goldCost, notAffordGold); var itemTotals = new Dictionary(); if (spellBlueprintArray != null) { foreach (var spellBlueprint in spellBlueprintArray) { @@ -2295,7 +2282,7 @@ private static bool BuildCostString(out string cost, ItemCraftingData craftingDa notAffordItems = new L10NString("craftMagicItems-label-cost-items-too-much"); } - cost += L10NFormat("craftMagicItems-label-cost-gold-and-items", pair.Value, pair.Key.Name, notAffordItems); + cost += LocalizationHelper.FormatLocalizedString("craftMagicItems-label-cost-gold-and-items", pair.Value, pair.Key.Name, notAffordItems); } } @@ -2343,7 +2330,7 @@ private static void CalculateProjectEstimate(CraftingProjectData project) { var adventuringDayCount = (project.TargetCost + progressPerDayAdventuring - 1) / progressPerDayAdventuring; project.AddMessage(adventuringDayCount == 1 ? new L10NString("craftMagicItems-time-estimate-one-day") - : L10NFormat("craftMagicItems-time-estimate-adventuring-capital", adventuringDayCount)); + : LocalizationHelper.FormatLocalizedString("craftMagicItems-time-estimate-adventuring-capital", adventuringDayCount)); } GameLogContext.Clear(); @@ -2356,7 +2343,7 @@ private static void AttemptSpellBasedCraftItemAndRender(UnitEntityData caster, S var itemBlueprintList = FindItemBlueprintsForSpell(spellBlueprint, craftingData.UsableItemType); if (itemBlueprintList == null && craftingData.NewItemBaseIDs == null) { - var message = L10NFormat("craftMagicItems-label-no-item-exists", new L10NString(craftingData.NamePrefixId), spellBlueprint.Name); + var message = LocalizationHelper.FormatLocalizedString("craftMagicItems-label-no-item-exists", new L10NString(craftingData.NamePrefixId), spellBlueprint.Name); UmmUiRenderer.RenderLabel(message); return; } @@ -2368,7 +2355,7 @@ private static void AttemptSpellBasedCraftItemAndRender(UnitEntityData caster, S var custom = existingItemBlueprint == null || existingItemBlueprint.AssetGuid.Contains(CraftMagicItemsBlueprintPatcher.BlueprintPrefix) ? new L10NString("craftMagicItems-label-custom").ToString() : ""; - var label = L10NFormat("craftMagicItems-label-craft-spell-item", custom, new L10NString(craftingData.NamePrefixId), spellBlueprint.Name, cost); + var label = LocalizationHelper.FormatLocalizedString("craftMagicItems-label-craft-spell-item", custom, new L10NString(craftingData.NamePrefixId), spellBlueprint.Name, cost); //if the player cannot afford the time (not enough gold), alert them if (!canAfford) @@ -2431,10 +2418,10 @@ private static void BeginCraftingSpellBasedItem(UnitEntityData caster, SpellBase } var resultItem = BuildItemEntity(itemBlueprint, craftingData, caster); - AddBattleLogMessage(L10NFormat("craftMagicItems-logMessage-begin-crafting", cost, itemBlueprint.Name), resultItem); + AddBattleLogMessage(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-begin-crafting", cost, itemBlueprint.Name), resultItem); if (ModSettings.CraftingTakesNoTime) { - AddBattleLogMessage(L10NFormat("craftMagicItems-logMessage-expend-spell", spell.Name)); + AddBattleLogMessage(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-expend-spell", spell.Name)); spell.SpendFromSpellbook(); CraftItem(resultItem); } @@ -2519,10 +2506,10 @@ private static void RenderRecipeBasedCraftItemControl(UnitEntityData caster, Ite ? new L10NString("craftMagicItems-label-custom").ToString() : ""; var label = upgradeItem == null - ? L10NFormat("craftMagicItems-label-craft-item", custom, itemBlueprint.Name, cost) + ? LocalizationHelper.FormatLocalizedString("craftMagicItems-label-craft-item", custom, itemBlueprint.Name, cost) : itemBlueprint is BlueprintItemWeapon otherWeapon && otherWeapon.Double - ? L10NFormat("craftMagicItems-label-upgrade-weapon-double", upgradeItem.Blueprint.Name, custom, itemBlueprint.Name, otherWeapon.SecondWeapon.Name, cost) - : L10NFormat("craftMagicItems-label-upgrade-item", upgradeItem.Blueprint.Name, custom, itemBlueprint.Name, cost); + ? LocalizationHelper.FormatLocalizedString("craftMagicItems-label-upgrade-weapon-double", upgradeItem.Blueprint.Name, custom, itemBlueprint.Name, otherWeapon.SecondWeapon.Name, cost) + : LocalizationHelper.FormatLocalizedString("craftMagicItems-label-upgrade-item", upgradeItem.Blueprint.Name, custom, itemBlueprint.Name, cost); if (!canAfford) { GUILayout.Label(label); } else if (GUILayout.Button(label, GUILayout.ExpandWidth(false))) { @@ -2543,7 +2530,7 @@ private static void RenderRecipeBasedCraftItemControl(UnitEntityData caster, Ite } var resultItem = BuildItemEntity(itemBlueprint, craftingData, caster); - AddBattleLogMessage(L10NFormat("craftMagicItems-logMessage-begin-crafting", cost, itemBlueprint.Name), resultItem); + AddBattleLogMessage(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-begin-crafting", cost, itemBlueprint.Name), resultItem); if (ModSettings.CraftingTakesNoTime) { CraftItem(resultItem, upgradeItem); } else { @@ -2589,10 +2576,10 @@ public static LocalizedString BuildCustomRecipeItemDescription(BlueprintItem blu var newBonus = recipe.Enchantments.FindIndex(e => e == enchantment) + 1; var bonusString = GetBonusString(newBonus, recipe); var bonusDescription = recipe.BonusTypeId != null - ? L10NFormat("craftMagicItems-custom-description-bonus-to", new L10NString(recipe.BonusTypeId), recipe.NameId) + ? LocalizationHelper.FormatLocalizedString("craftMagicItems-custom-description-bonus-to", new L10NString(recipe.BonusTypeId), recipe.NameId) : recipe.BonusToId != null - ? L10NFormat("craftMagicItems-custom-description-bonus-to", recipe.NameId, new L10NString(recipe.BonusToId)) - : L10NFormat("craftMagicItems-custom-description-bonus", recipe.NameId); + ? LocalizationHelper.FormatLocalizedString("craftMagicItems-custom-description-bonus-to", recipe.NameId, new L10NString(recipe.BonusToId)) + : LocalizationHelper.FormatLocalizedString("craftMagicItems-custom-description-bonus", recipe.NameId); var upgradeFrom = removed.FirstOrDefault(remove => FindSourceRecipe(remove.AssetGuid, blueprint) == recipe); var oldBonus = int.MaxValue; if (upgradeFrom != null) { @@ -2602,12 +2589,12 @@ public static LocalizedString BuildCustomRecipeItemDescription(BlueprintItem blu if (skipped.Contains(enchantment)) { return new L10NString(""); } else { - return L10NFormat("craftMagicItems-custom-description-enchantment-template", bonusString, bonusDescription); + return LocalizationHelper.FormatLocalizedString("craftMagicItems-custom-description-enchantment-template", bonusString, bonusDescription); } } else { removed.Remove(upgradeFrom); } - return L10NFormat("craftMagicItems-custom-description-enchantment-upgrade-template", bonusDescription, + return LocalizationHelper.FormatLocalizedString("craftMagicItems-custom-description-enchantment-upgrade-template", bonusDescription, GetBonusString(oldBonus, recipe), bonusString); }) .OrderBy(enchantmentDescription => enchantmentDescription) @@ -2615,8 +2602,8 @@ public static LocalizedString BuildCustomRecipeItemDescription(BlueprintItem blu .Join(""); if (blueprint is BlueprintItemEquipment equipment && (ability != null && ability != "null" || casterLevel > -1 || perDay > -1)) { GameLogContext.Count = equipment.Charges; - extraDescription += "\n* " + (equipment.Charges == 1 ? L10NFormat("craftMagicItems-label-cast-spell-n-times-details-single", equipment.Ability.Name, equipment.CasterLevel) : - L10NFormat("craftMagicItems-label-cast-spell-n-times-details-multiple", equipment.Ability.Name, equipment.CasterLevel, equipment.Charges)); + extraDescription += "\n* " + (equipment.Charges == 1 ? LocalizationHelper.FormatLocalizedString("craftMagicItems-label-cast-spell-n-times-details-single", equipment.Ability.Name, equipment.CasterLevel) : + LocalizationHelper.FormatLocalizedString("craftMagicItems-label-cast-spell-n-times-details-multiple", equipment.Ability.Name, equipment.CasterLevel, equipment.Charges)); GameLogContext.Clear(); } @@ -3497,7 +3484,7 @@ private static int CheckFeatPrerequisites(BlueprintFeature[] prerequisites, bool private static int CheckCrafterPrerequisites(CraftingProjectData project, UnitDescriptor caster) { var missing = GetMissingCrafterPrerequisites(project.CrafterPrerequisites, caster); foreach (var prerequisite in missing) { - AddBattleLogMessage(L10NFormat("craftMagicItems-logMessage-missing-crafter-prerequisite", + AddBattleLogMessage(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-missing-crafter-prerequisite", new L10NString($"craftMagicItems-crafter-prerequisite-{prerequisite}"), DifficultyClass.MissingPrerequisiteDCModifier)); } @@ -3546,7 +3533,7 @@ private static void WorkOnProjects(UnitDescriptor caster, bool returningToCapita var daysAvailableToCraft = (int) Math.Ceiling(interval.TotalDays); if (daysAvailableToCraft <= 0) { if (isAdventuring) { - AddBattleLogMessage(L10NFormat("craftMagicItems-logMessage-not-full-day")); + AddBattleLogMessage(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-not-full-day")); } return; @@ -3581,7 +3568,7 @@ private static void WorkOnProjects(UnitDescriptor caster, bool returningToCapita if ((!playerInCapital || returningToCapital) && (project.UpgradeItem.Collection != Game.Instance.Player.SharedStash || withPlayer) && (project.UpgradeItem.Collection != Game.Instance.Player.Inventory || ((!withPlayer || !wieldedInParty) && (withPlayer || wieldedInParty)))) { - project.AddMessage(L10NFormat("craftMagicItems-logMessage-missing-upgrade-item", project.UpgradeItem.Blueprint.Name)); + project.AddMessage(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-missing-upgrade-item", project.UpgradeItem.Blueprint.Name)); AddBattleLogMessage(project.LastMessage); continue; } @@ -3610,20 +3597,20 @@ private static void WorkOnProjects(UnitDescriptor caster, bool returningToCapita if (missing > 0) { var missingSpellNames = missingSpells.Select(ability => ability.Name).BuildCommaList(project.AnyPrerequisite); if (craftingData.PrerequisitesMandatory || project.PrerequisitesMandatory) { - project.AddMessage(L10NFormat("craftMagicItems-logMessage-missing-prerequisite", + project.AddMessage(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-missing-prerequisite", project.ResultItem.Name, missingSpellNames)); AddBattleLogMessage(project.LastMessage); // If the item type has mandatory prerequisites and some are missing, move on to the next project. continue; } - AddBattleLogMessage(L10NFormat("craftMagicItems-logMessage-missing-spell", missingSpellNames, + AddBattleLogMessage(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-missing-spell", missingSpellNames, DifficultyClass.MissingPrerequisiteDCModifier * missing)); } var missing2 = CheckFeatPrerequisites(project, caster, out var missingFeats); if (missing2 > 0) { var missingFeatNames = missingFeats.Select(ability => ability.Name).BuildCommaList(project.AnyPrerequisite); - AddBattleLogMessage(L10NFormat("craftMagicItems-logMessage-missing-feat", missingFeatNames, + AddBattleLogMessage(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-missing-feat", missingFeatNames, DifficultyClass.MissingPrerequisiteDCModifier * missing2)); } missing += missing2; @@ -3637,19 +3624,19 @@ private static void WorkOnProjects(UnitDescriptor caster, bool returningToCapita ? DifficultyClass.MissingPrerequisiteDCModifier : DifficultyClass.MissingPrerequisiteDCModifier * (project.CasterLevel - casterLevel); dc += casterLevelPenalty; - AddBattleLogMessage(L10NFormat("craftMagicItems-logMessage-low-caster-level", project.CasterLevel, casterLevelPenalty)); + AddBattleLogMessage(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-low-caster-level", project.CasterLevel, casterLevelPenalty)); } var oppositionSchool = CheckForOppositionSchool(caster, project.SpellPrerequisites); if (oppositionSchool != SpellSchool.None) { dc += DifficultyClass.OppositionSchoolDCModifier; - AddBattleLogMessage(L10NFormat("craftMagicItems-logMessage-opposition-school", + AddBattleLogMessage(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-opposition-school", LocalizedTexts.Instance.SpellSchoolNames.GetText(oppositionSchool), DifficultyClass.OppositionSchoolDCModifier)); } var skillCheck = 10 + caster.Stats.GetStat(craftingSkill).ModifiedValue; if (skillCheck < dc) { // Can't succeed by taking 10... move on to the next project. - project.AddMessage(L10NFormat("craftMagicItems-logMessage-dc-too-high", project.ResultItem.Name, + project.AddMessage(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-dc-too-high", project.ResultItem.Name, LocalizedTexts.Instance.Stats.GetText(craftingSkill), skillCheck, dc)); AddBattleLogMessage(project.LastMessage); continue; @@ -3679,15 +3666,15 @@ private static void WorkOnProjects(UnitDescriptor caster, bool returningToCapita progressGold -= progressPerDay * (daysCrafting - day); skillCheck -= DifficultyClass.MissingPrerequisiteDCModifier; if (craftingData.PrerequisitesMandatory || project.PrerequisitesMandatory) { - AddBattleLogMessage(L10NFormat("craftMagicItems-logMessage-missing-prerequisite", project.ResultItem.Name, spell.Name)); + AddBattleLogMessage(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-missing-prerequisite", project.ResultItem.Name, spell.Name)); daysCrafting = day; break; } - AddBattleLogMessage(L10NFormat("craftMagicItems-logMessage-missing-spell", spell.Name, DifficultyClass.MissingPrerequisiteDCModifier)); + AddBattleLogMessage(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-missing-spell", spell.Name, DifficultyClass.MissingPrerequisiteDCModifier)); if (skillCheck < dc) { // Can no longer make progress - AddBattleLogMessage(L10NFormat("craftMagicItems-logMessage-dc-too-high", project.ResultItem.Name, + AddBattleLogMessage(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-dc-too-high", project.ResultItem.Name, LocalizedTexts.Instance.Stats.GetText(craftingSkill), skillCheck, dc)); daysCrafting = day; } else { @@ -3711,7 +3698,7 @@ private static void WorkOnProjects(UnitDescriptor caster, bool returningToCapita } } else if (isAdventuring) { // Actually cast the spells if we're adventuring. - AddBattleLogMessage(L10NFormat("craftMagicItems-logMessage-expend-spell", spell.Name)); + AddBattleLogMessage(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-expend-spell", spell.Name)); spell.SpendFromSpellbook(); } } @@ -3719,8 +3706,8 @@ private static void WorkOnProjects(UnitDescriptor caster, bool returningToCapita var progressKey = project.ItemType == BondedItemRitual ? "craftMagicItems-logMessage-made-progress-bondedItem" : "craftMagicItems-logMessage-made-progress"; - var progress = L10NFormat(progressKey, progressGold, project.TargetCost - project.Progress, project.ResultItem.Name); - var checkResult = L10NFormat("craftMagicItems-logMessage-made-progress-check", LocalizedTexts.Instance.Stats.GetText(craftingSkill), + var progress = LocalizationHelper.FormatLocalizedString(progressKey, progressGold, project.TargetCost - project.Progress, project.ResultItem.Name); + var checkResult = LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-made-progress-check", LocalizedTexts.Instance.Stats.GetText(craftingSkill), skillCheck, dc); AddBattleLogMessage(progress, checkResult); daysAvailableToCraft -= daysCrafting; @@ -3728,10 +3715,10 @@ private static void WorkOnProjects(UnitDescriptor caster, bool returningToCapita if (project.Progress >= project.TargetCost) { // Completed the project! if (project.ItemType == BondedItemRitual) { - AddBattleLogMessage(L10NFormat("craftMagicItems-logMessage-bonding-ritual-complete", project.ResultItem.Name), project.ResultItem); + AddBattleLogMessage(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-bonding-ritual-complete", project.ResultItem.Name), project.ResultItem); BondWithObject(project.Crafter, project.ResultItem); } else { - AddBattleLogMessage(L10NFormat("craftMagicItems-logMessage-crafting-complete", project.ResultItem.Name), project.ResultItem); + AddBattleLogMessage(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-crafting-complete", project.ResultItem.Name), project.ResultItem); CraftItem(project.ResultItem, project.UpgradeItem); } timer.CraftingProjects.Remove(project); @@ -3744,7 +3731,7 @@ private static void WorkOnProjects(UnitDescriptor caster, bool returningToCapita var completeKey = project.ItemType == BondedItemRitual ? "craftMagicItems-logMessage-made-progress-bonding-ritual-amount-complete" : "craftMagicItems-logMessage-made-progress-amount-complete"; - var amountComplete = L10NFormat(completeKey, project.ResultItem.Name, 100 * project.Progress / project.TargetCost); + var amountComplete = LocalizationHelper.FormatLocalizedString(completeKey, project.ResultItem.Name, 100 * project.Progress / project.TargetCost); AddBattleLogMessage(amountComplete, project.ResultItem); project.AddMessage($"{progress} {checkResult}"); } @@ -4138,7 +4125,7 @@ private static bool Prefix(ItemEntity __instance, ref string __result) { if (__instance.VendorBlueprint != null && __instance.VendorBlueprint.IsCompanion) { foreach (var companion in UIUtility.GetGroup(true)) { if (companion.Blueprint == __instance.VendorBlueprint) { - __result = L10NFormat("craftMagicItems-crafted-source-description", companion.CharacterName); + __result = LocalizationHelper.FormatLocalizedString("craftMagicItems-crafted-source-description", companion.CharacterName); break; } } @@ -4146,7 +4133,7 @@ private static bool Prefix(ItemEntity __instance, ref string __result) { } #else if (__instance.Vendor != null && __instance.Vendor.IsPlayerFaction) { - __result = L10NFormat("craftMagicItems-crafted-source-description", __instance.Vendor.CharacterName); + __result = LocalizationHelper.FormatLocalizedString("craftMagicItems-crafted-source-description", __instance.Vendor.CharacterName); return false; } #endif From 387fdd2e90bbd01b1600ab6e4c89311309c7fc8a Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 14 Dec 2020 12:06:27 -0600 Subject: [PATCH 072/132] Extracting the Harmony patches from Main into their own files: MainMenuStartPatch --- CraftMagicItems/CraftMagicItems.csproj | 1 + CraftMagicItems/Main.cs | 376 +------------- .../Patches/Harmony/MainMenuStartPatch.cs | 474 ++++++++++++++++++ 3 files changed, 487 insertions(+), 364 deletions(-) create mode 100644 CraftMagicItems/Patches/Harmony/MainMenuStartPatch.cs diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index 1186d92..f67d7bf 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -107,6 +107,7 @@ + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 6420023..1cb537c 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -10,6 +10,7 @@ using CraftMagicItems.Constants; using CraftMagicItems.Localization; using CraftMagicItems.Patches; +using CraftMagicItems.Patches.Harmony; using CraftMagicItems.UI; using CraftMagicItems.UI.Sections; using CraftMagicItems.UI.UnityModManager; @@ -107,7 +108,7 @@ private enum ItemLocationFilter Stash } - struct MethodPatch { + public struct MethodPatch { public MethodPatch(MethodBase original, HarmonyLib.HarmonyMethod prefix = null, HarmonyLib.HarmonyMethod postfix = null) { m_original = original; m_prefix = prefix; @@ -130,7 +131,7 @@ public bool MatchPostfix(MethodBase method) { HarmonyLib.HarmonyMethod m_postfix; } - private static readonly MethodPatch[] MethodPatchList = + public static readonly MethodPatch[] MethodPatchList = { new MethodPatch( typeof(ResourcesLibrary).GetMethods().Single(m => m.Name == "TryGetBlueprint" && m.ReturnType == typeof(BlueprintScriptableObject)), @@ -163,7 +164,7 @@ public bool MatchPostfix(MethodBase method) { public static readonly List PendingLogItems = new List(); #endif - private static bool modEnabled = true; + public static bool modEnabled = true; private static HarmonyLib.Harmony harmonyInstance; private static CraftMagicItemsBlueprintPatcher blueprintPatcher; @@ -176,7 +177,7 @@ public bool MatchPostfix(MethodBase method) { /** * Patch all HarmonyPatch classes in the assembly, starting in the order of the methods named in methodNameOrder, and the rest after that. */ - private static void PatchAllOrdered(params MethodPatch[] orderedMethods) { + public static void PatchAllOrdered(params MethodPatch[] orderedMethods) { foreach (var method in orderedMethods) { method.Patch(harmonyInstance); } @@ -186,7 +187,7 @@ private static void PatchAllOrdered(params MethodPatch[] orderedMethods) { /** * Unpatch all HarmonyPatch classes for harmonyInstance, except the ones whose method names match exceptMethodName */ - private static void UnpatchAllExcept(params MethodPatch[] exceptMethods) { + public static void UnpatchAllExcept(params MethodPatch[] exceptMethods) { if (harmonyInstance != null) { try { foreach (var method in harmonyInstance.GetPatchedMethods().ToArray()) { @@ -790,7 +791,7 @@ private static string FindSupersededEnchantmentId(BlueprintItem blueprint, strin return null; } - private static bool DoesItemMatchAllEnchantments(BlueprintItemEquipment blueprint, string selectedEnchantmentId, + public static bool DoesItemMatchAllEnchantments(BlueprintItemEquipment blueprint, string selectedEnchantmentId, string selectedEnchantmentIdSecond = null, BlueprintItemEquipment upgradeItem = null, bool checkPrice = false) { var isNotable = upgradeItem && upgradeItem.IsNotable; var ability = upgradeItem ? upgradeItem.Ability : null; @@ -2141,14 +2142,14 @@ public static List FindItemBlueprintsForSpell(BlueprintS return LoadedData.SpellIdToItem[itemType].ContainsKey(spell.AssetGuid) ? LoadedData.SpellIdToItem[itemType][spell.AssetGuid] : null; } - private static void AddItemForType(BlueprintItem blueprint) { + public static void AddItemForType(BlueprintItem blueprint) { string assetGuid = GetBlueprintItemType(blueprint); if (!string.IsNullOrEmpty(assetGuid)) { LoadedData.TypeToItem.Add(assetGuid, blueprint); } } - private static void AddItemIdForEnchantment(BlueprintItemEquipment itemBlueprint) { + public static void AddItemIdForEnchantment(BlueprintItemEquipment itemBlueprint) { if (itemBlueprint != null) { foreach (var enchantment in GetEnchantments(itemBlueprint)) { if (!LoadedData.EnchantmentIdToItem.ContainsKey(enchantment.AssetGuid)) { @@ -2160,7 +2161,7 @@ private static void AddItemIdForEnchantment(BlueprintItemEquipment itemBlueprint } } - private static void AddRecipeForEnchantment(string enchantmentId, RecipeData recipe) { + public static void AddRecipeForEnchantment(string enchantmentId, RecipeData recipe) { if (!LoadedData.EnchantmentIdToRecipe.ContainsKey(enchantmentId)) { LoadedData.EnchantmentIdToRecipe.Add(enchantmentId, new List()); } @@ -2170,7 +2171,7 @@ private static void AddRecipeForEnchantment(string enchantmentId, RecipeData rec } } - private static void AddRecipeForMaterial(PhysicalDamageMaterial material, RecipeData recipe) { + public static void AddRecipeForMaterial(PhysicalDamageMaterial material, RecipeData recipe) { if (!LoadedData.MaterialToRecipe.ContainsKey(material)) { LoadedData.MaterialToRecipe.Add(material, new List()); } @@ -2848,7 +2849,7 @@ public static int RulesRecipeItemCost(BlueprintItem blueprint, int baseCost = -1 // Attempt to work out the cost of enchantments which aren't in recipes by checking if blueprint, which contains the enchantment, contains only other // enchantments whose cost is known. - private static bool ReverseEngineerEnchantmentCost(BlueprintItemEquipment blueprint, string enchantmentId) { + public static bool ReverseEngineerEnchantmentCost(BlueprintItemEquipment blueprint, string enchantmentId) { if (blueprint == null || blueprint.IsNotable || blueprint.Ability != null || blueprint.ActivatableAbility != null) { return false; } @@ -2888,359 +2889,6 @@ private static bool ReverseEngineerEnchantmentCost(BlueprintItemEquipment bluepr return true; } - [HarmonyLib.HarmonyPatch(typeof(MainMenu), "Start")] - private static class MainMenuStartPatch { - private static bool mainMenuStarted; - - private static void InitialiseCraftingData() { - // Read the crafting data now that ResourcesLibrary is loaded. - LoadedData.ItemCraftingData = ReadJsonFile($"{ModEntry.Path}/Data/ItemTypes.json", new CraftingTypeConverter()); - // Initialise lookup tables. - foreach (var itemData in LoadedData.ItemCraftingData) { - if (itemData is RecipeBasedItemCraftingData recipeBased) { - recipeBased.Recipes = recipeBased.RecipeFileNames.Aggregate(Enumerable.Empty(), - (all, fileName) => all.Concat(ReadJsonFile($"{ModEntry.Path}/Data/{fileName}")) - ).Where(recipe => { - return (recipe.ResultItem != null) - || (recipe.Enchantments.Length > 0) - || (recipe.NoResultItem && recipe.NoEnchantments); - }).ToArray(); - - foreach (var recipe in recipeBased.Recipes) { - if (recipe.ResultItem != null) { - if (recipe.NameId == null) { - recipe.NameId = recipe.ResultItem.Name; - } else { - recipe.NameId = new L10NString(recipe.NameId).ToString(); - } - } else if (recipe.NameId != null) { - recipe.NameId = new L10NString(recipe.NameId).ToString(); - } - if (recipe.ParentNameId != null) { - recipe.ParentNameId = new L10NString(recipe.ParentNameId).ToString(); - } - recipe.Enchantments.ForEach(enchantment => AddRecipeForEnchantment(enchantment.AssetGuid, recipe)); - if (recipe.Material != 0) { - AddRecipeForMaterial(recipe.Material, recipe); - } - - if (recipe.ParentNameId != null) { - recipeBased.SubRecipes = recipeBased.SubRecipes ?? new Dictionary>(); - if (!recipeBased.SubRecipes.ContainsKey(recipe.ParentNameId)) { - recipeBased.SubRecipes[recipe.ParentNameId] = new List(); - } - - recipeBased.SubRecipes[recipe.ParentNameId].Add(recipe); - } - } - - if (recipeBased.Name.StartsWith("CraftMundane")) { - foreach (var blueprint in recipeBased.NewItemBaseIDs) { - if (!blueprint.AssetGuid.Contains("#CraftMagicItems")) { - AddItemForType(blueprint); - } - } - } - } - - if (itemData.ParentNameId != null) { - if (!LoadedData.SubCraftingData.ContainsKey(itemData.ParentNameId)) { - LoadedData.SubCraftingData[itemData.ParentNameId] = new List(); - } - - LoadedData.SubCraftingData[itemData.ParentNameId].Add(itemData); - } - } - - var allUsableItems = ResourcesLibrary.GetBlueprints(); - foreach (var item in allUsableItems) { - AddItemIdForEnchantment(item); - } - - var allNonRecipeEnchantmentsInItems = ResourcesLibrary.GetBlueprints() - .Where(enchantment => !LoadedData.EnchantmentIdToRecipe.ContainsKey(enchantment.AssetGuid) && LoadedData.EnchantmentIdToItem.ContainsKey(enchantment.AssetGuid)) - .ToArray(); - // BlueprintEnchantment.EnchantmentCost seems to be full of nonsense values - attempt to set cost of each enchantment by using the prices of - // items with enchantments. - foreach (var enchantment in allNonRecipeEnchantmentsInItems) { - var itemsWithEnchantment = LoadedData.EnchantmentIdToItem[enchantment.AssetGuid]; - foreach (var item in itemsWithEnchantment) { - if (DoesItemMatchAllEnchantments(item, enchantment.AssetGuid)) { - LoadedData.EnchantmentIdToCost[enchantment.AssetGuid] = item.Cost; - break; - } - } - } - - foreach (var enchantment in allNonRecipeEnchantmentsInItems) { - if (!LoadedData.EnchantmentIdToCost.ContainsKey(enchantment.AssetGuid)) { - var itemsWithEnchantment = LoadedData.EnchantmentIdToItem[enchantment.AssetGuid]; - foreach (var item in itemsWithEnchantment) { - if (ReverseEngineerEnchantmentCost(item, enchantment.AssetGuid)) { - break; - } - } - } - } - - LoadedData.CustomLootItems = ReadJsonFile($"{ModEntry.Path}/Data/LootItems.json"); - } - - private static void AddCraftingFeats(ObjectIDGenerator idGenerator, BlueprintProgression progression) { - foreach (var levelEntry in progression.LevelEntries) { - foreach (var featureBase in levelEntry.Features) { - var selection = featureBase as BlueprintFeatureSelection; - if (selection != null && (Features.CraftingFeatGroups.Contains(selection.Group) || Features.CraftingFeatGroups.Contains(selection.Group2))) { - // Use ObjectIDGenerator to detect which shared lists we've added the feats to. - idGenerator.GetId(selection.AllFeatures, out var firstTime); - if (firstTime) { - foreach (var data in LoadedData.ItemCraftingData) { - if (data.FeatGuid != null) { - var featBlueprint = ResourcesLibrary.TryGetBlueprint(data.FeatGuid) as BlueprintFeature; - var list = selection.AllFeatures.ToList(); - list.Add(featBlueprint); - selection.AllFeatures = list.ToArray(); - idGenerator.GetId(selection.AllFeatures, out firstTime); - } - } - } - } - } - } - } - - private static void AddAllCraftingFeats() { - var idGenerator = new ObjectIDGenerator(); - // Add crafting feats to general feat selection - AddCraftingFeats(idGenerator, Game.Instance.BlueprintRoot.Progression.FeatsProgression); - // ... and to relevant class feat selections. - foreach (var characterClass in Game.Instance.BlueprintRoot.Progression.CharacterClasses) { - AddCraftingFeats(idGenerator, characterClass.Progression); - } - - // Alchemists get Brew Potion as a bonus 1st level feat, except for Grenadier archetype alchemists. - var brewPotionData = LoadedData.ItemCraftingData.First(data => data.Name == "Potion"); - var brewPotion = ResourcesLibrary.TryGetBlueprint(brewPotionData.FeatGuid); - var alchemistProgression = ResourcesLibrary.TryGetBlueprint(ClassBlueprints.AlchemistProgressionGuid); - var grenadierArchetype = ResourcesLibrary.TryGetBlueprint(ClassBlueprints.AlchemistGrenadierArchetypeGuid); - if (brewPotion != null && alchemistProgression != null && grenadierArchetype != null) { - var firstLevelIndex = alchemistProgression.LevelEntries.FindIndex((levelEntry) => (levelEntry.Level == 1)); - alchemistProgression.LevelEntries[firstLevelIndex].Features.Add(brewPotion); - alchemistProgression.UIDeterminatorsGroup = alchemistProgression.UIDeterminatorsGroup.Concat(new[] {brewPotion}).ToArray(); - // Vanilla Grenadier has no level 1 RemoveFeatures, but a mod may have changed that, so search for it as well. - var firstLevelGrenadierRemoveIndex = grenadierArchetype.RemoveFeatures.FindIndex((levelEntry) => (levelEntry.Level == 1)); - if (firstLevelGrenadierRemoveIndex < 0) { - var removeFeatures = new[] {new LevelEntry {Level = 1}}; - grenadierArchetype.RemoveFeatures = removeFeatures.Concat(grenadierArchetype.RemoveFeatures).ToArray(); - firstLevelGrenadierRemoveIndex = 0; - } - grenadierArchetype.RemoveFeatures[firstLevelGrenadierRemoveIndex].Features.Add(brewPotion); - } else { - ModEntry.Logger.Warning("Failed to locate Alchemist progression, Grenadier archetype or Brew Potion feat!"); - } - - // Scroll Savant should get Scribe Scroll as a bonus 1st level feat. - var scribeScrollData = LoadedData.ItemCraftingData.First(data => data.Name == "Scroll"); - var scribeScroll = ResourcesLibrary.TryGetBlueprint(scribeScrollData.FeatGuid); - var scrollSavantArchetype = ResourcesLibrary.TryGetBlueprint(ClassBlueprints.ScrollSavantArchetypeGuid); - if (scribeScroll != null && scrollSavantArchetype != null) { - var firstLevelAdd = scrollSavantArchetype.AddFeatures.First((levelEntry) => (levelEntry.Level == 1)); - firstLevelAdd.Features.Add(scribeScroll); - } else { - ModEntry.Logger.Warning("Failed to locate Scroll Savant archetype or Scribe Scroll feat!"); - } - } - - [HarmonyLib.HarmonyPatch(typeof(TwoWeaponFightingAttackPenalty), "OnEventAboutToTrigger", new Type[] { typeof(RuleCalculateAttackBonusWithoutTarget) })] - private static class TwoWeaponFightingAttackPenaltyOnEventAboutToTriggerPatch { - static public BlueprintFeature ShieldMaster; - static MethodInfo methodToFind; - private static bool Prepare() { - try { - methodToFind = HarmonyLib.AccessTools.Property(typeof(ItemEntityWeapon), nameof(ItemEntityWeapon.IsShield)).GetGetMethod(); - } catch (Exception ex) { - Main.ModEntry.Logger.Log($"Error Preparing: {ex.Message}"); - return false; - } - return true; - } - private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator il) { - Label start = il.DefineLabel(); - yield return new HarmonyLib.CodeInstruction(OpCodes.Ldarg_0); - yield return new HarmonyLib.CodeInstruction(OpCodes.Ldarg_1); - yield return new HarmonyLib.CodeInstruction(OpCodes.Call, new Func(CheckShieldMaster).Method); - yield return new HarmonyLib.CodeInstruction(OpCodes.Brfalse_S, start); - yield return new HarmonyLib.CodeInstruction(OpCodes.Ret); - var skip = 0; - HarmonyLib.CodeInstruction prev = instructions.First(); - prev.labels.Add(start); - foreach (var inst in instructions.Skip(1)) { - if (prev.opcode == OpCodes.Ldloc_1 && inst.opcode == OpCodes.Callvirt && inst.operand as MethodInfo == methodToFind) { - // ldloc.1 - // callvirt instance bool Kingmaker.Items.ItemEntityWeapon::get_IsShield() - // brtrue.s IL_0152 - skip = 3; - } - if (skip > 0) { - skip--; - } else { - yield return prev; - } - prev = inst; - } - if (skip == 0) { - yield return prev; - } - } - - private static bool CheckShieldMaster(TwoWeaponFightingAttackPenalty component, RuleCalculateAttackBonusWithoutTarget evt) { - ItemEntityWeapon maybeWeapon2 = evt.Initiator.Body.SecondaryHand.MaybeWeapon; -#if !PATCH21 - RuleAttackWithWeapon ruleAttackWithWeapon = evt.Reason.Rule as RuleAttackWithWeapon; - if (ruleAttackWithWeapon != null && !ruleAttackWithWeapon.IsFullAttack) - return true; -#endif - return maybeWeapon2 != null && evt.Weapon == maybeWeapon2 && maybeWeapon2.IsShield && component.Owner.Progression.Features.HasFact(ShieldMaster); - } - } - - [AllowMultipleComponents] - public class ShieldMasterPatch : GameLogicComponent, IInitiatorRulebookHandler, IInitiatorRulebookHandler, IInitiatorRulebookHandler { - public void OnEventAboutToTrigger(RuleCalculateDamage evt) { - if (!evt.Initiator.Body.SecondaryHand.HasShield || evt.DamageBundle.Weapon == null || !evt.DamageBundle.Weapon.IsShield) { - return; - } - var armorEnhancementBonus = GameHelper.GetItemEnhancementBonus(evt.Initiator.Body.SecondaryHand.Shield.ArmorComponent); - var weaponEnhancementBonus = GameHelper.GetItemEnhancementBonus(evt.Initiator.Body.SecondaryHand.Shield.WeaponComponent); - if (weaponEnhancementBonus == 0 && evt.Initiator.Body.SecondaryHand.Shield.WeaponComponent.Blueprint.IsMasterwork) { - weaponEnhancementBonus = 1; - } - var itemEnhancementBonus = armorEnhancementBonus - weaponEnhancementBonus; - PhysicalDamage physicalDamage = evt.DamageBundle.WeaponDamage as PhysicalDamage; - if (physicalDamage != null && itemEnhancementBonus > 0) { - physicalDamage.Enchantment += itemEnhancementBonus; - physicalDamage.EnchantmentTotal += itemEnhancementBonus; - } - } - public void OnEventDidTrigger(RuleCalculateWeaponStats evt) { } - - public void OnEventAboutToTrigger(RuleCalculateWeaponStats evt) { - if (!evt.Initiator.Body.SecondaryHand.HasShield || evt.Weapon == null || !evt.Weapon.IsShield) { - return; - } - var armorEnhancementBonus = GameHelper.GetItemEnhancementBonus(evt.Initiator.Body.SecondaryHand.Shield.ArmorComponent); - var weaponEnhancementBonus = GameHelper.GetItemEnhancementBonus(evt.Initiator.Body.SecondaryHand.Shield.WeaponComponent); - var itemEnhancementBonus = armorEnhancementBonus - weaponEnhancementBonus; - if (itemEnhancementBonus > 0) { - evt.AddBonusDamage(itemEnhancementBonus); - } - } - public void OnEventDidTrigger(RuleCalculateDamage evt) { } - - public void OnEventAboutToTrigger(RuleCalculateAttackBonusWithoutTarget evt) { - if (!evt.Initiator.Body.SecondaryHand.HasShield || evt.Weapon == null || !evt.Weapon.IsShield) { - return; - } - var armorEnhancementBonus = GameHelper.GetItemEnhancementBonus(evt.Initiator.Body.SecondaryHand.Shield.ArmorComponent); - var weaponEnhancementBonus = GameHelper.GetItemEnhancementBonus(evt.Initiator.Body.SecondaryHand.Shield.WeaponComponent); - var num = armorEnhancementBonus - weaponEnhancementBonus; - if (num > 0) { - evt.AddBonus(num, base.Fact); - } - } - public void OnEventDidTrigger(RuleCalculateAttackBonusWithoutTarget evt) { } - } - - private static void PatchBlueprints() { - var shieldMaster = ResourcesLibrary.TryGetBlueprint(Features.ShieldMasterGuid); - var twoWeaponFighting = ResourcesLibrary.TryGetBlueprint(MechanicsBlueprints.TwoWeaponFightingBasicMechanicsGuid); - TwoWeaponFightingAttackPenaltyOnEventAboutToTriggerPatch.ShieldMaster = shieldMaster; - Accessors.SetBlueprintUnitFactDisplayName(twoWeaponFighting) = new L10NString("e32ce256-78dc-4fd0-bf15-21f9ebdf9921"); - - for (int i = 0; i < shieldMaster.ComponentsArray.Length; i++) { - if (shieldMaster.ComponentsArray[i] is ShieldMaster component) { - shieldMaster.ComponentsArray[i] = CraftMagicItems.Accessors.Create(a => { - a.name = component.name.Replace("ShieldMaster", "ShieldMasterPatch"); - }); - } - } - - var lightShield = ResourcesLibrary.TryGetBlueprint(ItemQualityBlueprints.WeaponLightShieldGuid); - Accessors.SetBlueprintItemBaseDamage(lightShield) = new DiceFormula(1, DiceType.D3); - var heavyShield = ResourcesLibrary.TryGetBlueprint(ItemQualityBlueprints.WeaponHeavyShieldGuid); - Accessors.SetBlueprintItemBaseDamage(heavyShield) = new DiceFormula(1, DiceType.D4); - - for (int i = 0; i < EnchantmentBlueprints.ItemEnchantmentGuids.Length; i++) { - var source = ResourcesLibrary.TryGetBlueprint(EnchantmentBlueprints.ItemEnchantmentGuids[i].WeaponEnchantmentGuid); - var dest = ResourcesLibrary.TryGetBlueprint(EnchantmentBlueprints.ItemEnchantmentGuids[i].UnarmedEnchantmentGuid); - Accessors.SetBlueprintItemEnchantmentEnchantName(dest) = Accessors.GetBlueprintItemEnchantmentEnchantName(source); - Accessors.SetBlueprintItemEnchantmentDescription(dest) = Accessors.GetBlueprintItemEnchantmentDescription(source); - } - - var longshankBane = ResourcesLibrary.TryGetBlueprint(EnchantmentBlueprints.LongshankBaneGuid); - if (longshankBane.ComponentsArray.Length >= 2 && longshankBane.ComponentsArray[1] is WeaponConditionalDamageDice conditional) { - for (int i = 0; i < conditional.Conditions.Conditions.Length; i++) { - if (conditional.Conditions.Conditions[i] is Kingmaker.Designers.EventConditionActionSystem.Conditions.HasFact condition) { -#if PATCH21_BETA - var replace = SerializedScriptableObject.CreateInstance(); -#else - var replace = ScriptableObject.CreateInstance(); -#endif - replace.Fact = condition.Fact; - replace.name = condition.name.Replace("HasFact", "ContextConditionHasFact"); - conditional.Conditions.Conditions[i] = replace; - } - } - } - } - - private static void InitialiseMod() { - if (modEnabled) { - PatchBlueprints(); - LeftHandVisualDisplayPatcher.PatchLeftHandedWeaponModels(); - InitialiseCraftingData(); - AddAllCraftingFeats(); - } - } - - [HarmonyLib.HarmonyPriority(HarmonyLib.Priority.Last)] - public static void Postfix() { - if (!mainMenuStarted) { - mainMenuStarted = true; - InitialiseMod(); - } - } - - public static void ModEnabledChanged() { - if (!mainMenuStarted && ResourcesLibrary.LibraryObject != null) { - mainMenuStarted = true; - L10N.SetEnabled(true); - SustenanceEnchantment.MainMenuStartPatch.Postfix(); - WildEnchantment.MainMenuStartPatch.Postfix(); - CreateQuiverAbility.MainMenuStartPatch.Postfix(); - InitialiseMod(); - return; - } - - if (!modEnabled) { - // Reset everything InitialiseMod initialises - LoadedData.ItemCraftingData = null; - LoadedData.SubCraftingData.Clear(); - LoadedData.SpellIdToItem.Clear(); - LoadedData.TypeToItem.Clear(); - LoadedData.EnchantmentIdToItem.Clear(); - LoadedData.EnchantmentIdToCost.Clear(); - LoadedData.EnchantmentIdToRecipe.Clear(); - UnpatchAllExcept(MethodPatchList); - } else if (mainMenuStarted) { - // If the mod is enabled and we're past the Start of main menu, (re-)initialise. - PatchAllOrdered(); - InitialiseMod(); - } - L10N.SetEnabled(modEnabled); - } - } - #if PATCH21 [HarmonyLib.HarmonyPatch(typeof(MainMenuUiContext), "Initialize")] private static class MainMenuUiContextInitializePatch { diff --git a/CraftMagicItems/Patches/Harmony/MainMenuStartPatch.cs b/CraftMagicItems/Patches/Harmony/MainMenuStartPatch.cs new file mode 100644 index 0000000..1189c56 --- /dev/null +++ b/CraftMagicItems/Patches/Harmony/MainMenuStartPatch.cs @@ -0,0 +1,474 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.Serialization; +using CraftMagicItems.Constants; +using CraftMagicItems.Localization; +using Kingmaker; +#if PATCH21 +#endif +using Kingmaker.Blueprints; +using Kingmaker.Blueprints.Classes; +using Kingmaker.Blueprints.Classes.Selection; +using Kingmaker.Blueprints.Items.Ecnchantments; +using Kingmaker.Blueprints.Items.Equipment; +using Kingmaker.Blueprints.Items.Weapons; +using Kingmaker.Designers; +using Kingmaker.Designers.Mechanics.Facts; +using Kingmaker.Enums.Damage; +using Kingmaker.Items; +#if !PATCH21 +using Kingmaker.Items.Slots; +#endif +using Kingmaker.PubSubSystem; +using Kingmaker.RuleSystem; +using Kingmaker.RuleSystem.Rules; +using Kingmaker.RuleSystem.Rules.Damage; +#if !PATCH21 +using Kingmaker.UnitLogic.ActivatableAbilities; +#endif +using Kingmaker.UnitLogic.Mechanics.Components; +using Kingmaker.Utility; +using UnityEngine; + +namespace CraftMagicItems.Patches.Harmony +{ + [HarmonyLib.HarmonyPatch(typeof(MainMenu), "Start")] + public static class MainMenuStartPatch + { + private static bool mainMenuStarted; + + private static void InitialiseCraftingData() + { + // Read the crafting data now that ResourcesLibrary is loaded. + Main.LoadedData.ItemCraftingData = Main.ReadJsonFile($"{Main.ModEntry.Path}/Data/ItemTypes.json", new CraftingTypeConverter()); + // Initialise lookup tables. + foreach (var itemData in Main.LoadedData.ItemCraftingData) + { + if (itemData is RecipeBasedItemCraftingData recipeBased) + { + recipeBased.Recipes = recipeBased.RecipeFileNames.Aggregate(Enumerable.Empty(), + (all, fileName) => all.Concat(Main.ReadJsonFile($"{Main.ModEntry.Path}/Data/{fileName}")) + ).Where(recipe => { + return (recipe.ResultItem != null) + || (recipe.Enchantments.Length > 0) + || (recipe.NoResultItem && recipe.NoEnchantments); + }).ToArray(); + + foreach (var recipe in recipeBased.Recipes) + { + if (recipe.ResultItem != null) + { + if (recipe.NameId == null) + { + recipe.NameId = recipe.ResultItem.Name; + } + else + { + recipe.NameId = new L10NString(recipe.NameId).ToString(); + } + } + else if (recipe.NameId != null) + { + recipe.NameId = new L10NString(recipe.NameId).ToString(); + } + if (recipe.ParentNameId != null) + { + recipe.ParentNameId = new L10NString(recipe.ParentNameId).ToString(); + } + recipe.Enchantments.ForEach(enchantment => Main.AddRecipeForEnchantment(enchantment.AssetGuid, recipe)); + if (recipe.Material != 0) + { + Main.AddRecipeForMaterial(recipe.Material, recipe); + } + + if (recipe.ParentNameId != null) + { + recipeBased.SubRecipes = recipeBased.SubRecipes ?? new Dictionary>(); + if (!recipeBased.SubRecipes.ContainsKey(recipe.ParentNameId)) + { + recipeBased.SubRecipes[recipe.ParentNameId] = new List(); + } + + recipeBased.SubRecipes[recipe.ParentNameId].Add(recipe); + } + } + + if (recipeBased.Name.StartsWith("CraftMundane")) + { + foreach (var blueprint in recipeBased.NewItemBaseIDs) + { + if (!blueprint.AssetGuid.Contains("#CraftMagicItems")) + { + Main.AddItemForType(blueprint); + } + } + } + } + + if (itemData.ParentNameId != null) + { + if (!Main.LoadedData.SubCraftingData.ContainsKey(itemData.ParentNameId)) + { + Main.LoadedData.SubCraftingData[itemData.ParentNameId] = new List(); + } + + Main.LoadedData.SubCraftingData[itemData.ParentNameId].Add(itemData); + } + } + + var allUsableItems = ResourcesLibrary.GetBlueprints(); + foreach (var item in allUsableItems) + { + Main.AddItemIdForEnchantment(item); + } + + var allNonRecipeEnchantmentsInItems = ResourcesLibrary.GetBlueprints() + .Where(enchantment => !Main.LoadedData.EnchantmentIdToRecipe.ContainsKey(enchantment.AssetGuid) && Main.LoadedData.EnchantmentIdToItem.ContainsKey(enchantment.AssetGuid)) + .ToArray(); + // BlueprintEnchantment.EnchantmentCost seems to be full of nonsense values - attempt to set cost of each enchantment by using the prices of + // items with enchantments. + foreach (var enchantment in allNonRecipeEnchantmentsInItems) + { + var itemsWithEnchantment = Main.LoadedData.EnchantmentIdToItem[enchantment.AssetGuid]; + foreach (var item in itemsWithEnchantment) + { + if (Main.DoesItemMatchAllEnchantments(item, enchantment.AssetGuid)) + { + Main.LoadedData.EnchantmentIdToCost[enchantment.AssetGuid] = item.Cost; + break; + } + } + } + + foreach (var enchantment in allNonRecipeEnchantmentsInItems) + { + if (!Main.LoadedData.EnchantmentIdToCost.ContainsKey(enchantment.AssetGuid)) + { + var itemsWithEnchantment = Main.LoadedData.EnchantmentIdToItem[enchantment.AssetGuid]; + foreach (var item in itemsWithEnchantment) + { + if (Main.ReverseEngineerEnchantmentCost(item, enchantment.AssetGuid)) + { + break; + } + } + } + } + + Main.LoadedData.CustomLootItems = Main.ReadJsonFile($"{Main.ModEntry.Path}/Data/LootItems.json"); + } + + private static void AddCraftingFeats(ObjectIDGenerator idGenerator, BlueprintProgression progression) + { + foreach (var levelEntry in progression.LevelEntries) + { + foreach (var featureBase in levelEntry.Features) + { + var selection = featureBase as BlueprintFeatureSelection; + if (selection != null && (Features.CraftingFeatGroups.Contains(selection.Group) || Features.CraftingFeatGroups.Contains(selection.Group2))) + { + // Use ObjectIDGenerator to detect which shared lists we've added the feats to. + idGenerator.GetId(selection.AllFeatures, out var firstTime); + if (firstTime) + { + foreach (var data in Main.LoadedData.ItemCraftingData) + { + if (data.FeatGuid != null) + { + var featBlueprint = ResourcesLibrary.TryGetBlueprint(data.FeatGuid) as BlueprintFeature; + var list = selection.AllFeatures.ToList(); + list.Add(featBlueprint); + selection.AllFeatures = list.ToArray(); + idGenerator.GetId(selection.AllFeatures, out firstTime); + } + } + } + } + } + } + } + + private static void AddAllCraftingFeats() + { + var idGenerator = new ObjectIDGenerator(); + // Add crafting feats to general feat selection + AddCraftingFeats(idGenerator, Game.Instance.BlueprintRoot.Progression.FeatsProgression); + // ... and to relevant class feat selections. + foreach (var characterClass in Game.Instance.BlueprintRoot.Progression.CharacterClasses) + { + AddCraftingFeats(idGenerator, characterClass.Progression); + } + + // Alchemists get Brew Potion as a bonus 1st level feat, except for Grenadier archetype alchemists. + var brewPotionData = Main.LoadedData.ItemCraftingData.First(data => data.Name == "Potion"); + var brewPotion = ResourcesLibrary.TryGetBlueprint(brewPotionData.FeatGuid); + var alchemistProgression = ResourcesLibrary.TryGetBlueprint(ClassBlueprints.AlchemistProgressionGuid); + var grenadierArchetype = ResourcesLibrary.TryGetBlueprint(ClassBlueprints.AlchemistGrenadierArchetypeGuid); + if (brewPotion != null && alchemistProgression != null && grenadierArchetype != null) + { + var firstLevelIndex = alchemistProgression.LevelEntries.FindIndex((levelEntry) => (levelEntry.Level == 1)); + alchemistProgression.LevelEntries[firstLevelIndex].Features.Add(brewPotion); + alchemistProgression.UIDeterminatorsGroup = alchemistProgression.UIDeterminatorsGroup.Concat(new[] { brewPotion }).ToArray(); + // Vanilla Grenadier has no level 1 RemoveFeatures, but a mod may have changed that, so search for it as well. + var firstLevelGrenadierRemoveIndex = grenadierArchetype.RemoveFeatures.FindIndex((levelEntry) => (levelEntry.Level == 1)); + if (firstLevelGrenadierRemoveIndex < 0) + { + var removeFeatures = new[] { new LevelEntry { Level = 1 } }; + grenadierArchetype.RemoveFeatures = removeFeatures.Concat(grenadierArchetype.RemoveFeatures).ToArray(); + firstLevelGrenadierRemoveIndex = 0; + } + grenadierArchetype.RemoveFeatures[firstLevelGrenadierRemoveIndex].Features.Add(brewPotion); + } + else + { + Main.ModEntry.Logger.Warning("Failed to locate Alchemist progression, Grenadier archetype or Brew Potion feat!"); + } + + // Scroll Savant should get Scribe Scroll as a bonus 1st level feat. + var scribeScrollData = Main.LoadedData.ItemCraftingData.First(data => data.Name == "Scroll"); + var scribeScroll = ResourcesLibrary.TryGetBlueprint(scribeScrollData.FeatGuid); + var scrollSavantArchetype = ResourcesLibrary.TryGetBlueprint(ClassBlueprints.ScrollSavantArchetypeGuid); + if (scribeScroll != null && scrollSavantArchetype != null) + { + var firstLevelAdd = scrollSavantArchetype.AddFeatures.First((levelEntry) => (levelEntry.Level == 1)); + firstLevelAdd.Features.Add(scribeScroll); + } + else + { + Main.ModEntry.Logger.Warning("Failed to locate Scroll Savant archetype or Scribe Scroll feat!"); + } + } + + [HarmonyLib.HarmonyPatch(typeof(TwoWeaponFightingAttackPenalty), "OnEventAboutToTrigger", new Type[] { typeof(RuleCalculateAttackBonusWithoutTarget) })] + private static class TwoWeaponFightingAttackPenaltyOnEventAboutToTriggerPatch + { + static public BlueprintFeature ShieldMaster; + static MethodInfo methodToFind; + private static bool Prepare() + { + try + { + methodToFind = HarmonyLib.AccessTools.Property(typeof(ItemEntityWeapon), nameof(ItemEntityWeapon.IsShield)).GetGetMethod(); + } + catch (Exception ex) + { + Main.ModEntry.Logger.Log($"Error Preparing: {ex.Message}"); + return false; + } + return true; + } + private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator il) + { + Label start = il.DefineLabel(); + yield return new HarmonyLib.CodeInstruction(OpCodes.Ldarg_0); + yield return new HarmonyLib.CodeInstruction(OpCodes.Ldarg_1); + yield return new HarmonyLib.CodeInstruction(OpCodes.Call, new Func(CheckShieldMaster).Method); + yield return new HarmonyLib.CodeInstruction(OpCodes.Brfalse_S, start); + yield return new HarmonyLib.CodeInstruction(OpCodes.Ret); + var skip = 0; + HarmonyLib.CodeInstruction prev = instructions.First(); + prev.labels.Add(start); + foreach (var inst in instructions.Skip(1)) + { + if (prev.opcode == OpCodes.Ldloc_1 && inst.opcode == OpCodes.Callvirt && inst.operand as MethodInfo == methodToFind) + { + // ldloc.1 + // callvirt instance bool Kingmaker.Items.ItemEntityWeapon::get_IsShield() + // brtrue.s IL_0152 + skip = 3; + } + if (skip > 0) + { + skip--; + } + else + { + yield return prev; + } + prev = inst; + } + if (skip == 0) + { + yield return prev; + } + } + + private static bool CheckShieldMaster(TwoWeaponFightingAttackPenalty component, RuleCalculateAttackBonusWithoutTarget evt) + { + ItemEntityWeapon maybeWeapon2 = evt.Initiator.Body.SecondaryHand.MaybeWeapon; +#if !PATCH21 + RuleAttackWithWeapon ruleAttackWithWeapon = evt.Reason.Rule as RuleAttackWithWeapon; + if (ruleAttackWithWeapon != null && !ruleAttackWithWeapon.IsFullAttack) + return true; +#endif + return maybeWeapon2 != null && evt.Weapon == maybeWeapon2 && maybeWeapon2.IsShield && component.Owner.Progression.Features.HasFact(ShieldMaster); + } + } + + [AllowMultipleComponents] + public class ShieldMasterPatch : GameLogicComponent, IInitiatorRulebookHandler, IInitiatorRulebookHandler, IInitiatorRulebookHandler + { + public void OnEventAboutToTrigger(RuleCalculateDamage evt) + { + if (!evt.Initiator.Body.SecondaryHand.HasShield || evt.DamageBundle.Weapon == null || !evt.DamageBundle.Weapon.IsShield) + { + return; + } + var armorEnhancementBonus = GameHelper.GetItemEnhancementBonus(evt.Initiator.Body.SecondaryHand.Shield.ArmorComponent); + var weaponEnhancementBonus = GameHelper.GetItemEnhancementBonus(evt.Initiator.Body.SecondaryHand.Shield.WeaponComponent); + if (weaponEnhancementBonus == 0 && evt.Initiator.Body.SecondaryHand.Shield.WeaponComponent.Blueprint.IsMasterwork) + { + weaponEnhancementBonus = 1; + } + var itemEnhancementBonus = armorEnhancementBonus - weaponEnhancementBonus; + PhysicalDamage physicalDamage = evt.DamageBundle.WeaponDamage as PhysicalDamage; + if (physicalDamage != null && itemEnhancementBonus > 0) + { + physicalDamage.Enchantment += itemEnhancementBonus; + physicalDamage.EnchantmentTotal += itemEnhancementBonus; + } + } + public void OnEventDidTrigger(RuleCalculateWeaponStats evt) { } + + public void OnEventAboutToTrigger(RuleCalculateWeaponStats evt) + { + if (!evt.Initiator.Body.SecondaryHand.HasShield || evt.Weapon == null || !evt.Weapon.IsShield) + { + return; + } + var armorEnhancementBonus = GameHelper.GetItemEnhancementBonus(evt.Initiator.Body.SecondaryHand.Shield.ArmorComponent); + var weaponEnhancementBonus = GameHelper.GetItemEnhancementBonus(evt.Initiator.Body.SecondaryHand.Shield.WeaponComponent); + var itemEnhancementBonus = armorEnhancementBonus - weaponEnhancementBonus; + if (itemEnhancementBonus > 0) + { + evt.AddBonusDamage(itemEnhancementBonus); + } + } + public void OnEventDidTrigger(RuleCalculateDamage evt) { } + + public void OnEventAboutToTrigger(RuleCalculateAttackBonusWithoutTarget evt) + { + if (!evt.Initiator.Body.SecondaryHand.HasShield || evt.Weapon == null || !evt.Weapon.IsShield) + { + return; + } + var armorEnhancementBonus = GameHelper.GetItemEnhancementBonus(evt.Initiator.Body.SecondaryHand.Shield.ArmorComponent); + var weaponEnhancementBonus = GameHelper.GetItemEnhancementBonus(evt.Initiator.Body.SecondaryHand.Shield.WeaponComponent); + var num = armorEnhancementBonus - weaponEnhancementBonus; + if (num > 0) + { + evt.AddBonus(num, base.Fact); + } + } + public void OnEventDidTrigger(RuleCalculateAttackBonusWithoutTarget evt) { } + } + + private static void PatchBlueprints() + { + var shieldMaster = ResourcesLibrary.TryGetBlueprint(Features.ShieldMasterGuid); + var twoWeaponFighting = ResourcesLibrary.TryGetBlueprint(MechanicsBlueprints.TwoWeaponFightingBasicMechanicsGuid); + TwoWeaponFightingAttackPenaltyOnEventAboutToTriggerPatch.ShieldMaster = shieldMaster; + Main.Accessors.SetBlueprintUnitFactDisplayName(twoWeaponFighting) = new L10NString("e32ce256-78dc-4fd0-bf15-21f9ebdf9921"); + + for (int i = 0; i < shieldMaster.ComponentsArray.Length; i++) + { + if (shieldMaster.ComponentsArray[i] is ShieldMaster component) + { + shieldMaster.ComponentsArray[i] = Accessors.Create(a => { + a.name = component.name.Replace("ShieldMaster", "ShieldMasterPatch"); + }); + } + } + + var lightShield = ResourcesLibrary.TryGetBlueprint(ItemQualityBlueprints.WeaponLightShieldGuid); + Main.Accessors.SetBlueprintItemBaseDamage(lightShield) = new DiceFormula(1, DiceType.D3); + var heavyShield = ResourcesLibrary.TryGetBlueprint(ItemQualityBlueprints.WeaponHeavyShieldGuid); + Main.Accessors.SetBlueprintItemBaseDamage(heavyShield) = new DiceFormula(1, DiceType.D4); + + for (int i = 0; i < EnchantmentBlueprints.ItemEnchantmentGuids.Length; i++) + { + var source = ResourcesLibrary.TryGetBlueprint(EnchantmentBlueprints.ItemEnchantmentGuids[i].WeaponEnchantmentGuid); + var dest = ResourcesLibrary.TryGetBlueprint(EnchantmentBlueprints.ItemEnchantmentGuids[i].UnarmedEnchantmentGuid); + Main.Accessors.SetBlueprintItemEnchantmentEnchantName(dest) = Main.Accessors.GetBlueprintItemEnchantmentEnchantName(source); + Main.Accessors.SetBlueprintItemEnchantmentDescription(dest) = Main.Accessors.GetBlueprintItemEnchantmentDescription(source); + } + + var longshankBane = ResourcesLibrary.TryGetBlueprint(EnchantmentBlueprints.LongshankBaneGuid); + if (longshankBane.ComponentsArray.Length >= 2 && longshankBane.ComponentsArray[1] is WeaponConditionalDamageDice conditional) + { + for (int i = 0; i < conditional.Conditions.Conditions.Length; i++) + { + if (conditional.Conditions.Conditions[i] is Kingmaker.Designers.EventConditionActionSystem.Conditions.HasFact condition) + { +#if PATCH21_BETA + var replace = SerializedScriptableObject.CreateInstance(); +#else + var replace = ScriptableObject.CreateInstance(); +#endif + replace.Fact = condition.Fact; + replace.name = condition.name.Replace("HasFact", "ContextConditionHasFact"); + conditional.Conditions.Conditions[i] = replace; + } + } + } + } + + private static void InitialiseMod() + { + if (Main.modEnabled) + { + PatchBlueprints(); + LeftHandVisualDisplayPatcher.PatchLeftHandedWeaponModels(); + InitialiseCraftingData(); + AddAllCraftingFeats(); + } + } + + [HarmonyLib.HarmonyPriority(HarmonyLib.Priority.Last)] + public static void Postfix() + { + if (!mainMenuStarted) + { + mainMenuStarted = true; + InitialiseMod(); + } + } + + public static void ModEnabledChanged() + { + if (!mainMenuStarted && ResourcesLibrary.LibraryObject != null) + { + mainMenuStarted = true; + L10N.SetEnabled(true); + SustenanceEnchantment.MainMenuStartPatch.Postfix(); + WildEnchantment.MainMenuStartPatch.Postfix(); + CreateQuiverAbility.MainMenuStartPatch.Postfix(); + InitialiseMod(); + return; + } + + if (!Main.modEnabled) + { + // Reset everything InitialiseMod initialises + Main.LoadedData.ItemCraftingData = null; + Main.LoadedData.SubCraftingData.Clear(); + Main.LoadedData.SpellIdToItem.Clear(); + Main.LoadedData.TypeToItem.Clear(); + Main.LoadedData.EnchantmentIdToItem.Clear(); + Main.LoadedData.EnchantmentIdToCost.Clear(); + Main.LoadedData.EnchantmentIdToRecipe.Clear(); + Main.UnpatchAllExcept(Main.MethodPatchList); + } + else if (mainMenuStarted) + { + // If the mod is enabled and we're past the Start of main menu, (re-)initialise. + Main.PatchAllOrdered(); + InitialiseMod(); + } + L10N.SetEnabled(Main.modEnabled); + } + } +} \ No newline at end of file From 0312dfb8a32faddaefd621d37dfdd8b587ba7b7f Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 14 Dec 2020 12:08:43 -0600 Subject: [PATCH 073/132] Extracting the Harmony patches from Main into their own files: TwoWeaponFightingAttackPenaltyOnEventAboutToTriggerPatch --- CraftMagicItems/CraftMagicItems.csproj | 1 + .../Patches/Harmony/MainMenuStartPatch.cs | 72 +-------------- ...AttackPenaltyOnEventAboutToTriggerPatch.cs | 87 +++++++++++++++++++ 3 files changed, 89 insertions(+), 71 deletions(-) create mode 100644 CraftMagicItems/Patches/Harmony/TwoWeaponFightingAttackPenaltyOnEventAboutToTriggerPatch.cs diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index f67d7bf..63b92ce 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -108,6 +108,7 @@ + diff --git a/CraftMagicItems/Patches/Harmony/MainMenuStartPatch.cs b/CraftMagicItems/Patches/Harmony/MainMenuStartPatch.cs index 1189c56..43bd653 100644 --- a/CraftMagicItems/Patches/Harmony/MainMenuStartPatch.cs +++ b/CraftMagicItems/Patches/Harmony/MainMenuStartPatch.cs @@ -1,8 +1,5 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; -using System.Reflection; -using System.Reflection.Emit; using System.Runtime.Serialization; using CraftMagicItems.Constants; using CraftMagicItems.Localization; @@ -18,7 +15,6 @@ using Kingmaker.Designers; using Kingmaker.Designers.Mechanics.Facts; using Kingmaker.Enums.Damage; -using Kingmaker.Items; #if !PATCH21 using Kingmaker.Items.Slots; #endif @@ -242,72 +238,6 @@ private static void AddAllCraftingFeats() } } - [HarmonyLib.HarmonyPatch(typeof(TwoWeaponFightingAttackPenalty), "OnEventAboutToTrigger", new Type[] { typeof(RuleCalculateAttackBonusWithoutTarget) })] - private static class TwoWeaponFightingAttackPenaltyOnEventAboutToTriggerPatch - { - static public BlueprintFeature ShieldMaster; - static MethodInfo methodToFind; - private static bool Prepare() - { - try - { - methodToFind = HarmonyLib.AccessTools.Property(typeof(ItemEntityWeapon), nameof(ItemEntityWeapon.IsShield)).GetGetMethod(); - } - catch (Exception ex) - { - Main.ModEntry.Logger.Log($"Error Preparing: {ex.Message}"); - return false; - } - return true; - } - private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator il) - { - Label start = il.DefineLabel(); - yield return new HarmonyLib.CodeInstruction(OpCodes.Ldarg_0); - yield return new HarmonyLib.CodeInstruction(OpCodes.Ldarg_1); - yield return new HarmonyLib.CodeInstruction(OpCodes.Call, new Func(CheckShieldMaster).Method); - yield return new HarmonyLib.CodeInstruction(OpCodes.Brfalse_S, start); - yield return new HarmonyLib.CodeInstruction(OpCodes.Ret); - var skip = 0; - HarmonyLib.CodeInstruction prev = instructions.First(); - prev.labels.Add(start); - foreach (var inst in instructions.Skip(1)) - { - if (prev.opcode == OpCodes.Ldloc_1 && inst.opcode == OpCodes.Callvirt && inst.operand as MethodInfo == methodToFind) - { - // ldloc.1 - // callvirt instance bool Kingmaker.Items.ItemEntityWeapon::get_IsShield() - // brtrue.s IL_0152 - skip = 3; - } - if (skip > 0) - { - skip--; - } - else - { - yield return prev; - } - prev = inst; - } - if (skip == 0) - { - yield return prev; - } - } - - private static bool CheckShieldMaster(TwoWeaponFightingAttackPenalty component, RuleCalculateAttackBonusWithoutTarget evt) - { - ItemEntityWeapon maybeWeapon2 = evt.Initiator.Body.SecondaryHand.MaybeWeapon; -#if !PATCH21 - RuleAttackWithWeapon ruleAttackWithWeapon = evt.Reason.Rule as RuleAttackWithWeapon; - if (ruleAttackWithWeapon != null && !ruleAttackWithWeapon.IsFullAttack) - return true; -#endif - return maybeWeapon2 != null && evt.Weapon == maybeWeapon2 && maybeWeapon2.IsShield && component.Owner.Progression.Features.HasFact(ShieldMaster); - } - } - [AllowMultipleComponents] public class ShieldMasterPatch : GameLogicComponent, IInitiatorRulebookHandler, IInitiatorRulebookHandler, IInitiatorRulebookHandler { diff --git a/CraftMagicItems/Patches/Harmony/TwoWeaponFightingAttackPenaltyOnEventAboutToTriggerPatch.cs b/CraftMagicItems/Patches/Harmony/TwoWeaponFightingAttackPenaltyOnEventAboutToTriggerPatch.cs new file mode 100644 index 0000000..46f5856 --- /dev/null +++ b/CraftMagicItems/Patches/Harmony/TwoWeaponFightingAttackPenaltyOnEventAboutToTriggerPatch.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; +#if PATCH21 +#endif +using Kingmaker.Blueprints.Classes; +using Kingmaker.Designers.Mechanics.Facts; +using Kingmaker.Items; +#if !PATCH21 +using Kingmaker.Items.Slots; +#endif +using Kingmaker.RuleSystem.Rules; +#if !PATCH21 +using Kingmaker.UnitLogic.ActivatableAbilities; +#endif + +namespace CraftMagicItems.Patches.Harmony +{ + + [HarmonyLib.HarmonyPatch(typeof(TwoWeaponFightingAttackPenalty), "OnEventAboutToTrigger", new Type[] { typeof(RuleCalculateAttackBonusWithoutTarget) })] + public static class TwoWeaponFightingAttackPenaltyOnEventAboutToTriggerPatch + { + static public BlueprintFeature ShieldMaster; + static MethodInfo methodToFind; + private static bool Prepare() + { + try + { + methodToFind = HarmonyLib.AccessTools.Property(typeof(ItemEntityWeapon), nameof(ItemEntityWeapon.IsShield)).GetGetMethod(); + } + catch (Exception ex) + { + Main.ModEntry.Logger.Log($"Error Preparing: {ex.Message}"); + return false; + } + return true; + } + private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator il) + { + Label start = il.DefineLabel(); + yield return new HarmonyLib.CodeInstruction(OpCodes.Ldarg_0); + yield return new HarmonyLib.CodeInstruction(OpCodes.Ldarg_1); + yield return new HarmonyLib.CodeInstruction(OpCodes.Call, new Func(CheckShieldMaster).Method); + yield return new HarmonyLib.CodeInstruction(OpCodes.Brfalse_S, start); + yield return new HarmonyLib.CodeInstruction(OpCodes.Ret); + var skip = 0; + HarmonyLib.CodeInstruction prev = instructions.First(); + prev.labels.Add(start); + foreach (var inst in instructions.Skip(1)) + { + if (prev.opcode == OpCodes.Ldloc_1 && inst.opcode == OpCodes.Callvirt && inst.operand as MethodInfo == methodToFind) + { + // ldloc.1 + // callvirt instance bool Kingmaker.Items.ItemEntityWeapon::get_IsShield() + // brtrue.s IL_0152 + skip = 3; + } + if (skip > 0) + { + skip--; + } + else + { + yield return prev; + } + prev = inst; + } + if (skip == 0) + { + yield return prev; + } + } + + private static bool CheckShieldMaster(TwoWeaponFightingAttackPenalty component, RuleCalculateAttackBonusWithoutTarget evt) + { + ItemEntityWeapon maybeWeapon2 = evt.Initiator.Body.SecondaryHand.MaybeWeapon; +#if !PATCH21 + RuleAttackWithWeapon ruleAttackWithWeapon = evt.Reason.Rule as RuleAttackWithWeapon; + if (ruleAttackWithWeapon != null && !ruleAttackWithWeapon.IsFullAttack) + return true; +#endif + return maybeWeapon2 != null && evt.Weapon == maybeWeapon2 && maybeWeapon2.IsShield && component.Owner.Progression.Features.HasFact(ShieldMaster); + } + } +} From 4ca90aceb29ada79634e522fde4fc9063e887f74 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 14 Dec 2020 12:22:02 -0600 Subject: [PATCH 074/132] Extracting the Harmony patches from Main into their own files: ShieldMasterPatch --- CraftMagicItems/CraftMagicItems.csproj | 1 + .../Patches/Harmony/MainMenuStartPatch.cs | 62 -------------- .../Patches/Harmony/ShieldMasterPatch.cs | 81 +++++++++++++++++++ 3 files changed, 82 insertions(+), 62 deletions(-) create mode 100644 CraftMagicItems/Patches/Harmony/ShieldMasterPatch.cs diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index 63b92ce..3371342 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -108,6 +108,7 @@ + diff --git a/CraftMagicItems/Patches/Harmony/MainMenuStartPatch.cs b/CraftMagicItems/Patches/Harmony/MainMenuStartPatch.cs index 43bd653..e092b4e 100644 --- a/CraftMagicItems/Patches/Harmony/MainMenuStartPatch.cs +++ b/CraftMagicItems/Patches/Harmony/MainMenuStartPatch.cs @@ -12,16 +12,12 @@ using Kingmaker.Blueprints.Items.Ecnchantments; using Kingmaker.Blueprints.Items.Equipment; using Kingmaker.Blueprints.Items.Weapons; -using Kingmaker.Designers; using Kingmaker.Designers.Mechanics.Facts; using Kingmaker.Enums.Damage; #if !PATCH21 using Kingmaker.Items.Slots; #endif -using Kingmaker.PubSubSystem; using Kingmaker.RuleSystem; -using Kingmaker.RuleSystem.Rules; -using Kingmaker.RuleSystem.Rules.Damage; #if !PATCH21 using Kingmaker.UnitLogic.ActivatableAbilities; #endif @@ -238,64 +234,6 @@ private static void AddAllCraftingFeats() } } - [AllowMultipleComponents] - public class ShieldMasterPatch : GameLogicComponent, IInitiatorRulebookHandler, IInitiatorRulebookHandler, IInitiatorRulebookHandler - { - public void OnEventAboutToTrigger(RuleCalculateDamage evt) - { - if (!evt.Initiator.Body.SecondaryHand.HasShield || evt.DamageBundle.Weapon == null || !evt.DamageBundle.Weapon.IsShield) - { - return; - } - var armorEnhancementBonus = GameHelper.GetItemEnhancementBonus(evt.Initiator.Body.SecondaryHand.Shield.ArmorComponent); - var weaponEnhancementBonus = GameHelper.GetItemEnhancementBonus(evt.Initiator.Body.SecondaryHand.Shield.WeaponComponent); - if (weaponEnhancementBonus == 0 && evt.Initiator.Body.SecondaryHand.Shield.WeaponComponent.Blueprint.IsMasterwork) - { - weaponEnhancementBonus = 1; - } - var itemEnhancementBonus = armorEnhancementBonus - weaponEnhancementBonus; - PhysicalDamage physicalDamage = evt.DamageBundle.WeaponDamage as PhysicalDamage; - if (physicalDamage != null && itemEnhancementBonus > 0) - { - physicalDamage.Enchantment += itemEnhancementBonus; - physicalDamage.EnchantmentTotal += itemEnhancementBonus; - } - } - public void OnEventDidTrigger(RuleCalculateWeaponStats evt) { } - - public void OnEventAboutToTrigger(RuleCalculateWeaponStats evt) - { - if (!evt.Initiator.Body.SecondaryHand.HasShield || evt.Weapon == null || !evt.Weapon.IsShield) - { - return; - } - var armorEnhancementBonus = GameHelper.GetItemEnhancementBonus(evt.Initiator.Body.SecondaryHand.Shield.ArmorComponent); - var weaponEnhancementBonus = GameHelper.GetItemEnhancementBonus(evt.Initiator.Body.SecondaryHand.Shield.WeaponComponent); - var itemEnhancementBonus = armorEnhancementBonus - weaponEnhancementBonus; - if (itemEnhancementBonus > 0) - { - evt.AddBonusDamage(itemEnhancementBonus); - } - } - public void OnEventDidTrigger(RuleCalculateDamage evt) { } - - public void OnEventAboutToTrigger(RuleCalculateAttackBonusWithoutTarget evt) - { - if (!evt.Initiator.Body.SecondaryHand.HasShield || evt.Weapon == null || !evt.Weapon.IsShield) - { - return; - } - var armorEnhancementBonus = GameHelper.GetItemEnhancementBonus(evt.Initiator.Body.SecondaryHand.Shield.ArmorComponent); - var weaponEnhancementBonus = GameHelper.GetItemEnhancementBonus(evt.Initiator.Body.SecondaryHand.Shield.WeaponComponent); - var num = armorEnhancementBonus - weaponEnhancementBonus; - if (num > 0) - { - evt.AddBonus(num, base.Fact); - } - } - public void OnEventDidTrigger(RuleCalculateAttackBonusWithoutTarget evt) { } - } - private static void PatchBlueprints() { var shieldMaster = ResourcesLibrary.TryGetBlueprint(Features.ShieldMasterGuid); diff --git a/CraftMagicItems/Patches/Harmony/ShieldMasterPatch.cs b/CraftMagicItems/Patches/Harmony/ShieldMasterPatch.cs new file mode 100644 index 0000000..d33f0b7 --- /dev/null +++ b/CraftMagicItems/Patches/Harmony/ShieldMasterPatch.cs @@ -0,0 +1,81 @@ +#if PATCH21 +#endif +using Kingmaker.Blueprints; +using Kingmaker.Designers; +#if !PATCH21 +using Kingmaker.Items.Slots; +#endif +using Kingmaker.PubSubSystem; +using Kingmaker.RuleSystem.Rules; +using Kingmaker.RuleSystem.Rules.Damage; +#if !PATCH21 +using Kingmaker.UnitLogic.ActivatableAbilities; +#endif + +namespace CraftMagicItems.Patches.Harmony +{ + [AllowMultipleComponents] + public class ShieldMasterPatch : GameLogicComponent, IInitiatorRulebookHandler, IInitiatorRulebookHandler, IInitiatorRulebookHandler + { + public void OnEventAboutToTrigger(RuleCalculateDamage evt) + { + if (!evt.Initiator.Body.SecondaryHand.HasShield || evt.DamageBundle.Weapon == null || !evt.DamageBundle.Weapon.IsShield) + { + return; + } + + var armorEnhancementBonus = GameHelper.GetItemEnhancementBonus(evt.Initiator.Body.SecondaryHand.Shield.ArmorComponent); + var weaponEnhancementBonus = GameHelper.GetItemEnhancementBonus(evt.Initiator.Body.SecondaryHand.Shield.WeaponComponent); + if (weaponEnhancementBonus == 0 && evt.Initiator.Body.SecondaryHand.Shield.WeaponComponent.Blueprint.IsMasterwork) + { + weaponEnhancementBonus = 1; + } + + var itemEnhancementBonus = armorEnhancementBonus - weaponEnhancementBonus; + PhysicalDamage physicalDamage = evt.DamageBundle.WeaponDamage as PhysicalDamage; + if (physicalDamage != null && itemEnhancementBonus > 0) + { + physicalDamage.Enchantment += itemEnhancementBonus; + physicalDamage.EnchantmentTotal += itemEnhancementBonus; + } + } + + public void OnEventDidTrigger(RuleCalculateWeaponStats evt) { } + + public void OnEventAboutToTrigger(RuleCalculateWeaponStats evt) + { + if (!evt.Initiator.Body.SecondaryHand.HasShield || evt.Weapon == null || !evt.Weapon.IsShield) + { + return; + } + var armorEnhancementBonus = GameHelper.GetItemEnhancementBonus(evt.Initiator.Body.SecondaryHand.Shield.ArmorComponent); + var weaponEnhancementBonus = GameHelper.GetItemEnhancementBonus(evt.Initiator.Body.SecondaryHand.Shield.WeaponComponent); + var itemEnhancementBonus = armorEnhancementBonus - weaponEnhancementBonus; + if (itemEnhancementBonus > 0) + { + evt.AddBonusDamage(itemEnhancementBonus); + } + } + + public void OnEventDidTrigger(RuleCalculateDamage evt) { } + + public void OnEventAboutToTrigger(RuleCalculateAttackBonusWithoutTarget evt) + { + if (!evt.Initiator.Body.SecondaryHand.HasShield || evt.Weapon == null || !evt.Weapon.IsShield) + { + return; + } + + var armorEnhancementBonus = GameHelper.GetItemEnhancementBonus(evt.Initiator.Body.SecondaryHand.Shield.ArmorComponent); + var weaponEnhancementBonus = GameHelper.GetItemEnhancementBonus(evt.Initiator.Body.SecondaryHand.Shield.WeaponComponent); + var num = armorEnhancementBonus - weaponEnhancementBonus; + + if (num > 0) + { + evt.AddBonus(num, base.Fact); + } + } + + public void OnEventDidTrigger(RuleCalculateAttackBonusWithoutTarget evt) { } + } +} \ No newline at end of file From 2b1494c8d78257042c51c6bd09fe4ad3f1e17e28 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 14 Dec 2020 12:23:53 -0600 Subject: [PATCH 075/132] Extracting the Harmony patches from Main into their own files: MainMenuUiContextInitializePatch --- CraftMagicItems/CraftMagicItems.csproj | 1 + CraftMagicItems/Main.cs | 10 ---------- .../MainMenuUiContextInitializePatch.cs | 18 ++++++++++++++++++ 3 files changed, 19 insertions(+), 10 deletions(-) create mode 100644 CraftMagicItems/Patches/Harmony/MainMenuUiContextInitializePatch.cs diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index 3371342..1c88242 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -108,6 +108,7 @@ + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 1cb537c..758c0cf 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -2889,16 +2889,6 @@ public static bool ReverseEngineerEnchantmentCost(BlueprintItemEquipment bluepri return true; } -#if PATCH21 - [HarmonyLib.HarmonyPatch(typeof(MainMenuUiContext), "Initialize")] - private static class MainMenuUiContextInitializePatch { - [HarmonyLib.HarmonyPriority(HarmonyLib.Priority.Last)] - private static void Postfix() { - MainMenuStartPatch.Postfix(); - } - } -#endif - #if !PATCH21 // Fix issue in Owlcat's UI - ActionBarManager.Update does not refresh the Groups (spells/Actions/Belt) [HarmonyLib.HarmonyPatch(typeof(ActionBarManager), "Update")] diff --git a/CraftMagicItems/Patches/Harmony/MainMenuUiContextInitializePatch.cs b/CraftMagicItems/Patches/Harmony/MainMenuUiContextInitializePatch.cs new file mode 100644 index 0000000..fe59a0e --- /dev/null +++ b/CraftMagicItems/Patches/Harmony/MainMenuUiContextInitializePatch.cs @@ -0,0 +1,18 @@ +#if PATCH21 +using Kingmaker.Assets.UI.Context; +#endif + +namespace CraftMagicItems.Patches.Harmony +{ +#if PATCH21 + [HarmonyLib.HarmonyPatch(typeof(MainMenuUiContext), "Initialize")] + public static class MainMenuUiContextInitializePatch + { + [HarmonyLib.HarmonyPriority(HarmonyLib.Priority.Last)] + private static void Postfix() + { + MainMenuStartPatch.Postfix(); + } + } +#endif +} \ No newline at end of file From c11bef83e02b1338932a91a7abfdae33e96a7134 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 14 Dec 2020 12:27:08 -0600 Subject: [PATCH 076/132] Extracting the Harmony patches from Main into their own files: ActionBarManagerUpdatePatch --- CraftMagicItems/CraftMagicItems.csproj | 1 + CraftMagicItems/Main.cs | 14 ------------- .../Harmony/ActionBarManagerUpdatePatch.cs | 20 +++++++++++++++++++ 3 files changed, 21 insertions(+), 14 deletions(-) create mode 100644 CraftMagicItems/Patches/Harmony/ActionBarManagerUpdatePatch.cs diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index 1c88242..cb38e5a 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -107,6 +107,7 @@ + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 758c0cf..34a341e 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -2889,20 +2889,6 @@ public static bool ReverseEngineerEnchantmentCost(BlueprintItemEquipment bluepri return true; } -#if !PATCH21 - // Fix issue in Owlcat's UI - ActionBarManager.Update does not refresh the Groups (spells/Actions/Belt) - [HarmonyLib.HarmonyPatch(typeof(ActionBarManager), "Update")] - private static class ActionBarManagerUpdatePatch { - private static void Prefix(ActionBarManager __instance) { - var mNeedReset = Accessors.GetActionBarManagerNeedReset(__instance); - if (mNeedReset) { - var mSelected = Accessors.GetActionBarManagerSelected(__instance); - __instance.Group.Set(mSelected); - } - } - } -#endif - [HarmonyLib.HarmonyPatch(typeof(BlueprintItemEquipmentUsable), "Cost", HarmonyLib.MethodType.Getter)] // ReSharper disable once UnusedMember.Local private static class BlueprintItemEquipmentUsableCostPatch { diff --git a/CraftMagicItems/Patches/Harmony/ActionBarManagerUpdatePatch.cs b/CraftMagicItems/Patches/Harmony/ActionBarManagerUpdatePatch.cs new file mode 100644 index 0000000..1b848ec --- /dev/null +++ b/CraftMagicItems/Patches/Harmony/ActionBarManagerUpdatePatch.cs @@ -0,0 +1,20 @@ +#if !PATCH21 +using Kingmaker.UI.ActionBar; +#endif + +namespace CraftMagicItems.Patches.Harmony +{ +#if !PATCH21 + // Fix issue in Owlcat's UI - ActionBarManager.Update does not refresh the Groups (spells/Actions/Belt) + [HarmonyLib.HarmonyPatch(typeof(ActionBarManager), "Update")] + public static class ActionBarManagerUpdatePatch { + private static void Prefix(ActionBarManager __instance) { + var mNeedReset = Main.Accessors.GetActionBarManagerNeedReset(__instance); + if (mNeedReset) { + var mSelected = Main.Accessors.GetActionBarManagerSelected(__instance); + __instance.Group.Set(mSelected); + } + } + } +#endif +} \ No newline at end of file From b60c45716ea53b391637ba69446ef9f2db11d62c Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 14 Dec 2020 12:30:05 -0600 Subject: [PATCH 077/132] Extracting the Harmony patches from Main into their own files: BlueprintItemEquipmentUsableCostPatch --- CraftMagicItems/CraftMagicItems.csproj | 1 + CraftMagicItems/Main.cs | 26 -------------- .../BlueprintItemEquipmentUsableCostPatch.cs | 35 +++++++++++++++++++ 3 files changed, 36 insertions(+), 26 deletions(-) create mode 100644 CraftMagicItems/Patches/Harmony/BlueprintItemEquipmentUsableCostPatch.cs diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index cb38e5a..9ff380d 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -108,6 +108,7 @@ + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 34a341e..25ac9bf 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -2889,32 +2889,6 @@ public static bool ReverseEngineerEnchantmentCost(BlueprintItemEquipment bluepri return true; } - [HarmonyLib.HarmonyPatch(typeof(BlueprintItemEquipmentUsable), "Cost", HarmonyLib.MethodType.Getter)] - // ReSharper disable once UnusedMember.Local - private static class BlueprintItemEquipmentUsableCostPatch { - // ReSharper disable once UnusedMember.Local - private static void Postfix(BlueprintItemEquipmentUsable __instance, ref int __result) { - if (__result == 0 && __instance.SpellLevel == 0) { - // Owlcat's cost calculation doesn't handle level 0 spells properly. - int chargeCost; - switch (__instance.Type) { - case UsableItemType.Wand: - chargeCost = 15; - break; - case UsableItemType.Scroll: - chargeCost = 25; - break; - case UsableItemType.Potion: - chargeCost = 50; - break; - default: - return; - } - __result = __instance.CasterLevel * chargeCost * __instance.Charges / 2; - } - } - } - // Load Variant spells into m_KnownSpellLevels [HarmonyLib.HarmonyPatch(typeof(Spellbook), "PostLoad")] // ReSharper disable once UnusedMember.Local diff --git a/CraftMagicItems/Patches/Harmony/BlueprintItemEquipmentUsableCostPatch.cs b/CraftMagicItems/Patches/Harmony/BlueprintItemEquipmentUsableCostPatch.cs new file mode 100644 index 0000000..73f72be --- /dev/null +++ b/CraftMagicItems/Patches/Harmony/BlueprintItemEquipmentUsableCostPatch.cs @@ -0,0 +1,35 @@ +using Kingmaker.Blueprints.Items.Equipment; + +namespace CraftMagicItems.Patches.Harmony +{ + [HarmonyLib.HarmonyPatch(typeof(BlueprintItemEquipmentUsable), "Cost", HarmonyLib.MethodType.Getter)] + // ReSharper disable once UnusedMember.Local + public static class BlueprintItemEquipmentUsableCostPatch + { + // ReSharper disable once UnusedMember.Local + private static void Postfix(BlueprintItemEquipmentUsable __instance, ref int __result) + { + if (__result == 0 && __instance.SpellLevel == 0) + { + // Owlcat's cost calculation doesn't handle level 0 spells properly. + int chargeCost; + switch (__instance.Type) + { + case UsableItemType.Wand: + chargeCost = 15; + break; + case UsableItemType.Scroll: + chargeCost = 25; + break; + case UsableItemType.Potion: + chargeCost = 50; + break; + default: + return; + } + + __result = __instance.CasterLevel * chargeCost * __instance.Charges / 2; + } + } + } +} \ No newline at end of file From d97980f9eec037618d4fea1935591f7c03ad2cc4 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 14 Dec 2020 12:32:50 -0600 Subject: [PATCH 078/132] Extracting the Harmony patches from Main into their own files: SpellbookPostLoadPatch --- CraftMagicItems/CraftMagicItems.csproj | 1 + CraftMagicItems/Main.cs | 24 ------------- .../Patches/Harmony/SpellbookPostLoadPatch.cs | 35 +++++++++++++++++++ 3 files changed, 36 insertions(+), 24 deletions(-) create mode 100644 CraftMagicItems/Patches/Harmony/SpellbookPostLoadPatch.cs diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index 9ff380d..ab5d4d9 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -112,6 +112,7 @@ + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 25ac9bf..9ff4090 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -2889,30 +2889,6 @@ public static bool ReverseEngineerEnchantmentCost(BlueprintItemEquipment bluepri return true; } - // Load Variant spells into m_KnownSpellLevels - [HarmonyLib.HarmonyPatch(typeof(Spellbook), "PostLoad")] - // ReSharper disable once UnusedMember.Local - private static class SpellbookPostLoadPatch { - // ReSharper disable once UnusedMember.Local - private static void Postfix(Spellbook __instance) { - if (!modEnabled) { - return; - } - - var mKnownSpells = Accessors.GetSpellbookKnownSpells(__instance); - var mKnownSpellLevels = Accessors.GetSpellbookKnownSpellLevels(__instance); - for (var level = 0; level < mKnownSpells.Length; ++level) { - foreach (var spell in mKnownSpells[level]) { - if (spell.Blueprint.Variants != null) { - foreach (var variant in spell.Blueprint.Variants) { - mKnownSpellLevels[variant] = level; - } - } - } - } - } - } - // Owlcat's code doesn't correctly detect that a variant spell is in a spellList when its parent spell is. [HarmonyLib.HarmonyPatch(typeof(BlueprintAbility), "IsInSpellList")] // ReSharper disable once UnusedMember.Global diff --git a/CraftMagicItems/Patches/Harmony/SpellbookPostLoadPatch.cs b/CraftMagicItems/Patches/Harmony/SpellbookPostLoadPatch.cs new file mode 100644 index 0000000..a2c63e7 --- /dev/null +++ b/CraftMagicItems/Patches/Harmony/SpellbookPostLoadPatch.cs @@ -0,0 +1,35 @@ +using Kingmaker.UnitLogic; + +namespace CraftMagicItems.Patches.Harmony +{ + // Load Variant spells into m_KnownSpellLevels + [HarmonyLib.HarmonyPatch(typeof(Spellbook), "PostLoad")] + // ReSharper disable once UnusedMember.Local + public static class SpellbookPostLoadPatch + { + // ReSharper disable once UnusedMember.Local + private static void Postfix(Spellbook __instance) + { + if (!Main.modEnabled) + { + return; + } + + var mKnownSpells = Main.Accessors.GetSpellbookKnownSpells(__instance); + var mKnownSpellLevels = Main.Accessors.GetSpellbookKnownSpellLevels(__instance); + for (var level = 0; level < mKnownSpells.Length; ++level) + { + foreach (var spell in mKnownSpells[level]) + { + if (spell.Blueprint.Variants != null) + { + foreach (var variant in spell.Blueprint.Variants) + { + mKnownSpellLevels[variant] = level; + } + } + } + } + } + } +} \ No newline at end of file From c01ac4851f8762276464cca00e97526ad8ff5c36 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 14 Dec 2020 12:34:20 -0600 Subject: [PATCH 079/132] Extracting the Harmony patches from Main into their own files: BlueprintAbilityIsInSpellListPatch --- CraftMagicItems/CraftMagicItems.csproj | 1 + CraftMagicItems/Main.cs | 12 ----------- .../BlueprintAbilityIsInSpellListPatch.cs | 20 +++++++++++++++++++ 3 files changed, 21 insertions(+), 12 deletions(-) create mode 100644 CraftMagicItems/Patches/Harmony/BlueprintAbilityIsInSpellListPatch.cs diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index ab5d4d9..3cd1b58 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -108,6 +108,7 @@ + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 9ff4090..b34e503 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -2889,18 +2889,6 @@ public static bool ReverseEngineerEnchantmentCost(BlueprintItemEquipment bluepri return true; } - // Owlcat's code doesn't correctly detect that a variant spell is in a spellList when its parent spell is. - [HarmonyLib.HarmonyPatch(typeof(BlueprintAbility), "IsInSpellList")] - // ReSharper disable once UnusedMember.Global - public static class BlueprintAbilityIsInSpellListPatch { - // ReSharper disable once UnusedMember.Local - private static void Postfix(BlueprintAbility __instance, BlueprintSpellList spellList, ref bool __result) { - if (!__result && __instance.Parent != null && __instance.Parent != __instance) { - __result = __instance.Parent.IsInSpellList(spellList); - } - } - } - public static void AddBattleLogMessage(string message, object tooltip = null, Color? color = null) { #if PATCH21 var data = new LogItemData(message, color ?? GameLogStrings.Instance.DefaultColor, tooltip, PrefixIcon.None, new List { LogChannel.Combat }); diff --git a/CraftMagicItems/Patches/Harmony/BlueprintAbilityIsInSpellListPatch.cs b/CraftMagicItems/Patches/Harmony/BlueprintAbilityIsInSpellListPatch.cs new file mode 100644 index 0000000..cba43f2 --- /dev/null +++ b/CraftMagicItems/Patches/Harmony/BlueprintAbilityIsInSpellListPatch.cs @@ -0,0 +1,20 @@ +using Kingmaker.Blueprints.Classes.Spells; +using Kingmaker.UnitLogic.Abilities.Blueprints; + +namespace CraftMagicItems.Patches.Harmony +{ + // Owlcat's code doesn't correctly detect that a variant spell is in a spellList when its parent spell is. + [HarmonyLib.HarmonyPatch(typeof(BlueprintAbility), "IsInSpellList")] + // ReSharper disable once UnusedMember.Global + public static class BlueprintAbilityIsInSpellListPatch + { + // ReSharper disable once UnusedMember.Local + private static void Postfix(BlueprintAbility __instance, BlueprintSpellList spellList, ref bool __result) + { + if (!__result && __instance.Parent != null && __instance.Parent != __instance) + { + __result = __instance.Parent.IsInSpellList(spellList); + } + } + } +} \ No newline at end of file From 71cd1688eff110cbb6f74051c6ce22f547e47e4f Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 14 Dec 2020 12:37:48 -0600 Subject: [PATCH 080/132] Extracting the Harmony patches from Main into their own files: LogItemDataUpdateSizePatch --- CraftMagicItems/CraftMagicItems.csproj | 1 + CraftMagicItems/Main.cs | 11 ---------- .../Harmony/LogItemDataUpdateSizePatch.cs | 22 +++++++++++++++++++ 3 files changed, 23 insertions(+), 11 deletions(-) create mode 100644 CraftMagicItems/Patches/Harmony/LogItemDataUpdateSizePatch.cs diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index 3cd1b58..9bfba2b 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -110,6 +110,7 @@ + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index b34e503..0919638 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -2902,17 +2902,6 @@ public static void AddBattleLogMessage(string message, object tooltip = null, Co } } -#if !PATCH21 - [HarmonyLib.HarmonyPatch(typeof(LogDataManager.LogItemData), "UpdateSize")] - private static class LogItemDataUpdateSizePatch { - // ReSharper disable once UnusedMember.Local - private static bool Prefix() { - // Avoid null pointer exception when BattleLogManager not set. - return Game.Instance.UI.BattleLogManager != null; - } - } -#endif - // Add "pending" log items when the battle log becomes available again, so crafting messages sent when e.g. camping // in the overland map are still shown eventually. [HarmonyLib.HarmonyPatch(typeof(BattleLogManager), "Initialize")] diff --git a/CraftMagicItems/Patches/Harmony/LogItemDataUpdateSizePatch.cs b/CraftMagicItems/Patches/Harmony/LogItemDataUpdateSizePatch.cs new file mode 100644 index 0000000..1dca45e --- /dev/null +++ b/CraftMagicItems/Patches/Harmony/LogItemDataUpdateSizePatch.cs @@ -0,0 +1,22 @@ +#if !PATCH21 +using Kingmaker; +using Kingmaker.Items.Slots; +using Kingmaker.UI.Log; +using Kingmaker.UnitLogic.ActivatableAbilities; +#endif + +namespace CraftMagicItems.Patches.Harmony +{ +#if !PATCH21 + [HarmonyLib.HarmonyPatch(typeof(LogDataManager.LogItemData), "UpdateSize")] + public static class LogItemDataUpdateSizePatch + { + // ReSharper disable once UnusedMember.Local + private static bool Prefix() + { + // Avoid null pointer exception when BattleLogManager not set. + return Game.Instance.UI.BattleLogManager != null; + } + } +#endif +} \ No newline at end of file From 8dafd13a311fc5c047bb71cd20d4883ccf29aaca Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 14 Dec 2020 12:39:40 -0600 Subject: [PATCH 081/132] Extracting the Harmony patches from Main into their own files: BattleLogManagerInitializePatch --- CraftMagicItems/CraftMagicItems.csproj | 1 + CraftMagicItems/Main.cs | 18 --- .../BattleLogManagerInitializePatch.cs | 103 ++++++++++++++++++ 3 files changed, 104 insertions(+), 18 deletions(-) create mode 100644 CraftMagicItems/Patches/Harmony/BattleLogManagerInitializePatch.cs diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index 9bfba2b..80259eb 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -108,6 +108,7 @@ + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 0919638..b661b67 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -2902,24 +2902,6 @@ public static void AddBattleLogMessage(string message, object tooltip = null, Co } } - // Add "pending" log items when the battle log becomes available again, so crafting messages sent when e.g. camping - // in the overland map are still shown eventually. - [HarmonyLib.HarmonyPatch(typeof(BattleLogManager), "Initialize")] - // ReSharper disable once UnusedMember.Local - private static class BattleLogManagerInitializePatch { - // ReSharper disable once UnusedMember.Local - private static void Postfix() { - if (Enumerable.Any(PendingLogItems)) { - foreach (var item in PendingLogItems) { - item.UpdateSize(); - Game.Instance.UI.BattleLogManager.LogView.AddLogEntry(item); - } - - PendingLogItems.Clear(); - } - } - } - private static AbilityData FindCasterSpell(UnitDescriptor caster, BlueprintAbility spellBlueprint, bool mustHavePrepared, IReadOnlyCollection spellsToCast) { foreach (var spellbook in caster.Spellbooks) { diff --git a/CraftMagicItems/Patches/Harmony/BattleLogManagerInitializePatch.cs b/CraftMagicItems/Patches/Harmony/BattleLogManagerInitializePatch.cs new file mode 100644 index 0000000..cc59bfc --- /dev/null +++ b/CraftMagicItems/Patches/Harmony/BattleLogManagerInitializePatch.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.Serialization; +using System.Text.RegularExpressions; +using CraftMagicItems.Config; +using CraftMagicItems.Constants; +using CraftMagicItems.Localization; +using CraftMagicItems.Patches; +using CraftMagicItems.UI; +using CraftMagicItems.UI.Sections; +using CraftMagicItems.UI.UnityModManager; +using Kingmaker; +#if PATCH21 +using Kingmaker.Assets.UI.Context; +#endif +using Kingmaker.Blueprints; +using Kingmaker.Blueprints.Classes; +using Kingmaker.Blueprints.Classes.Selection; +using Kingmaker.Blueprints.Classes.Spells; +using Kingmaker.Blueprints.Facts; +using Kingmaker.Blueprints.Items; +using Kingmaker.Blueprints.Items.Armors; +using Kingmaker.Blueprints.Items.Ecnchantments; +using Kingmaker.Blueprints.Items.Equipment; +using Kingmaker.Blueprints.Items.Shields; +using Kingmaker.Blueprints.Items.Weapons; +using Kingmaker.Blueprints.Loot; +using Kingmaker.Blueprints.Root; +using Kingmaker.Blueprints.Root.Strings.GameLog; +using Kingmaker.Controllers.Rest; +using Kingmaker.Designers; +using Kingmaker.Designers.Mechanics.EquipmentEnchants; +using Kingmaker.Designers.Mechanics.Facts; +using Kingmaker.Designers.Mechanics.WeaponEnchants; +using Kingmaker.Designers.TempMapCode.Capital; +using Kingmaker.EntitySystem.Entities; +using Kingmaker.EntitySystem.Stats; +using Kingmaker.Enums; +using Kingmaker.Enums.Damage; +using Kingmaker.GameModes; +using Kingmaker.Items; +#if !PATCH21 +using Kingmaker.Items.Slots; +#endif +using Kingmaker.Kingdom; +using Kingmaker.Localization; +using Kingmaker.PubSubSystem; +using Kingmaker.RuleSystem; +using Kingmaker.RuleSystem.Rules; +using Kingmaker.RuleSystem.Rules.Abilities; +using Kingmaker.RuleSystem.Rules.Damage; +using Kingmaker.UI; +using Kingmaker.UI.ActionBar; +using Kingmaker.UI.Common; +using Kingmaker.UI.Log; +using Kingmaker.UI.Tooltip; +using Kingmaker.UnitLogic; +using Kingmaker.UnitLogic.Abilities; +using Kingmaker.UnitLogic.Abilities.Blueprints; +#if !PATCH21 +using Kingmaker.UnitLogic.ActivatableAbilities; +#endif +using Kingmaker.UnitLogic.Alignments; +using Kingmaker.UnitLogic.Buffs; +using Kingmaker.UnitLogic.Buffs.Blueprints; +using Kingmaker.UnitLogic.FactLogic; +using Kingmaker.UnitLogic.Mechanics.Components; +using Kingmaker.UnitLogic.Parts; +using Kingmaker.Utility; +using Kingmaker.View.Equipment; +using Newtonsoft.Json; +using UnityEngine; +using UnityModManagerNet; +using Random = System.Random; + +namespace CraftMagicItems.Patches.Harmony +{ + // Add "pending" log items when the battle log becomes available again, so crafting messages sent when e.g. camping + // in the overland map are still shown eventually. + [HarmonyLib.HarmonyPatch(typeof(BattleLogManager), "Initialize")] + // ReSharper disable once UnusedMember.Local + public static class BattleLogManagerInitializePatch + { + // ReSharper disable once UnusedMember.Local + private static void Postfix() + { + if (Enumerable.Any(Main.PendingLogItems)) + { + foreach (var item in Main.PendingLogItems) + { + item.UpdateSize(); + Game.Instance.UI.BattleLogManager.LogView.AddLogEntry(item); + } + + Main.PendingLogItems.Clear(); + } + } + } +} \ No newline at end of file From 40c4dcec8109f354a13d0f26ff03bdc27837e0c6 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 14 Dec 2020 12:41:35 -0600 Subject: [PATCH 082/132] Extracting the Harmony patches from Main into their own files: CapitalCompanionLogicOnFactActivatePatch --- CraftMagicItems/CraftMagicItems.csproj | 1 + CraftMagicItems/Main.cs | 16 +------------ ...apitalCompanionLogicOnFactActivatePatch.cs | 23 +++++++++++++++++++ 3 files changed, 25 insertions(+), 15 deletions(-) create mode 100644 CraftMagicItems/Patches/Harmony/CapitalCompanionLogicOnFactActivatePatch.cs diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index 80259eb..d3ed1be 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -111,6 +111,7 @@ + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index b661b67..f7ee319 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -3040,7 +3040,7 @@ private static List GetMissingCrafterPrerequisites(Craf return missingCrafterPrerequisites; } - private static void WorkOnProjects(UnitDescriptor caster, bool returningToCapital) { + public static void WorkOnProjects(UnitDescriptor caster, bool returningToCapital) { if (!caster.IsPlayerFaction || caster.State.IsDead || caster.State.IsFinallyDead) { return; } @@ -3280,20 +3280,6 @@ private static void WorkOnProjects(UnitDescriptor caster, bool returningToCapita } } - [HarmonyLib.HarmonyPatch(typeof(CapitalCompanionLogic), "OnFactActivate")] - // ReSharper disable once UnusedMember.Local - private static class CapitalCompanionLogicOnFactActivatePatch { - // ReSharper disable once UnusedMember.Local - private static void Prefix() { - // Trigger project work on companions left behind in the capital, with a flag saying the party wasn't around while they were working. - foreach (var companion in Game.Instance.Player.RemoteCompanions) { - if (companion.Value != null) { - WorkOnProjects(companion.Value.Descriptor, true); - } - } - } - } - // Make characters in the party work on their crafting projects when they rest. [HarmonyLib.HarmonyPatch(typeof(RestController), "ApplyRest")] // ReSharper disable once UnusedMember.Local diff --git a/CraftMagicItems/Patches/Harmony/CapitalCompanionLogicOnFactActivatePatch.cs b/CraftMagicItems/Patches/Harmony/CapitalCompanionLogicOnFactActivatePatch.cs new file mode 100644 index 0000000..e749028 --- /dev/null +++ b/CraftMagicItems/Patches/Harmony/CapitalCompanionLogicOnFactActivatePatch.cs @@ -0,0 +1,23 @@ +using Kingmaker; +using Kingmaker.Designers.TempMapCode.Capital; + +namespace CraftMagicItems.Patches.Harmony +{ + [HarmonyLib.HarmonyPatch(typeof(CapitalCompanionLogic), "OnFactActivate")] + // ReSharper disable once UnusedMember.Local + public static class CapitalCompanionLogicOnFactActivatePatch + { + // ReSharper disable once UnusedMember.Local + private static void Prefix() + { + // Trigger project work on companions left behind in the capital, with a flag saying the party wasn't around while they were working. + foreach (var companion in Game.Instance.Player.RemoteCompanions) + { + if (companion.Value != null) + { + Main.WorkOnProjects(companion.Value.Descriptor, true); + } + } + } + } +} \ No newline at end of file From 0ecdc4fe560127ce4e8d472c2ba9d5b125970356 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 14 Dec 2020 12:43:10 -0600 Subject: [PATCH 083/132] Extracting the Harmony patches from Main into their own files: RestControllerApplyRestPatch --- CraftMagicItems/CraftMagicItems.csproj | 1 + CraftMagicItems/Main.cs | 10 ---------- .../Harmony/RestControllerApplyRestPatch.cs | 17 +++++++++++++++++ 3 files changed, 18 insertions(+), 10 deletions(-) create mode 100644 CraftMagicItems/Patches/Harmony/RestControllerApplyRestPatch.cs diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index d3ed1be..18983aa 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -115,6 +115,7 @@ + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index f7ee319..ada5221 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -3280,16 +3280,6 @@ public static void WorkOnProjects(UnitDescriptor caster, bool returningToCapital } } - // Make characters in the party work on their crafting projects when they rest. - [HarmonyLib.HarmonyPatch(typeof(RestController), "ApplyRest")] - // ReSharper disable once UnusedMember.Local - private static class RestControllerApplyRestPatch { - // ReSharper disable once UnusedMember.Local - private static void Prefix(UnitDescriptor unit) { - WorkOnProjects(unit, false); - } - } - private static void AddToLootTables(BlueprintItem blueprint, string[] tableNames, bool firstTime) { var tableCount = tableNames.Length; foreach (var loot in ResourcesLibrary.GetBlueprints()) { diff --git a/CraftMagicItems/Patches/Harmony/RestControllerApplyRestPatch.cs b/CraftMagicItems/Patches/Harmony/RestControllerApplyRestPatch.cs new file mode 100644 index 0000000..272d747 --- /dev/null +++ b/CraftMagicItems/Patches/Harmony/RestControllerApplyRestPatch.cs @@ -0,0 +1,17 @@ +using Kingmaker.Controllers.Rest; +using Kingmaker.UnitLogic; + +namespace CraftMagicItems.Patches.Harmony +{ + // Make characters in the party work on their crafting projects when they rest. + [HarmonyLib.HarmonyPatch(typeof(RestController), "ApplyRest")] + // ReSharper disable once UnusedMember.Local + public static class RestControllerApplyRestPatch + { + // ReSharper disable once UnusedMember.Local + private static void Prefix(UnitDescriptor unit) + { + Main.WorkOnProjects(unit, false); + } + } +} \ No newline at end of file From cd0dee08bf66f7a800560394ecb1bce391712ae4 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 14 Dec 2020 12:46:29 -0600 Subject: [PATCH 084/132] Extracting the Harmony patches from Main into their own files: PlayerPostLoadPatch --- CraftMagicItems/CraftMagicItems.csproj | 1 + CraftMagicItems/Main.cs | 76 +-------------- .../Patches/Harmony/PlayerPostLoadPatch.cs | 92 +++++++++++++++++++ 3 files changed, 98 insertions(+), 71 deletions(-) create mode 100644 CraftMagicItems/Patches/Harmony/PlayerPostLoadPatch.cs diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index 18983aa..5725cda 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -115,6 +115,7 @@ + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index ada5221..45c3441 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -168,8 +168,8 @@ public bool MatchPostfix(MethodBase method) { private static HarmonyLib.Harmony harmonyInstance; private static CraftMagicItemsBlueprintPatcher blueprintPatcher; - private static readonly Dictionary ItemUpgradeProjects = new Dictionary(); - private static readonly List ItemCreationProjects = new List(); + public static readonly Dictionary ItemUpgradeProjects = new Dictionary(); + public static readonly List ItemCreationProjects = new List(); private static readonly Random RandomGenerator = new Random(); @@ -338,7 +338,7 @@ public static T ReadJsonFile(string fileName, params JsonConverter[] converte } } - private static CraftingTimerComponent GetCraftingTimerComponentForCaster(UnitDescriptor caster, bool create = false) { + public static CraftingTimerComponent GetCraftingTimerComponentForCaster(UnitDescriptor caster, bool create = false) { // Manually search caster.Buffs rather than using GetFact, because we don't want to TryGetBlueprint if the mod is disabled. var timerBuff = caster.Buffs.Enumerable.FirstOrDefault(fact => fact.Blueprint.AssetGuid == CraftMagicItemsBlueprintPatcher.TimerBlueprintGuid); if (timerBuff == null) { @@ -1016,7 +1016,7 @@ private static bool RecipeAppliesToBlueprint(RecipeData recipe, BlueprintItem bl ; } - private static ItemEntity BuildItemEntity(BlueprintItem blueprint, ItemCraftingData craftingData, UnitEntityData crafter) { + public static ItemEntity BuildItemEntity(BlueprintItem blueprint, ItemCraftingData craftingData, UnitEntityData crafter) { var item = blueprint.CreateEntity(); item.Identify(); item.SetVendorIfNull(crafter); @@ -3321,7 +3321,7 @@ private static void AddToLootTables(BlueprintItem blueprint, string[] tableNames } } - private static void UpgradeSave(Version version) { + public static void UpgradeSave(Version version) { foreach (var lootItem in LoadedData.CustomLootItems) { var firstTime = (version == null || version.CompareTo(lootItem.AddInVersion) < 0); var item = ResourcesLibrary.TryGetBlueprint(lootItem.AssetGuid); @@ -3333,72 +3333,6 @@ private static void UpgradeSave(Version version) { } } - public static class PlayerPostLoadPatch { - private static void Postfix() { - ItemUpgradeProjects.Clear(); - ItemCreationProjects.Clear(); - - var characterList = UIUtility.GetGroup(true); - foreach (var character in characterList) { - // If the mod is disabled, this will clean up crafting timer "buff" from all casters. - var timer = GetCraftingTimerComponentForCaster(character.Descriptor, character.IsMainCharacter); - var bondedItemComponent = GetBondedItemComponentForCaster(character.Descriptor); - - if (!modEnabled) { - continue; - } - - if (timer != null) { - foreach (var project in timer.CraftingProjects) { - if (project.ItemBlueprint != null) { - // Migrate all projects using ItemBlueprint to use ResultItem - var craftingData = LoadedData.ItemCraftingData.First(data => data.Name == project.ItemType); - project.ResultItem = BuildItemEntity(project.ItemBlueprint, craftingData, character); - project.ItemBlueprint = null; - } - - project.Crafter = character; - if (!project.ResultItem.HasUniqueVendor) { - // Set "vendor" of item if it's already in progress - project.ResultItem.SetVendorIfNull(character); - } - project.ResultItem.PostLoad(); - if (project.UpgradeItem == null) { - ItemCreationProjects.Add(project); - } else { - ItemUpgradeProjects[project.UpgradeItem] = project; - project.UpgradeItem.PostLoad(); - } - } - - if (character.IsMainCharacter) { - UpgradeSave(string.IsNullOrEmpty(timer.Version) ? null : Version.Parse(timer.Version)); - timer.Version = ModEntry.Version.ToString(); - } - } - - if (bondedItemComponent != null) { - bondedItemComponent.ownerItem?.PostLoad(); - bondedItemComponent.everyoneElseItem?.PostLoad(); - } - - // Retroactively give character any crafting feats in their past progression data which they don't actually have - // (e.g. Alchemists getting Brew Potion) - foreach (var characterClass in character.Descriptor.Progression.Classes) { - foreach (var levelData in characterClass.CharacterClass.Progression.LevelEntries) { - if (levelData.Level <= characterClass.Level) { - foreach (var feature in levelData.Features.OfType()) { - if (feature.AssetGuid.Contains("#CraftMagicItems(feat=") && !CharacterHasFeat(character, feature.AssetGuid)) { - character.Descriptor.Progression.Features.AddFeature(feature); - } - } - } - } - } - } - } - } - public static class GameOnAreaLoadedPatch { private static void Postfix() { if (CustomBlueprintBuilder.DidDowngrade) { diff --git a/CraftMagicItems/Patches/Harmony/PlayerPostLoadPatch.cs b/CraftMagicItems/Patches/Harmony/PlayerPostLoadPatch.cs new file mode 100644 index 0000000..add2b1c --- /dev/null +++ b/CraftMagicItems/Patches/Harmony/PlayerPostLoadPatch.cs @@ -0,0 +1,92 @@ +using System; +using System.Linq; +using Kingmaker.Blueprints.Classes; +using Kingmaker.UI.Common; + +namespace CraftMagicItems.Patches.Harmony +{ + public static class PlayerPostLoadPatch + { + private static void Postfix() + { + Main.ItemUpgradeProjects.Clear(); + Main.ItemCreationProjects.Clear(); + + var characterList = UIUtility.GetGroup(true); + foreach (var character in characterList) + { + // If the mod is disabled, this will clean up crafting timer "buff" from all casters. + var timer = Main.GetCraftingTimerComponentForCaster(character.Descriptor, character.IsMainCharacter); + var bondedItemComponent = Main.GetBondedItemComponentForCaster(character.Descriptor); + + if (!Main.modEnabled) + { + continue; + } + + if (timer != null) + { + foreach (var project in timer.CraftingProjects) + { + if (project.ItemBlueprint != null) + { + // Migrate all projects using ItemBlueprint to use ResultItem + var craftingData = Main.LoadedData.ItemCraftingData.First(data => data.Name == project.ItemType); + project.ResultItem = Main.BuildItemEntity(project.ItemBlueprint, craftingData, character); + project.ItemBlueprint = null; + } + + project.Crafter = character; + if (!project.ResultItem.HasUniqueVendor) + { + // Set "vendor" of item if it's already in progress + project.ResultItem.SetVendorIfNull(character); + } + project.ResultItem.PostLoad(); + + if (project.UpgradeItem == null) + { + Main.ItemCreationProjects.Add(project); + } + else + { + Main.ItemUpgradeProjects[project.UpgradeItem] = project; + project.UpgradeItem.PostLoad(); + } + } + + if (character.IsMainCharacter) + { + Main.UpgradeSave(string.IsNullOrEmpty(timer.Version) ? null : Version.Parse(timer.Version)); + timer.Version = Main.ModEntry.Version.ToString(); + } + } + + if (bondedItemComponent != null) + { + bondedItemComponent.ownerItem?.PostLoad(); + bondedItemComponent.everyoneElseItem?.PostLoad(); + } + + // Retroactively give character any crafting feats in their past progression data which they don't actually have + // (e.g. Alchemists getting Brew Potion) + foreach (var characterClass in character.Descriptor.Progression.Classes) + { + foreach (var levelData in characterClass.CharacterClass.Progression.LevelEntries) + { + if (levelData.Level <= characterClass.Level) + { + foreach (var feature in levelData.Features.OfType()) + { + if (feature.AssetGuid.Contains("#CraftMagicItems(feat=") && !Main.CharacterHasFeat(character, feature.AssetGuid)) + { + character.Descriptor.Progression.Features.AddFeature(feature); + } + } + } + } + } + } + } + } +} \ No newline at end of file From de3b3a658ed9c229f8214b20201dd6bffb1cc002 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 14 Dec 2020 12:48:54 -0600 Subject: [PATCH 085/132] Extracting the Harmony patches from Main into their own files: GameOnAreaLoadedPatch --- CraftMagicItems/CraftMagicItems.csproj | 1 + CraftMagicItems/Main.cs | 15 ------------ .../Patches/Harmony/GameOnAreaLoadedPatch.cs | 24 +++++++++++++++++++ 3 files changed, 25 insertions(+), 15 deletions(-) create mode 100644 CraftMagicItems/Patches/Harmony/GameOnAreaLoadedPatch.cs diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index 5725cda..2687e26 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -112,6 +112,7 @@ + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 45c3441..b02a8ad 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -3333,21 +3333,6 @@ public static void UpgradeSave(Version version) { } } - public static class GameOnAreaLoadedPatch { - private static void Postfix() { - if (CustomBlueprintBuilder.DidDowngrade) { - UIUtility.ShowMessageBox("Craft Magic Items is disabled. All your custom enchanted items and crafting feats have been replaced with " + -#if PATCH21 - "vanilla versions.", DialogMessageBoxBase.BoxType.Message, null); -#else - "vanilla versions.", DialogMessageBox.BoxType.Message, null); -#endif - - CustomBlueprintBuilder.Reset(); - } - } - } - [HarmonyLib.HarmonyPatch(typeof(WeaponParametersAttackBonus), "OnEventAboutToTrigger")] // ReSharper disable once UnusedMember.Local private static class WeaponParametersAttackBonusOnEventAboutToTriggerPatch { diff --git a/CraftMagicItems/Patches/Harmony/GameOnAreaLoadedPatch.cs b/CraftMagicItems/Patches/Harmony/GameOnAreaLoadedPatch.cs new file mode 100644 index 0000000..0c4d360 --- /dev/null +++ b/CraftMagicItems/Patches/Harmony/GameOnAreaLoadedPatch.cs @@ -0,0 +1,24 @@ +using Kingmaker.UI; +using Kingmaker.UI.Common; + +namespace CraftMagicItems.Patches.Harmony +{ + public static class GameOnAreaLoadedPatch + { + private static void Postfix() + { + if (CustomBlueprintBuilder.DidDowngrade) + { + UIUtility.ShowMessageBox("Craft Magic Items is disabled. All your custom enchanted items and crafting feats have been replaced with vanilla versions.", +#if PATCH21 + DialogMessageBoxBase.BoxType.Message, +#else + DialogMessageBox.BoxType.Message, +#endif + null); + + CustomBlueprintBuilder.Reset(); + } + } + } +} \ No newline at end of file From 712198251387300deb6bd8a3628c8f77d61f31db Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 14 Dec 2020 12:50:50 -0600 Subject: [PATCH 086/132] Extracting the Harmony patches from Main into their own files: WeaponParametersAttackBonusOnEventAboutToTriggerPatch --- CraftMagicItems/CraftMagicItems.csproj | 1 + CraftMagicItems/Main.cs | 15 +---------- ...rsAttackBonusOnEventAboutToTriggerPatch.cs | 26 +++++++++++++++++++ 3 files changed, 28 insertions(+), 14 deletions(-) create mode 100644 CraftMagicItems/Patches/Harmony/WeaponParametersAttackBonusOnEventAboutToTriggerPatch.cs diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index 2687e26..698240a 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -121,6 +121,7 @@ + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index b02a8ad..a6cfde5 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -886,7 +886,7 @@ private static bool IsMasterwork(BlueprintItem blueprint) { return GetEnchantments(blueprint).Any(enchantment => enchantment.AssetGuid == ItemQualityBlueprints.MasterworkGuid); } - private static bool IsOversized(BlueprintItem blueprint) { + public static bool IsOversized(BlueprintItem blueprint) { return GetEnchantments(blueprint).Any(enchantment => enchantment.AssetGuid.StartsWith(ItemQualityBlueprints.OversizedGuid) && !enchantment.GetComponent()); } @@ -3333,19 +3333,6 @@ public static void UpgradeSave(Version version) { } } - [HarmonyLib.HarmonyPatch(typeof(WeaponParametersAttackBonus), "OnEventAboutToTrigger")] - // ReSharper disable once UnusedMember.Local - private static class WeaponParametersAttackBonusOnEventAboutToTriggerPatch { - private static bool Prefix(WeaponParametersAttackBonus __instance, RuleCalculateAttackBonusWithoutTarget evt) { - if (evt.Weapon != null && __instance.OnlyFinessable && evt.Weapon.Blueprint.Type.Category.HasSubCategory(WeaponSubCategory.Finessable) && - IsOversized(evt.Weapon.Blueprint)) { - return false; - } else { - return true; - } - } - } - [HarmonyLib.HarmonyPatch(typeof(WeaponParametersDamageBonus), "OnEventAboutToTrigger", new Type[] { typeof(RuleCalculateWeaponStats) })] // ReSharper disable once UnusedMember.Local private static class WeaponParametersDamageBonusOnEventAboutToTriggerPatch { diff --git a/CraftMagicItems/Patches/Harmony/WeaponParametersAttackBonusOnEventAboutToTriggerPatch.cs b/CraftMagicItems/Patches/Harmony/WeaponParametersAttackBonusOnEventAboutToTriggerPatch.cs new file mode 100644 index 0000000..9628a37 --- /dev/null +++ b/CraftMagicItems/Patches/Harmony/WeaponParametersAttackBonusOnEventAboutToTriggerPatch.cs @@ -0,0 +1,26 @@ +using Kingmaker.Designers.Mechanics.Facts; +using Kingmaker.Enums; +using Kingmaker.RuleSystem.Rules; + +namespace CraftMagicItems.Patches.Harmony +{ + [HarmonyLib.HarmonyPatch(typeof(WeaponParametersAttackBonus), "OnEventAboutToTrigger")] + // ReSharper disable once UnusedMember.Local + public static class WeaponParametersAttackBonusOnEventAboutToTriggerPatch + { + private static bool Prefix(WeaponParametersAttackBonus __instance, RuleCalculateAttackBonusWithoutTarget evt) + { + if (evt.Weapon != null + && __instance.OnlyFinessable + && evt.Weapon.Blueprint.Type.Category.HasSubCategory(WeaponSubCategory.Finessable) + && Main.IsOversized(evt.Weapon.Blueprint)) + { + return false; + } + else + { + return true; + } + } + } +} \ No newline at end of file From 7abcc59735ba2c3c6da067b7cb20c7eb81591798 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 14 Dec 2020 12:58:12 -0600 Subject: [PATCH 087/132] Extracting the Harmony patches from Main into their own files: WeaponParametersDamageBonusOnEventAboutToTriggerPatch --- CraftMagicItems/CraftMagicItems.csproj | 1 + CraftMagicItems/Main.cs | 13 --------- ...rsDamageBonusOnEventAboutToTriggerPatch.cs | 27 +++++++++++++++++++ 3 files changed, 28 insertions(+), 13 deletions(-) create mode 100644 CraftMagicItems/Patches/Harmony/WeaponParametersDamageBonusOnEventAboutToTriggerPatch.cs diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index 698240a..3a4cffa 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -122,6 +122,7 @@ + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index a6cfde5..7ae3177 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -3333,19 +3333,6 @@ public static void UpgradeSave(Version version) { } } - [HarmonyLib.HarmonyPatch(typeof(WeaponParametersDamageBonus), "OnEventAboutToTrigger", new Type[] { typeof(RuleCalculateWeaponStats) })] - // ReSharper disable once UnusedMember.Local - private static class WeaponParametersDamageBonusOnEventAboutToTriggerPatch { - private static bool Prefix(WeaponParametersDamageBonus __instance, RuleCalculateWeaponStats evt) { - if (evt.Weapon != null && __instance.OnlyFinessable && evt.Weapon.Blueprint.Type.Category.HasSubCategory(WeaponSubCategory.Finessable) && - IsOversized(evt.Weapon.Blueprint)) { - return false; - } else { - return true; - } - } - } - [HarmonyLib.HarmonyPatch(typeof(AttackStatReplacement), "OnEventAboutToTrigger")] // ReSharper disable once UnusedMember.Local private static class AttackStatReplacementOnEventAboutToTriggerPatch { diff --git a/CraftMagicItems/Patches/Harmony/WeaponParametersDamageBonusOnEventAboutToTriggerPatch.cs b/CraftMagicItems/Patches/Harmony/WeaponParametersDamageBonusOnEventAboutToTriggerPatch.cs new file mode 100644 index 0000000..f3e39c9 --- /dev/null +++ b/CraftMagicItems/Patches/Harmony/WeaponParametersDamageBonusOnEventAboutToTriggerPatch.cs @@ -0,0 +1,27 @@ +using System; +using Kingmaker.Designers.Mechanics.Facts; +using Kingmaker.Enums; +using Kingmaker.RuleSystem.Rules; + +namespace CraftMagicItems.Patches.Harmony +{ + [HarmonyLib.HarmonyPatch(typeof(WeaponParametersDamageBonus), "OnEventAboutToTrigger", new Type[] { typeof(RuleCalculateWeaponStats) })] + // ReSharper disable once UnusedMember.Local + public static class WeaponParametersDamageBonusOnEventAboutToTriggerPatch + { + private static bool Prefix(WeaponParametersDamageBonus __instance, RuleCalculateWeaponStats evt) + { + if (evt.Weapon != null + && __instance.OnlyFinessable + && evt.Weapon.Blueprint.Type.Category.HasSubCategory(WeaponSubCategory.Finessable) + && Main.IsOversized(evt.Weapon.Blueprint)) + { + return false; + } + else + { + return true; + } + } + } +} \ No newline at end of file From f397c6eb2abbce39267b5eec56e40391cfd90826 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 14 Dec 2020 13:00:09 -0600 Subject: [PATCH 088/132] Extracting the Harmony patches from Main into their own files: AttackStatReplacementOnEventAboutToTriggerPatch --- CraftMagicItems/CraftMagicItems.csproj | 1 + CraftMagicItems/Main.cs | 13 ---------- ...atReplacementOnEventAboutToTriggerPatch.cs | 26 +++++++++++++++++++ 3 files changed, 27 insertions(+), 13 deletions(-) create mode 100644 CraftMagicItems/Patches/Harmony/AttackStatReplacementOnEventAboutToTriggerPatch.cs diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index 3a4cffa..c9221e2 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -108,6 +108,7 @@ + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 7ae3177..bd13941 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -3333,19 +3333,6 @@ public static void UpgradeSave(Version version) { } } - [HarmonyLib.HarmonyPatch(typeof(AttackStatReplacement), "OnEventAboutToTrigger")] - // ReSharper disable once UnusedMember.Local - private static class AttackStatReplacementOnEventAboutToTriggerPatch { - private static bool Prefix(AttackStatReplacement __instance, RuleCalculateAttackBonusWithoutTarget evt) { - if (evt.Weapon != null && __instance.SubCategory == WeaponSubCategory.Finessable && - evt.Weapon.Blueprint.Type.Category.HasSubCategory(WeaponSubCategory.Finessable) && IsOversized(evt.Weapon.Blueprint)) { - return false; - } else { - return true; - } - } - } - [HarmonyLib.HarmonyPatch(typeof(DamageGrace), "OnEventAboutToTrigger")] // ReSharper disable once UnusedMember.Local private static class DamageGraceOnEventAboutToTriggerPatch { diff --git a/CraftMagicItems/Patches/Harmony/AttackStatReplacementOnEventAboutToTriggerPatch.cs b/CraftMagicItems/Patches/Harmony/AttackStatReplacementOnEventAboutToTriggerPatch.cs new file mode 100644 index 0000000..ecb0f87 --- /dev/null +++ b/CraftMagicItems/Patches/Harmony/AttackStatReplacementOnEventAboutToTriggerPatch.cs @@ -0,0 +1,26 @@ +using Kingmaker.Enums; +using Kingmaker.RuleSystem.Rules; +using Kingmaker.UnitLogic.FactLogic; + +namespace CraftMagicItems.Patches.Harmony +{ + [HarmonyLib.HarmonyPatch(typeof(AttackStatReplacement), "OnEventAboutToTrigger")] + // ReSharper disable once UnusedMember.Local + public static class AttackStatReplacementOnEventAboutToTriggerPatch + { + private static bool Prefix(AttackStatReplacement __instance, RuleCalculateAttackBonusWithoutTarget evt) + { + if (evt.Weapon != null + && __instance.SubCategory == WeaponSubCategory.Finessable + && evt.Weapon.Blueprint.Type.Category.HasSubCategory(WeaponSubCategory.Finessable) && + Main.IsOversized(evt.Weapon.Blueprint)) + { + return false; + } + else + { + return true; + } + } + } +} \ No newline at end of file From 3cfd727811573c82c5ae5ca6941b28d20021cc1c Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 14 Dec 2020 13:03:40 -0600 Subject: [PATCH 089/132] Extracting the Harmony patches from Main into their own files: DamageGraceOnEventAboutToTriggerPatch --- CraftMagicItems/CraftMagicItems.csproj | 1 + CraftMagicItems/Main.cs | 13 ---------- .../DamageGraceOnEventAboutToTriggerPatch.cs | 25 +++++++++++++++++++ 3 files changed, 26 insertions(+), 13 deletions(-) create mode 100644 CraftMagicItems/Patches/Harmony/DamageGraceOnEventAboutToTriggerPatch.cs diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index c9221e2..9833e13 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -113,6 +113,7 @@ + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index bd13941..30e5f63 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -3333,19 +3333,6 @@ public static void UpgradeSave(Version version) { } } - [HarmonyLib.HarmonyPatch(typeof(DamageGrace), "OnEventAboutToTrigger")] - // ReSharper disable once UnusedMember.Local - private static class DamageGraceOnEventAboutToTriggerPatch { - private static bool Prefix(DamageGrace __instance, RuleCalculateWeaponStats evt) { - if (evt.Weapon != null && evt.Weapon.Blueprint.Type.Category.HasSubCategory(WeaponSubCategory.Finessable) && - IsOversized(evt.Weapon.Blueprint)) { - return false; - } else { - return true; - } - } - } - [HarmonyLib.HarmonyPatch(typeof(UIUtilityItem), "GetQualities")] // ReSharper disable once UnusedMember.Local private static class UIUtilityItemGetQualitiesPatch { diff --git a/CraftMagicItems/Patches/Harmony/DamageGraceOnEventAboutToTriggerPatch.cs b/CraftMagicItems/Patches/Harmony/DamageGraceOnEventAboutToTriggerPatch.cs new file mode 100644 index 0000000..5fcc67c --- /dev/null +++ b/CraftMagicItems/Patches/Harmony/DamageGraceOnEventAboutToTriggerPatch.cs @@ -0,0 +1,25 @@ +using Kingmaker.Designers.Mechanics.Facts; +using Kingmaker.Enums; +using Kingmaker.RuleSystem.Rules; + +namespace CraftMagicItems.Patches.Harmony +{ + [HarmonyLib.HarmonyPatch(typeof(DamageGrace), "OnEventAboutToTrigger")] + // ReSharper disable once UnusedMember.Local + public static class DamageGraceOnEventAboutToTriggerPatch + { + private static bool Prefix(DamageGrace __instance, RuleCalculateWeaponStats evt) + { + if (evt.Weapon != null + && evt.Weapon.Blueprint.Type.Category.HasSubCategory(WeaponSubCategory.Finessable) + && Main.IsOversized(evt.Weapon.Blueprint)) + { + return false; + } + else + { + return true; + } + } + } +} \ No newline at end of file From 311ee055584bda6b918f7dad3fdd87771b2a9e42 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 14 Dec 2020 13:06:06 -0600 Subject: [PATCH 090/132] Extracting the Harmony patches from Main into their own files: UIUtilityItemGetQualitiesPatch --- CraftMagicItems/CraftMagicItems.csproj | 1 + CraftMagicItems/Main.cs | 22 ------------ .../Harmony/UIUtilityItemGetQualitiesPatch.cs | 36 +++++++++++++++++++ 3 files changed, 37 insertions(+), 22 deletions(-) create mode 100644 CraftMagicItems/Patches/Harmony/UIUtilityItemGetQualitiesPatch.cs diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index 9833e13..97b4e8d 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -123,6 +123,7 @@ + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 30e5f63..dd3511b 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -3333,28 +3333,6 @@ public static void UpgradeSave(Version version) { } } - [HarmonyLib.HarmonyPatch(typeof(UIUtilityItem), "GetQualities")] - // ReSharper disable once UnusedMember.Local - private static class UIUtilityItemGetQualitiesPatch { - // ReSharper disable once UnusedMember.Local - private static void Postfix(ItemEntity item, ref string __result) { - if (!item.IsIdentified) { - return; - } - ItemEntityWeapon itemEntityWeapon = item as ItemEntityWeapon; - if (itemEntityWeapon == null) { - return; - } - WeaponCategory category = itemEntityWeapon.Blueprint.Category; - if (category.HasSubCategory(WeaponSubCategory.Finessable) && IsOversized(itemEntityWeapon.Blueprint)) { - __result = __result.Replace(LocalizedTexts.Instance.WeaponSubCategories.GetText(WeaponSubCategory.Finessable), ""); - __result = __result.Replace(", ,", ","); - char[] charsToTrim = { ',', ' ' }; - __result = __result.Trim(charsToTrim); - } - } - } - [HarmonyLib.HarmonyPatch(typeof(UIUtilityItem), "FillArmorEnchantments")] // ReSharper disable once UnusedMember.Local private static class UIUtilityItemFillArmorEnchantmentsPatch { diff --git a/CraftMagicItems/Patches/Harmony/UIUtilityItemGetQualitiesPatch.cs b/CraftMagicItems/Patches/Harmony/UIUtilityItemGetQualitiesPatch.cs new file mode 100644 index 0000000..d1d9d3e --- /dev/null +++ b/CraftMagicItems/Patches/Harmony/UIUtilityItemGetQualitiesPatch.cs @@ -0,0 +1,36 @@ +using Kingmaker.Blueprints.Root; +using Kingmaker.Enums; +using Kingmaker.Items; +using Kingmaker.UI.Common; + +namespace CraftMagicItems.Patches.Harmony +{ + [HarmonyLib.HarmonyPatch(typeof(UIUtilityItem), "GetQualities")] + // ReSharper disable once UnusedMember.Local + public static class UIUtilityItemGetQualitiesPatch + { + // ReSharper disable once UnusedMember.Local + private static void Postfix(ItemEntity item, ref string __result) + { + if (!item.IsIdentified) + { + return; + } + + ItemEntityWeapon itemEntityWeapon = item as ItemEntityWeapon; + if (itemEntityWeapon == null) + { + return; + } + + WeaponCategory category = itemEntityWeapon.Blueprint.Category; + if (category.HasSubCategory(WeaponSubCategory.Finessable) && Main.IsOversized(itemEntityWeapon.Blueprint)) + { + __result = __result.Replace(LocalizedTexts.Instance.WeaponSubCategories.GetText(WeaponSubCategory.Finessable), ""); + __result = __result.Replace(", ,", ","); + char[] charsToTrim = { ',', ' ' }; + __result = __result.Trim(charsToTrim); + } + } + } +} \ No newline at end of file From 64fc8afebc3ba1f308dd10422573e55345679ea8 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 14 Dec 2020 13:07:24 -0600 Subject: [PATCH 091/132] Extracting the Harmony patches from Main into their own files: UIUtilityItemFillArmorEnchantmentsPatch --- CraftMagicItems/CraftMagicItems.csproj | 1 + CraftMagicItems/Main.cs | 26 ----------- ...UIUtilityItemFillArmorEnchantmentsPatch.cs | 46 +++++++++++++++++++ 3 files changed, 47 insertions(+), 26 deletions(-) create mode 100644 CraftMagicItems/Patches/Harmony/UIUtilityItemFillArmorEnchantmentsPatch.cs diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index 97b4e8d..f9828c5 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -123,6 +123,7 @@ + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index dd3511b..ff50b8c 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -3333,32 +3333,6 @@ public static void UpgradeSave(Version version) { } } - [HarmonyLib.HarmonyPatch(typeof(UIUtilityItem), "FillArmorEnchantments")] - // ReSharper disable once UnusedMember.Local - private static class UIUtilityItemFillArmorEnchantmentsPatch { - // ReSharper disable once UnusedMember.Local - private static void Postfix(TooltipData data, ItemEntityShield armor) { - if (armor.IsIdentified) { - foreach (var itemEnchantment in armor.Enchantments) { - itemEnchantment.Blueprint.CallComponents(c => { - if (c.Descriptor != ModifierDescriptor.ArmorEnhancement && c.Descriptor != ModifierDescriptor.ShieldEnhancement && !data.StatBonus.ContainsKey(c.Stat)) { - data.StatBonus.Add(c.Stat, UIUtility.AddSign(c.Value)); - } - }); - var component = itemEnchantment.Blueprint.GetComponent(); - if (component != null) { - StatType[] saves = { StatType.SaveReflex, StatType.SaveWill, StatType.SaveFortitude }; - foreach (var save in saves) { - if (!data.StatBonus.ContainsKey(save)) { - data.StatBonus.Add(save, UIUtility.AddSign(component.Value)); - } - } - } - } - } - } - } - [HarmonyLib.HarmonyPatch(typeof(UIUtilityItem), "FillEnchantmentDescription")] // ReSharper disable once UnusedMember.Local private static class UIUtilityItemFillEnchantmentDescriptionPatch { diff --git a/CraftMagicItems/Patches/Harmony/UIUtilityItemFillArmorEnchantmentsPatch.cs b/CraftMagicItems/Patches/Harmony/UIUtilityItemFillArmorEnchantmentsPatch.cs new file mode 100644 index 0000000..13f5bc4 --- /dev/null +++ b/CraftMagicItems/Patches/Harmony/UIUtilityItemFillArmorEnchantmentsPatch.cs @@ -0,0 +1,46 @@ +using Kingmaker.Blueprints; +using Kingmaker.Designers.Mechanics.EquipmentEnchants; +using Kingmaker.EntitySystem.Stats; +using Kingmaker.Enums; +using Kingmaker.Items; +using Kingmaker.UI.Common; +using Kingmaker.UI.Tooltip; + +namespace CraftMagicItems.Patches.Harmony +{ + [HarmonyLib.HarmonyPatch(typeof(UIUtilityItem), "FillArmorEnchantments")] + // ReSharper disable once UnusedMember.Local + public static class UIUtilityItemFillArmorEnchantmentsPatch + { + // ReSharper disable once UnusedMember.Local + private static void Postfix(TooltipData data, ItemEntityShield armor) + { + if (armor.IsIdentified) + { + foreach (var itemEnchantment in armor.Enchantments) + { + itemEnchantment.Blueprint.CallComponents(c => + { + if (c.Descriptor != ModifierDescriptor.ArmorEnhancement && c.Descriptor != ModifierDescriptor.ShieldEnhancement && !data.StatBonus.ContainsKey(c.Stat)) + { + data.StatBonus.Add(c.Stat, UIUtility.AddSign(c.Value)); + } + }); + + var component = itemEnchantment.Blueprint.GetComponent(); + if (component != null) + { + StatType[] saves = { StatType.SaveReflex, StatType.SaveWill, StatType.SaveFortitude }; + foreach (var save in saves) + { + if (!data.StatBonus.ContainsKey(save)) + { + data.StatBonus.Add(save, UIUtility.AddSign(component.Value)); + } + } + } + } + } + } + } +} \ No newline at end of file From 670a3e4dfccfd4e8d7c930c9dbb8db86d09a1f79 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 14 Dec 2020 13:09:43 -0600 Subject: [PATCH 092/132] Extracting the Harmony patches from Main into their own files: UIUtilityItemFillEnchantmentDescriptionPatch --- CraftMagicItems/CraftMagicItems.csproj | 1 + CraftMagicItems/Main.cs | 99 ----------- ...lityItemFillEnchantmentDescriptionPatch.cs | 157 ++++++++++++++++++ 3 files changed, 158 insertions(+), 99 deletions(-) create mode 100644 CraftMagicItems/Patches/Harmony/UIUtilityItemFillEnchantmentDescriptionPatch.cs diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index f9828c5..530ada1 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -124,6 +124,7 @@ + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index ff50b8c..adab8ab 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -3333,105 +3333,6 @@ public static void UpgradeSave(Version version) { } } - [HarmonyLib.HarmonyPatch(typeof(UIUtilityItem), "FillEnchantmentDescription")] - // ReSharper disable once UnusedMember.Local - private static class UIUtilityItemFillEnchantmentDescriptionPatch { - // ReSharper disable once UnusedMember.Local - private static bool Prefix(ItemEntity item, TooltipData data, ref string __result) { - string text = string.Empty; - if (item is ItemEntityShield shield && shield.IsIdentified) { - // It appears that shields are not properly identified when found. - shield.ArmorComponent.Identify(); - shield.WeaponComponent?.Identify(); - return true; - } else if (item.Blueprint.ItemType == ItemsFilter.ItemType.Neck && ItemPlusEquivalent(item.Blueprint) > 0) { - if (item.IsIdentified) { - foreach (ItemEnchantment itemEnchantment in item.Enchantments) { - itemEnchantment.Blueprint.CallComponents(c => { - if (!data.StatBonus.ContainsKey(c.Stat)) { - data.StatBonus.Add(c.Stat, UIUtility.AddSign(c.Value)); - } - }); - if (!data.Texts.ContainsKey(TooltipElement.Qualities)) { - data.Texts[TooltipElement.Qualities] = Accessors.CallUIUtilityItemGetQualities(item); - } - if (!string.IsNullOrEmpty(itemEnchantment.Blueprint.Description)) { - text += string.Format("{0}\n", itemEnchantment.Blueprint.Name); - text = text + itemEnchantment.Blueprint.Description + "\n\n"; - } - } - if (item.Enchantments.Any() && !data.Texts.ContainsKey(TooltipElement.Qualities)) { - data.Texts[TooltipElement.Qualities] = GetEnhancementBonus(item); - } - if (GetItemEnhancementBonus(item) > 0) { - data.Texts[TooltipElement.Enhancement] = GetEnhancementBonus(item); - } - } - __result = text; - return false; - } else { - return true; - } - } - private static string GetEnhancementBonus(ItemEntity item) { - if (!item.IsIdentified) { - return string.Empty; - } - int itemEnhancementBonus = GetItemEnhancementBonus(item); - return (itemEnhancementBonus == 0) ? string.Empty : UIUtility.AddSign(itemEnhancementBonus); - } - public static int GetItemEnhancementBonus(ItemEntity item) { - return item.Enchantments.SelectMany((ItemEnchantment f) => f.SelectComponents()).Aggregate(0, (int s, EquipmentWeaponTypeEnhancement e) => s + e.Enhancement); - } - - private static void Postfix(ItemEntity item, TooltipData data, ref string __result) { - if (item is ItemEntityShield shield) { - if (shield.WeaponComponent != null) { - TooltipData tmp = new TooltipData(); - string result = Accessors.CallUIUtilityItemFillEnchantmentDescription(shield.WeaponComponent, tmp); - if (!string.IsNullOrEmpty(result)) { - __result += $"{LocalizedStringBlueprints.ShieldBashLocalized}\n"; - __result += result; - } - data.Texts[TooltipElement.AttackType] = tmp.Texts[TooltipElement.AttackType]; - data.Texts[TooltipElement.ProficiencyGroup] = tmp.Texts[TooltipElement.ProficiencyGroup]; - if (tmp.Texts.ContainsKey(TooltipElement.Qualities) && !string.IsNullOrEmpty(tmp.Texts[TooltipElement.Qualities])) { - if (data.Texts.ContainsKey(TooltipElement.Qualities)) { - data.Texts[TooltipElement.Qualities] += $", {LocalizedStringBlueprints.ShieldBashLocalized}: {tmp.Texts[TooltipElement.Qualities]}"; - } else { - data.Texts[TooltipElement.Qualities] = $"{LocalizedStringBlueprints.ShieldBashLocalized}: {tmp.Texts[TooltipElement.Qualities]}"; - } - } - data.Texts[TooltipElement.Damage] = tmp.Texts[TooltipElement.Damage]; - if (tmp.Texts.ContainsKey(TooltipElement.EquipDamage)) { - data.Texts[TooltipElement.EquipDamage] = tmp.Texts[TooltipElement.EquipDamage]; - } - if (tmp.Texts.ContainsKey(TooltipElement.PhysicalDamage)) { - data.Texts[TooltipElement.PhysicalDamage] = tmp.Texts[TooltipElement.PhysicalDamage]; - data.PhysicalDamage = tmp.PhysicalDamage; - } - data.Energy = tmp.Energy; - data.OtherDamage = tmp.OtherDamage; - data.Texts[TooltipElement.Range] = tmp.Texts[TooltipElement.Range]; - data.Texts[TooltipElement.CriticalHit] = tmp.Texts[TooltipElement.CriticalHit]; - if (tmp.Texts.ContainsKey(TooltipElement.Enhancement)) { - data.Texts[TooltipElement.Enhancement] = tmp.Texts[TooltipElement.Enhancement]; - } - } - if (GameHelper.GetItemEnhancementBonus(shield.ArmorComponent) > 0) { - if (data.Texts.ContainsKey(TooltipElement.Damage)) { - data.Texts[Enum.GetValues(typeof(TooltipElement)).Cast().Max() + 1] = UIUtility.AddSign(GameHelper.GetItemEnhancementBonus(shield.ArmorComponent)); - } else { - data.Texts[TooltipElement.Enhancement] = UIUtility.AddSign(GameHelper.GetItemEnhancementBonus(shield.ArmorComponent)); - } - } - } - if (data.Texts.ContainsKey(TooltipElement.Qualities)) { - data.Texts[TooltipElement.Qualities] = data.Texts[TooltipElement.Qualities].Replace(" ,", ","); - } - } - } - [HarmonyLib.HarmonyPatch(typeof(UIUtility), "IsMagicItem")] // ReSharper disable once UnusedMember.Local private static class UIUtilityIsMagicItem { diff --git a/CraftMagicItems/Patches/Harmony/UIUtilityItemFillEnchantmentDescriptionPatch.cs b/CraftMagicItems/Patches/Harmony/UIUtilityItemFillEnchantmentDescriptionPatch.cs new file mode 100644 index 0000000..fb0b02a --- /dev/null +++ b/CraftMagicItems/Patches/Harmony/UIUtilityItemFillEnchantmentDescriptionPatch.cs @@ -0,0 +1,157 @@ +using System; +using System.Linq; +using CraftMagicItems.Constants; +using Kingmaker.Blueprints; +using Kingmaker.Blueprints.Items.Ecnchantments; +using Kingmaker.Designers; +using Kingmaker.Designers.Mechanics.EquipmentEnchants; +using Kingmaker.Items; +using Kingmaker.UI.Common; +using Kingmaker.UI.Tooltip; +using Kingmaker.Utility; + +namespace CraftMagicItems.Patches.Harmony +{ + [HarmonyLib.HarmonyPatch(typeof(UIUtilityItem), "FillEnchantmentDescription")] + // ReSharper disable once UnusedMember.Local + public static class UIUtilityItemFillEnchantmentDescriptionPatch + { + // ReSharper disable once UnusedMember.Local + private static bool Prefix(ItemEntity item, TooltipData data, ref string __result) + { + string text = string.Empty; + if (item is ItemEntityShield shield && shield.IsIdentified) + { + // It appears that shields are not properly identified when found. + shield.ArmorComponent.Identify(); + shield.WeaponComponent?.Identify(); + return true; + } + else if (item.Blueprint.ItemType == ItemsFilter.ItemType.Neck && Main.ItemPlusEquivalent(item.Blueprint) > 0) + { + if (item.IsIdentified) + { + foreach (ItemEnchantment itemEnchantment in item.Enchantments) + { + itemEnchantment.Blueprint.CallComponents(c => + { + if (!data.StatBonus.ContainsKey(c.Stat)) + { + data.StatBonus.Add(c.Stat, UIUtility.AddSign(c.Value)); + } + }); + + if (!data.Texts.ContainsKey(TooltipElement.Qualities)) + { + data.Texts[TooltipElement.Qualities] = Main.Accessors.CallUIUtilityItemGetQualities(item); + } + + if (!string.IsNullOrEmpty(itemEnchantment.Blueprint.Description)) + { + text += string.Format("{0}\n", itemEnchantment.Blueprint.Name); + text = text + itemEnchantment.Blueprint.Description + "\n\n"; + } + } + + if (item.Enchantments.Any() && !data.Texts.ContainsKey(TooltipElement.Qualities)) + { + data.Texts[TooltipElement.Qualities] = GetEnhancementBonus(item); + } + + if (GetItemEnhancementBonus(item) > 0) + { + data.Texts[TooltipElement.Enhancement] = GetEnhancementBonus(item); + } + } + __result = text; + return false; + } + else + { + return true; + } + } + + private static string GetEnhancementBonus(ItemEntity item) + { + if (!item.IsIdentified) + { + return string.Empty; + } + int itemEnhancementBonus = GetItemEnhancementBonus(item); + return (itemEnhancementBonus == 0) ? string.Empty : UIUtility.AddSign(itemEnhancementBonus); + } + + public static int GetItemEnhancementBonus(ItemEntity item) + { + return item.Enchantments.SelectMany((ItemEnchantment f) => f.SelectComponents()).Aggregate(0, (int s, EquipmentWeaponTypeEnhancement e) => s + e.Enhancement); + } + + private static void Postfix(ItemEntity item, TooltipData data, ref string __result) + { + if (item is ItemEntityShield shield) + { + if (shield.WeaponComponent != null) + { + TooltipData tmp = new TooltipData(); + string result = Main.Accessors.CallUIUtilityItemFillEnchantmentDescription(shield.WeaponComponent, tmp); + if (!string.IsNullOrEmpty(result)) + { + __result += $"{LocalizedStringBlueprints.ShieldBashLocalized}\n"; + __result += result; + } + + data.Texts[TooltipElement.AttackType] = tmp.Texts[TooltipElement.AttackType]; + data.Texts[TooltipElement.ProficiencyGroup] = tmp.Texts[TooltipElement.ProficiencyGroup]; + if (tmp.Texts.ContainsKey(TooltipElement.Qualities) && !string.IsNullOrEmpty(tmp.Texts[TooltipElement.Qualities])) + { + if (data.Texts.ContainsKey(TooltipElement.Qualities)) + { + data.Texts[TooltipElement.Qualities] += $", {LocalizedStringBlueprints.ShieldBashLocalized}: {tmp.Texts[TooltipElement.Qualities]}"; + } + else + { + data.Texts[TooltipElement.Qualities] = $"{LocalizedStringBlueprints.ShieldBashLocalized}: {tmp.Texts[TooltipElement.Qualities]}"; + } + } + + data.Texts[TooltipElement.Damage] = tmp.Texts[TooltipElement.Damage]; + if (tmp.Texts.ContainsKey(TooltipElement.EquipDamage)) + { + data.Texts[TooltipElement.EquipDamage] = tmp.Texts[TooltipElement.EquipDamage]; + } + if (tmp.Texts.ContainsKey(TooltipElement.PhysicalDamage)) + { + data.Texts[TooltipElement.PhysicalDamage] = tmp.Texts[TooltipElement.PhysicalDamage]; + data.PhysicalDamage = tmp.PhysicalDamage; + } + data.Energy = tmp.Energy; + data.OtherDamage = tmp.OtherDamage; + data.Texts[TooltipElement.Range] = tmp.Texts[TooltipElement.Range]; + data.Texts[TooltipElement.CriticalHit] = tmp.Texts[TooltipElement.CriticalHit]; + if (tmp.Texts.ContainsKey(TooltipElement.Enhancement)) + { + data.Texts[TooltipElement.Enhancement] = tmp.Texts[TooltipElement.Enhancement]; + } + } + + if (GameHelper.GetItemEnhancementBonus(shield.ArmorComponent) > 0) + { + if (data.Texts.ContainsKey(TooltipElement.Damage)) + { + data.Texts[Enum.GetValues(typeof(TooltipElement)).Cast().Max() + 1] = UIUtility.AddSign(GameHelper.GetItemEnhancementBonus(shield.ArmorComponent)); + } + else + { + data.Texts[TooltipElement.Enhancement] = UIUtility.AddSign(GameHelper.GetItemEnhancementBonus(shield.ArmorComponent)); + } + } + } + + if (data.Texts.ContainsKey(TooltipElement.Qualities)) + { + data.Texts[TooltipElement.Qualities] = data.Texts[TooltipElement.Qualities].Replace(" ,", ","); + } + } + } +} \ No newline at end of file From 2710084daacba689d50c7915b187c6b9f65deeef Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 14 Dec 2020 13:11:05 -0600 Subject: [PATCH 093/132] Extracting the Harmony patches from Main into their own files: UIUtilityIsMagicItem --- CraftMagicItems/CraftMagicItems.csproj | 1 + CraftMagicItems/Main.cs | 11 --------- .../Patches/Harmony/UIUtilityIsMagicItem.cs | 23 +++++++++++++++++++ 3 files changed, 24 insertions(+), 11 deletions(-) create mode 100644 CraftMagicItems/Patches/Harmony/UIUtilityIsMagicItem.cs diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index 530ada1..d8d9f75 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -123,6 +123,7 @@ + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index adab8ab..282b9a4 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -3333,17 +3333,6 @@ public static void UpgradeSave(Version version) { } } - [HarmonyLib.HarmonyPatch(typeof(UIUtility), "IsMagicItem")] - // ReSharper disable once UnusedMember.Local - private static class UIUtilityIsMagicItem { - // ReSharper disable once UnusedMember.Local - private static void Postfix(ItemEntity item, ref bool __result) { - if (__result == false && item != null && item.IsIdentified && item is ItemEntityShield shield && shield.WeaponComponent != null) { - __result = ItemPlusEquivalent(shield.WeaponComponent.Blueprint) > 0; - } - } - } - [HarmonyLib.HarmonyPatch(typeof(ItemEntity), "VendorDescription", HarmonyLib.MethodType.Getter)] // ReSharper disable once UnusedMember.Local private static class ItemEntityVendorDescriptionPatch { diff --git a/CraftMagicItems/Patches/Harmony/UIUtilityIsMagicItem.cs b/CraftMagicItems/Patches/Harmony/UIUtilityIsMagicItem.cs new file mode 100644 index 0000000..1db8562 --- /dev/null +++ b/CraftMagicItems/Patches/Harmony/UIUtilityIsMagicItem.cs @@ -0,0 +1,23 @@ +using Kingmaker.Items; +using Kingmaker.UI.Common; + +namespace CraftMagicItems.Patches.Harmony +{ + [HarmonyLib.HarmonyPatch(typeof(UIUtility), "IsMagicItem")] + // ReSharper disable once UnusedMember.Local + public static class UIUtilityIsMagicItem + { + // ReSharper disable once UnusedMember.Local + private static void Postfix(ItemEntity item, ref bool __result) + { + if (__result == false + && item != null + && item.IsIdentified + && item is ItemEntityShield shield + && shield.WeaponComponent != null) + { + __result = Main.ItemPlusEquivalent(shield.WeaponComponent.Blueprint) > 0; + } + } + } +} \ No newline at end of file From ba9aed2812a73e576291461dfd9f013cecf61921 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 14 Dec 2020 13:12:45 -0600 Subject: [PATCH 094/132] Extracting the Harmony patches from Main into their own files: ItemEntityVendorDescriptionPatch --- CraftMagicItems/CraftMagicItems.csproj | 1 + CraftMagicItems/Main.cs | 26 ------------- .../ItemEntityVendorDescriptionPatch.cs | 37 +++++++++++++++++++ 3 files changed, 38 insertions(+), 26 deletions(-) create mode 100644 CraftMagicItems/Patches/Harmony/ItemEntityVendorDescriptionPatch.cs diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index d8d9f75..e9ba1a6 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -115,6 +115,7 @@ + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 282b9a4..e6a0c5a 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -3333,32 +3333,6 @@ public static void UpgradeSave(Version version) { } } - [HarmonyLib.HarmonyPatch(typeof(ItemEntity), "VendorDescription", HarmonyLib.MethodType.Getter)] - // ReSharper disable once UnusedMember.Local - private static class ItemEntityVendorDescriptionPatch { - // ReSharper disable once UnusedMember.Local - private static bool Prefix(ItemEntity __instance, ref string __result) { - // If the "vendor" is a party member, return that the item was crafted rather than from a merchant -#if PATCH21 - if (__instance.VendorBlueprint != null && __instance.VendorBlueprint.IsCompanion) { - foreach (var companion in UIUtility.GetGroup(true)) { - if (companion.Blueprint == __instance.VendorBlueprint) { - __result = LocalizationHelper.FormatLocalizedString("craftMagicItems-crafted-source-description", companion.CharacterName); - break; - } - } - return false; - } -#else - if (__instance.Vendor != null && __instance.Vendor.IsPlayerFaction) { - __result = LocalizationHelper.FormatLocalizedString("craftMagicItems-crafted-source-description", __instance.Vendor.CharacterName); - return false; - } -#endif - return true; - } - } - // Owlcat's code doesn't filter out undamaged characters, so it will always return someone. This meant that with the "auto-cast healing" camping // option enabled on, healers would burn all their spell slots healing undamaged characters when they started resting, leaving them no spells to cast // when crafting. Change it so it returns null if the most damaged character is undamaged. diff --git a/CraftMagicItems/Patches/Harmony/ItemEntityVendorDescriptionPatch.cs b/CraftMagicItems/Patches/Harmony/ItemEntityVendorDescriptionPatch.cs new file mode 100644 index 0000000..af20d89 --- /dev/null +++ b/CraftMagicItems/Patches/Harmony/ItemEntityVendorDescriptionPatch.cs @@ -0,0 +1,37 @@ +using CraftMagicItems.Localization; +using Kingmaker.Items; +using Kingmaker.UI.Common; + +namespace CraftMagicItems.Patches.Harmony +{ + [HarmonyLib.HarmonyPatch(typeof(ItemEntity), "VendorDescription", HarmonyLib.MethodType.Getter)] + // ReSharper disable once UnusedMember.Local + public static class ItemEntityVendorDescriptionPatch + { + // ReSharper disable once UnusedMember.Local + private static bool Prefix(ItemEntity __instance, ref string __result) + { + // If the "vendor" is a party member, return that the item was crafted rather than from a merchant +#if PATCH21 + if (__instance.VendorBlueprint != null && __instance.VendorBlueprint.IsCompanion) + { + foreach (var companion in UIUtility.GetGroup(true)) + { + if (companion.Blueprint == __instance.VendorBlueprint) + { + __result = LocalizationHelper.FormatLocalizedString("craftMagicItems-crafted-source-description", companion.CharacterName); + break; + } + } + return false; + } +#else + if (__instance.Vendor != null && __instance.Vendor.IsPlayerFaction) { + __result = LocalizationHelper.FormatLocalizedString("craftMagicItems-crafted-source-description", __instance.Vendor.CharacterName); + return false; + } +#endif + return true; + } + } +} \ No newline at end of file From 1d16d93976096e63d6125f25cfcdad6743f9a031 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 14 Dec 2020 13:15:07 -0600 Subject: [PATCH 095/132] Extracting the Harmony patches from Main into their own files: UnitUseSpellsOnRestGetUnitWithMaxDamagePatch --- CraftMagicItems/CraftMagicItems.csproj | 1 + CraftMagicItems/Main.cs | 14 ----------- ...seSpellsOnRestGetUnitWithMaxDamagePatch.cs | 25 +++++++++++++++++++ 3 files changed, 26 insertions(+), 14 deletions(-) create mode 100644 CraftMagicItems/Patches/Harmony/UnitUseSpellsOnRestGetUnitWithMaxDamagePatch.cs diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index e9ba1a6..d4e561d 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -128,6 +128,7 @@ + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index e6a0c5a..65560ef 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -3333,20 +3333,6 @@ public static void UpgradeSave(Version version) { } } - // Owlcat's code doesn't filter out undamaged characters, so it will always return someone. This meant that with the "auto-cast healing" camping - // option enabled on, healers would burn all their spell slots healing undamaged characters when they started resting, leaving them no spells to cast - // when crafting. Change it so it returns null if the most damaged character is undamaged. - [HarmonyLib.HarmonyPatch(typeof(UnitUseSpellsOnRest), "GetUnitWithMaxDamage")] - // ReSharper disable once UnusedMember.Local - private static class UnitUseSpellsOnRestGetUnitWithMaxDamagePatch { - // ReSharper disable once UnusedMember.Local - private static void Postfix(ref UnitEntityData __result) { - if (__result.Damage == 0 && (UnitPartDualCompanion.GetPair(__result)?.Damage ?? 0) == 0) { - __result = null; - } - } - } - private static bool EquipmentEnchantmentValid(ItemEntityWeapon weapon, ItemEntity owner) { if ((weapon == owner) || (weapon != null && (weapon.Blueprint.IsNatural || weapon.Blueprint.IsUnarmed))) { diff --git a/CraftMagicItems/Patches/Harmony/UnitUseSpellsOnRestGetUnitWithMaxDamagePatch.cs b/CraftMagicItems/Patches/Harmony/UnitUseSpellsOnRestGetUnitWithMaxDamagePatch.cs new file mode 100644 index 0000000..fa486de --- /dev/null +++ b/CraftMagicItems/Patches/Harmony/UnitUseSpellsOnRestGetUnitWithMaxDamagePatch.cs @@ -0,0 +1,25 @@ +using Kingmaker.EntitySystem.Entities; +using Kingmaker.UnitLogic; +using Kingmaker.UnitLogic.Parts; + +namespace CraftMagicItems.Patches.Harmony +{ + /// + /// Owlcat's code doesn't filter out undamaged characters, so it will always return someone. This meant that with the "auto-cast healing" camping + /// option enabled on, healers would burn all their spell slots healing undamaged characters when they started resting, leaving them no spells to cast + /// when crafting. Change it so it returns null if the most damaged character is undamaged. + /// + [HarmonyLib.HarmonyPatch(typeof(UnitUseSpellsOnRest), "GetUnitWithMaxDamage")] + // ReSharper disable once UnusedMember.Local + public static class UnitUseSpellsOnRestGetUnitWithMaxDamagePatch + { + // ReSharper disable once UnusedMember.Local + private static void Postfix(ref UnitEntityData __result) + { + if (__result.Damage == 0 && (UnitPartDualCompanion.GetPair(__result)?.Damage ?? 0) == 0) + { + __result = null; + } + } + } +} \ No newline at end of file From 9c82ece862867c968d35b7c983886650b4b33c4a Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 14 Dec 2020 13:16:30 -0600 Subject: [PATCH 096/132] Extracting the Harmony patches from Main into their own files: WeaponEnergyDamageDiceOnEventAboutToTriggerPatch --- CraftMagicItems/CraftMagicItems.csproj | 1 + CraftMagicItems/Main.cs | 27 +------------ ...rgyDamageDiceOnEventAboutToTriggerPatch.cs | 38 +++++++++++++++++++ 3 files changed, 40 insertions(+), 26 deletions(-) create mode 100644 CraftMagicItems/Patches/Harmony/WeaponEnergyDamageDiceOnEventAboutToTriggerPatch.cs diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index d4e561d..27059d8 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -129,6 +129,7 @@ + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 65560ef..eb18e40 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -3333,7 +3333,7 @@ public static void UpgradeSave(Version version) { } } - private static bool EquipmentEnchantmentValid(ItemEntityWeapon weapon, ItemEntity owner) { + public static bool EquipmentEnchantmentValid(ItemEntityWeapon weapon, ItemEntity owner) { if ((weapon == owner) || (weapon != null && (weapon.Blueprint.IsNatural || weapon.Blueprint.IsUnarmed))) { return true; @@ -3342,31 +3342,6 @@ private static bool EquipmentEnchantmentValid(ItemEntityWeapon weapon, ItemEntit } } - [HarmonyLib.HarmonyPatch(typeof(WeaponEnergyDamageDice), "OnEventAboutToTrigger")] - // ReSharper disable once UnusedMember.Local - private static class WeaponEnergyDamageDiceOnEventAboutToTriggerPatch { - // ReSharper disable once UnusedMember.Local - private static bool Prefix(WeaponEnergyDamageDice __instance, RuleCalculateWeaponStats evt) { - if (__instance is ItemEnchantmentLogic logic) { - if (EquipmentEnchantmentValid(evt.Weapon, logic.Owner)) { - DamageDescription item = new DamageDescription - { - TypeDescription = new DamageTypeDescription - { - Type = DamageType.Energy, - Energy = __instance.Element - }, - Dice = __instance.EnergyDamageDice - }; - evt.DamageDescription.Add(item); - } - return false; - } else { - return true; - } - } - } - [HarmonyLib.HarmonyPatch(typeof(WeaponEnergyBurst), "OnEventAboutToTrigger")] // ReSharper disable once UnusedMember.Local private static class WeaponEnergyBurstOnEventAboutToTriggerPatch { diff --git a/CraftMagicItems/Patches/Harmony/WeaponEnergyDamageDiceOnEventAboutToTriggerPatch.cs b/CraftMagicItems/Patches/Harmony/WeaponEnergyDamageDiceOnEventAboutToTriggerPatch.cs new file mode 100644 index 0000000..334c5cc --- /dev/null +++ b/CraftMagicItems/Patches/Harmony/WeaponEnergyDamageDiceOnEventAboutToTriggerPatch.cs @@ -0,0 +1,38 @@ +using Kingmaker.Blueprints.Items.Ecnchantments; +using Kingmaker.Designers.Mechanics.Facts; +using Kingmaker.RuleSystem.Rules; +using Kingmaker.RuleSystem.Rules.Damage; + +namespace CraftMagicItems.Patches.Harmony +{ + [HarmonyLib.HarmonyPatch(typeof(WeaponEnergyDamageDice), "OnEventAboutToTrigger")] + // ReSharper disable once UnusedMember.Local + public static class WeaponEnergyDamageDiceOnEventAboutToTriggerPatch + { + // ReSharper disable once UnusedMember.Local + private static bool Prefix(WeaponEnergyDamageDice __instance, RuleCalculateWeaponStats evt) + { + if (__instance is ItemEnchantmentLogic logic) + { + if (Main.EquipmentEnchantmentValid(evt.Weapon, logic.Owner)) + { + DamageDescription item = new DamageDescription + { + TypeDescription = new DamageTypeDescription + { + Type = DamageType.Energy, + Energy = __instance.Element + }, + Dice = __instance.EnergyDamageDice + }; + evt.DamageDescription.Add(item); + } + return false; + } + else + { + return true; + } + } + } +} \ No newline at end of file From d30731a8cba7e616b7412bcd7083008f750921d3 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 14 Dec 2020 13:17:49 -0600 Subject: [PATCH 097/132] Extracting the Harmony patches from Main into their own files: WeaponEnergyBurstOnEventAboutToTriggerPatch --- CraftMagicItems/CraftMagicItems.csproj | 1 + CraftMagicItems/Main.cs | 21 ---------- ...onEnergyBurstOnEventAboutToTriggerPatch.cs | 39 +++++++++++++++++++ 3 files changed, 40 insertions(+), 21 deletions(-) create mode 100644 CraftMagicItems/Patches/Harmony/WeaponEnergyBurstOnEventAboutToTriggerPatch.cs diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index 27059d8..9774389 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -129,6 +129,7 @@ + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index eb18e40..4626117 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -3342,27 +3342,6 @@ public static bool EquipmentEnchantmentValid(ItemEntityWeapon weapon, ItemEntity } } - [HarmonyLib.HarmonyPatch(typeof(WeaponEnergyBurst), "OnEventAboutToTrigger")] - // ReSharper disable once UnusedMember.Local - private static class WeaponEnergyBurstOnEventAboutToTriggerPatch { - // ReSharper disable once UnusedMember.Local - private static bool Prefix(WeaponEnergyBurst __instance, RuleDealDamage evt) { - if (__instance is ItemEnchantmentLogic logic) { - if (logic.Owner == null || evt.AttackRoll == null || !evt.AttackRoll.IsCriticalConfirmed || evt.AttackRoll.FortificationNegatesCriticalHit || evt.DamageBundle.WeaponDamage == null) { - return false; - } - if (EquipmentEnchantmentValid(evt.DamageBundle.Weapon, logic.Owner)) { - RuleCalculateWeaponStats ruleCalculateWeaponStats = Rulebook.Trigger(new RuleCalculateWeaponStats(Game.Instance.DefaultUnit, evt.DamageBundle.Weapon, null)); - DiceFormula dice = new DiceFormula(Math.Max(ruleCalculateWeaponStats.CriticalMultiplier - 1, 1), __instance.Dice); - evt.DamageBundle.Add(new EnergyDamage(dice, __instance.Element)); - } - return false; - } else { - return true; - } - } - } - [HarmonyLib.HarmonyPatch(typeof(WeaponExtraAttack), "OnEventAboutToTrigger")] // ReSharper disable once UnusedMember.Local private static class WeaponExtraAttackOnEventAboutToTriggerPatch { diff --git a/CraftMagicItems/Patches/Harmony/WeaponEnergyBurstOnEventAboutToTriggerPatch.cs b/CraftMagicItems/Patches/Harmony/WeaponEnergyBurstOnEventAboutToTriggerPatch.cs new file mode 100644 index 0000000..d1a14fe --- /dev/null +++ b/CraftMagicItems/Patches/Harmony/WeaponEnergyBurstOnEventAboutToTriggerPatch.cs @@ -0,0 +1,39 @@ +using System; +using Kingmaker; +using Kingmaker.Blueprints.Items.Ecnchantments; +using Kingmaker.Designers.Mechanics.Facts; +using Kingmaker.RuleSystem; +using Kingmaker.RuleSystem.Rules; +using Kingmaker.RuleSystem.Rules.Damage; + +namespace CraftMagicItems.Patches.Harmony +{ + [HarmonyLib.HarmonyPatch(typeof(WeaponEnergyBurst), "OnEventAboutToTrigger")] + // ReSharper disable once UnusedMember.Local + public static class WeaponEnergyBurstOnEventAboutToTriggerPatch + { + // ReSharper disable once UnusedMember.Local + private static bool Prefix(WeaponEnergyBurst __instance, RuleDealDamage evt) + { + if (__instance is ItemEnchantmentLogic logic) + { + if (logic.Owner == null || evt.AttackRoll == null || !evt.AttackRoll.IsCriticalConfirmed || evt.AttackRoll.FortificationNegatesCriticalHit || evt.DamageBundle.WeaponDamage == null) + { + return false; + } + + if (Main.EquipmentEnchantmentValid(evt.DamageBundle.Weapon, logic.Owner)) + { + RuleCalculateWeaponStats ruleCalculateWeaponStats = Rulebook.Trigger(new RuleCalculateWeaponStats(Game.Instance.DefaultUnit, evt.DamageBundle.Weapon, null)); + DiceFormula dice = new DiceFormula(Math.Max(ruleCalculateWeaponStats.CriticalMultiplier - 1, 1), __instance.Dice); + evt.DamageBundle.Add(new EnergyDamage(dice, __instance.Element)); + } + return false; + } + else + { + return true; + } + } + } +} \ No newline at end of file From dfcc393e743479e47b972218bcc90749753b645c Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 14 Dec 2020 13:19:06 -0600 Subject: [PATCH 098/132] Extracting the Harmony patches from Main into their own files: WeaponExtraAttackOnEventAboutToTriggerPatch --- CraftMagicItems/CraftMagicItems.csproj | 1 + CraftMagicItems/Main.cs | 19 ----------- ...onExtraAttackOnEventAboutToTriggerPatch.cs | 34 +++++++++++++++++++ 3 files changed, 35 insertions(+), 19 deletions(-) create mode 100644 CraftMagicItems/Patches/Harmony/WeaponExtraAttackOnEventAboutToTriggerPatch.cs diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index 9774389..ac3eba9 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -131,6 +131,7 @@ + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 4626117..0dfca0c 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -3342,25 +3342,6 @@ public static bool EquipmentEnchantmentValid(ItemEntityWeapon weapon, ItemEntity } } - [HarmonyLib.HarmonyPatch(typeof(WeaponExtraAttack), "OnEventAboutToTrigger")] - // ReSharper disable once UnusedMember.Local - private static class WeaponExtraAttackOnEventAboutToTriggerPatch { - // ReSharper disable once UnusedMember.Local - private static bool Prefix(WeaponExtraAttack __instance, RuleCalculateAttacksCount evt) { - if (__instance is ItemEnchantmentLogic logic) { - if (logic.Owner is ItemEntityWeapon) { - evt.AddExtraAttacks(__instance.Number, __instance.Haste, __instance.Owner); - } else if (evt.Initiator.GetFirstWeapon() != null && - (evt.Initiator.GetFirstWeapon().Blueprint.IsNatural || evt.Initiator.GetFirstWeapon().Blueprint.IsUnarmed)) { - evt.AddExtraAttacks(__instance.Number, __instance.Haste); - } - return false; - } else { - return true; - } - } - } - [HarmonyLib.HarmonyPatch(typeof(WeaponDamageAgainstAlignment), "OnEventAboutToTrigger")] // ReSharper disable once UnusedMember.Local private static class WeaponDamageAgainstAlignmentOnEventAboutToTriggerPatch { diff --git a/CraftMagicItems/Patches/Harmony/WeaponExtraAttackOnEventAboutToTriggerPatch.cs b/CraftMagicItems/Patches/Harmony/WeaponExtraAttackOnEventAboutToTriggerPatch.cs new file mode 100644 index 0000000..5c47046 --- /dev/null +++ b/CraftMagicItems/Patches/Harmony/WeaponExtraAttackOnEventAboutToTriggerPatch.cs @@ -0,0 +1,34 @@ +using Kingmaker.Blueprints.Items.Ecnchantments; +using Kingmaker.Designers.Mechanics.Facts; +using Kingmaker.Items; +using Kingmaker.RuleSystem.Rules; + +namespace CraftMagicItems.Patches.Harmony +{ + [HarmonyLib.HarmonyPatch(typeof(WeaponExtraAttack), "OnEventAboutToTrigger")] + // ReSharper disable once UnusedMember.Local + public static class WeaponExtraAttackOnEventAboutToTriggerPatch + { + // ReSharper disable once UnusedMember.Local + private static bool Prefix(WeaponExtraAttack __instance, RuleCalculateAttacksCount evt) + { + if (__instance is ItemEnchantmentLogic logic) + { + if (logic.Owner is ItemEntityWeapon) + { + evt.AddExtraAttacks(__instance.Number, __instance.Haste, __instance.Owner); + } + else if (evt.Initiator.GetFirstWeapon() != null + && (evt.Initiator.GetFirstWeapon().Blueprint.IsNatural || evt.Initiator.GetFirstWeapon().Blueprint.IsUnarmed)) + { + evt.AddExtraAttacks(__instance.Number, __instance.Haste); + } + return false; + } + else + { + return true; + } + } + } +} \ No newline at end of file From 68d537825ed23634a99db034b17a86e276f42294 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 14 Dec 2020 13:20:58 -0600 Subject: [PATCH 099/132] Extracting the Harmony patches from Main into their own files: WeaponDamageAgainstAlignmentOnEventAboutToTriggerPatch --- CraftMagicItems/CraftMagicItems.csproj | 1 + CraftMagicItems/Main.cs | 25 ----------- ...instAlignmentOnEventAboutToTriggerPatch.cs | 41 +++++++++++++++++++ 3 files changed, 42 insertions(+), 25 deletions(-) create mode 100644 CraftMagicItems/Patches/Harmony/WeaponDamageAgainstAlignmentOnEventAboutToTriggerPatch.cs diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index ac3eba9..ecef421 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -129,6 +129,7 @@ + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 0dfca0c..a19acd6 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -3342,31 +3342,6 @@ public static bool EquipmentEnchantmentValid(ItemEntityWeapon weapon, ItemEntity } } - [HarmonyLib.HarmonyPatch(typeof(WeaponDamageAgainstAlignment), "OnEventAboutToTrigger")] - // ReSharper disable once UnusedMember.Local - private static class WeaponDamageAgainstAlignmentOnEventAboutToTriggerPatch { - // ReSharper disable once UnusedMember.Local - private static bool Prefix(WeaponDamageAgainstAlignment __instance, RulePrepareDamage evt) { - if (__instance is ItemEnchantmentLogic logic) { - if (evt.DamageBundle.WeaponDamage == null) { - return false; - } - evt.DamageBundle.WeaponDamage.AddAlignment(__instance.WeaponAlignment); - - if (evt.Target.Descriptor.Alignment.Value.HasComponent(__instance.EnemyAlignment) && EquipmentEnchantmentValid(evt.DamageBundle.Weapon, logic.Owner)) { - int rollsCount = __instance.Value.DiceCountValue.Calculate(logic.Context); - int bonusDamage = __instance.Value.BonusValue.Calculate(logic.Context); - EnergyDamage energyDamage = new EnergyDamage(new DiceFormula(rollsCount, __instance.Value.DiceType), __instance.DamageType); - energyDamage.AddBonusTargetRelated(bonusDamage); - evt.DamageBundle.Add(energyDamage); - } - return false; - } else { - return true; - } - } - } - [HarmonyLib.HarmonyPatch(typeof(WeaponConditionalEnhancementBonus), "OnEventAboutToTrigger", new Type[] { typeof(RuleCalculateWeaponStats) })] // ReSharper disable once UnusedMember.Local private static class WeaponConditionalEnhancementBonusOnEventAboutToTriggerRuleCalculateWeaponStatsPatch { diff --git a/CraftMagicItems/Patches/Harmony/WeaponDamageAgainstAlignmentOnEventAboutToTriggerPatch.cs b/CraftMagicItems/Patches/Harmony/WeaponDamageAgainstAlignmentOnEventAboutToTriggerPatch.cs new file mode 100644 index 0000000..b738c55 --- /dev/null +++ b/CraftMagicItems/Patches/Harmony/WeaponDamageAgainstAlignmentOnEventAboutToTriggerPatch.cs @@ -0,0 +1,41 @@ +using Kingmaker.Blueprints.Items.Ecnchantments; +using Kingmaker.Designers.Mechanics.Facts; +using Kingmaker.Enums; +using Kingmaker.RuleSystem; +using Kingmaker.RuleSystem.Rules.Damage; + +namespace CraftMagicItems.Patches.Harmony +{ + [HarmonyLib.HarmonyPatch(typeof(WeaponDamageAgainstAlignment), "OnEventAboutToTrigger")] + // ReSharper disable once UnusedMember.Local + public static class WeaponDamageAgainstAlignmentOnEventAboutToTriggerPatch + { + // ReSharper disable once UnusedMember.Local + private static bool Prefix(WeaponDamageAgainstAlignment __instance, RulePrepareDamage evt) + { + if (__instance is ItemEnchantmentLogic logic) + { + if (evt.DamageBundle.WeaponDamage == null) + { + return false; + } + evt.DamageBundle.WeaponDamage.AddAlignment(__instance.WeaponAlignment); + + if (evt.Target.Descriptor.Alignment.Value.HasComponent(__instance.EnemyAlignment) + && Main.EquipmentEnchantmentValid(evt.DamageBundle.Weapon, logic.Owner)) + { + int rollsCount = __instance.Value.DiceCountValue.Calculate(logic.Context); + int bonusDamage = __instance.Value.BonusValue.Calculate(logic.Context); + EnergyDamage energyDamage = new EnergyDamage(new DiceFormula(rollsCount, __instance.Value.DiceType), __instance.DamageType); + energyDamage.AddBonusTargetRelated(bonusDamage); + evt.DamageBundle.Add(energyDamage); + } + return false; + } + else + { + return true; + } + } + } +} \ No newline at end of file From 6ba7d3d3f6ac99d209172745a5a041bae4933bc2 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 14 Dec 2020 13:22:38 -0600 Subject: [PATCH 100/132] Extracting the Harmony patches from Main into their own files: WeaponConditionalEnhancementBonusOnEventAboutToTriggerRuleCalculateWeaponStatsPatch --- CraftMagicItems/CraftMagicItems.csproj | 1 + CraftMagicItems/Main.cs | 35 ----------- ...tToTriggerRuleCalculateWeaponStatsPatch.cs | 60 +++++++++++++++++++ 3 files changed, 61 insertions(+), 35 deletions(-) create mode 100644 CraftMagicItems/Patches/Harmony/WeaponConditionalEnhancementBonusOnEventAboutToTriggerRuleCalculateWeaponStatsPatch.cs diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index ecef421..b9ad69c 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -129,6 +129,7 @@ + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index a19acd6..b886dcd 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -3342,41 +3342,6 @@ public static bool EquipmentEnchantmentValid(ItemEntityWeapon weapon, ItemEntity } } - [HarmonyLib.HarmonyPatch(typeof(WeaponConditionalEnhancementBonus), "OnEventAboutToTrigger", new Type[] { typeof(RuleCalculateWeaponStats) })] - // ReSharper disable once UnusedMember.Local - private static class WeaponConditionalEnhancementBonusOnEventAboutToTriggerRuleCalculateWeaponStatsPatch { - // ReSharper disable once UnusedMember.Local - private static bool Prefix(WeaponConditionalEnhancementBonus __instance, RuleCalculateWeaponStats evt) { - if (__instance is ItemEnchantmentLogic logic) { - if (__instance.IsBane) { - if (logic.Owner.Enchantments.Any((ItemEnchantment e) => e.Get())) { - return false; - } - } - if (__instance.CheckWielder) { - using (logic.Enchantment.Context.GetDataScope(evt.Initiator)) { - if (EquipmentEnchantmentValid(evt.Weapon, logic.Owner) && __instance.Conditions.Check(null)) { - evt.AddBonusDamage(__instance.EnhancementBonus); - evt.Enhancement += __instance.EnhancementBonus; - evt.EnhancementTotal += __instance.EnhancementBonus; - } - } - } else if (evt.AttackWithWeapon != null) { - using (logic.Enchantment.Context.GetDataScope(evt.AttackWithWeapon.Target)) { - if (EquipmentEnchantmentValid(evt.Weapon, logic.Owner) && __instance.Conditions.Check(null)) { - evt.AddBonusDamage(__instance.EnhancementBonus); - evt.Enhancement += __instance.EnhancementBonus; - evt.EnhancementTotal += __instance.EnhancementBonus; - } - } - } - return false; - } else { - return true; - } - } - } - [HarmonyLib.HarmonyPatch(typeof(WeaponConditionalEnhancementBonus), "OnEventAboutToTrigger", new Type[] { typeof(RuleCalculateAttackBonus) })] // ReSharper disable once UnusedMember.Local private static class WeaponConditionalEnhancementBonusOnEventAboutToTriggerRuleCalculateAttackBonusPatch { diff --git a/CraftMagicItems/Patches/Harmony/WeaponConditionalEnhancementBonusOnEventAboutToTriggerRuleCalculateWeaponStatsPatch.cs b/CraftMagicItems/Patches/Harmony/WeaponConditionalEnhancementBonusOnEventAboutToTriggerRuleCalculateWeaponStatsPatch.cs new file mode 100644 index 0000000..033f705 --- /dev/null +++ b/CraftMagicItems/Patches/Harmony/WeaponConditionalEnhancementBonusOnEventAboutToTriggerRuleCalculateWeaponStatsPatch.cs @@ -0,0 +1,60 @@ +using System; +using System.Linq; +using Kingmaker.Blueprints.Items.Ecnchantments; +using Kingmaker.Designers.Mechanics.Facts; +using Kingmaker.Designers.Mechanics.WeaponEnchants; +using Kingmaker.RuleSystem.Rules; +using Kingmaker.Utility; + +namespace CraftMagicItems.Patches.Harmony +{ + [HarmonyLib.HarmonyPatch(typeof(WeaponConditionalEnhancementBonus), "OnEventAboutToTrigger", new Type[] { typeof(RuleCalculateWeaponStats) })] + // ReSharper disable once UnusedMember.Local + public static class WeaponConditionalEnhancementBonusOnEventAboutToTriggerRuleCalculateWeaponStatsPatch + { + // ReSharper disable once UnusedMember.Local + private static bool Prefix(WeaponConditionalEnhancementBonus __instance, RuleCalculateWeaponStats evt) + { + if (__instance is ItemEnchantmentLogic logic) + { + if (__instance.IsBane) + { + if (logic.Owner.Enchantments.Any((ItemEnchantment e) => e.Get())) + { + return false; + } + } + + if (__instance.CheckWielder) + { + using (logic.Enchantment.Context.GetDataScope(evt.Initiator)) + { + if (Main.EquipmentEnchantmentValid(evt.Weapon, logic.Owner) && __instance.Conditions.Check(null)) + { + evt.AddBonusDamage(__instance.EnhancementBonus); + evt.Enhancement += __instance.EnhancementBonus; + evt.EnhancementTotal += __instance.EnhancementBonus; + } + } + } + else if (evt.AttackWithWeapon != null) + { + using (logic.Enchantment.Context.GetDataScope(evt.AttackWithWeapon.Target)) + { + if (Main.EquipmentEnchantmentValid(evt.Weapon, logic.Owner) && __instance.Conditions.Check(null)) + { + evt.AddBonusDamage(__instance.EnhancementBonus); + evt.Enhancement += __instance.EnhancementBonus; + evt.EnhancementTotal += __instance.EnhancementBonus; + } + } + } + return false; + } + else + { + return true; + } + } + } +} \ No newline at end of file From af41e73c2a8450fb1980e8875c0aeb5f744d9323 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 14 Dec 2020 13:24:29 -0600 Subject: [PATCH 101/132] Extracting the Harmony patches from Main into their own files: WeaponConditionalEnhancementBonusOnEventAboutToTriggerRuleCalculateAttackBonusPatch --- CraftMagicItems/CraftMagicItems.csproj | 1 + CraftMagicItems/Main.cs | 31 ----------- ...tToTriggerRuleCalculateAttackBonusPatch.cs | 55 +++++++++++++++++++ 3 files changed, 56 insertions(+), 31 deletions(-) create mode 100644 CraftMagicItems/Patches/Harmony/WeaponConditionalEnhancementBonusOnEventAboutToTriggerRuleCalculateAttackBonusPatch.cs diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index b9ad69c..9253907 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -129,6 +129,7 @@ + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index b886dcd..1c22e72 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -3342,37 +3342,6 @@ public static bool EquipmentEnchantmentValid(ItemEntityWeapon weapon, ItemEntity } } - [HarmonyLib.HarmonyPatch(typeof(WeaponConditionalEnhancementBonus), "OnEventAboutToTrigger", new Type[] { typeof(RuleCalculateAttackBonus) })] - // ReSharper disable once UnusedMember.Local - private static class WeaponConditionalEnhancementBonusOnEventAboutToTriggerRuleCalculateAttackBonusPatch { - // ReSharper disable once UnusedMember.Local - private static bool Prefix(WeaponConditionalEnhancementBonus __instance, RuleCalculateAttackBonus evt) { - if (__instance is ItemEnchantmentLogic logic) { - if (__instance.IsBane) { - if (logic.Owner.Enchantments.Any((ItemEnchantment e) => e.Get())) { - return false; - } - } - if (__instance.CheckWielder) { - using (logic.Enchantment.Context.GetDataScope(evt.Initiator)) { - if (EquipmentEnchantmentValid(evt.Weapon, logic.Owner) && __instance.Conditions.Check(null)) { - evt.AddBonus(__instance.EnhancementBonus, logic.Fact); - } - } - } else { - using (logic.Enchantment.Context.GetDataScope(evt.Target)) { - if (EquipmentEnchantmentValid(evt.Weapon, logic.Owner) && __instance.Conditions.Check(null)) { - evt.AddBonus(__instance.EnhancementBonus, logic.Fact); - } - } - } - return false; - } else { - return true; - } - } - } - [HarmonyLib.HarmonyPatch(typeof(WeaponConditionalDamageDice), "OnEventAboutToTrigger")] // ReSharper disable once UnusedMember.Local private static class WeaponConditionalDamageDiceOnEventAboutToTriggerPatch { diff --git a/CraftMagicItems/Patches/Harmony/WeaponConditionalEnhancementBonusOnEventAboutToTriggerRuleCalculateAttackBonusPatch.cs b/CraftMagicItems/Patches/Harmony/WeaponConditionalEnhancementBonusOnEventAboutToTriggerRuleCalculateAttackBonusPatch.cs new file mode 100644 index 0000000..06b9289 --- /dev/null +++ b/CraftMagicItems/Patches/Harmony/WeaponConditionalEnhancementBonusOnEventAboutToTriggerRuleCalculateAttackBonusPatch.cs @@ -0,0 +1,55 @@ +using System; +using System.Linq; +using Kingmaker.Blueprints.Items.Ecnchantments; +using Kingmaker.Designers.Mechanics.Facts; +using Kingmaker.Designers.Mechanics.WeaponEnchants; +using Kingmaker.RuleSystem.Rules; +using Kingmaker.Utility; + +namespace CraftMagicItems.Patches.Harmony +{ + [HarmonyLib.HarmonyPatch(typeof(WeaponConditionalEnhancementBonus), "OnEventAboutToTrigger", new Type[] { typeof(RuleCalculateAttackBonus) })] + // ReSharper disable once UnusedMember.Local + public static class WeaponConditionalEnhancementBonusOnEventAboutToTriggerRuleCalculateAttackBonusPatch + { + // ReSharper disable once UnusedMember.Local + private static bool Prefix(WeaponConditionalEnhancementBonus __instance, RuleCalculateAttackBonus evt) + { + if (__instance is ItemEnchantmentLogic logic) + { + if (__instance.IsBane) + { + if (logic.Owner.Enchantments.Any((ItemEnchantment e) => e.Get())) + { + return false; + } + } + if (__instance.CheckWielder) + { + using (logic.Enchantment.Context.GetDataScope(evt.Initiator)) + { + if (Main.EquipmentEnchantmentValid(evt.Weapon, logic.Owner) && __instance.Conditions.Check(null)) + { + evt.AddBonus(__instance.EnhancementBonus, logic.Fact); + } + } + } + else + { + using (logic.Enchantment.Context.GetDataScope(evt.Target)) + { + if (Main.EquipmentEnchantmentValid(evt.Weapon, logic.Owner) && __instance.Conditions.Check(null)) + { + evt.AddBonus(__instance.EnhancementBonus, logic.Fact); + } + } + } + return false; + } + else + { + return true; + } + } + } +} \ No newline at end of file From a61648cec125e3f1460b1d5776451190776cbbb2 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 14 Dec 2020 13:25:55 -0600 Subject: [PATCH 102/132] Extracting the Harmony patches from Main into their own files: WeaponConditionalDamageDiceOnEventAboutToTriggerPatch --- CraftMagicItems/CraftMagicItems.csproj | 1 + CraftMagicItems/Main.cs | 36 ----------- ...nalDamageDiceOnEventAboutToTriggerPatch.cs | 62 +++++++++++++++++++ 3 files changed, 63 insertions(+), 36 deletions(-) create mode 100644 CraftMagicItems/Patches/Harmony/WeaponConditionalDamageDiceOnEventAboutToTriggerPatch.cs diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index 9253907..5de00eb 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -129,6 +129,7 @@ + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 1c22e72..affa450 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -3342,42 +3342,6 @@ public static bool EquipmentEnchantmentValid(ItemEntityWeapon weapon, ItemEntity } } - [HarmonyLib.HarmonyPatch(typeof(WeaponConditionalDamageDice), "OnEventAboutToTrigger")] - // ReSharper disable once UnusedMember.Local - private static class WeaponConditionalDamageDiceOnEventAboutToTriggerPatch { - // ReSharper disable once UnusedMember.Local - private static bool Prefix(WeaponConditionalDamageDice __instance, RulePrepareDamage evt) { - if (__instance is ItemEnchantmentLogic logic) { - if (evt.DamageBundle.WeaponDamage == null) { - return false; - } - if (__instance.IsBane) { - if (logic.Owner.Enchantments.Any((ItemEnchantment e) => e.Get())) { - return false; - } - } - if (__instance.CheckWielder) { - using (logic.Enchantment.Context.GetDataScope(logic.Owner.Wielder.Unit)) { - if (EquipmentEnchantmentValid(evt.DamageBundle.Weapon, logic.Owner) && __instance.Conditions.Check(null)) { - BaseDamage damage = __instance.Damage.CreateDamage(); - evt.DamageBundle.Add(damage); - } - } - } else { - using (logic.Enchantment.Context.GetDataScope(evt.Target)) { - if (EquipmentEnchantmentValid(evt.DamageBundle.Weapon, logic.Owner) && __instance.Conditions.Check(null)) { - BaseDamage damage2 = __instance.Damage.CreateDamage(); - evt.DamageBundle.Add(damage2); - } - } - } - return false; - } else { - return true; - } - } - } - [HarmonyLib.HarmonyPatch(typeof(BrilliantEnergy), "OnEventAboutToTrigger")] // ReSharper disable once UnusedMember.Local private static class BrilliantEnergyOnEventAboutToTriggerPatch { diff --git a/CraftMagicItems/Patches/Harmony/WeaponConditionalDamageDiceOnEventAboutToTriggerPatch.cs b/CraftMagicItems/Patches/Harmony/WeaponConditionalDamageDiceOnEventAboutToTriggerPatch.cs new file mode 100644 index 0000000..5f0c2f3 --- /dev/null +++ b/CraftMagicItems/Patches/Harmony/WeaponConditionalDamageDiceOnEventAboutToTriggerPatch.cs @@ -0,0 +1,62 @@ +using System.Linq; +using Kingmaker.Blueprints.Items.Ecnchantments; +using Kingmaker.Designers.Mechanics.Facts; +using Kingmaker.Designers.Mechanics.WeaponEnchants; +using Kingmaker.RuleSystem.Rules.Damage; +using Kingmaker.Utility; + +namespace CraftMagicItems.Patches.Harmony +{ + [HarmonyLib.HarmonyPatch(typeof(WeaponConditionalDamageDice), "OnEventAboutToTrigger")] + // ReSharper disable once UnusedMember.Local + public static class WeaponConditionalDamageDiceOnEventAboutToTriggerPatch + { + // ReSharper disable once UnusedMember.Local + private static bool Prefix(WeaponConditionalDamageDice __instance, RulePrepareDamage evt) + { + if (__instance is ItemEnchantmentLogic logic) + { + if (evt.DamageBundle.WeaponDamage == null) + { + return false; + } + + if (__instance.IsBane) + { + if (logic.Owner.Enchantments.Any((ItemEnchantment e) => e.Get())) + { + return false; + } + } + + if (__instance.CheckWielder) + { + using (logic.Enchantment.Context.GetDataScope(logic.Owner.Wielder.Unit)) + { + if (Main.EquipmentEnchantmentValid(evt.DamageBundle.Weapon, logic.Owner) && __instance.Conditions.Check(null)) + { + BaseDamage damage = __instance.Damage.CreateDamage(); + evt.DamageBundle.Add(damage); + } + } + } + else + { + using (logic.Enchantment.Context.GetDataScope(evt.Target)) + { + if (Main.EquipmentEnchantmentValid(evt.DamageBundle.Weapon, logic.Owner) && __instance.Conditions.Check(null)) + { + BaseDamage damage2 = __instance.Damage.CreateDamage(); + evt.DamageBundle.Add(damage2); + } + } + } + return false; + } + else + { + return true; + } + } + } +} \ No newline at end of file From 755ee4c2abff3fdbc3df205843b7cb2aa5409406 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 14 Dec 2020 13:27:10 -0600 Subject: [PATCH 103/132] Extracting the Harmony patches from Main into their own files: BrilliantEnergyOnEventAboutToTriggerPatch --- CraftMagicItems/CraftMagicItems.csproj | 1 + CraftMagicItems/Main.cs | 16 ---------- ...illiantEnergyOnEventAboutToTriggerPatch.cs | 29 +++++++++++++++++++ 3 files changed, 30 insertions(+), 16 deletions(-) create mode 100644 CraftMagicItems/Patches/Harmony/BrilliantEnergyOnEventAboutToTriggerPatch.cs diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index 5de00eb..34d8fc5 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -112,6 +112,7 @@ + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index affa450..d6e3d1a 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -3342,22 +3342,6 @@ public static bool EquipmentEnchantmentValid(ItemEntityWeapon weapon, ItemEntity } } - [HarmonyLib.HarmonyPatch(typeof(BrilliantEnergy), "OnEventAboutToTrigger")] - // ReSharper disable once UnusedMember.Local - private static class BrilliantEnergyOnEventAboutToTriggerPatch { - // ReSharper disable once UnusedMember.Local - private static bool Prefix(BrilliantEnergy __instance, RuleCalculateAC evt) { - if (__instance is ItemEnchantmentLogic logic) { - if (evt.Reason.Item is ItemEntityWeapon weapon && EquipmentEnchantmentValid(weapon, logic.Owner)) { - evt.BrilliantEnergy = logic.Fact; - } - return false; - } else { - return true; - } - } - } - [HarmonyLib.HarmonyPatch(typeof(MissAgainstFactOwner), "OnEventAboutToTrigger")] // ReSharper disable once UnusedMember.Local private static class MissAgainstFactOwnerOnEventAboutToTriggerPatch { diff --git a/CraftMagicItems/Patches/Harmony/BrilliantEnergyOnEventAboutToTriggerPatch.cs b/CraftMagicItems/Patches/Harmony/BrilliantEnergyOnEventAboutToTriggerPatch.cs new file mode 100644 index 0000000..f79bd99 --- /dev/null +++ b/CraftMagicItems/Patches/Harmony/BrilliantEnergyOnEventAboutToTriggerPatch.cs @@ -0,0 +1,29 @@ +using Kingmaker.Blueprints.Items.Ecnchantments; +using Kingmaker.Designers.Mechanics.Facts; +using Kingmaker.Items; +using Kingmaker.RuleSystem.Rules; + +namespace CraftMagicItems.Patches.Harmony +{ + [HarmonyLib.HarmonyPatch(typeof(BrilliantEnergy), "OnEventAboutToTrigger")] + // ReSharper disable once UnusedMember.Local + public static class BrilliantEnergyOnEventAboutToTriggerPatch + { + // ReSharper disable once UnusedMember.Local + private static bool Prefix(BrilliantEnergy __instance, RuleCalculateAC evt) + { + if (__instance is ItemEnchantmentLogic logic) + { + if (evt.Reason.Item is ItemEntityWeapon weapon && Main.EquipmentEnchantmentValid(weapon, logic.Owner)) + { + evt.BrilliantEnergy = logic.Fact; + } + return false; + } + else + { + return true; + } + } + } +} \ No newline at end of file From 1241e49809c02aa06545f039a2ba021eb0a815b5 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 14 Dec 2020 13:28:29 -0600 Subject: [PATCH 104/132] Extracting the Harmony patches from Main into their own files: MissAgainstFactOwnerOnEventAboutToTriggerPatch --- CraftMagicItems/CraftMagicItems.csproj | 1 + CraftMagicItems/Main.cs | 21 ---------- ...instFactOwnerOnEventAboutToTriggerPatch.cs | 38 +++++++++++++++++++ 3 files changed, 39 insertions(+), 21 deletions(-) create mode 100644 CraftMagicItems/Patches/Harmony/MissAgainstFactOwnerOnEventAboutToTriggerPatch.cs diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index 34d8fc5..8c3c04e 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -120,6 +120,7 @@ + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index d6e3d1a..8de964e 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -3342,27 +3342,6 @@ public static bool EquipmentEnchantmentValid(ItemEntityWeapon weapon, ItemEntity } } - [HarmonyLib.HarmonyPatch(typeof(MissAgainstFactOwner), "OnEventAboutToTrigger")] - // ReSharper disable once UnusedMember.Local - private static class MissAgainstFactOwnerOnEventAboutToTriggerPatch { - // ReSharper disable once UnusedMember.Local - private static bool Prefix(MissAgainstFactOwner __instance, RuleAttackRoll evt) { - if (__instance is ItemEnchantmentLogic logic) { - if (EquipmentEnchantmentValid(evt.Weapon, logic.Owner)) { - foreach (BlueprintUnitFact blueprint in __instance.Facts) { - if (evt.Target.Descriptor.HasFact(blueprint)) { - evt.AutoMiss = true; - return false; - } - } - } - return false; - } else { - return true; - } - } - } - [HarmonyLib.HarmonyPatch(typeof(WeaponReality), "OnEventAboutToTrigger")] // ReSharper disable once UnusedMember.Local private static class WeaponRealityOnEventAboutToTriggerPatch { diff --git a/CraftMagicItems/Patches/Harmony/MissAgainstFactOwnerOnEventAboutToTriggerPatch.cs b/CraftMagicItems/Patches/Harmony/MissAgainstFactOwnerOnEventAboutToTriggerPatch.cs new file mode 100644 index 0000000..f17e487 --- /dev/null +++ b/CraftMagicItems/Patches/Harmony/MissAgainstFactOwnerOnEventAboutToTriggerPatch.cs @@ -0,0 +1,38 @@ +using Kingmaker.Blueprints.Facts; +using Kingmaker.Blueprints.Items.Ecnchantments; +using Kingmaker.Designers.Mechanics.Facts; +using Kingmaker.RuleSystem.Rules; +using Kingmaker.UnitLogic; +using Kingmaker.Utility; + +namespace CraftMagicItems.Patches.Harmony +{ + [HarmonyLib.HarmonyPatch(typeof(MissAgainstFactOwner), "OnEventAboutToTrigger")] + // ReSharper disable once UnusedMember.Local + public static class MissAgainstFactOwnerOnEventAboutToTriggerPatch + { + // ReSharper disable once UnusedMember.Local + private static bool Prefix(MissAgainstFactOwner __instance, RuleAttackRoll evt) + { + if (__instance is ItemEnchantmentLogic logic) + { + if (Main.EquipmentEnchantmentValid(evt.Weapon, logic.Owner)) + { + foreach (BlueprintUnitFact blueprint in __instance.Facts) + { + if (evt.Target.Descriptor.HasFact(blueprint)) + { + evt.AutoMiss = true; + return false; + } + } + } + return false; + } + else + { + return true; + } + } + } +} \ No newline at end of file From aba81a5b54213bc8e3c72417e84a75bfa132b4b4 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 14 Dec 2020 13:29:53 -0600 Subject: [PATCH 105/132] Extracting the Harmony patches from Main into their own files: WeaponRealityOnEventAboutToTriggerPatch --- CraftMagicItems/CraftMagicItems.csproj | 1 + CraftMagicItems/Main.cs | 19 ----------- ...WeaponRealityOnEventAboutToTriggerPatch.cs | 32 +++++++++++++++++++ 3 files changed, 33 insertions(+), 19 deletions(-) create mode 100644 CraftMagicItems/Patches/Harmony/WeaponRealityOnEventAboutToTriggerPatch.cs diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index 8c3c04e..eee86e0 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -140,6 +140,7 @@ + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 8de964e..1fe7210 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -3342,25 +3342,6 @@ public static bool EquipmentEnchantmentValid(ItemEntityWeapon weapon, ItemEntity } } - [HarmonyLib.HarmonyPatch(typeof(WeaponReality), "OnEventAboutToTrigger")] - // ReSharper disable once UnusedMember.Local - private static class WeaponRealityOnEventAboutToTriggerPatch { - // ReSharper disable once UnusedMember.Local - private static bool Prefix(WeaponReality __instance, RulePrepareDamage evt) { - if (__instance is ItemEnchantmentLogic logic) { - if (evt.DamageBundle.WeaponDamage == null) { - return false; - } - if (EquipmentEnchantmentValid(evt.DamageBundle.Weapon, logic.Owner)) { - evt.DamageBundle.WeaponDamage.Reality |= __instance.Reality; - } - return false; - } else { - return true; - } - } - } - [HarmonyLib.HarmonyPatch(typeof(AddInitiatorAttackRollTrigger), "CheckConditions")] // ReSharper disable once UnusedMember.Local private static class AddInitiatorAttackRollTriggerCheckConditionsPatch { diff --git a/CraftMagicItems/Patches/Harmony/WeaponRealityOnEventAboutToTriggerPatch.cs b/CraftMagicItems/Patches/Harmony/WeaponRealityOnEventAboutToTriggerPatch.cs new file mode 100644 index 0000000..1321b33 --- /dev/null +++ b/CraftMagicItems/Patches/Harmony/WeaponRealityOnEventAboutToTriggerPatch.cs @@ -0,0 +1,32 @@ +using Kingmaker.Blueprints.Items.Ecnchantments; +using Kingmaker.Designers.Mechanics.Facts; +using Kingmaker.RuleSystem.Rules.Damage; + +namespace CraftMagicItems.Patches.Harmony +{ + [HarmonyLib.HarmonyPatch(typeof(WeaponReality), "OnEventAboutToTrigger")] + // ReSharper disable once UnusedMember.Local + public static class WeaponRealityOnEventAboutToTriggerPatch + { + // ReSharper disable once UnusedMember.Local + private static bool Prefix(WeaponReality __instance, RulePrepareDamage evt) + { + if (__instance is ItemEnchantmentLogic logic) + { + if (evt.DamageBundle.WeaponDamage == null) + { + return false; + } + if (Main.EquipmentEnchantmentValid(evt.DamageBundle.Weapon, logic.Owner)) + { + evt.DamageBundle.WeaponDamage.Reality |= __instance.Reality; + } + return false; + } + else + { + return true; + } + } + } +} \ No newline at end of file From e35a0e01c21cb78604936adc5214838ece03d319 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 14 Dec 2020 13:32:30 -0600 Subject: [PATCH 106/132] Extracting the Harmony patches from Main into their own files: AddInitiatorAttackRollTriggerCheckConditionsPatch --- CraftMagicItems/CraftMagicItems.csproj | 1 + CraftMagicItems/Main.cs | 22 ------------ ...orAttackRollTriggerCheckConditionsPatch.cs | 36 +++++++++++++++++++ 3 files changed, 37 insertions(+), 22 deletions(-) create mode 100644 CraftMagicItems/Patches/Harmony/AddInitiatorAttackRollTriggerCheckConditionsPatch.cs diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index eee86e0..77d2d48 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -108,6 +108,7 @@ + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 1fe7210..e659542 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -3342,28 +3342,6 @@ public static bool EquipmentEnchantmentValid(ItemEntityWeapon weapon, ItemEntity } } - [HarmonyLib.HarmonyPatch(typeof(AddInitiatorAttackRollTrigger), "CheckConditions")] - // ReSharper disable once UnusedMember.Local - private static class AddInitiatorAttackRollTriggerCheckConditionsPatch { - // ReSharper disable once UnusedMember.Local - private static bool Prefix(AddInitiatorAttackRollTrigger __instance, RuleAttackRoll evt, ref bool __result) { - if (__instance is GameLogicComponent logic) { - ItemEnchantment itemEnchantment = logic.Fact as ItemEnchantment; - ItemEntity itemEntity = (itemEnchantment != null) ? itemEnchantment.Owner : null; - RuleAttackWithWeapon ruleAttackWithWeapon = evt.Reason.Rule as RuleAttackWithWeapon; - ItemEntityWeapon itemEntityWeapon = (ruleAttackWithWeapon != null) ? ruleAttackWithWeapon.Weapon : null; - __result = (itemEntity == null || itemEntity == itemEntityWeapon || evt.Weapon.Blueprint.IsNatural || evt.Weapon.Blueprint.IsUnarmed) && - (!__instance.CheckWeapon || (itemEntityWeapon != null && __instance.WeaponCategory == itemEntityWeapon.Blueprint.Category)) && - (!__instance.OnlyHit || evt.IsHit) && (!__instance.CriticalHit || (evt.IsCriticalConfirmed && !evt.FortificationNegatesCriticalHit)) && - (!__instance.SneakAttack || (evt.IsSneakAttack && !evt.FortificationNegatesSneakAttack)) && - (__instance.AffectFriendlyTouchSpells || evt.Initiator.IsEnemy(evt.Target) || evt.Weapon.Blueprint.Type.AttackType != AttackType.Touch); - return false; - } else { - return true; - } - } - } - #if !PATCH21 [HarmonyLib.HarmonyPatch(typeof(RuleCalculateAttacksCount), "OnTrigger")] private static class RuleCalculateAttacksCountOnTriggerPatch { diff --git a/CraftMagicItems/Patches/Harmony/AddInitiatorAttackRollTriggerCheckConditionsPatch.cs b/CraftMagicItems/Patches/Harmony/AddInitiatorAttackRollTriggerCheckConditionsPatch.cs new file mode 100644 index 0000000..ed25651 --- /dev/null +++ b/CraftMagicItems/Patches/Harmony/AddInitiatorAttackRollTriggerCheckConditionsPatch.cs @@ -0,0 +1,36 @@ +using Kingmaker.Blueprints; +using Kingmaker.Blueprints.Items.Ecnchantments; +using Kingmaker.Items; +using Kingmaker.RuleSystem; +using Kingmaker.RuleSystem.Rules; +using Kingmaker.UnitLogic.Mechanics.Components; + +namespace CraftMagicItems.Patches.Harmony +{ + [HarmonyLib.HarmonyPatch(typeof(AddInitiatorAttackRollTrigger), "CheckConditions")] + // ReSharper disable once UnusedMember.Local + public static class AddInitiatorAttackRollTriggerCheckConditionsPatch + { + // ReSharper disable once UnusedMember.Local + private static bool Prefix(AddInitiatorAttackRollTrigger __instance, RuleAttackRoll evt, ref bool __result) + { + if (__instance is GameLogicComponent logic) + { + ItemEnchantment itemEnchantment = logic.Fact as ItemEnchantment; + ItemEntity itemEntity = (itemEnchantment != null) ? itemEnchantment.Owner : null; + RuleAttackWithWeapon ruleAttackWithWeapon = evt.Reason.Rule as RuleAttackWithWeapon; + ItemEntityWeapon itemEntityWeapon = (ruleAttackWithWeapon != null) ? ruleAttackWithWeapon.Weapon : null; + __result = (itemEntity == null || itemEntity == itemEntityWeapon || evt.Weapon.Blueprint.IsNatural || evt.Weapon.Blueprint.IsUnarmed) && + (!__instance.CheckWeapon || (itemEntityWeapon != null && __instance.WeaponCategory == itemEntityWeapon.Blueprint.Category)) && + (!__instance.OnlyHit || evt.IsHit) && (!__instance.CriticalHit || (evt.IsCriticalConfirmed && !evt.FortificationNegatesCriticalHit)) && + (!__instance.SneakAttack || (evt.IsSneakAttack && !evt.FortificationNegatesSneakAttack)) && + (__instance.AffectFriendlyTouchSpells || evt.Initiator.IsEnemy(evt.Target) || evt.Weapon.Blueprint.Type.AttackType != AttackType.Touch); + return false; + } + else + { + return true; + } + } + } +} \ No newline at end of file From ad74cec85734050b11ee76923587439059c0fc2b Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 14 Dec 2020 13:38:06 -0600 Subject: [PATCH 107/132] Extracting the Harmony patches from Main into their own files: RuleCalculateAttacksCountOnTriggerPatch --- CraftMagicItems/CraftMagicItems.csproj | 1 + CraftMagicItems/Main.cs | 27 ---------- ...RuleCalculateAttacksCountOnTriggerPatch.cs | 49 +++++++++++++++++++ 3 files changed, 50 insertions(+), 27 deletions(-) create mode 100644 CraftMagicItems/Patches/Harmony/RuleCalculateAttacksCountOnTriggerPatch.cs diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index 77d2d48..89529b7 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -124,6 +124,7 @@ + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index e659542..d92fe5d 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -3343,33 +3343,6 @@ public static bool EquipmentEnchantmentValid(ItemEntityWeapon weapon, ItemEntity } #if !PATCH21 - [HarmonyLib.HarmonyPatch(typeof(RuleCalculateAttacksCount), "OnTrigger")] - private static class RuleCalculateAttacksCountOnTriggerPatch { - private static void Postfix(RuleCalculateAttacksCount __instance) { - int num = __instance.Initiator.Stats.BaseAttackBonus; - int val = Math.Min(Math.Max(0, num / 5 - ((num % 5 != 0) ? 0 : 1)), 3); - HandSlot primaryHand = __instance.Initiator.Body.PrimaryHand; - HandSlot secondaryHand = __instance.Initiator.Body.SecondaryHand; - ItemEntityWeapon maybeWeapon = primaryHand.MaybeWeapon; - BlueprintItemWeapon blueprintItemWeapon = (maybeWeapon != null) ? maybeWeapon.Blueprint : null; - BlueprintItemWeapon blueprintItemWeapon2; - if (secondaryHand.MaybeShield != null) { - if (__instance.Initiator.Descriptor.State.Features.ShieldBash) { - ItemEntityWeapon weaponComponent = secondaryHand.MaybeShield.WeaponComponent; - blueprintItemWeapon2 = ((weaponComponent != null) ? weaponComponent.Blueprint : null); - } else { - blueprintItemWeapon2 = null; - } - } else { - ItemEntityWeapon maybeWeapon2 = secondaryHand.MaybeWeapon; - blueprintItemWeapon2 = ((maybeWeapon2 != null) ? maybeWeapon2.Blueprint : null); - } - if ((primaryHand.MaybeWeapon == null || !primaryHand.MaybeWeapon.HoldInTwoHands) && (blueprintItemWeapon == null || blueprintItemWeapon.IsUnarmed) && blueprintItemWeapon2 && !blueprintItemWeapon2.IsUnarmed) { - __instance.SecondaryHand.PenalizedAttacks += Math.Max(0, val); - } - } - } - [HarmonyLib.HarmonyPatch(typeof(ItemEntityWeapon), "HoldInTwoHands", MethodType.Getter)] private static class ItemEntityWeaponHoldInTwoHandsPatch { private static void Postfix(ItemEntityWeapon __instance, ref bool __result) { diff --git a/CraftMagicItems/Patches/Harmony/RuleCalculateAttacksCountOnTriggerPatch.cs b/CraftMagicItems/Patches/Harmony/RuleCalculateAttacksCountOnTriggerPatch.cs new file mode 100644 index 0000000..2953e77 --- /dev/null +++ b/CraftMagicItems/Patches/Harmony/RuleCalculateAttacksCountOnTriggerPatch.cs @@ -0,0 +1,49 @@ +#if !PATCH21 +using System; +using Kingmaker.Blueprints.Items.Weapons; +using Kingmaker.Items; +using Kingmaker.Items.Slots; +using Kingmaker.RuleSystem.Rules; +#endif + +namespace CraftMagicItems.Patches.Harmony +{ +#if !PATCH21 + [HarmonyLib.HarmonyPatch(typeof(RuleCalculateAttacksCount), "OnTrigger")] + public static class RuleCalculateAttacksCountOnTriggerPatch + { + private static void Postfix(RuleCalculateAttacksCount __instance) + { + int num = __instance.Initiator.Stats.BaseAttackBonus; + int val = Math.Min(Math.Max(0, num / 5 - ((num % 5 != 0) ? 0 : 1)), 3); + HandSlot primaryHand = __instance.Initiator.Body.PrimaryHand; + HandSlot secondaryHand = __instance.Initiator.Body.SecondaryHand; + ItemEntityWeapon maybeWeapon = primaryHand.MaybeWeapon; + BlueprintItemWeapon blueprintItemWeapon = (maybeWeapon != null) ? maybeWeapon.Blueprint : null; + BlueprintItemWeapon blueprintItemWeapon2; + if (secondaryHand.MaybeShield != null) + { + if (__instance.Initiator.Descriptor.State.Features.ShieldBash) + { + ItemEntityWeapon weaponComponent = secondaryHand.MaybeShield.WeaponComponent; + blueprintItemWeapon2 = ((weaponComponent != null) ? weaponComponent.Blueprint : null); + } + else + { + blueprintItemWeapon2 = null; + } + } + else + { + ItemEntityWeapon maybeWeapon2 = secondaryHand.MaybeWeapon; + blueprintItemWeapon2 = ((maybeWeapon2 != null) ? maybeWeapon2.Blueprint : null); + } + + if ((primaryHand.MaybeWeapon == null || !primaryHand.MaybeWeapon.HoldInTwoHands) && (blueprintItemWeapon == null || blueprintItemWeapon.IsUnarmed) && blueprintItemWeapon2 && !blueprintItemWeapon2.IsUnarmed) + { + __instance.SecondaryHand.PenalizedAttacks += Math.Max(0, val); + } + } + } +#endif +} \ No newline at end of file From 3204d077bdf372d469863988f8f96089a101327d Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 14 Dec 2020 13:44:43 -0600 Subject: [PATCH 108/132] Extracting the Harmony patches from Main into their own files: ItemEntityWeaponHoldInTwoHandsPatch --- CraftMagicItems/CraftMagicItems.csproj | 1 + CraftMagicItems/Main.cs | 14 ------------ .../ItemEntityWeaponHoldInTwoHandsPatch.cs | 22 +++++++++++++++++++ 3 files changed, 23 insertions(+), 14 deletions(-) create mode 100644 CraftMagicItems/Patches/Harmony/ItemEntityWeaponHoldInTwoHandsPatch.cs diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index 89529b7..a3abab6 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -118,6 +118,7 @@ + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index d92fe5d..c29491b 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -3342,20 +3342,6 @@ public static bool EquipmentEnchantmentValid(ItemEntityWeapon weapon, ItemEntity } } -#if !PATCH21 - [HarmonyLib.HarmonyPatch(typeof(ItemEntityWeapon), "HoldInTwoHands", MethodType.Getter)] - private static class ItemEntityWeaponHoldInTwoHandsPatch { - private static void Postfix(ItemEntityWeapon __instance, ref bool __result) { - if (!__result) { - if (__instance.IsShield && __instance.Blueprint.IsOneHandedWhichCanBeUsedWithTwoHands && __instance.Wielder != null) { - HandSlot handSlot = __instance.Wielder.Body.PrimaryHand; - __result = handSlot != null && !handSlot.HasItem; - } - } - } - } -#endif - [HarmonyLib.HarmonyPatch(typeof(DescriptionTemplatesItem), "ItemEnergy")] private static class DescriptionTemplatesItemItemEnergyPatch { private static void Postfix(TooltipData data, bool __result) { diff --git a/CraftMagicItems/Patches/Harmony/ItemEntityWeaponHoldInTwoHandsPatch.cs b/CraftMagicItems/Patches/Harmony/ItemEntityWeaponHoldInTwoHandsPatch.cs new file mode 100644 index 0000000..96905c6 --- /dev/null +++ b/CraftMagicItems/Patches/Harmony/ItemEntityWeaponHoldInTwoHandsPatch.cs @@ -0,0 +1,22 @@ +#if !PATCH21 +using HarmonyLib; +using Kingmaker.Items; +using Kingmaker.Items.Slots; +#endif + +namespace CraftMagicItems.Patches.Harmony +{ +#if !PATCH21 + [HarmonyLib.HarmonyPatch(typeof(ItemEntityWeapon), "HoldInTwoHands", MethodType.Getter)] + public static class ItemEntityWeaponHoldInTwoHandsPatch { + private static void Postfix(ItemEntityWeapon __instance, ref bool __result) { + if (!__result) { + if (__instance.IsShield && __instance.Blueprint.IsOneHandedWhichCanBeUsedWithTwoHands && __instance.Wielder != null) { + HandSlot handSlot = __instance.Wielder.Body.PrimaryHand; + __result = handSlot != null && !handSlot.HasItem; + } + } + } + } +#endif +} \ No newline at end of file From f5937fef7f1b1e1905fc9ddc1a502f73f094cf6a Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 14 Dec 2020 13:52:23 -0600 Subject: [PATCH 109/132] Extracting the Harmony patches from Main into their own files: DescriptionTemplatesItemItemEnergyPatch --- CraftMagicItems/CraftMagicItems.csproj | 1 + CraftMagicItems/Main.cs | 11 ----------- ...DescriptionTemplatesItemItemEnergyPatch.cs | 19 +++++++++++++++++++ 3 files changed, 20 insertions(+), 11 deletions(-) create mode 100644 CraftMagicItems/Patches/Harmony/DescriptionTemplatesItemItemEnergyPatch.cs diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index a3abab6..fbfc515 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -116,6 +116,7 @@ + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index c29491b..d618133 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -3342,17 +3342,6 @@ public static bool EquipmentEnchantmentValid(ItemEntityWeapon weapon, ItemEntity } } - [HarmonyLib.HarmonyPatch(typeof(DescriptionTemplatesItem), "ItemEnergy")] - private static class DescriptionTemplatesItemItemEnergyPatch { - private static void Postfix(TooltipData data, bool __result) { - if (__result) { - if (data.Energy.Count > 0) { - data.Energy.Clear(); - } - } - } - } - [HarmonyLib.HarmonyPatch(typeof(DescriptionTemplatesItem), "ItemEnhancement")] private static class DescriptionTemplatesItemItemEnhancementPatch { private static void Postfix(TooltipData data) { diff --git a/CraftMagicItems/Patches/Harmony/DescriptionTemplatesItemItemEnergyPatch.cs b/CraftMagicItems/Patches/Harmony/DescriptionTemplatesItemItemEnergyPatch.cs new file mode 100644 index 0000000..6f493b5 --- /dev/null +++ b/CraftMagicItems/Patches/Harmony/DescriptionTemplatesItemItemEnergyPatch.cs @@ -0,0 +1,19 @@ +using Kingmaker.UI.Tooltip; + +namespace CraftMagicItems.Patches.Harmony +{ + [HarmonyLib.HarmonyPatch(typeof(DescriptionTemplatesItem), "ItemEnergy")] + public static class DescriptionTemplatesItemItemEnergyPatch + { + private static void Postfix(TooltipData data, bool __result) + { + if (__result) + { + if (data.Energy.Count > 0) + { + data.Energy.Clear(); + } + } + } + } +} \ No newline at end of file From 1d9ead22cbae9ce1eb8ff5babfee887a6ce78459 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 14 Dec 2020 14:07:19 -0600 Subject: [PATCH 110/132] Extracting the Harmony patches from Main into their own files: DescriptionTemplatesItemItemEnhancementPatch --- CraftMagicItems/CraftMagicItems.csproj | 1 + CraftMagicItems/Main.cs | 19 ---------------- ...iptionTemplatesItemItemEnhancementPatch.cs | 22 +++++++++++++++++++ 3 files changed, 23 insertions(+), 19 deletions(-) create mode 100644 CraftMagicItems/Patches/Harmony/DescriptionTemplatesItemItemEnhancementPatch.cs diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index fbfc515..9561a7b 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -117,6 +117,7 @@ + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index d618133..e88f5a1 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -3342,25 +3342,6 @@ public static bool EquipmentEnchantmentValid(ItemEntityWeapon weapon, ItemEntity } } - [HarmonyLib.HarmonyPatch(typeof(DescriptionTemplatesItem), "ItemEnhancement")] - private static class DescriptionTemplatesItemItemEnhancementPatch { - private static void Postfix(TooltipData data) { - if (data.Texts.ContainsKey(Enum.GetValues(typeof(TooltipElement)).Cast().Max() + 1)) { - data.Texts[TooltipElement.Enhancement] = data.Texts[Enum.GetValues(typeof(TooltipElement)).Cast().Max() + 1]; - } else if (data.Texts.ContainsKey(TooltipElement.Enhancement)) { - data.Texts.Remove(TooltipElement.Enhancement); - } - } - } - - [HarmonyLib.HarmonyPatch(typeof(DescriptionTemplatesItem), "ItemEnergyResisit")] - private static class DescriptionTemplatesItemItemEnergyResisitPatch { - private static bool Prefix(ref bool __result) { - __result = false; - return false; - } - } - [HarmonyLib.HarmonyPatch(typeof(UnitViewHandSlotData), "OwnerWeaponScale", HarmonyLib.MethodType.Getter)] private static class UnitViewHandSlotDataWeaponScalePatch { private static void Postfix(UnitViewHandSlotData __instance, ref float __result) { diff --git a/CraftMagicItems/Patches/Harmony/DescriptionTemplatesItemItemEnhancementPatch.cs b/CraftMagicItems/Patches/Harmony/DescriptionTemplatesItemItemEnhancementPatch.cs new file mode 100644 index 0000000..2d93bd0 --- /dev/null +++ b/CraftMagicItems/Patches/Harmony/DescriptionTemplatesItemItemEnhancementPatch.cs @@ -0,0 +1,22 @@ +using System; +using System.Linq; +using Kingmaker.UI.Tooltip; + +namespace CraftMagicItems.Patches.Harmony +{ + [HarmonyLib.HarmonyPatch(typeof(DescriptionTemplatesItem), "ItemEnhancement")] + public static class DescriptionTemplatesItemItemEnhancementPatch + { + private static void Postfix(TooltipData data) + { + if (data.Texts.ContainsKey(Enum.GetValues(typeof(TooltipElement)).Cast().Max() + 1)) + { + data.Texts[TooltipElement.Enhancement] = data.Texts[Enum.GetValues(typeof(TooltipElement)).Cast().Max() + 1]; + } + else if (data.Texts.ContainsKey(TooltipElement.Enhancement)) + { + data.Texts.Remove(TooltipElement.Enhancement); + } + } + } +} \ No newline at end of file From bb636643807040168db905405761601f923d5986 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 14 Dec 2020 14:08:41 -0600 Subject: [PATCH 111/132] Extracting the Harmony patches from Main into their own files: UnitViewHandSlotDataWeaponScalePatch --- CraftMagicItems/CraftMagicItems.csproj | 1 + CraftMagicItems/Main.cs | 21 +---------- .../UnitViewHandSlotDataWeaponScalePatch.cs | 36 +++++++++++++++++++ 3 files changed, 38 insertions(+), 20 deletions(-) create mode 100644 CraftMagicItems/Patches/Harmony/UnitViewHandSlotDataWeaponScalePatch.cs diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index 9561a7b..ea336fa 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -136,6 +136,7 @@ + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index e88f5a1..2771be3 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -707,7 +707,7 @@ private static string GetSlotStringKey(ItemsFilter.ItemType slot, SlotRestrictio } } - private static IEnumerable GetEnchantments(BlueprintItem blueprint, RecipeData sourceRecipe = null) { + public static IEnumerable GetEnchantments(BlueprintItem blueprint, RecipeData sourceRecipe = null) { if (blueprint is BlueprintItemShield shield) { // A shield can be treated as armor or as a weapon... assume armor unless being used by a recipe which applies to weapons. var weaponRecipe = sourceRecipe?.OnlyForSlots?.Contains(ItemsFilter.ItemType.Weapon) ?? false; @@ -3342,25 +3342,6 @@ public static bool EquipmentEnchantmentValid(ItemEntityWeapon weapon, ItemEntity } } - [HarmonyLib.HarmonyPatch(typeof(UnitViewHandSlotData), "OwnerWeaponScale", HarmonyLib.MethodType.Getter)] - private static class UnitViewHandSlotDataWeaponScalePatch { - private static void Postfix(UnitViewHandSlotData __instance, ref float __result) { - if (__instance.VisibleItem is ItemEntityWeapon weapon && !weapon.Blueprint.AssetGuid.Contains(",visual=")) { - var enchantment = GetEnchantments(weapon.Blueprint).FirstOrDefault(e => e.AssetGuid.StartsWith(ItemQualityBlueprints.OversizedGuid)); - if (enchantment != null) { - var component = enchantment.GetComponent(); - if (component != null) { - if (component.SizeCategoryChange > 0) { - __result *= 4.0f / 3.0f; - } else if (component.SizeCategoryChange < 0) { - __result *= 0.75f; - } - } - } - } - } - } - #if !PATCH21 [HarmonyLib.HarmonyPatch(typeof(ActivatableAbility), "OnEventDidTrigger", new Type[] { typeof(RuleAttackWithWeaponResolve) })] private static class ActivatableAbilityOnEventDidTriggerRuleAttackWithWeaponResolvePatch { diff --git a/CraftMagicItems/Patches/Harmony/UnitViewHandSlotDataWeaponScalePatch.cs b/CraftMagicItems/Patches/Harmony/UnitViewHandSlotDataWeaponScalePatch.cs new file mode 100644 index 0000000..e5cd8f6 --- /dev/null +++ b/CraftMagicItems/Patches/Harmony/UnitViewHandSlotDataWeaponScalePatch.cs @@ -0,0 +1,36 @@ +using System.Linq; +using CraftMagicItems.Constants; +using Kingmaker.Blueprints; +using Kingmaker.Items; +using Kingmaker.Utility; +using Kingmaker.View.Equipment; + +namespace CraftMagicItems.Patches.Harmony +{ + [HarmonyLib.HarmonyPatch(typeof(UnitViewHandSlotData), "OwnerWeaponScale", HarmonyLib.MethodType.Getter)] + public static class UnitViewHandSlotDataWeaponScalePatch + { + private static void Postfix(UnitViewHandSlotData __instance, ref float __result) + { + if (__instance.VisibleItem is ItemEntityWeapon weapon && !weapon.Blueprint.AssetGuid.Contains(",visual=")) + { + var enchantment = Main.GetEnchantments(weapon.Blueprint).FirstOrDefault(e => e.AssetGuid.StartsWith(ItemQualityBlueprints.OversizedGuid)); + if (enchantment != null) + { + var component = enchantment.GetComponent(); + if (component != null) + { + if (component.SizeCategoryChange > 0) + { + __result *= 4.0f / 3.0f; + } + else if (component.SizeCategoryChange < 0) + { + __result *= 0.75f; + } + } + } + } + } + } +} \ No newline at end of file From 1d6f5355ef9706ed14086ca61da3c033eba2acad Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 14 Dec 2020 14:12:58 -0600 Subject: [PATCH 112/132] Extracting the Harmony patches from Main into their own files: ActivatableAbilityOnEventDidTriggerRuleAttackWithWeaponResolvePatch --- CraftMagicItems/CraftMagicItems.csproj | 1 + CraftMagicItems/Main.cs | 13 ------------ ...TriggerRuleAttackWithWeaponResolvePatch.cs | 21 +++++++++++++++++++ 3 files changed, 22 insertions(+), 13 deletions(-) create mode 100644 CraftMagicItems/Patches/Harmony/ActivatableAbilityOnEventDidTriggerRuleAttackWithWeaponResolvePatch.cs diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index ea336fa..23fd09f 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -108,6 +108,7 @@ + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 2771be3..cd402d6 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -3341,18 +3341,5 @@ public static bool EquipmentEnchantmentValid(ItemEntityWeapon weapon, ItemEntity return false; } } - -#if !PATCH21 - [HarmonyLib.HarmonyPatch(typeof(ActivatableAbility), "OnEventDidTrigger", new Type[] { typeof(RuleAttackWithWeaponResolve) })] - private static class ActivatableAbilityOnEventDidTriggerRuleAttackWithWeaponResolvePatch { - private static bool Prefix(ActivatableAbility __instance, RuleAttackWithWeaponResolve evt) { - if (evt.Damage != null && evt.AttackRoll.IsHit) { - return false; - } else { - return true; - } - } - } -#endif } } \ No newline at end of file diff --git a/CraftMagicItems/Patches/Harmony/ActivatableAbilityOnEventDidTriggerRuleAttackWithWeaponResolvePatch.cs b/CraftMagicItems/Patches/Harmony/ActivatableAbilityOnEventDidTriggerRuleAttackWithWeaponResolvePatch.cs new file mode 100644 index 0000000..92ec38a --- /dev/null +++ b/CraftMagicItems/Patches/Harmony/ActivatableAbilityOnEventDidTriggerRuleAttackWithWeaponResolvePatch.cs @@ -0,0 +1,21 @@ +#if !PATCH21 +using System; +using Kingmaker.RuleSystem.Rules; +using Kingmaker.UnitLogic.ActivatableAbilities; +#endif + +namespace CraftMagicItems.Patches.Harmony +{ +#if !PATCH21 + [HarmonyLib.HarmonyPatch(typeof(ActivatableAbility), "OnEventDidTrigger", new Type[] { typeof(RuleAttackWithWeaponResolve) })] + public static class ActivatableAbilityOnEventDidTriggerRuleAttackWithWeaponResolvePatch { + private static bool Prefix(ActivatableAbility __instance, RuleAttackWithWeaponResolve evt) { + if (evt.Damage != null && evt.AttackRoll.IsHit) { + return false; + } else { + return true; + } + } + } +#endif +} \ No newline at end of file From 389ebcffc6de6ee1869e57001334f6cd11a1fb16 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 14 Dec 2020 14:44:25 -0600 Subject: [PATCH 113/132] Cleanup --- CraftMagicItems/Patches/Harmony/MainMenuStartPatch.cs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/CraftMagicItems/Patches/Harmony/MainMenuStartPatch.cs b/CraftMagicItems/Patches/Harmony/MainMenuStartPatch.cs index e092b4e..29924eb 100644 --- a/CraftMagicItems/Patches/Harmony/MainMenuStartPatch.cs +++ b/CraftMagicItems/Patches/Harmony/MainMenuStartPatch.cs @@ -4,8 +4,6 @@ using CraftMagicItems.Constants; using CraftMagicItems.Localization; using Kingmaker; -#if PATCH21 -#endif using Kingmaker.Blueprints; using Kingmaker.Blueprints.Classes; using Kingmaker.Blueprints.Classes.Selection; @@ -14,13 +12,7 @@ using Kingmaker.Blueprints.Items.Weapons; using Kingmaker.Designers.Mechanics.Facts; using Kingmaker.Enums.Damage; -#if !PATCH21 -using Kingmaker.Items.Slots; -#endif using Kingmaker.RuleSystem; -#if !PATCH21 -using Kingmaker.UnitLogic.ActivatableAbilities; -#endif using Kingmaker.UnitLogic.Mechanics.Components; using Kingmaker.Utility; using UnityEngine; @@ -272,7 +264,7 @@ private static void PatchBlueprints() if (conditional.Conditions.Conditions[i] is Kingmaker.Designers.EventConditionActionSystem.Conditions.HasFact condition) { #if PATCH21_BETA - var replace = SerializedScriptableObject.CreateInstance(); + var replace = SerializedScriptableObject.CreateInstance(); #else var replace = ScriptableObject.CreateInstance(); #endif From 89449330d21462b20ba14fef191395d4e6b27f88 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 20 Jul 2020 12:19:11 -0500 Subject: [PATCH 114/132] Extracting the harmony patching into its own class --- CraftMagicItems/CraftMagicItems.csproj | 2 + CraftMagicItems/Main.cs | 87 ++--------------- .../Patches/Harmony/MainMenuStartPatch.cs | 6 +- CraftMagicItems/Patches/HarmonyPatcher.cs | 93 +++++++++++++++++++ CraftMagicItems/Patches/MethodPatch.cs | 39 ++++++++ 5 files changed, 148 insertions(+), 79 deletions(-) create mode 100644 CraftMagicItems/Patches/HarmonyPatcher.cs create mode 100644 CraftMagicItems/Patches/MethodPatch.cs diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index 23fd09f..2dc25df 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -107,6 +107,7 @@ + @@ -150,6 +151,7 @@ + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index cd402d6..0f394cd 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -108,29 +108,6 @@ private enum ItemLocationFilter Stash } - public struct MethodPatch { - public MethodPatch(MethodBase original, HarmonyLib.HarmonyMethod prefix = null, HarmonyLib.HarmonyMethod postfix = null) { - m_original = original; - m_prefix = prefix; - m_postfix = postfix; - } - public MethodInfo Patch(HarmonyLib.Harmony instance) { - return instance.Patch(m_original, m_prefix, m_postfix); - } - public bool MatchOriginal(MethodBase method) { - return m_original != null & m_original == method; - } - public bool MatchPrefix(MethodBase method) { - return m_prefix != null && m_prefix.method == method; - } - public bool MatchPostfix(MethodBase method) { - return m_postfix != null && m_postfix.method == method; - } - MethodBase m_original; - HarmonyLib.HarmonyMethod m_prefix; - HarmonyLib.HarmonyMethod m_postfix; - } - public static readonly MethodPatch[] MethodPatchList = { new MethodPatch( @@ -165,7 +142,6 @@ public bool MatchPostfix(MethodBase method) { #endif public static bool modEnabled = true; - private static HarmonyLib.Harmony harmonyInstance; private static CraftMagicItemsBlueprintPatcher blueprintPatcher; public static readonly Dictionary ItemUpgradeProjects = new Dictionary(); @@ -173,55 +149,12 @@ public bool MatchPostfix(MethodBase method) { private static readonly Random RandomGenerator = new Random(); - - /** - * Patch all HarmonyPatch classes in the assembly, starting in the order of the methods named in methodNameOrder, and the rest after that. - */ - public static void PatchAllOrdered(params MethodPatch[] orderedMethods) { - foreach (var method in orderedMethods) { - method.Patch(harmonyInstance); - } - harmonyInstance.PatchAll(Assembly.GetExecutingAssembly()); - } - - /** - * Unpatch all HarmonyPatch classes for harmonyInstance, except the ones whose method names match exceptMethodName - */ - public static void UnpatchAllExcept(params MethodPatch[] exceptMethods) { - if (harmonyInstance != null) { - try { - foreach (var method in harmonyInstance.GetPatchedMethods().ToArray()) { - var patchInfo = HarmonyLib.Harmony.GetPatchInfo(method); - if (patchInfo.Owners.Contains(harmonyInstance.Id)) { - var methodPatches = exceptMethods.Where(m => m.MatchOriginal(method)); - if (methodPatches.Count() > 0) { - foreach (var patch in patchInfo.Prefixes) { - if (!methodPatches.Any(m => m.MatchPrefix(patch.PatchMethod))) { - harmonyInstance.Unpatch(method, patch.PatchMethod); - } - } - foreach (var patch in patchInfo.Postfixes) { - if (!methodPatches.Any(m => m.MatchPostfix(patch.PatchMethod))) { - harmonyInstance.Unpatch(method, patch.PatchMethod); - } - } - harmonyInstance.Unpatch(method, HarmonyLib.HarmonyPatchType.Finalizer, harmonyInstance.Id); - harmonyInstance.Unpatch(method, HarmonyLib.HarmonyPatchType.Transpiler, harmonyInstance.Id); - harmonyInstance.Unpatch(method, HarmonyLib.HarmonyPatchType.ReversePatch, harmonyInstance.Id); - } else { - harmonyInstance.Unpatch(method, HarmonyLib.HarmonyPatchType.All, harmonyInstance.Id); - } - } - } - } catch (Exception e) { - ModEntry.Logger.Error($"Exception during Un-patching: {e}"); - } - } - } - // ReSharper disable once UnusedMember.Local - private static void Load(UnityModManager.ModEntry modEntry) { - try { + private static void Load(UnityModManager.ModEntry modEntry) + { + HarmonyPatcher patcher = new HarmonyPatcher(modEntry.Logger.Error); + try + { Selections = new Selections(); ModEntry = modEntry; ModSettings = UnityModManager.ModSettings.Load(modEntry); @@ -232,17 +165,17 @@ private static void Load(UnityModManager.ModEntry modEntry) { modEntry.OnToggle = OnToggle; modEntry.OnGUI = OnGui; CustomBlueprintBuilder.InitialiseBlueprintRegex(CraftMagicItemsBlueprintPatcher.BlueprintRegex); - harmonyInstance = new HarmonyLib.Harmony("kingmaker.craftMagicItems"); - // Patch the recovery methods first. - PatchAllOrdered(MethodPatchList); + patcher.PatchAllOrdered(MethodPatchList); // Patch the recovery methods first. Accessors = new CraftMagicItemsAccessors(); blueprintPatcher = new CraftMagicItemsBlueprintPatcher(Accessors, modEnabled); - } catch (Exception e) { + } + catch (Exception e) + { modEntry.Logger.Error($"Exception during Load: {e}"); modEnabled = false; CustomBlueprintBuilder.Enabled = false; // Unpatch everything except methods involved in recovering a save when mod is disabled. - UnpatchAllExcept(MethodPatchList); + patcher.UnpatchAllExcept(MethodPatchList); throw; } } diff --git a/CraftMagicItems/Patches/Harmony/MainMenuStartPatch.cs b/CraftMagicItems/Patches/Harmony/MainMenuStartPatch.cs index 29924eb..e0a0e94 100644 --- a/CraftMagicItems/Patches/Harmony/MainMenuStartPatch.cs +++ b/CraftMagicItems/Patches/Harmony/MainMenuStartPatch.cs @@ -310,6 +310,8 @@ public static void ModEnabledChanged() return; } + HarmonyPatcher patcher = new HarmonyPatcher(Main.ModEntry.Logger.Error); + if (!Main.modEnabled) { // Reset everything InitialiseMod initialises @@ -320,12 +322,12 @@ public static void ModEnabledChanged() Main.LoadedData.EnchantmentIdToItem.Clear(); Main.LoadedData.EnchantmentIdToCost.Clear(); Main.LoadedData.EnchantmentIdToRecipe.Clear(); - Main.UnpatchAllExcept(Main.MethodPatchList); + patcher.UnpatchAllExcept(Main.MethodPatchList); } else if (mainMenuStarted) { // If the mod is enabled and we're past the Start of main menu, (re-)initialise. - Main.PatchAllOrdered(); + patcher.PatchAllOrdered(); InitialiseMod(); } L10N.SetEnabled(Main.modEnabled); diff --git a/CraftMagicItems/Patches/HarmonyPatcher.cs b/CraftMagicItems/Patches/HarmonyPatcher.cs new file mode 100644 index 0000000..aab19f3 --- /dev/null +++ b/CraftMagicItems/Patches/HarmonyPatcher.cs @@ -0,0 +1,93 @@ +using System; +using System.Linq; +using System.Reflection; +using Kingmaker.Utility; + +namespace CraftMagicItems.Patches +{ + /// Class that performs the Harmony patching + public class HarmonyPatcher + { + /// that logs error messages + protected Action LogError; + + /// Harmony instance used to patch code + protected HarmonyLib.Harmony HarmonyInstance; + + /// Definition constructor + /// that logs error messages + public HarmonyPatcher(Action logger) + { + HarmonyInstance = new HarmonyLib.Harmony("kingmaker.craftMagicItems"); + LogError = logger; + } + + /// + /// Patches all classes in the assembly decorated with , + /// starting in the order of the methods named in . + /// + /// + /// Ordered array of method names that should be patched in this order before any + /// other methods are patched. + /// + public void PatchAllOrdered(params MethodPatch[] orderedMethods) + { + foreach (var method in orderedMethods) + { + method.Patch(HarmonyInstance); + } + HarmonyInstance.PatchAll(Assembly.GetExecutingAssembly()); + } + + /// + /// Unpatches all classes in the assembly decorated with , + /// except the ones whose method names match any in . + /// + /// Array of method names that should be not be unpatched + public void UnpatchAllExcept(params MethodPatch[] exceptMethods) + { + if (HarmonyInstance != null) + { + try + { + foreach (var method in HarmonyInstance.GetPatchedMethods().ToArray()) + { + var patchInfo = HarmonyLib.Harmony.GetPatchInfo(method); + if (patchInfo.Owners.Contains(HarmonyInstance.Id)) + { + var methodPatches = exceptMethods.Where(m => m.MatchOriginal(method)); + if (methodPatches.Count() > 0) + { + foreach (var patch in patchInfo.Prefixes) + { + if (!methodPatches.Any(m => m.MatchPrefix(patch.PatchMethod))) + { + HarmonyInstance.Unpatch(method, patch.PatchMethod); + } + } + foreach (var patch in patchInfo.Postfixes) + { + if (!methodPatches.Any(m => m.MatchPostfix(patch.PatchMethod))) + { + HarmonyInstance.Unpatch(method, patch.PatchMethod); + } + } + HarmonyInstance.Unpatch(method, HarmonyLib.HarmonyPatchType.Finalizer, HarmonyInstance.Id); + HarmonyInstance.Unpatch(method, HarmonyLib.HarmonyPatchType.Transpiler, HarmonyInstance.Id); + HarmonyInstance.Unpatch(method, HarmonyLib.HarmonyPatchType.ReversePatch, HarmonyInstance.Id); + } + else + { + HarmonyInstance.Unpatch(method, HarmonyLib.HarmonyPatchType.All, HarmonyInstance.Id); + } + } + } + } + catch (Exception e) + { + Main.ModEntry.Logger.Error($"Exception during Un-patching: {e}"); + } + } + } + } +} \ No newline at end of file diff --git a/CraftMagicItems/Patches/MethodPatch.cs b/CraftMagicItems/Patches/MethodPatch.cs new file mode 100644 index 0000000..7911fb5 --- /dev/null +++ b/CraftMagicItems/Patches/MethodPatch.cs @@ -0,0 +1,39 @@ +using HarmonyLib; +using System.Reflection; + +namespace CraftMagicItems.Patches +{ + public struct MethodPatch + { + public MethodPatch(MethodBase original, HarmonyLib.HarmonyMethod prefix = null, HarmonyLib.HarmonyMethod postfix = null) + { + m_original = original; + m_prefix = prefix; + m_postfix = postfix; + } + + public MethodInfo Patch(HarmonyLib.Harmony instance) + { + return instance.Patch(m_original, m_prefix, m_postfix); + } + + public bool MatchOriginal(MethodBase method) + { + return m_original != null & m_original == method; + } + + public bool MatchPrefix(MethodBase method) + { + return m_prefix != null && m_prefix.method == method; + } + + public bool MatchPostfix(MethodBase method) + { + return m_postfix != null && m_postfix.method == method; + } + + MethodBase m_original; + HarmonyMethod m_prefix; + HarmonyMethod m_postfix; + } +} \ No newline at end of file From 0bb53d2a470aebd58ee4409aa385433f35adb417 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 20 Jul 2020 12:40:13 -0500 Subject: [PATCH 115/132] Moving classes that only contain Harmony patches into that namespace --- CraftMagicItems/CraftMagicItems.csproj | 6 +++--- CraftMagicItems/Main.cs | 21 +++---------------- .../CraftMagicItemsBlueprintPatcher.cs | 2 ++ .../{ => Patches/Harmony}/QuiverAbility.cs | 2 +- .../Harmony}/WeaponBaseSizeChange.cs | 3 ++- .../{ => Patches/Harmony}/WeaponSizeChange.cs | 3 ++- 6 files changed, 13 insertions(+), 24 deletions(-) rename CraftMagicItems/{ => Patches/Harmony}/QuiverAbility.cs (99%) rename CraftMagicItems/{ => Patches/Harmony}/WeaponBaseSizeChange.cs (99%) rename CraftMagicItems/{ => Patches/Harmony}/WeaponSizeChange.cs (98%) diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index 2dc25df..de6fe6a 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -152,7 +152,7 @@ - + @@ -166,7 +166,7 @@ - + @@ -179,7 +179,7 @@ - + diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 0f394cd..acf264d 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -3,8 +3,6 @@ using System.IO; using System.Linq; using System.Reflection; -using System.Reflection.Emit; -using System.Runtime.Serialization; using System.Text.RegularExpressions; using CraftMagicItems.Config; using CraftMagicItems.Constants; @@ -16,13 +14,10 @@ using CraftMagicItems.UI.UnityModManager; using Kingmaker; #if PATCH21 -using Kingmaker.Assets.UI.Context; #endif using Kingmaker.Blueprints; using Kingmaker.Blueprints.Classes; -using Kingmaker.Blueprints.Classes.Selection; using Kingmaker.Blueprints.Classes.Spells; -using Kingmaker.Blueprints.Facts; using Kingmaker.Blueprints.Items; using Kingmaker.Blueprints.Items.Armors; using Kingmaker.Blueprints.Items.Ecnchantments; @@ -32,12 +27,8 @@ using Kingmaker.Blueprints.Loot; using Kingmaker.Blueprints.Root; using Kingmaker.Blueprints.Root.Strings.GameLog; -using Kingmaker.Controllers.Rest; -using Kingmaker.Designers; using Kingmaker.Designers.Mechanics.EquipmentEnchants; using Kingmaker.Designers.Mechanics.Facts; -using Kingmaker.Designers.Mechanics.WeaponEnchants; -using Kingmaker.Designers.TempMapCode.Capital; using Kingmaker.EntitySystem.Entities; using Kingmaker.EntitySystem.Stats; using Kingmaker.Enums; @@ -51,14 +42,10 @@ using Kingmaker.Localization; using Kingmaker.PubSubSystem; using Kingmaker.RuleSystem; -using Kingmaker.RuleSystem.Rules; using Kingmaker.RuleSystem.Rules.Abilities; -using Kingmaker.RuleSystem.Rules.Damage; using Kingmaker.UI; -using Kingmaker.UI.ActionBar; using Kingmaker.UI.Common; using Kingmaker.UI.Log; -using Kingmaker.UI.Tooltip; using Kingmaker.UnitLogic; using Kingmaker.UnitLogic.Abilities; using Kingmaker.UnitLogic.Abilities.Blueprints; @@ -69,16 +56,14 @@ using Kingmaker.UnitLogic.Buffs; using Kingmaker.UnitLogic.Buffs.Blueprints; using Kingmaker.UnitLogic.FactLogic; -using Kingmaker.UnitLogic.Mechanics.Components; -using Kingmaker.UnitLogic.Parts; using Kingmaker.Utility; -using Kingmaker.View.Equipment; using Newtonsoft.Json; using UnityEngine; using UnityModManagerNet; using Random = System.Random; -namespace CraftMagicItems { +namespace CraftMagicItems +{ public static class Main { private const string BondedItemRitual = "bondedItemRitual"; @@ -820,7 +805,7 @@ private static bool IsMasterwork(BlueprintItem blueprint) { } public static bool IsOversized(BlueprintItem blueprint) { - return GetEnchantments(blueprint).Any(enchantment => enchantment.AssetGuid.StartsWith(ItemQualityBlueprints.OversizedGuid) && !enchantment.GetComponent()); + return GetEnchantments(blueprint).Any(enchantment => enchantment.AssetGuid.StartsWith(ItemQualityBlueprints.OversizedGuid) && !enchantment.GetComponent()); } // Use instead of UIUtility.IsMagicItem. diff --git a/CraftMagicItems/Patches/CraftMagicItemsBlueprintPatcher.cs b/CraftMagicItems/Patches/CraftMagicItemsBlueprintPatcher.cs index 93c6faf..c1b06a3 100644 --- a/CraftMagicItems/Patches/CraftMagicItemsBlueprintPatcher.cs +++ b/CraftMagicItems/Patches/CraftMagicItemsBlueprintPatcher.cs @@ -5,6 +5,7 @@ using System.Text.RegularExpressions; using CraftMagicItems.Constants; using CraftMagicItems.Localization; +using CraftMagicItems.Patches.Harmony; using Kingmaker.Blueprints; using Kingmaker.Blueprints.Classes; using Kingmaker.Blueprints.Items; @@ -23,6 +24,7 @@ using Kingmaker.UnitLogic.Abilities.Blueprints; using Kingmaker.UnitLogic.ActivatableAbilities; using Kingmaker.UnitLogic.Buffs.Blueprints; +using Kingmaker.Utility; using Kingmaker.View.Animation; using Kingmaker.Utility; #if PATCH21_BETA diff --git a/CraftMagicItems/QuiverAbility.cs b/CraftMagicItems/Patches/Harmony/QuiverAbility.cs similarity index 99% rename from CraftMagicItems/QuiverAbility.cs rename to CraftMagicItems/Patches/Harmony/QuiverAbility.cs index c19ed66..6e260b6 100644 --- a/CraftMagicItems/QuiverAbility.cs +++ b/CraftMagicItems/Patches/Harmony/QuiverAbility.cs @@ -19,7 +19,7 @@ using Object = UnityEngine.Object; #endif -namespace CraftMagicItems { +namespace CraftMagicItems.Patches.Harmony { public class CreateQuiverAbility : ScriptableObject { private static bool initialised; diff --git a/CraftMagicItems/WeaponBaseSizeChange.cs b/CraftMagicItems/Patches/Harmony/WeaponBaseSizeChange.cs similarity index 99% rename from CraftMagicItems/WeaponBaseSizeChange.cs rename to CraftMagicItems/Patches/Harmony/WeaponBaseSizeChange.cs index 8fb0708..e69c9df 100644 --- a/CraftMagicItems/WeaponBaseSizeChange.cs +++ b/CraftMagicItems/Patches/Harmony/WeaponBaseSizeChange.cs @@ -4,7 +4,8 @@ using Kingmaker.EntitySystem.Stats; using Kingmaker.RuleSystem; -namespace CraftMagicItems { +namespace CraftMagicItems.Patches.Harmony +{ [ComponentName("Weapon Base Size Change")] [AllowMultipleComponents] /** diff --git a/CraftMagicItems/WeaponSizeChange.cs b/CraftMagicItems/Patches/Harmony/WeaponSizeChange.cs similarity index 98% rename from CraftMagicItems/WeaponSizeChange.cs rename to CraftMagicItems/Patches/Harmony/WeaponSizeChange.cs index 72b516a..09d6e9a 100644 --- a/CraftMagicItems/WeaponSizeChange.cs +++ b/CraftMagicItems/Patches/Harmony/WeaponSizeChange.cs @@ -4,7 +4,8 @@ using Kingmaker.RuleSystem; using Kingmaker.RuleSystem.Rules; -namespace CraftMagicItems { +namespace CraftMagicItems.Patches.Harmony +{ [ComponentName("Weapon Size Change")] public class WeaponSizeChange : GameLogicComponent { public int SizeCategoryChange; From 218ae1b14cdde9d42794063c45b5c221fc445507 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 20 Jul 2020 12:46:50 -0500 Subject: [PATCH 116/132] Ctrl + K, D --- .../Patches/Harmony/MainMenuStartPatch.cs | 1 + CraftMagicItems/SustenanceEnchantment.cs | 83 ++++++++++------ CraftMagicItems/WildEnchantment.cs | 98 ++++++++++++------- 3 files changed, 119 insertions(+), 63 deletions(-) diff --git a/CraftMagicItems/Patches/Harmony/MainMenuStartPatch.cs b/CraftMagicItems/Patches/Harmony/MainMenuStartPatch.cs index e0a0e94..ee62694 100644 --- a/CraftMagicItems/Patches/Harmony/MainMenuStartPatch.cs +++ b/CraftMagicItems/Patches/Harmony/MainMenuStartPatch.cs @@ -2,6 +2,7 @@ using System.Linq; using System.Runtime.Serialization; using CraftMagicItems.Constants; +using CraftMagicItems.Enchantments; using CraftMagicItems.Localization; using Kingmaker; using Kingmaker.Blueprints; diff --git a/CraftMagicItems/SustenanceEnchantment.cs b/CraftMagicItems/SustenanceEnchantment.cs index 1920641..358e7d6 100644 --- a/CraftMagicItems/SustenanceEnchantment.cs +++ b/CraftMagicItems/SustenanceEnchantment.cs @@ -18,12 +18,15 @@ using Kingmaker.UnitLogic.Buffs.Blueprints; using Kingmaker.UnitLogic.Mechanics; -namespace CraftMagicItems { +namespace CraftMagicItems.Enchantments +{ // A living character with a SustenanceFact does not need rations when they camp, and can perform two camp roles. - public class SustenanceFact : BlueprintBuff { + public class SustenanceFact : BlueprintBuff + { } - public class SustenanceEnchantment : BlueprintItemEnchantment { + public class SustenanceEnchantment : BlueprintItemEnchantment + { private static readonly SustenanceEnchantment BlueprintSustenanceEnchantment = CreateInstance(); private const string SustenanceEnchantmentGuid = "8eb9d1c94b1e4894a88c228aa71b79e5#CraftMagicItems(sustenanceEnchantment)"; private static readonly SustenanceFact BlueprintSustenanceFact = CreateInstance(); @@ -32,16 +35,20 @@ public class SustenanceEnchantment : BlueprintItemEnchantment { private static bool initialised; [HarmonyLib.HarmonyPatch(typeof(MainMenu), "Start")] - public static class MainMenuStartPatch { - private static void AddBlueprint(string guid, BlueprintScriptableObject blueprint) { + public static class MainMenuStartPatch + { + private static void AddBlueprint(string guid, BlueprintScriptableObject blueprint) + { Main.Accessors.SetBlueprintScriptableObjectAssetGuid(blueprint) = guid; ResourcesLibrary.LibraryObject.BlueprintsByAssetId?.Add(guid, blueprint); ResourcesLibrary.LibraryObject.GetAllBlueprints()?.Add(blueprint); } // ReSharper disable once UnusedMember.Local - public static void Postfix() { - if (!initialised) { + public static void Postfix() + { + if (!initialised) + { initialised = true; AddBlueprint(SustenanceEnchantmentGuid, BlueprintSustenanceEnchantment); AddBlueprint(SustenanceFactGuid, BlueprintSustenanceFact); @@ -56,7 +63,7 @@ public static void Postfix() { var addSustenanceFact = CreateInstance(); addSustenanceFact.Blueprint = BlueprintSustenanceFact; addSustenanceFact.name = "AddUnitFactEquipment-SustenanceFact"; - BlueprintSustenanceEnchantment.ComponentsArray = new BlueprintComponent[] {addSustenanceFact}; + BlueprintSustenanceEnchantment.ComponentsArray = new BlueprintComponent[] { addSustenanceFact }; Main.Accessors.SetBlueprintBuffFlags(BlueprintSustenanceFact, 2 + 8); // Enum is private... 2 = HiddenInUi, 8 = StayOnDeath BlueprintSustenanceFact.Stacking = StackingType.Replace; BlueprintSustenanceFact.Frequency = DurationRate.Rounds; @@ -78,26 +85,31 @@ private static void Postfix() { } #endif - private static bool UnitHasSustenance(UnitEntityData unit) { + private static bool UnitHasSustenance(UnitEntityData unit) + { return unit?.Descriptor.GetFact(BlueprintSustenanceFact) != null; } [HarmonyLib.HarmonyPatch(typeof(RestController), "CalculateNeededRations")] // ReSharper disable once UnusedMember.Local - private static class RestControllerCalculateNeededRationsPatch { + private static class RestControllerCalculateNeededRationsPatch + { // ReSharper disable once UnusedMember.Local - private static void Postfix(ref int __result) { + private static void Postfix(ref int __result) + { var sustenanceCount = Game.Instance.Player.Party.NotDead().Count(UnitHasSustenance); __result = Math.Max(0, __result - sustenanceCount); } } - private static int CountRoles(UnitEntityData unit) { + private static int CountRoles(UnitEntityData unit) + { var roles = (Game.Instance.Player.Camping.Builders.Contains(unit) ? 1 : 0) + (Game.Instance.Player.Camping.Hunters.Contains(unit) ? 1 : 0) + (Game.Instance.Player.Camping.Cookers.Contains(unit) ? 1 : 0) + (Game.Instance.Player.Camping.Special.Contains(unit) ? 1 : 0); - foreach (var guardShift in Game.Instance.Player.Camping.Guards) { + foreach (var guardShift in Game.Instance.Player.Camping.Guards) + { roles += guardShift.Contains(unit) ? 1 : 0; } @@ -106,10 +118,13 @@ private static int CountRoles(UnitEntityData unit) { [HarmonyLib.HarmonyPatch(typeof(CampManager), "RemoveAllCompanionRoles")] // ReSharper disable once UnusedMember.Local - private static class CampManagerRemoveAllCompanionRolesPatch { + private static class CampManagerRemoveAllCompanionRolesPatch + { // ReSharper disable once UnusedMember.Local - private static bool Prefix(UnitEntityData unit) { - if (UnitHasSustenance(unit)) { + private static bool Prefix(UnitEntityData unit) + { + if (UnitHasSustenance(unit)) + { // Only return true (and thus remove their roles) if they're already doing 2 roles. return CountRoles(unit) >= 2; } @@ -119,11 +134,14 @@ private static bool Prefix(UnitEntityData unit) { } [HarmonyLib.HarmonyPatch(typeof(MemberUIBody), "CheckHasRole")] - private static class MemberUiBodyCheckHasRolePatch { + private static class MemberUiBodyCheckHasRolePatch + { // ReSharper disable once UnusedMember.Local - private static bool Prefix(MemberUIBody __instance, ref bool __result) { + private static bool Prefix(MemberUIBody __instance, ref bool __result) + { var unit = __instance.CharacterSlot.Unit(); - if (UnitHasSustenance(unit) && CountRoles(unit) < 2) { + if (UnitHasSustenance(unit) && CountRoles(unit) < 2) + { // The unit can still be assigned to another role. __instance.HasRole = false; HarmonyLib.Traverse.Create(__instance).Method("SetupRoleView").GetValue(); @@ -135,18 +153,23 @@ private static bool Prefix(MemberUIBody __instance, ref bool __result) { } } - private static List FindBestRoleToDrop(UnitEntityData unit, List current, List best) { + private static List FindBestRoleToDrop(UnitEntityData unit, List current, List best) + { return current.Contains(unit) && (best == null || current.Count > best.Count) ? current : best; } [HarmonyLib.HarmonyPatch(typeof(CampingState), "CleanupRoles")] // ReSharper disable once UnusedMember.Local - private static class CampingStateCleanupRolesPatch { + private static class CampingStateCleanupRolesPatch + { // ReSharper disable once UnusedMember.Local - private static void Postfix() { + private static void Postfix() + { // Ensure that anyone assigned to multiple roles actually has Sustenance - foreach (var unit in Game.Instance.Player.Party) { - if (CountRoles(unit) > 1 && !UnitHasSustenance(unit)) { + foreach (var unit in Game.Instance.Player.Party) + { + if (CountRoles(unit) > 1 && !UnitHasSustenance(unit)) + { // Need to drop one role - prefer roles that others are doing. #if PATCH21 var roleToDrop = FindBestRoleToDrop(unit, Game.Instance.Player.Camping.Builders.ToList(), null); @@ -161,7 +184,8 @@ private static void Postfix() { roleToDrop = FindBestRoleToDrop(unit, Game.Instance.Player.Camping.Hunters, roleToDrop); roleToDrop = FindBestRoleToDrop(unit, Game.Instance.Player.Camping.Cookers, roleToDrop); roleToDrop = FindBestRoleToDrop(unit, Game.Instance.Player.Camping.Special, roleToDrop); - foreach (var guardShift in Game.Instance.Player.Camping.Guards) { + foreach (var guardShift in Game.Instance.Player.Camping.Guards) + { roleToDrop = FindBestRoleToDrop(unit, guardShift, roleToDrop); } #endif @@ -174,10 +198,13 @@ private static void Postfix() { [HarmonyLib.HarmonyPatch(typeof(CampingState), "GetRolesCount")] // ReSharper disable once UnusedMember.Local - private static class RestControllerIsTiredPatch { + private static class RestControllerIsTiredPatch + { // ReSharper disable once UnusedMember.Local - private static void Postfix(UnitEntityData unit, ref int __result) { - if (UnitHasSustenance(unit)) { + private static void Postfix(UnitEntityData unit, ref int __result) + { + if (UnitHasSustenance(unit)) + { __result = Math.Min(1, __result); } } diff --git a/CraftMagicItems/WildEnchantment.cs b/CraftMagicItems/WildEnchantment.cs index e66622e..5e812dc 100644 --- a/CraftMagicItems/WildEnchantment.cs +++ b/CraftMagicItems/WildEnchantment.cs @@ -16,9 +16,12 @@ using Kingmaker.UnitLogic.Buffs.Blueprints; using Kingmaker.UnitLogic.Mechanics; -namespace CraftMagicItems { - public class WildFact : BlueprintBuff { - public WildFact() { +namespace CraftMagicItems.Enchantments +{ + public class WildFact : BlueprintBuff + { + public WildFact() + { Main.Accessors.SetBlueprintBuffFlags(this, 2 + 8); // Enum is private... 2 = HiddenInUi, 8 = StayOnDeath Stacking = StackingType.Replace; Frequency = DurationRate.Rounds; @@ -29,71 +32,89 @@ public WildFact() { } } - public class WildEnchantmentLogic : AddUnitFactEquipment { + public class WildEnchantmentLogic : AddUnitFactEquipment + { private const string WildShapeTurnBackAbilityGuid = "a2cb181ee69860b46b82844a3a8569b8"; private ModifiableValue.Modifier modifier; - public WildEnchantmentLogic() { + public WildEnchantmentLogic() + { name = "AddUnitFactEquipment-WildFact"; } - private bool IsOwnerWildShaped() { + private bool IsOwnerWildShaped() + { var wildShapeTurnBackAbility = ResourcesLibrary.TryGetBlueprint(WildShapeTurnBackAbilityGuid); return Owner.Owner.HasFact(wildShapeTurnBackAbility); } - private void ApplyModifier() { + private void ApplyModifier() + { // Apply AC modifier - if (Owner.Owner == null || modifier != null) { + if (Owner.Owner == null || modifier != null) + { return; } var stat = Owner.Owner.Stats.GetStat(StatType.AC); - switch (Owner) { - case ItemEntityArmor armor: { - var acBonus = armor.Blueprint.ArmorBonus + GameHelper.GetItemEnhancementBonus(armor); - modifier = stat.AddItemModifier(acBonus, armor, ModifierDescriptor.Armor); - break; - } - case ItemEntityShield shield: { - var acBonus = shield.Blueprint.ArmorComponent.ArmorBonus + GameHelper.GetItemEnhancementBonus(shield.ArmorComponent); - modifier = stat.AddItemModifier(acBonus, shield, ModifierDescriptor.Shield); - break; - } + switch (Owner) + { + case ItemEntityArmor armor: + { + var acBonus = armor.Blueprint.ArmorBonus + GameHelper.GetItemEnhancementBonus(armor); + modifier = stat.AddItemModifier(acBonus, armor, ModifierDescriptor.Armor); + break; + } + case ItemEntityShield shield: + { + var acBonus = shield.Blueprint.ArmorComponent.ArmorBonus + GameHelper.GetItemEnhancementBonus(shield.ArmorComponent); + modifier = stat.AddItemModifier(acBonus, shield, ModifierDescriptor.Shield); + break; + } } } - public override void OnFactActivate() { + public override void OnFactActivate() + { base.OnFactActivate(); - if (!IsOwnerWildShaped() && modifier != null) { + if (!IsOwnerWildShaped() && modifier != null) + { modifier.Remove(); modifier = null; } } - public override void OnFactDeactivate() { - if (IsOwnerWildShaped()) { + public override void OnFactDeactivate() + { + if (IsOwnerWildShaped()) + { ApplyModifier(); - } else { + } + else + { base.OnFactDeactivate(); } } - public override void PostLoad() { + public override void PostLoad() + { base.PostLoad(); - if (IsOwnerWildShaped()) { + if (IsOwnerWildShaped()) + { ApplyModifier(); } } } - public class WildEnchantment : BlueprintItemEnchantment { + public class WildEnchantment : BlueprintItemEnchantment + { private const string WildEnchantmentGuid = "dd0e096412423d646929d9b945fd6d4c#CraftMagicItems(wildEnchantment)"; private const string WildFactGuid = "28384b1d7e25c8743b8bbfc56211ac8c#CraftMagicItems(wildFact)"; private static bool initialised; - public WildEnchantment() { + public WildEnchantment() + { this.name = "Wild"; Main.Accessors.SetBlueprintItemEnchantmentEnchantName(this) = new L10NString("craftMagicItems-enchantment-wild-name"); Main.Accessors.SetBlueprintItemEnchantmentDescription(this) = new L10NString("craftMagicItems-enchantment-wild-description"); @@ -105,16 +126,20 @@ public WildEnchantment() { [HarmonyLib.HarmonyPatch(typeof(MainMenu), "Start")] // ReSharper disable once UnusedMember.Local - public static class MainMenuStartPatch { - private static void AddBlueprint(string guid, BlueprintScriptableObject blueprint) { + public static class MainMenuStartPatch + { + private static void AddBlueprint(string guid, BlueprintScriptableObject blueprint) + { Main.Accessors.SetBlueprintScriptableObjectAssetGuid(blueprint) = guid; ResourcesLibrary.LibraryObject.BlueprintsByAssetId?.Add(guid, blueprint); ResourcesLibrary.LibraryObject.GetAllBlueprints()?.Add(blueprint); } // ReSharper disable once UnusedMember.Local - public static void Postfix() { - if (!initialised) { + public static void Postfix() + { + if (!initialised) + { initialised = true; var blueprintWildEnchantment = CreateInstance(); AddBlueprint(WildEnchantmentGuid, blueprintWildEnchantment); @@ -122,14 +147,17 @@ public static void Postfix() { AddBlueprint(WildFactGuid, blueprintWildFact); var wildEnchantmentLogic = CreateInstance(); wildEnchantmentLogic.Blueprint = blueprintWildFact; - blueprintWildEnchantment.ComponentsArray = new BlueprintComponent[] {wildEnchantmentLogic}; + blueprintWildEnchantment.ComponentsArray = new BlueprintComponent[] { wildEnchantmentLogic }; } } } + #if PATCH21 [HarmonyLib.HarmonyPatch(typeof(MainMenuUiContext), "Initialize")] - private static class MainMenuUiContextInitializePatch { - private static void Postfix() { + private static class MainMenuUiContextInitializePatch + { + private static void Postfix() + { MainMenuStartPatch.Postfix(); } } From 72a8b73a6a43f148fa7915515458d9cf8d2c846d Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 20 Jul 2020 12:57:54 -0500 Subject: [PATCH 117/132] Moving UpgradeSave and AddToLootTables to PlayerPostLoadPatch due to those being only referenced in that class --- CraftMagicItems/Main.cs | 55 +------------ .../Patches/Harmony/PlayerPostLoadPatch.cs | 77 ++++++++++++++++++- 2 files changed, 77 insertions(+), 55 deletions(-) diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index acf264d..50c1070 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -127,7 +127,7 @@ private enum ItemLocationFilter #endif public static bool modEnabled = true; - private static CraftMagicItemsBlueprintPatcher blueprintPatcher; + public static CraftMagicItemsBlueprintPatcher blueprintPatcher; public static readonly Dictionary ItemUpgradeProjects = new Dictionary(); public static readonly List ItemCreationProjects = new List(); @@ -3198,59 +3198,6 @@ public static void WorkOnProjects(UnitDescriptor caster, bool returningToCapital } } - private static void AddToLootTables(BlueprintItem blueprint, string[] tableNames, bool firstTime) { - var tableCount = tableNames.Length; - foreach (var loot in ResourcesLibrary.GetBlueprints()) { - if (tableNames.Contains(loot.name)) { - tableCount--; - if (!loot.Items.Any(entry => entry.Item == blueprint)) { - var lootItems = loot.Items.ToList(); - lootItems.Add(new LootEntry {Count = 1, Item = blueprint}); - loot.Items = lootItems.ToArray(); - } - } - } - foreach (var unitLoot in ResourcesLibrary.GetBlueprints()) { - if (tableNames.Contains(unitLoot.name)) { - tableCount--; - if (unitLoot is BlueprintSharedVendorTable vendor) { - if (firstTime) { - var vendorTable = Game.Instance.Player.SharedVendorTables.GetTable(vendor); - vendorTable.Add(blueprint.CreateEntity()); - } - } else if (!unitLoot.ComponentsArray.Any(component => component is LootItemsPackFixed pack && pack.Item.Item == blueprint)) { - var lootItem = new LootItem(); - Accessors.SetLootItemItem(lootItem) = blueprint; -#if PATCH21_BETA - var lootComponent = SerializedScriptableObject.CreateInstance(); -#else - var lootComponent = ScriptableObject.CreateInstance(); -#endif - Accessors.SetLootItemsPackFixedItem(lootComponent) = lootItem; - blueprintPatcher.EnsureComponentNameUnique(lootComponent, unitLoot.ComponentsArray); - var components = unitLoot.ComponentsArray.ToList(); - components.Add(lootComponent); - unitLoot.ComponentsArray = components.ToArray(); - } - } - } - if (tableCount > 0) { - HarmonyLib.FileLog.Log($"!!! Failed to match all loot table names for {blueprint.Name}. {tableCount} table names not found."); - } - } - - public static void UpgradeSave(Version version) { - foreach (var lootItem in LoadedData.CustomLootItems) { - var firstTime = (version == null || version.CompareTo(lootItem.AddInVersion) < 0); - var item = ResourcesLibrary.TryGetBlueprint(lootItem.AssetGuid); - if (item == null) { - HarmonyLib.FileLog.Log($"!!! Loot item not created: {lootItem.AssetGuid}"); - } else { - AddToLootTables(item, lootItem.LootTables, firstTime); - } - } - } - public static bool EquipmentEnchantmentValid(ItemEntityWeapon weapon, ItemEntity owner) { if ((weapon == owner) || (weapon != null && (weapon.Blueprint.IsNatural || weapon.Blueprint.IsUnarmed))) { diff --git a/CraftMagicItems/Patches/Harmony/PlayerPostLoadPatch.cs b/CraftMagicItems/Patches/Harmony/PlayerPostLoadPatch.cs index add2b1c..b2f83f4 100644 --- a/CraftMagicItems/Patches/Harmony/PlayerPostLoadPatch.cs +++ b/CraftMagicItems/Patches/Harmony/PlayerPostLoadPatch.cs @@ -1,7 +1,13 @@ using System; using System.Linq; +using Kingmaker; +using Kingmaker.Blueprints; using Kingmaker.Blueprints.Classes; +using Kingmaker.Blueprints.Items; +using Kingmaker.Blueprints.Loot; +using Kingmaker.Items; using Kingmaker.UI.Common; +using UnityEngine; namespace CraftMagicItems.Patches.Harmony { @@ -57,7 +63,7 @@ private static void Postfix() if (character.IsMainCharacter) { - Main.UpgradeSave(string.IsNullOrEmpty(timer.Version) ? null : Version.Parse(timer.Version)); + UpgradeSave(string.IsNullOrEmpty(timer.Version) ? null : Version.Parse(timer.Version)); timer.Version = Main.ModEntry.Version.ToString(); } } @@ -88,5 +94,74 @@ private static void Postfix() } } } + + private static void AddToLootTables(BlueprintItem blueprint, string[] tableNames, bool firstTime) + { + var tableCount = tableNames.Length; + foreach (var loot in ResourcesLibrary.GetBlueprints()) + { + if (tableNames.Contains(loot.name)) + { + tableCount--; + if (!loot.Items.Any(entry => entry.Item == blueprint)) + { + var lootItems = loot.Items.ToList(); + lootItems.Add(new LootEntry { Count = 1, Item = blueprint }); + loot.Items = lootItems.ToArray(); + } + } + } + foreach (var unitLoot in ResourcesLibrary.GetBlueprints()) + { + if (tableNames.Contains(unitLoot.name)) + { + tableCount--; + if (unitLoot is BlueprintSharedVendorTable vendor) + { + if (firstTime) + { + var vendorTable = Game.Instance.Player.SharedVendorTables.GetTable(vendor); + vendorTable.Add(blueprint.CreateEntity()); + } + } + else if (!unitLoot.ComponentsArray.Any(component => component is LootItemsPackFixed pack && pack.Item.Item == blueprint)) + { + var lootItem = new LootItem(); + Main.Accessors.SetLootItemItem(lootItem) = blueprint; +#if PATCH21_BETA + var lootComponent = SerializedScriptableObject.CreateInstance(); +#else + var lootComponent = ScriptableObject.CreateInstance(); +#endif + Main.Accessors.SetLootItemsPackFixedItem(lootComponent) = lootItem; + Main.blueprintPatcher.EnsureComponentNameUnique(lootComponent, unitLoot.ComponentsArray); + var components = unitLoot.ComponentsArray.ToList(); + components.Add(lootComponent); + unitLoot.ComponentsArray = components.ToArray(); + } + } + } + if (tableCount > 0) + { + HarmonyLib.FileLog.Log($"!!! Failed to match all loot table names for {blueprint.Name}. {tableCount} table names not found."); + } + } + + public static void UpgradeSave(Version version) + { + foreach (var lootItem in Main.LoadedData.CustomLootItems) + { + var firstTime = (version == null || version.CompareTo(lootItem.AddInVersion) < 0); + var item = ResourcesLibrary.TryGetBlueprint(lootItem.AssetGuid); + if (item == null) + { + HarmonyLib.FileLog.Log($"!!! Loot item not created: {lootItem.AssetGuid}"); + } + else + { + AddToLootTables(item, lootItem.LootTables, firstTime); + } + } + } } } \ No newline at end of file From f0c964281d60cb0414fd1a9a254fbfc7d6b35915 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 20 Jul 2020 13:45:40 -0500 Subject: [PATCH 118/132] Moving ReverseEngineerEnchantmentCost into MainMenuStartPatch --- CraftMagicItems/Main.cs | 49 ++-------------- .../Patches/Harmony/MainMenuStartPatch.cs | 58 ++++++++++++++++++- 2 files changed, 62 insertions(+), 45 deletions(-) diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 50c1070..97a4198 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -2567,6 +2567,7 @@ private static int ItemPlus(BlueprintItem blueprint) { return 0; } + private static int ItemMaxEnchantmentLevel(BlueprintItem blueprint) { if (blueprint is BlueprintItemWeapon || blueprint is BlueprintItemArmor || blueprint is BlueprintItemShield) { return 10; @@ -2615,9 +2616,11 @@ private static bool IsItemLegalEnchantmentLevel(BlueprintItem blueprint) { return ItemPlusEquivalent(blueprint) <= ItemMaxEnchantmentLevel(blueprint); } - private static int GetEnchantmentCost(string enchantmentId, BlueprintItem blueprint) { + public static int GetEnchantmentCost(string enchantmentId, BlueprintItem blueprint) + { var recipe = FindSourceRecipe(enchantmentId, blueprint); - if (recipe != null) { + if (recipe != null) + { var index = recipe.Enchantments.FindIndex(enchantment => enchantment.AssetGuid == enchantmentId); var casterLevel = recipe.CasterLevelStart + index * recipe.CasterLevelMultiplier; var epicFactor = casterLevel > 20 ? 2 : 1; @@ -2765,48 +2768,6 @@ public static int RulesRecipeItemCost(BlueprintItem blueprint, int baseCost = -1 return (3 * (baseCost + cost) - mostExpensiveEnchantmentCost) / (blueprint is BlueprintItemEquipmentUsable ? 1 : 2); } - // Attempt to work out the cost of enchantments which aren't in recipes by checking if blueprint, which contains the enchantment, contains only other - // enchantments whose cost is known. - public static bool ReverseEngineerEnchantmentCost(BlueprintItemEquipment blueprint, string enchantmentId) { - if (blueprint == null || blueprint.IsNotable || blueprint.Ability != null || blueprint.ActivatableAbility != null) { - return false; - } - - if (blueprint is BlueprintItemShield || blueprint is BlueprintItemWeapon || blueprint is BlueprintItemArmor) { - // Cost of enchantments on arms and armor is different, and can be treated as a straight delta. - return true; - } - - var mostExpensiveEnchantmentCost = 0; - var costSum = 0; - foreach (var enchantment in blueprint.Enchantments) { - if (enchantment.AssetGuid == enchantmentId) { - continue; - } - - if (!LoadedData.EnchantmentIdToRecipe.ContainsKey(enchantment.AssetGuid) && !LoadedData.EnchantmentIdToCost.ContainsKey(enchantment.AssetGuid)) { - return false; - } - - var enchantmentCost = GetEnchantmentCost(enchantment.AssetGuid, blueprint); - costSum += enchantmentCost; - if (mostExpensiveEnchantmentCost < enchantmentCost) { - mostExpensiveEnchantmentCost = enchantmentCost; - } - } - - var remainder = blueprint.Cost - 3 * costSum / 2; - if (remainder >= mostExpensiveEnchantmentCost) { - // enchantmentId is the most expensive enchantment - LoadedData.EnchantmentIdToCost[enchantmentId] = remainder; - } else { - // mostExpensiveEnchantmentCost is the most expensive enchantment - LoadedData.EnchantmentIdToCost[enchantmentId] = (2 * remainder + mostExpensiveEnchantmentCost) / 3; - } - - return true; - } - public static void AddBattleLogMessage(string message, object tooltip = null, Color? color = null) { #if PATCH21 var data = new LogItemData(message, color ?? GameLogStrings.Instance.DefaultColor, tooltip, PrefixIcon.None, new List { LogChannel.Combat }); diff --git a/CraftMagicItems/Patches/Harmony/MainMenuStartPatch.cs b/CraftMagicItems/Patches/Harmony/MainMenuStartPatch.cs index ee62694..dd4f334 100644 --- a/CraftMagicItems/Patches/Harmony/MainMenuStartPatch.cs +++ b/CraftMagicItems/Patches/Harmony/MainMenuStartPatch.cs @@ -8,8 +8,10 @@ using Kingmaker.Blueprints; using Kingmaker.Blueprints.Classes; using Kingmaker.Blueprints.Classes.Selection; +using Kingmaker.Blueprints.Items.Armors; using Kingmaker.Blueprints.Items.Ecnchantments; using Kingmaker.Blueprints.Items.Equipment; +using Kingmaker.Blueprints.Items.Shields; using Kingmaker.Blueprints.Items.Weapons; using Kingmaker.Designers.Mechanics.Facts; using Kingmaker.Enums.Damage; @@ -135,7 +137,7 @@ private static void InitialiseCraftingData() var itemsWithEnchantment = Main.LoadedData.EnchantmentIdToItem[enchantment.AssetGuid]; foreach (var item in itemsWithEnchantment) { - if (Main.ReverseEngineerEnchantmentCost(item, enchantment.AssetGuid)) + if (ReverseEngineerEnchantmentCost(item, enchantment.AssetGuid)) { break; } @@ -333,5 +335,59 @@ public static void ModEnabledChanged() } L10N.SetEnabled(Main.modEnabled); } + + /// + /// Attempt to work out the cost of enchantments which aren't in recipes by checking if blueprint, which contains the enchantment, contains only other + /// enchantments whose cost is known. + /// + public static bool ReverseEngineerEnchantmentCost(BlueprintItemEquipment blueprint, string enchantmentId) + { + if (blueprint == null || blueprint.IsNotable || blueprint.Ability != null || blueprint.ActivatableAbility != null) + { + return false; + } + + if (blueprint is BlueprintItemShield || blueprint is BlueprintItemWeapon || blueprint is BlueprintItemArmor) + { + // Cost of enchantments on arms and armor is different, and can be treated as a straight delta. + return true; + } + + var mostExpensiveEnchantmentCost = 0; + var costSum = 0; + foreach (var enchantment in blueprint.Enchantments) + { + if (enchantment.AssetGuid == enchantmentId) + { + continue; + } + + if (!Main.LoadedData.EnchantmentIdToRecipe.ContainsKey(enchantment.AssetGuid) && !Main.LoadedData.EnchantmentIdToCost.ContainsKey(enchantment.AssetGuid)) + { + return false; + } + + var enchantmentCost = Main.GetEnchantmentCost(enchantment.AssetGuid, blueprint); + costSum += enchantmentCost; + if (mostExpensiveEnchantmentCost < enchantmentCost) + { + mostExpensiveEnchantmentCost = enchantmentCost; + } + } + + var remainder = blueprint.Cost - 3 * costSum / 2; + if (remainder >= mostExpensiveEnchantmentCost) + { + // enchantmentId is the most expensive enchantment + Main.LoadedData.EnchantmentIdToCost[enchantmentId] = remainder; + } + else + { + // mostExpensiveEnchantmentCost is the most expensive enchantment + Main.LoadedData.EnchantmentIdToCost[enchantmentId] = (2 * remainder + mostExpensiveEnchantmentCost) / 3; + } + + return true; + } } } \ No newline at end of file From 9aa5389bdcd9450a0fa22dd2ca07bb043e0d0b33 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 20 Jul 2020 13:54:51 -0500 Subject: [PATCH 119/132] Moving BuildCustomRecipeItemDescription into CraftMagicItemsBlueprintPatcher as it is the only code referencing the method --- CraftMagicItems/Main.cs | 79 +------------ .../CraftMagicItemsBlueprintPatcher.cs | 105 +++++++++++++++++- 2 files changed, 106 insertions(+), 78 deletions(-) diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 97a4198..69bb76e 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -24,7 +24,6 @@ using Kingmaker.Blueprints.Items.Equipment; using Kingmaker.Blueprints.Items.Shields; using Kingmaker.Blueprints.Items.Weapons; -using Kingmaker.Blueprints.Loot; using Kingmaker.Blueprints.Root; using Kingmaker.Blueprints.Root.Strings.GameLog; using Kingmaker.Designers.Mechanics.EquipmentEnchants; @@ -39,7 +38,6 @@ using Kingmaker.Items.Slots; #endif using Kingmaker.Kingdom; -using Kingmaker.Localization; using Kingmaker.PubSubSystem; using Kingmaker.RuleSystem; using Kingmaker.RuleSystem.Rules.Abilities; @@ -777,7 +775,7 @@ private static IEnumerable PrependConditional(this IEnumerable target, return prepend ? items.Concat(target ?? throw new ArgumentException(nameof(target))) : target; } - private static string Join(this IEnumerable enumeration, string delimiter = ", ") { + public static string Join(this IEnumerable enumeration, string delimiter = ", ") { return enumeration.Aggregate("", (prev, curr) => prev + (prev != "" ? delimiter : "") + curr.ToString()); } @@ -976,7 +974,7 @@ private static bool DoesBlueprintMatchRestrictions(BlueprintItemEquipment bluepr return true; } - private static string GetBonusString(int bonus, RecipeData recipe) { + public static string GetBonusString(int bonus, RecipeData recipe) { bonus *= recipe.BonusMultiplier == 0 ? 1 : recipe.BonusMultiplier; return recipe.BonusDieSize != 0 ? new DiceFormula(bonus, recipe.BonusDieSize).ToString() : bonus.ToString(); } @@ -2468,79 +2466,6 @@ private static void RenderRecipeBasedCraftItemControl(UnitEntityData caster, Ite } } - public static LocalizedString BuildCustomRecipeItemDescription(BlueprintItem blueprint, IList enchantments, - IList skipped, IList removed, bool replaceAbility, string ability, int casterLevel, int perDay) { - var extraDescription = enchantments - .Select(enchantment => { - var recipe = FindSourceRecipe(enchantment.AssetGuid, blueprint); - if (recipe == null) { - if (skipped.Contains(enchantment)) { - return ""; - } else if (!string.IsNullOrEmpty(enchantment.Name)) { - return enchantment.Name; - } else { - return "Unknown"; - } - } else if (recipe.Enchantments.Length <= 1) { - if (skipped.Contains(enchantment)) { - return ""; - } else { - if (!string.IsNullOrEmpty(enchantment.Name)) { - return enchantment.Name; - } else { - return recipe.NameId; - } - } - } - var newBonus = recipe.Enchantments.FindIndex(e => e == enchantment) + 1; - var bonusString = GetBonusString(newBonus, recipe); - var bonusDescription = recipe.BonusTypeId != null - ? LocalizationHelper.FormatLocalizedString("craftMagicItems-custom-description-bonus-to", new L10NString(recipe.BonusTypeId), recipe.NameId) - : recipe.BonusToId != null - ? LocalizationHelper.FormatLocalizedString("craftMagicItems-custom-description-bonus-to", recipe.NameId, new L10NString(recipe.BonusToId)) - : LocalizationHelper.FormatLocalizedString("craftMagicItems-custom-description-bonus", recipe.NameId); - var upgradeFrom = removed.FirstOrDefault(remove => FindSourceRecipe(remove.AssetGuid, blueprint) == recipe); - var oldBonus = int.MaxValue; - if (upgradeFrom != null) { - oldBonus = recipe.Enchantments.FindIndex(e => e == upgradeFrom) + 1; - } - if (oldBonus > newBonus) { - if (skipped.Contains(enchantment)) { - return new L10NString(""); - } else { - return LocalizationHelper.FormatLocalizedString("craftMagicItems-custom-description-enchantment-template", bonusString, bonusDescription); - } - } else { - removed.Remove(upgradeFrom); - } - return LocalizationHelper.FormatLocalizedString("craftMagicItems-custom-description-enchantment-upgrade-template", bonusDescription, - GetBonusString(oldBonus, recipe), bonusString); - }) - .OrderBy(enchantmentDescription => enchantmentDescription) - .Select(enchantmentDescription => string.IsNullOrEmpty(enchantmentDescription) ? "" : "\n* " + enchantmentDescription) - .Join(""); - if (blueprint is BlueprintItemEquipment equipment && (ability != null && ability != "null" || casterLevel > -1 || perDay > -1)) { - GameLogContext.Count = equipment.Charges; - extraDescription += "\n* " + (equipment.Charges == 1 ? LocalizationHelper.FormatLocalizedString("craftMagicItems-label-cast-spell-n-times-details-single", equipment.Ability.Name, equipment.CasterLevel) : - LocalizationHelper.FormatLocalizedString("craftMagicItems-label-cast-spell-n-times-details-multiple", equipment.Ability.Name, equipment.CasterLevel, equipment.Charges)); - GameLogContext.Clear(); - } - - string description; - if (removed.Count == 0 && !replaceAbility) { - description = blueprint.Description; - if (extraDescription.Length > 0) { - description += new L10NString("craftMagicItems-custom-description-additional") + extraDescription; - } - } else if (extraDescription.Length > 0) { - description = new L10NString("craftMagicItems-custom-description-start") + extraDescription; - } else { - description = ""; - } - - return new FakeL10NString(description); - } - private static int ItemPlus(BlueprintItem blueprint) { switch (blueprint) { case BlueprintItemWeapon weapon: diff --git a/CraftMagicItems/Patches/CraftMagicItemsBlueprintPatcher.cs b/CraftMagicItems/Patches/CraftMagicItemsBlueprintPatcher.cs index c1b06a3..29bc6df 100644 --- a/CraftMagicItems/Patches/CraftMagicItemsBlueprintPatcher.cs +++ b/CraftMagicItems/Patches/CraftMagicItemsBlueprintPatcher.cs @@ -21,6 +21,7 @@ using Kingmaker.Localization; using Kingmaker.ResourceLinks; using Kingmaker.UI.Common; +using Kingmaker.UI.Log; using Kingmaker.UnitLogic.Abilities.Blueprints; using Kingmaker.UnitLogic.ActivatableAbilities; using Kingmaker.UnitLogic.Buffs.Blueprints; @@ -774,7 +775,7 @@ private string ApplyRecipeItemBlueprintPatch(BlueprintItemEquipment blueprint, M && (!DoesBlueprintShowEnchantments(blueprint) || enchantmentsForDescription.Count != skipped.Count || removed.Count > 0)) { accessors.SetBlueprintItemDescriptionText(blueprint) = - Main.BuildCustomRecipeItemDescription(blueprint, enchantmentsForDescription, skipped, removed, replaceAbility, ability, casterLevel, perDay); + BuildCustomRecipeItemDescription(blueprint, enchantmentsForDescription, skipped, removed, replaceAbility, ability, casterLevel, perDay); accessors.SetBlueprintItemFlavorText(blueprint) = new L10NString(""); } @@ -1083,5 +1084,107 @@ private string ApplyBlueprintPatch(BlueprintScriptableObject blueprint, Match ma } } } + + public static LocalizedString BuildCustomRecipeItemDescription(BlueprintItem blueprint, IList enchantments, + IList skipped, IList removed, bool replaceAbility, string ability, int casterLevel, int perDay) + { + var extraDescription = enchantments + .Select(enchantment => { + var recipe = Main.FindSourceRecipe(enchantment.AssetGuid, blueprint); + if (recipe == null) + { + if (skipped.Contains(enchantment)) + { + return ""; + } + else if (!string.IsNullOrEmpty(enchantment.Name)) + { + return enchantment.Name; + } + else + { + return "Unknown"; + } + } + else if (recipe.Enchantments.Length <= 1) + { + if (skipped.Contains(enchantment)) + { + return ""; + } + else + { + if (!string.IsNullOrEmpty(enchantment.Name)) + { + return enchantment.Name; + } + else + { + return recipe.NameId; + } + } + } + var newBonus = recipe.Enchantments.FindIndex(e => e == enchantment) + 1; + var bonusString = Main.GetBonusString(newBonus, recipe); + var bonusDescription = recipe.BonusTypeId != null + ? LocalizationHelper.FormatLocalizedString("craftMagicItems-custom-description-bonus-to", new L10NString(recipe.BonusTypeId), recipe.NameId) + : recipe.BonusToId != null + ? LocalizationHelper.FormatLocalizedString("craftMagicItems-custom-description-bonus-to", recipe.NameId, new L10NString(recipe.BonusToId)) + : LocalizationHelper.FormatLocalizedString("craftMagicItems-custom-description-bonus", recipe.NameId); + var upgradeFrom = removed.FirstOrDefault(remove => Main.FindSourceRecipe(remove.AssetGuid, blueprint) == recipe); + var oldBonus = int.MaxValue; + if (upgradeFrom != null) + { + oldBonus = recipe.Enchantments.FindIndex(e => e == upgradeFrom) + 1; + } + if (oldBonus > newBonus) + { + if (skipped.Contains(enchantment)) + { + return new L10NString(""); + } + else + { + return LocalizationHelper.FormatLocalizedString("craftMagicItems-custom-description-enchantment-template", bonusString, bonusDescription); + } + } + else + { + removed.Remove(upgradeFrom); + } + return LocalizationHelper.FormatLocalizedString("craftMagicItems-custom-description-enchantment-upgrade-template", bonusDescription, + Main.GetBonusString(oldBonus, recipe), bonusString); + }) + .OrderBy(enchantmentDescription => enchantmentDescription) + .Select(enchantmentDescription => string.IsNullOrEmpty(enchantmentDescription) ? "" : "\n* " + enchantmentDescription) + .Join(""); + if (blueprint is BlueprintItemEquipment equipment && (ability != null && ability != "null" || casterLevel > -1 || perDay > -1)) + { + GameLogContext.Count = equipment.Charges; + extraDescription += "\n* " + (equipment.Charges == 1 ? LocalizationHelper.FormatLocalizedString("craftMagicItems-label-cast-spell-n-times-details-single", equipment.Ability.Name, equipment.CasterLevel) : + LocalizationHelper.FormatLocalizedString("craftMagicItems-label-cast-spell-n-times-details-multiple", equipment.Ability.Name, equipment.CasterLevel, equipment.Charges)); + GameLogContext.Clear(); + } + + string description; + if (removed.Count == 0 && !replaceAbility) + { + description = blueprint.Description; + if (extraDescription.Length > 0) + { + description += new L10NString("craftMagicItems-custom-description-additional") + extraDescription; + } + } + else if (extraDescription.Length > 0) + { + description = new L10NString("craftMagicItems-custom-description-start") + extraDescription; + } + else + { + description = ""; + } + + return new FakeL10NString(description); + } } } \ No newline at end of file From 12bbb272d8de18a8fde71abb21f3f4a61ef9ce7c Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 20 Jul 2020 13:56:41 -0500 Subject: [PATCH 120/132] Moving AddRecipeForMaterial into MainMenuPatch, as it is the only code calling the method --- CraftMagicItems/Main.cs | 9 --------- .../Patches/Harmony/MainMenuStartPatch.cs | 14 +++++++++++++- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 69bb76e..0e0f673 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -2087,15 +2087,6 @@ public static void AddRecipeForEnchantment(string enchantmentId, RecipeData reci } } - public static void AddRecipeForMaterial(PhysicalDamageMaterial material, RecipeData recipe) { - if (!LoadedData.MaterialToRecipe.ContainsKey(material)) { - LoadedData.MaterialToRecipe.Add(material, new List()); - } - if (!LoadedData.MaterialToRecipe[material].Contains(recipe)) { - LoadedData.MaterialToRecipe[material].Add(recipe); - } - } - private static IEnumerable FindItemBlueprintForEnchantmentId(string assetGuid) { return LoadedData.EnchantmentIdToItem.ContainsKey(assetGuid) ? LoadedData.EnchantmentIdToItem[assetGuid] : null; } diff --git a/CraftMagicItems/Patches/Harmony/MainMenuStartPatch.cs b/CraftMagicItems/Patches/Harmony/MainMenuStartPatch.cs index dd4f334..356f833 100644 --- a/CraftMagicItems/Patches/Harmony/MainMenuStartPatch.cs +++ b/CraftMagicItems/Patches/Harmony/MainMenuStartPatch.cs @@ -68,7 +68,7 @@ private static void InitialiseCraftingData() recipe.Enchantments.ForEach(enchantment => Main.AddRecipeForEnchantment(enchantment.AssetGuid, recipe)); if (recipe.Material != 0) { - Main.AddRecipeForMaterial(recipe.Material, recipe); + AddRecipeForMaterial(recipe.Material, recipe); } if (recipe.ParentNameId != null) @@ -389,5 +389,17 @@ public static bool ReverseEngineerEnchantmentCost(BlueprintItemEquipment bluepri return true; } + + public static void AddRecipeForMaterial(PhysicalDamageMaterial material, RecipeData recipe) + { + if (!Main.LoadedData.MaterialToRecipe.ContainsKey(material)) + { + Main.LoadedData.MaterialToRecipe.Add(material, new List()); + } + if (!Main.LoadedData.MaterialToRecipe[material].Contains(recipe)) + { + Main.LoadedData.MaterialToRecipe[material].Add(recipe); + } + } } } \ No newline at end of file From f25619a3b6a4108f9df47b93e558bf1550de8f3c Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 20 Jul 2020 13:58:19 -0500 Subject: [PATCH 121/132] Moving AddRecipeForEnchantment into ManiMenuStartPatch, as it is the only code invoking the method --- CraftMagicItems/Main.cs | 10 ---------- .../Patches/Harmony/MainMenuStartPatch.cs | 15 ++++++++++++++- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 0e0f673..4a39bcd 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -2077,16 +2077,6 @@ public static void AddItemIdForEnchantment(BlueprintItemEquipment itemBlueprint) } } - public static void AddRecipeForEnchantment(string enchantmentId, RecipeData recipe) { - if (!LoadedData.EnchantmentIdToRecipe.ContainsKey(enchantmentId)) { - LoadedData.EnchantmentIdToRecipe.Add(enchantmentId, new List()); - } - - if (!LoadedData.EnchantmentIdToRecipe[enchantmentId].Contains(recipe)) { - LoadedData.EnchantmentIdToRecipe[enchantmentId].Add(recipe); - } - } - private static IEnumerable FindItemBlueprintForEnchantmentId(string assetGuid) { return LoadedData.EnchantmentIdToItem.ContainsKey(assetGuid) ? LoadedData.EnchantmentIdToItem[assetGuid] : null; } diff --git a/CraftMagicItems/Patches/Harmony/MainMenuStartPatch.cs b/CraftMagicItems/Patches/Harmony/MainMenuStartPatch.cs index 356f833..8123518 100644 --- a/CraftMagicItems/Patches/Harmony/MainMenuStartPatch.cs +++ b/CraftMagicItems/Patches/Harmony/MainMenuStartPatch.cs @@ -65,7 +65,7 @@ private static void InitialiseCraftingData() { recipe.ParentNameId = new L10NString(recipe.ParentNameId).ToString(); } - recipe.Enchantments.ForEach(enchantment => Main.AddRecipeForEnchantment(enchantment.AssetGuid, recipe)); + recipe.Enchantments.ForEach(enchantment => AddRecipeForEnchantment(enchantment.AssetGuid, recipe)); if (recipe.Material != 0) { AddRecipeForMaterial(recipe.Material, recipe); @@ -401,5 +401,18 @@ public static void AddRecipeForMaterial(PhysicalDamageMaterial material, RecipeD Main.LoadedData.MaterialToRecipe[material].Add(recipe); } } + + public static void AddRecipeForEnchantment(string enchantmentId, RecipeData recipe) + { + if (!Main.LoadedData.EnchantmentIdToRecipe.ContainsKey(enchantmentId)) + { + Main.LoadedData.EnchantmentIdToRecipe.Add(enchantmentId, new List()); + } + + if (!Main.LoadedData.EnchantmentIdToRecipe[enchantmentId].Contains(recipe)) + { + Main.LoadedData.EnchantmentIdToRecipe[enchantmentId].Add(recipe); + } + } } } \ No newline at end of file From 6a69602c18d503a01fa008c04a90918f757a68d0 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 20 Jul 2020 13:59:58 -0500 Subject: [PATCH 122/132] Moving AddItemIdForEnchantment into MainMenuStartPatch --- CraftMagicItems/Main.cs | 12 ------------ .../Patches/Harmony/MainMenuStartPatch.cs | 18 +++++++++++++++++- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 4a39bcd..e010cb9 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -2065,18 +2065,6 @@ public static void AddItemForType(BlueprintItem blueprint) { } } - public static void AddItemIdForEnchantment(BlueprintItemEquipment itemBlueprint) { - if (itemBlueprint != null) { - foreach (var enchantment in GetEnchantments(itemBlueprint)) { - if (!LoadedData.EnchantmentIdToItem.ContainsKey(enchantment.AssetGuid)) { - LoadedData.EnchantmentIdToItem[enchantment.AssetGuid] = new List(); - } - - LoadedData.EnchantmentIdToItem[enchantment.AssetGuid].Add(itemBlueprint); - } - } - } - private static IEnumerable FindItemBlueprintForEnchantmentId(string assetGuid) { return LoadedData.EnchantmentIdToItem.ContainsKey(assetGuid) ? LoadedData.EnchantmentIdToItem[assetGuid] : null; } diff --git a/CraftMagicItems/Patches/Harmony/MainMenuStartPatch.cs b/CraftMagicItems/Patches/Harmony/MainMenuStartPatch.cs index 8123518..48e06d6 100644 --- a/CraftMagicItems/Patches/Harmony/MainMenuStartPatch.cs +++ b/CraftMagicItems/Patches/Harmony/MainMenuStartPatch.cs @@ -109,7 +109,7 @@ private static void InitialiseCraftingData() var allUsableItems = ResourcesLibrary.GetBlueprints(); foreach (var item in allUsableItems) { - Main.AddItemIdForEnchantment(item); + AddItemIdForEnchantment(item); } var allNonRecipeEnchantmentsInItems = ResourcesLibrary.GetBlueprints() @@ -414,5 +414,21 @@ public static void AddRecipeForEnchantment(string enchantmentId, RecipeData reci Main.LoadedData.EnchantmentIdToRecipe[enchantmentId].Add(recipe); } } + + public static void AddItemIdForEnchantment(BlueprintItemEquipment itemBlueprint) + { + if (itemBlueprint != null) + { + foreach (var enchantment in Main.GetEnchantments(itemBlueprint)) + { + if (!Main.LoadedData.EnchantmentIdToItem.ContainsKey(enchantment.AssetGuid)) + { + Main.LoadedData.EnchantmentIdToItem[enchantment.AssetGuid] = new List(); + } + + Main.LoadedData.EnchantmentIdToItem[enchantment.AssetGuid].Add(itemBlueprint); + } + } + } } } \ No newline at end of file From d6b68b48bbe3d3e9f7f3a9c0c1b60aa88e63725f Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 20 Jul 2020 14:02:40 -0500 Subject: [PATCH 123/132] Moving AddItemForType into MainMenuStartPatch --- CraftMagicItems/Main.cs | 9 +-------- .../Patches/Harmony/MainMenuStartPatch.cs | 12 +++++++++++- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index e010cb9..9b4507b 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -655,7 +655,7 @@ public static BlueprintItem GetBaseBlueprint(BlueprintItem blueprint) { } } - private static string GetBlueprintItemType(BlueprintItem blueprint) { + public static string GetBlueprintItemType(BlueprintItem blueprint) { string assetGuid = null; switch (blueprint) { case BlueprintItemArmor armor: assetGuid = armor.Type.AssetGuid; break; @@ -2058,13 +2058,6 @@ public static List FindItemBlueprintsForSpell(BlueprintS return LoadedData.SpellIdToItem[itemType].ContainsKey(spell.AssetGuid) ? LoadedData.SpellIdToItem[itemType][spell.AssetGuid] : null; } - public static void AddItemForType(BlueprintItem blueprint) { - string assetGuid = GetBlueprintItemType(blueprint); - if (!string.IsNullOrEmpty(assetGuid)) { - LoadedData.TypeToItem.Add(assetGuid, blueprint); - } - } - private static IEnumerable FindItemBlueprintForEnchantmentId(string assetGuid) { return LoadedData.EnchantmentIdToItem.ContainsKey(assetGuid) ? LoadedData.EnchantmentIdToItem[assetGuid] : null; } diff --git a/CraftMagicItems/Patches/Harmony/MainMenuStartPatch.cs b/CraftMagicItems/Patches/Harmony/MainMenuStartPatch.cs index 48e06d6..3e784f6 100644 --- a/CraftMagicItems/Patches/Harmony/MainMenuStartPatch.cs +++ b/CraftMagicItems/Patches/Harmony/MainMenuStartPatch.cs @@ -8,6 +8,7 @@ using Kingmaker.Blueprints; using Kingmaker.Blueprints.Classes; using Kingmaker.Blueprints.Classes.Selection; +using Kingmaker.Blueprints.Items; using Kingmaker.Blueprints.Items.Armors; using Kingmaker.Blueprints.Items.Ecnchantments; using Kingmaker.Blueprints.Items.Equipment; @@ -89,7 +90,7 @@ private static void InitialiseCraftingData() { if (!blueprint.AssetGuid.Contains("#CraftMagicItems")) { - Main.AddItemForType(blueprint); + AddItemForType(blueprint); } } } @@ -430,5 +431,14 @@ public static void AddItemIdForEnchantment(BlueprintItemEquipment itemBlueprint) } } } + + public static void AddItemForType(BlueprintItem blueprint) + { + string assetGuid = Main.GetBlueprintItemType(blueprint); + if (!string.IsNullOrEmpty(assetGuid)) + { + Main.LoadedData.TypeToItem.Add(assetGuid, blueprint); + } + } } } \ No newline at end of file From 8312499cfb6329409abfa18f3215947f36ce7719 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 20 Jul 2020 14:24:25 -0500 Subject: [PATCH 124/132] Extracting WorkOnProjects into a common class for crafting logic, since none of the references to the method were in the Main class --- CraftMagicItems/CraftMagicItems.csproj | 1 + CraftMagicItems/CraftingLogic.cs | 310 ++++++++++++++++++ CraftMagicItems/Main.cs | 276 ++-------------- ...apitalCompanionLogicOnFactActivatePatch.cs | 2 +- .../Harmony/RestControllerApplyRestPatch.cs | 2 +- 5 files changed, 333 insertions(+), 258 deletions(-) create mode 100644 CraftMagicItems/CraftingLogic.cs diff --git a/CraftMagicItems/CraftMagicItems.csproj b/CraftMagicItems/CraftMagicItems.csproj index de6fe6a..742cf62 100644 --- a/CraftMagicItems/CraftMagicItems.csproj +++ b/CraftMagicItems/CraftMagicItems.csproj @@ -106,6 +106,7 @@ + diff --git a/CraftMagicItems/CraftingLogic.cs b/CraftMagicItems/CraftingLogic.cs new file mode 100644 index 0000000..9bf17d2 --- /dev/null +++ b/CraftMagicItems/CraftingLogic.cs @@ -0,0 +1,310 @@ +using System; +using System.Linq; +using CraftMagicItems.Constants; +using CraftMagicItems.Localization; +using Kingmaker; +using Kingmaker.Blueprints.Classes.Spells; +using Kingmaker.Blueprints.Items.Equipment; +using Kingmaker.Blueprints.Root; +using Kingmaker.EntitySystem.Stats; +using Kingmaker.UI.Log; +using Kingmaker.UnitLogic; +using Kingmaker.Utility; + +namespace CraftMagicItems +{ + /// Core logic class for crafting items + public static class CraftingLogic + { + public static void WorkOnProjects(UnitDescriptor caster, bool returningToCapital) + { + if (!caster.IsPlayerFaction || caster.State.IsDead || caster.State.IsFinallyDead) + { + return; + } + + Main.Selections.CurrentCaster = caster.Unit; + var withPlayer = Game.Instance.Player.PartyCharacters.Contains(caster.Unit); + var playerInCapital = Main.IsPlayerInCapital(); + // Only update characters in the capital when the player is also there. + if (!withPlayer && !playerInCapital) + { + // Character is back in the capital - skipping them for now. + return; + } + + var isAdventuring = withPlayer && !Main.IsPlayerSomewhereSafe(); + var timer = Main.GetCraftingTimerComponentForCaster(caster); + if (timer == null || timer.CraftingProjects.Count == 0) + { + // Character is not doing any crafting + return; + } + + // Round up the number of days, so caster makes some progress on a new project the first time they rest. + var interval = Game.Instance.Player.GameTime.Subtract(timer.LastUpdated); + var daysAvailableToCraft = (int)Math.Ceiling(interval.TotalDays); + if (daysAvailableToCraft <= 0) + { + if (isAdventuring) + { + Main.AddBattleLogMessage(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-not-full-day")); + } + + return; + } + + // Time passes for this character even if they end up making no progress on their projects. LastUpdated can go up to + // a day into the future, due to the rounding up of daysAvailableToCraft. + timer.LastUpdated += TimeSpan.FromDays(daysAvailableToCraft); + // Work on projects sequentially, skipping any that can't progress due to missing items, missing prerequisites or having too high a DC. + foreach (var project in timer.CraftingProjects.ToList()) + { + if (project.UpgradeItem != null) + { + // Check if the item has been dropped and picked up again, which apparently creates a new object with the same blueprint. + if (project.UpgradeItem.Collection != Game.Instance.Player.Inventory && project.UpgradeItem.Collection != Game.Instance.Player.SharedStash) + { + var itemInStash = Game.Instance.Player.SharedStash.FirstOrDefault(item => item.Blueprint == project.UpgradeItem.Blueprint); + if (itemInStash != null) + { + Main.ItemUpgradeProjects.Remove(project.UpgradeItem); + Main.ItemUpgradeProjects[itemInStash] = project; + project.UpgradeItem = itemInStash; + } + else + { + var itemInInventory = Game.Instance.Player.Inventory.FirstOrDefault(item => item.Blueprint == project.UpgradeItem.Blueprint); + if (itemInInventory != null) + { + Main.ItemUpgradeProjects.Remove(project.UpgradeItem); + Main.ItemUpgradeProjects[itemInInventory] = project; + project.UpgradeItem = itemInInventory; + } + } + } + + // Check that the caster can get at the item they're upgrading... it must be in the party inventory, and either un-wielded, or the crafter + // must be with the wielder (together in the capital or out in the party together). + var wieldedInParty = (project.UpgradeItem.Wielder == null || Game.Instance.Player.PartyCharacters.Contains(project.UpgradeItem.Wielder.Unit)); + if ((!playerInCapital || returningToCapital) + && (project.UpgradeItem.Collection != Game.Instance.Player.SharedStash || withPlayer) + && (project.UpgradeItem.Collection != Game.Instance.Player.Inventory || ((!withPlayer || !wieldedInParty) && (withPlayer || wieldedInParty)))) + { + project.AddMessage(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-missing-upgrade-item", project.UpgradeItem.Blueprint.Name)); + Main.AddBattleLogMessage(project.LastMessage); + continue; + } + } + + var craftingData = Main.LoadedData.ItemCraftingData.FirstOrDefault(data => data.Name == project.ItemType); + StatType craftingSkill; + int dc; + int progressRate; + + if (project.ItemType == Main.BondedItemRitual) + { + craftingSkill = StatType.SkillKnowledgeArcana; + dc = 10 + project.Crafter.Stats.GetStat(craftingSkill).ModifiedValue; + progressRate = Main.ModSettings.MagicCraftingRate; + } + else if (Main.IsMundaneCraftingData(craftingData)) + { + craftingSkill = StatType.SkillKnowledgeWorld; + dc = Main.CalculateMundaneCraftingDC((RecipeBasedItemCraftingData)craftingData, project.ResultItem.Blueprint, caster); + progressRate = Main.ModSettings.MundaneCraftingRate; + } + else + { + craftingSkill = StatType.SkillKnowledgeArcana; + dc = 5 + project.CasterLevel; + progressRate = Main.ModSettings.MagicCraftingRate; + } + + var missing = Main.CheckSpellPrerequisites(project, caster, isAdventuring, out var missingSpells, out var spellsToCast); + if (missing > 0) + { + var missingSpellNames = missingSpells + .Select(ability => ability.Name) + .BuildCommaList(project.AnyPrerequisite); + + if (craftingData.PrerequisitesMandatory || project.PrerequisitesMandatory) + { + project.AddMessage(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-missing-prerequisite", + project.ResultItem.Name, missingSpellNames)); + Main.AddBattleLogMessage(project.LastMessage); + // If the item type has mandatory prerequisites and some are missing, move on to the next project. + continue; + } + + Main.AddBattleLogMessage(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-missing-spell", missingSpellNames, + DifficultyClass.MissingPrerequisiteDCModifier * missing)); + } + var missing2 = Main.CheckFeatPrerequisites(project, caster, out var missingFeats); + if (missing2 > 0) + { + var missingFeatNames = missingFeats.Select(ability => ability.Name).BuildCommaList(project.AnyPrerequisite); + Main.AddBattleLogMessage(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-missing-feat", missingFeatNames, + DifficultyClass.MissingPrerequisiteDCModifier * missing2)); + } + missing += missing2; + missing += Main.CheckCrafterPrerequisites(project, caster); + dc += DifficultyClass.MissingPrerequisiteDCModifier * missing; + var casterLevel = Main.CharacterCasterLevel(caster); + if (casterLevel < project.CasterLevel) + { + // Rob's ruling... if you're below the prerequisite caster level, you're considered to be missing a prerequisite for each + // level you fall short, unless ModSettings.CasterLevelIsSinglePrerequisite is true. + var casterLevelPenalty = Main.ModSettings.CasterLevelIsSinglePrerequisite + ? DifficultyClass.MissingPrerequisiteDCModifier + : DifficultyClass.MissingPrerequisiteDCModifier * (project.CasterLevel - casterLevel); + dc += casterLevelPenalty; + Main.AddBattleLogMessage(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-low-caster-level", project.CasterLevel, casterLevelPenalty)); + } + var oppositionSchool = Main.CheckForOppositionSchool(caster, project.SpellPrerequisites); + if (oppositionSchool != SpellSchool.None) + { + dc += DifficultyClass.OppositionSchoolDCModifier; + Main.AddBattleLogMessage(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-opposition-school", + LocalizedTexts.Instance.SpellSchoolNames.GetText(oppositionSchool), DifficultyClass.OppositionSchoolDCModifier)); + } + + var skillCheck = 10 + caster.Stats.GetStat(craftingSkill).ModifiedValue; + if (skillCheck < dc) + { + // Can't succeed by taking 10... move on to the next project. + project.AddMessage(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-dc-too-high", project.ResultItem.Name, + LocalizedTexts.Instance.Stats.GetText(craftingSkill), skillCheck, dc)); + Main.AddBattleLogMessage(project.LastMessage); + continue; + } + + // Cleared the last hurdle, so caster is going to make progress on this project. + // You only work at 1/4 speed if you're crafting while adventuring. + var adventuringPenalty = !isAdventuring || Main.ModSettings.CraftAtFullSpeedWhileAdventuring ? 1 : DifficultyClass.AdventuringProgressPenalty; + // Each 1 by which the skill check exceeds the DC increases the crafting rate by 20% of the base progressRate + var progressPerDay = (int)(progressRate * (1 + (float)(skillCheck - dc) / 5) / adventuringPenalty); + var daysUntilProjectFinished = (int)Math.Ceiling(1.0 * (project.TargetCost - project.Progress) / progressPerDay); + var daysCrafting = Math.Min(daysUntilProjectFinished, daysAvailableToCraft); + var progressGold = daysCrafting * progressPerDay; + foreach (var spell in spellsToCast) + { + if (spell.SourceItem != null) + { + // Use items whether we're adventuring or not, one charge per day of daysCrafting. We might run out of charges... + if (spell.SourceItem.IsSpendCharges && !((BlueprintItemEquipment)spell.SourceItem.Blueprint).RestoreChargesOnRest) + { + var itemSpell = spell; + for (var day = 0; day < daysCrafting; ++day) + { + if (itemSpell.SourceItem.Charges <= 0) + { + // This item is exhausted and we haven't finished crafting - find another item. + itemSpell = Main.FindCasterSpell(caster, spell.Blueprint, isAdventuring, spellsToCast); + } + + if (itemSpell == null) + { + // We've run out of items that can cast the spell...crafting progress is going to slow, if not stop. + progressGold -= progressPerDay * (daysCrafting - day); + skillCheck -= DifficultyClass.MissingPrerequisiteDCModifier; + if (craftingData.PrerequisitesMandatory || project.PrerequisitesMandatory) + { + Main.AddBattleLogMessage(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-missing-prerequisite", project.ResultItem.Name, spell.Name)); + daysCrafting = day; + break; + } + + Main.AddBattleLogMessage(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-missing-spell", spell.Name, DifficultyClass.MissingPrerequisiteDCModifier)); + if (skillCheck < dc) + { + // Can no longer make progress + Main.AddBattleLogMessage(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-dc-too-high", project.ResultItem.Name, + LocalizedTexts.Instance.Stats.GetText(craftingSkill), skillCheck, dc)); + daysCrafting = day; + } + else + { + // Progress will be slower, but they don't need to cast this spell any longer. + progressPerDay = (int)(progressRate * (1 + (float)(skillCheck - dc) / 5) / adventuringPenalty); + daysUntilProjectFinished = + day + (int)Math.Ceiling(1.0 * (project.TargetCost - project.Progress - progressGold) / progressPerDay); + daysCrafting = Math.Min(daysUntilProjectFinished, daysAvailableToCraft); + progressGold += (daysCrafting - day) * progressPerDay; + } + + break; + } + + GameLogContext.SourceUnit = caster.Unit; + GameLogContext.Text = itemSpell.SourceItem.Name; + Main.AddBattleLogMessage(LocalizedStringBlueprints.CharacterUsedItemLocalized); + GameLogContext.Clear(); + itemSpell.SourceItem.SpendCharges(caster); + } + } + } + else if (isAdventuring) + { + // Actually cast the spells if we're adventuring. + Main.AddBattleLogMessage(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-expend-spell", spell.Name)); + spell.SpendFromSpellbook(); + } + } + + var progressKey = project.ItemType == Main.BondedItemRitual + ? "craftMagicItems-logMessage-made-progress-bondedItem" + : "craftMagicItems-logMessage-made-progress"; + var progress = LocalizationHelper.FormatLocalizedString(progressKey, progressGold, project.TargetCost - project.Progress, project.ResultItem.Name); + var checkResult = LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-made-progress-check", LocalizedTexts.Instance.Stats.GetText(craftingSkill), + skillCheck, dc); + Main.AddBattleLogMessage(progress, checkResult); + daysAvailableToCraft -= daysCrafting; + project.Progress += progressGold; + if (project.Progress >= project.TargetCost) + { + // Completed the project! + if (project.ItemType == Main.BondedItemRitual) + { + Main.AddBattleLogMessage(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-bonding-ritual-complete", project.ResultItem.Name), project.ResultItem); + Main.BondWithObject(project.Crafter, project.ResultItem); + } + else + { + Main.AddBattleLogMessage(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-crafting-complete", project.ResultItem.Name), project.ResultItem); + Main.CraftItem(project.ResultItem, project.UpgradeItem); + } + timer.CraftingProjects.Remove(project); + if (project.UpgradeItem == null) + { + Main.ItemCreationProjects.Remove(project); + } + else + { + Main.ItemUpgradeProjects.Remove(project.UpgradeItem); + } + } + else + { + var completeKey = project.ItemType == Main.BondedItemRitual + ? "craftMagicItems-logMessage-made-progress-bonding-ritual-amount-complete" + : "craftMagicItems-logMessage-made-progress-amount-complete"; + var amountComplete = LocalizationHelper.FormatLocalizedString(completeKey, project.ResultItem.Name, 100 * project.Progress / project.TargetCost); + Main.AddBattleLogMessage(amountComplete, project.ResultItem); + project.AddMessage($"{progress} {checkResult}"); + } + + if (daysAvailableToCraft <= 0) + { + return; + } + } + + if (daysAvailableToCraft > 0) + { + // They didn't use up all available days - reset the time they can start crafting to now. + timer.LastUpdated = Game.Instance.Player.GameTime; + } + } + } +} \ No newline at end of file diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 9b4507b..4da1aed 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -63,7 +63,7 @@ namespace CraftMagicItems { public static class Main { - private const string BondedItemRitual = "bondedItemRitual"; + public const string BondedItemRitual = "bondedItemRitual"; private const string CustomPriceLabel = "Crafting Cost: "; @@ -396,7 +396,7 @@ private static void UnBondFromCurrentBondedItem(UnitEntityData caster) { } } - private static void BondWithObject(UnitEntityData caster, ItemEntity item) { + public static void BondWithObject(UnitEntityData caster, ItemEntity item) { UnBondFromCurrentBondedItem(caster); var bondedComponent = GetBondedItemComponentForCaster(caster.Descriptor, true); bondedComponent.ownerItem = item; @@ -779,7 +779,7 @@ public static string Join(this IEnumerable enumeration, string delimiter = return enumeration.Aggregate("", (prev, curr) => prev + (prev != "" ? delimiter : "") + curr.ToString()); } - private static string BuildCommaList(this IEnumerable list, bool or) { + public static string BuildCommaList(this IEnumerable list, bool or) { var array = list.ToArray(); if (array.Length < 2) { return array.Join(); @@ -1570,7 +1570,7 @@ public static int CharacterCasterLevel(UnitDescriptor character, Spellbook forSp return casterLevel; } - private static SpellSchool CheckForOppositionSchool(UnitDescriptor crafter, BlueprintAbility[] prerequisiteSpells) { + public static SpellSchool CheckForOppositionSchool(UnitDescriptor crafter, BlueprintAbility[] prerequisiteSpells) { if (prerequisiteSpells != null) { foreach (var spell in prerequisiteSpells) { if (crafter.Spellbooks.Any(spellbook => spellbook.Blueprint.SpellList.Contains(spell) @@ -1712,7 +1712,7 @@ private static int CalculateBaseMundaneCraftingDC(RecipeBasedItemCraftingData cr return dc; } - private static int CalculateMundaneCraftingDC(RecipeBasedItemCraftingData craftingData, BlueprintItem blueprint, UnitDescriptor crafter, + public static int CalculateMundaneCraftingDC(RecipeBasedItemCraftingData craftingData, BlueprintItem blueprint, UnitDescriptor crafter, RecipeData recipe = null) { var dc = CalculateBaseMundaneCraftingDC(craftingData, blueprint, crafter); return dc + (recipe?.MundaneDC ?? blueprint.Enchantments @@ -1952,7 +1952,7 @@ private static void RenderProjectsSection() { } } - private static bool IsPlayerSomewhereSafe() { + public static bool IsPlayerSomewhereSafe() { if (Game.Instance.CurrentlyLoadedArea != null && Areas.SafeBlueprintAreaGuids.Contains(Game.Instance.CurrentlyLoadedArea.AssetGuid)) { return true; } @@ -1960,7 +1960,7 @@ private static bool IsPlayerSomewhereSafe() { return IsPlayerInCapital(); } - private static bool IsPlayerInCapital() { + public static bool IsPlayerInCapital() { // Detect if the player is in the capital, or in kingdom management from the throne room. return (Game.Instance.CurrentlyLoadedArea != null && Game.Instance.CurrentlyLoadedArea.IsCapital) || (Game.Instance.CurrentMode == GameModeType.Kingdom && KingdomTimelineManager.CanAdvanceTime()); @@ -2071,7 +2071,7 @@ private static BlueprintItemEquipment RandomBaseBlueprintId(ItemCraftingData ite return blueprintIds[RandomGenerator.Next(blueprintIds.Length)]; } - private static void CraftItem(ItemEntity resultItem, ItemEntity upgradeItem = null) { + public static void CraftItem(ItemEntity resultItem, ItemEntity upgradeItem = null) { var characters = UIUtility.GetGroup(true).Where(character => character.IsPlayerFaction && !character.Descriptor.IsPet); foreach (var character in characters) { var bondedComponent = GetBondedItemComponentForCaster(character.Descriptor); @@ -2314,7 +2314,7 @@ private static void BeginCraftingSpellBasedItem(UnitEntityData caster, SpellBase } } - private static bool IsMundaneCraftingData(ItemCraftingData craftingData) { + public static bool IsMundaneCraftingData(ItemCraftingData craftingData) { return craftingData.FeatGuid == null; } @@ -2668,7 +2668,7 @@ public static void AddBattleLogMessage(string message, object tooltip = null, Co } } - private static AbilityData FindCasterSpell(UnitDescriptor caster, BlueprintAbility spellBlueprint, bool mustHavePrepared, + public static AbilityData FindCasterSpell(UnitDescriptor caster, BlueprintAbility spellBlueprint, bool mustHavePrepared, IReadOnlyCollection spellsToCast) { foreach (var spellbook in caster.Spellbooks) { var spellLevel = spellbook.GetSpellLevel(spellBlueprint); @@ -2729,7 +2729,7 @@ private static AbilityData FindCasterSpell(UnitDescriptor caster, BlueprintAbili return fromItem?.Ability?.Data; } - private static int CheckSpellPrerequisites(CraftingProjectData project, UnitDescriptor caster, bool mustPrepare, + public static int CheckSpellPrerequisites(CraftingProjectData project, UnitDescriptor caster, bool mustPrepare, out List missingSpells, out List spellsToCast) { return CheckSpellPrerequisites(project.SpellPrerequisites, project.AnyPrerequisite, caster, mustPrepare, out missingSpells, out spellsToCast); } @@ -2756,7 +2756,7 @@ private static int CheckSpellPrerequisites(BlueprintAbility[] prerequisites, boo return anyPrerequisite ? Math.Min(1, missingSpells.Count) : missingSpells.Count; } - private static int CheckFeatPrerequisites(CraftingProjectData project, UnitDescriptor caster, out List missingFeats) { + public static int CheckFeatPrerequisites(CraftingProjectData project, UnitDescriptor caster, out List missingFeats) { return CheckFeatPrerequisites(project.FeatPrerequisites, project.AnyPrerequisite, caster, out missingFeats); } @@ -2780,7 +2780,7 @@ private static int CheckFeatPrerequisites(BlueprintFeature[] prerequisites, bool return anyPrerequisite ? Math.Min(1, missingFeats.Count) : missingFeats.Count; } - private static int CheckCrafterPrerequisites(CraftingProjectData project, UnitDescriptor caster) { + public static int CheckCrafterPrerequisites(CraftingProjectData project, UnitDescriptor caster) { var missing = GetMissingCrafterPrerequisites(project.CrafterPrerequisites, caster); foreach (var prerequisite in missing) { AddBattleLogMessage(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-missing-crafter-prerequisite", @@ -2806,251 +2806,15 @@ private static List GetMissingCrafterPrerequisites(Craf return missingCrafterPrerequisites; } - public static void WorkOnProjects(UnitDescriptor caster, bool returningToCapital) { - if (!caster.IsPlayerFaction || caster.State.IsDead || caster.State.IsFinallyDead) { - return; - } - - Selections.CurrentCaster = caster.Unit; - var withPlayer = Game.Instance.Player.PartyCharacters.Contains(caster.Unit); - var playerInCapital = IsPlayerInCapital(); - // Only update characters in the capital when the player is also there. - if (!withPlayer && !playerInCapital) { - // Character is back in the capital - skipping them for now. - return; - } - - var isAdventuring = withPlayer && !IsPlayerSomewhereSafe(); - var timer = GetCraftingTimerComponentForCaster(caster); - if (timer == null || timer.CraftingProjects.Count == 0) { - // Character is not doing any crafting - return; - } - - // Round up the number of days, so caster makes some progress on a new project the first time they rest. - var interval = Game.Instance.Player.GameTime.Subtract(timer.LastUpdated); - var daysAvailableToCraft = (int) Math.Ceiling(interval.TotalDays); - if (daysAvailableToCraft <= 0) { - if (isAdventuring) { - AddBattleLogMessage(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-not-full-day")); - } - - return; - } - - // Time passes for this character even if they end up making no progress on their projects. LastUpdated can go up to - // a day into the future, due to the rounding up of daysAvailableToCraft. - timer.LastUpdated += TimeSpan.FromDays(daysAvailableToCraft); - // Work on projects sequentially, skipping any that can't progress due to missing items, missing prerequisites or having too high a DC. - foreach (var project in timer.CraftingProjects.ToList()) { - if (project.UpgradeItem != null) { - // Check if the item has been dropped and picked up again, which apparently creates a new object with the same blueprint. - if (project.UpgradeItem.Collection != Game.Instance.Player.Inventory && project.UpgradeItem.Collection != Game.Instance.Player.SharedStash) { - var itemInStash = Game.Instance.Player.SharedStash.FirstOrDefault(item => item.Blueprint == project.UpgradeItem.Blueprint); - if (itemInStash != null) { - ItemUpgradeProjects.Remove(project.UpgradeItem); - ItemUpgradeProjects[itemInStash] = project; - project.UpgradeItem = itemInStash; - } else { - var itemInInventory = Game.Instance.Player.Inventory.FirstOrDefault(item => item.Blueprint == project.UpgradeItem.Blueprint); - if (itemInInventory != null) { - ItemUpgradeProjects.Remove(project.UpgradeItem); - ItemUpgradeProjects[itemInInventory] = project; - project.UpgradeItem = itemInInventory; - } - } - } - - // Check that the caster can get at the item they're upgrading... it must be in the party inventory, and either un-wielded, or the crafter - // must be with the wielder (together in the capital or out in the party together). - var wieldedInParty = (project.UpgradeItem.Wielder == null || Game.Instance.Player.PartyCharacters.Contains(project.UpgradeItem.Wielder.Unit)); - if ((!playerInCapital || returningToCapital) - && (project.UpgradeItem.Collection != Game.Instance.Player.SharedStash || withPlayer) - && (project.UpgradeItem.Collection != Game.Instance.Player.Inventory || ((!withPlayer || !wieldedInParty) && (withPlayer || wieldedInParty)))) { - project.AddMessage(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-missing-upgrade-item", project.UpgradeItem.Blueprint.Name)); - AddBattleLogMessage(project.LastMessage); - continue; - } - } - - var craftingData = LoadedData.ItemCraftingData.FirstOrDefault(data => data.Name == project.ItemType); - StatType craftingSkill; - int dc; - int progressRate; - - if (project.ItemType == BondedItemRitual) { - craftingSkill = StatType.SkillKnowledgeArcana; - dc = 10 + project.Crafter.Stats.GetStat(craftingSkill).ModifiedValue; - progressRate = ModSettings.MagicCraftingRate; - } else if (IsMundaneCraftingData(craftingData)) { - craftingSkill = StatType.SkillKnowledgeWorld; - dc = CalculateMundaneCraftingDC((RecipeBasedItemCraftingData) craftingData, project.ResultItem.Blueprint, caster); - progressRate = ModSettings.MundaneCraftingRate; - } else { - craftingSkill = StatType.SkillKnowledgeArcana; - dc = 5 + project.CasterLevel; - progressRate = ModSettings.MagicCraftingRate; - } - - var missing = CheckSpellPrerequisites(project, caster, isAdventuring, out var missingSpells, out var spellsToCast); - if (missing > 0) { - var missingSpellNames = missingSpells.Select(ability => ability.Name).BuildCommaList(project.AnyPrerequisite); - if (craftingData.PrerequisitesMandatory || project.PrerequisitesMandatory) { - project.AddMessage(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-missing-prerequisite", - project.ResultItem.Name, missingSpellNames)); - AddBattleLogMessage(project.LastMessage); - // If the item type has mandatory prerequisites and some are missing, move on to the next project. - continue; - } - - AddBattleLogMessage(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-missing-spell", missingSpellNames, - DifficultyClass.MissingPrerequisiteDCModifier * missing)); - } - var missing2 = CheckFeatPrerequisites(project, caster, out var missingFeats); - if (missing2 > 0) { - var missingFeatNames = missingFeats.Select(ability => ability.Name).BuildCommaList(project.AnyPrerequisite); - AddBattleLogMessage(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-missing-feat", missingFeatNames, - DifficultyClass.MissingPrerequisiteDCModifier * missing2)); - } - missing += missing2; - missing += CheckCrafterPrerequisites(project, caster); - dc += DifficultyClass.MissingPrerequisiteDCModifier * missing; - var casterLevel = CharacterCasterLevel(caster); - if (casterLevel < project.CasterLevel) { - // Rob's ruling... if you're below the prerequisite caster level, you're considered to be missing a prerequisite for each - // level you fall short, unless ModSettings.CasterLevelIsSinglePrerequisite is true. - var casterLevelPenalty = ModSettings.CasterLevelIsSinglePrerequisite - ? DifficultyClass.MissingPrerequisiteDCModifier - : DifficultyClass.MissingPrerequisiteDCModifier * (project.CasterLevel - casterLevel); - dc += casterLevelPenalty; - AddBattleLogMessage(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-low-caster-level", project.CasterLevel, casterLevelPenalty)); - } - var oppositionSchool = CheckForOppositionSchool(caster, project.SpellPrerequisites); - if (oppositionSchool != SpellSchool.None) { - dc += DifficultyClass.OppositionSchoolDCModifier; - AddBattleLogMessage(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-opposition-school", - LocalizedTexts.Instance.SpellSchoolNames.GetText(oppositionSchool), DifficultyClass.OppositionSchoolDCModifier)); - } - - var skillCheck = 10 + caster.Stats.GetStat(craftingSkill).ModifiedValue; - if (skillCheck < dc) { - // Can't succeed by taking 10... move on to the next project. - project.AddMessage(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-dc-too-high", project.ResultItem.Name, - LocalizedTexts.Instance.Stats.GetText(craftingSkill), skillCheck, dc)); - AddBattleLogMessage(project.LastMessage); - continue; - } - - // Cleared the last hurdle, so caster is going to make progress on this project. - // You only work at 1/4 speed if you're crafting while adventuring. - var adventuringPenalty = !isAdventuring || ModSettings.CraftAtFullSpeedWhileAdventuring ? 1 : DifficultyClass.AdventuringProgressPenalty; - // Each 1 by which the skill check exceeds the DC increases the crafting rate by 20% of the base progressRate - var progressPerDay = (int) (progressRate * (1 + (float) (skillCheck - dc) / 5) / adventuringPenalty); - var daysUntilProjectFinished = (int) Math.Ceiling(1.0 * (project.TargetCost - project.Progress) / progressPerDay); - var daysCrafting = Math.Min(daysUntilProjectFinished, daysAvailableToCraft); - var progressGold = daysCrafting * progressPerDay; - foreach (var spell in spellsToCast) { - if (spell.SourceItem != null) { - // Use items whether we're adventuring or not, one charge per day of daysCrafting. We might run out of charges... - if (spell.SourceItem.IsSpendCharges && !((BlueprintItemEquipment) spell.SourceItem.Blueprint).RestoreChargesOnRest) { - var itemSpell = spell; - for (var day = 0; day < daysCrafting; ++day) { - if (itemSpell.SourceItem.Charges <= 0) { - // This item is exhausted and we haven't finished crafting - find another item. - itemSpell = FindCasterSpell(caster, spell.Blueprint, isAdventuring, spellsToCast); - } - - if (itemSpell == null) { - // We've run out of items that can cast the spell...crafting progress is going to slow, if not stop. - progressGold -= progressPerDay * (daysCrafting - day); - skillCheck -= DifficultyClass.MissingPrerequisiteDCModifier; - if (craftingData.PrerequisitesMandatory || project.PrerequisitesMandatory) { - AddBattleLogMessage(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-missing-prerequisite", project.ResultItem.Name, spell.Name)); - daysCrafting = day; - break; - } - - AddBattleLogMessage(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-missing-spell", spell.Name, DifficultyClass.MissingPrerequisiteDCModifier)); - if (skillCheck < dc) { - // Can no longer make progress - AddBattleLogMessage(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-dc-too-high", project.ResultItem.Name, - LocalizedTexts.Instance.Stats.GetText(craftingSkill), skillCheck, dc)); - daysCrafting = day; - } else { - // Progress will be slower, but they don't need to cast this spell any longer. - progressPerDay = (int) (progressRate * (1 + (float) (skillCheck - dc) / 5) / adventuringPenalty); - daysUntilProjectFinished = - day + (int) Math.Ceiling(1.0 * (project.TargetCost - project.Progress - progressGold) / progressPerDay); - daysCrafting = Math.Min(daysUntilProjectFinished, daysAvailableToCraft); - progressGold += (daysCrafting - day) * progressPerDay; - } - - break; - } - - GameLogContext.SourceUnit = caster.Unit; - GameLogContext.Text = itemSpell.SourceItem.Name; - AddBattleLogMessage(LocalizedStringBlueprints.CharacterUsedItemLocalized); - GameLogContext.Clear(); - itemSpell.SourceItem.SpendCharges(caster); - } - } - } else if (isAdventuring) { - // Actually cast the spells if we're adventuring. - AddBattleLogMessage(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-expend-spell", spell.Name)); - spell.SpendFromSpellbook(); - } - } - - var progressKey = project.ItemType == BondedItemRitual - ? "craftMagicItems-logMessage-made-progress-bondedItem" - : "craftMagicItems-logMessage-made-progress"; - var progress = LocalizationHelper.FormatLocalizedString(progressKey, progressGold, project.TargetCost - project.Progress, project.ResultItem.Name); - var checkResult = LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-made-progress-check", LocalizedTexts.Instance.Stats.GetText(craftingSkill), - skillCheck, dc); - AddBattleLogMessage(progress, checkResult); - daysAvailableToCraft -= daysCrafting; - project.Progress += progressGold; - if (project.Progress >= project.TargetCost) { - // Completed the project! - if (project.ItemType == BondedItemRitual) { - AddBattleLogMessage(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-bonding-ritual-complete", project.ResultItem.Name), project.ResultItem); - BondWithObject(project.Crafter, project.ResultItem); - } else { - AddBattleLogMessage(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-crafting-complete", project.ResultItem.Name), project.ResultItem); - CraftItem(project.ResultItem, project.UpgradeItem); - } - timer.CraftingProjects.Remove(project); - if (project.UpgradeItem == null) { - ItemCreationProjects.Remove(project); - } else { - ItemUpgradeProjects.Remove(project.UpgradeItem); - } - } else { - var completeKey = project.ItemType == BondedItemRitual - ? "craftMagicItems-logMessage-made-progress-bonding-ritual-amount-complete" - : "craftMagicItems-logMessage-made-progress-amount-complete"; - var amountComplete = LocalizationHelper.FormatLocalizedString(completeKey, project.ResultItem.Name, 100 * project.Progress / project.TargetCost); - AddBattleLogMessage(amountComplete, project.ResultItem); - project.AddMessage($"{progress} {checkResult}"); - } - - if (daysAvailableToCraft <= 0) { - return; - } - } - - if (daysAvailableToCraft > 0) { - // They didn't use up all available days - reset the time they can start crafting to now. - timer.LastUpdated = Game.Instance.Player.GameTime; - } - } - - public static bool EquipmentEnchantmentValid(ItemEntityWeapon weapon, ItemEntity owner) { + public static bool EquipmentEnchantmentValid(ItemEntityWeapon weapon, ItemEntity owner) + { if ((weapon == owner) || - (weapon != null && (weapon.Blueprint.IsNatural || weapon.Blueprint.IsUnarmed))) { + (weapon != null && (weapon.Blueprint.IsNatural || weapon.Blueprint.IsUnarmed))) + { return true; - } else { + } + else + { return false; } } diff --git a/CraftMagicItems/Patches/Harmony/CapitalCompanionLogicOnFactActivatePatch.cs b/CraftMagicItems/Patches/Harmony/CapitalCompanionLogicOnFactActivatePatch.cs index e749028..7d2c455 100644 --- a/CraftMagicItems/Patches/Harmony/CapitalCompanionLogicOnFactActivatePatch.cs +++ b/CraftMagicItems/Patches/Harmony/CapitalCompanionLogicOnFactActivatePatch.cs @@ -15,7 +15,7 @@ private static void Prefix() { if (companion.Value != null) { - Main.WorkOnProjects(companion.Value.Descriptor, true); + CraftingLogic.WorkOnProjects(companion.Value.Descriptor, true); } } } diff --git a/CraftMagicItems/Patches/Harmony/RestControllerApplyRestPatch.cs b/CraftMagicItems/Patches/Harmony/RestControllerApplyRestPatch.cs index 272d747..34804d2 100644 --- a/CraftMagicItems/Patches/Harmony/RestControllerApplyRestPatch.cs +++ b/CraftMagicItems/Patches/Harmony/RestControllerApplyRestPatch.cs @@ -11,7 +11,7 @@ public static class RestControllerApplyRestPatch // ReSharper disable once UnusedMember.Local private static void Prefix(UnitDescriptor unit) { - Main.WorkOnProjects(unit, false); + CraftingLogic.WorkOnProjects(unit, false); } } } \ No newline at end of file From 5bcff57d64acac9208e62f20ebeaa7f5d39e6538 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 20 Jul 2020 14:26:52 -0500 Subject: [PATCH 125/132] Moving CheckCrafterPrerequisites into CraftingLogic --- CraftMagicItems/CraftingLogic.cs | 14 +++++++++++++- CraftMagicItems/Main.cs | 12 +----------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/CraftMagicItems/CraftingLogic.cs b/CraftMagicItems/CraftingLogic.cs index 9bf17d2..e6f8d70 100644 --- a/CraftMagicItems/CraftingLogic.cs +++ b/CraftMagicItems/CraftingLogic.cs @@ -148,7 +148,7 @@ public static void WorkOnProjects(UnitDescriptor caster, bool returningToCapital DifficultyClass.MissingPrerequisiteDCModifier * missing2)); } missing += missing2; - missing += Main.CheckCrafterPrerequisites(project, caster); + missing += CheckCrafterPrerequisites(project, caster); dc += DifficultyClass.MissingPrerequisiteDCModifier * missing; var casterLevel = Main.CharacterCasterLevel(caster); if (casterLevel < project.CasterLevel) @@ -306,5 +306,17 @@ public static void WorkOnProjects(UnitDescriptor caster, bool returningToCapital timer.LastUpdated = Game.Instance.Player.GameTime; } } + + private static int CheckCrafterPrerequisites(CraftingProjectData project, UnitDescriptor caster) + { + var missing = Main.GetMissingCrafterPrerequisites(project.CrafterPrerequisites, caster); + foreach (var prerequisite in missing) + { + Main.AddBattleLogMessage(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-missing-crafter-prerequisite", + new L10NString($"craftMagicItems-crafter-prerequisite-{prerequisite}"), DifficultyClass.MissingPrerequisiteDCModifier)); + } + + return missing.Count; + } } } \ No newline at end of file diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 4da1aed..a6e1546 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -2780,17 +2780,7 @@ private static int CheckFeatPrerequisites(BlueprintFeature[] prerequisites, bool return anyPrerequisite ? Math.Min(1, missingFeats.Count) : missingFeats.Count; } - public static int CheckCrafterPrerequisites(CraftingProjectData project, UnitDescriptor caster) { - var missing = GetMissingCrafterPrerequisites(project.CrafterPrerequisites, caster); - foreach (var prerequisite in missing) { - AddBattleLogMessage(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-missing-crafter-prerequisite", - new L10NString($"craftMagicItems-crafter-prerequisite-{prerequisite}"), DifficultyClass.MissingPrerequisiteDCModifier)); - } - - return missing.Count; - } - - private static List GetMissingCrafterPrerequisites(CrafterPrerequisiteType[] prerequisites, UnitDescriptor caster) { + public static List GetMissingCrafterPrerequisites(CrafterPrerequisiteType[] prerequisites, UnitDescriptor caster) { var missingCrafterPrerequisites = new List(); if (prerequisites != null) { missingCrafterPrerequisites.AddRange(prerequisites.Where(prerequisite => From d80d91fc7158c4e6e08de0bf7c1fa904288674c1 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 20 Jul 2020 14:30:39 -0500 Subject: [PATCH 126/132] Moving the CheckFeatPrerequisites into CraftingLogic --- CraftMagicItems/CraftingLogic.cs | 36 +++++++++++++++++++++++++++++++- CraftMagicItems/Main.cs | 26 +---------------------- 2 files changed, 36 insertions(+), 26 deletions(-) diff --git a/CraftMagicItems/CraftingLogic.cs b/CraftMagicItems/CraftingLogic.cs index e6f8d70..7f8ca3a 100644 --- a/CraftMagicItems/CraftingLogic.cs +++ b/CraftMagicItems/CraftingLogic.cs @@ -1,8 +1,10 @@ using System; +using System.Collections.Generic; using System.Linq; using CraftMagicItems.Constants; using CraftMagicItems.Localization; using Kingmaker; +using Kingmaker.Blueprints.Classes; using Kingmaker.Blueprints.Classes.Spells; using Kingmaker.Blueprints.Items.Equipment; using Kingmaker.Blueprints.Root; @@ -140,7 +142,7 @@ public static void WorkOnProjects(UnitDescriptor caster, bool returningToCapital Main.AddBattleLogMessage(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-missing-spell", missingSpellNames, DifficultyClass.MissingPrerequisiteDCModifier * missing)); } - var missing2 = Main.CheckFeatPrerequisites(project, caster, out var missingFeats); + var missing2 = CheckFeatPrerequisites(project, caster, out var missingFeats); if (missing2 > 0) { var missingFeatNames = missingFeats.Select(ability => ability.Name).BuildCommaList(project.AnyPrerequisite); @@ -318,5 +320,37 @@ private static int CheckCrafterPrerequisites(CraftingProjectData project, UnitDe return missing.Count; } + + private static int CheckFeatPrerequisites(CraftingProjectData project, UnitDescriptor caster, out List missingFeats) + { + return CheckFeatPrerequisites(project.FeatPrerequisites, project.AnyPrerequisite, caster, out missingFeats); + } + + public static int CheckFeatPrerequisites(BlueprintFeature[] prerequisites, bool anyPrerequisite, UnitDescriptor caster, + out List missingFeats) + { + missingFeats = new List(); + if (prerequisites != null) + { + foreach (var featBlueprint in prerequisites) + { + var feat = caster.GetFeature(featBlueprint); + if (feat != null) + { + if (anyPrerequisite) + { + missingFeats.Clear(); + return 0; + } + } + else + { + missingFeats.Add(featBlueprint); + } + } + } + + return anyPrerequisite ? Math.Min(1, missingFeats.Count) : missingFeats.Count; + } } } \ No newline at end of file diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index a6e1546..4b5eec9 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -1593,7 +1593,7 @@ private static int RenderCraftingSkillInformation(UnitEntityData crafter, StatTy var missing = CheckSpellPrerequisites(prerequisiteSpells, anyPrerequisite, crafter.Descriptor, false, out var missingSpells, // ReSharper disable once UnusedVariable out var spellsToCast); - missing += CheckFeatPrerequisites(prerequisiteFeats, anyPrerequisite, crafter.Descriptor, out var missingFeats); + missing += CraftingLogic.CheckFeatPrerequisites(prerequisiteFeats, anyPrerequisite, crafter.Descriptor, out var missingFeats); missing += GetMissingCrafterPrerequisites(crafterPrerequisites, crafter.Descriptor).Count; var crafterCasterLevel = CharacterCasterLevel(crafter.Descriptor); var casterLevelShortfall = Math.Max(0, casterLevel - crafterCasterLevel); @@ -2756,30 +2756,6 @@ private static int CheckSpellPrerequisites(BlueprintAbility[] prerequisites, boo return anyPrerequisite ? Math.Min(1, missingSpells.Count) : missingSpells.Count; } - public static int CheckFeatPrerequisites(CraftingProjectData project, UnitDescriptor caster, out List missingFeats) { - return CheckFeatPrerequisites(project.FeatPrerequisites, project.AnyPrerequisite, caster, out missingFeats); - } - - private static int CheckFeatPrerequisites(BlueprintFeature[] prerequisites, bool anyPrerequisite, UnitDescriptor caster, - out List missingFeats) { - missingFeats = new List(); - if (prerequisites != null) { - foreach (var featBlueprint in prerequisites) { - var feat = caster.GetFeature(featBlueprint); - if (feat != null) { - if (anyPrerequisite) { - missingFeats.Clear(); - return 0; - } - } else { - missingFeats.Add(featBlueprint); - } - } - } - - return anyPrerequisite ? Math.Min(1, missingFeats.Count) : missingFeats.Count; - } - public static List GetMissingCrafterPrerequisites(CrafterPrerequisiteType[] prerequisites, UnitDescriptor caster) { var missingCrafterPrerequisites = new List(); if (prerequisites != null) { From 37549326838cf5f5f3cb04f43865a267ed6c9853 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 20 Jul 2020 14:33:44 -0500 Subject: [PATCH 127/132] Moving CheckSpellPrerequisites methods into CraftingLogic --- CraftMagicItems/CraftingLogic.cs | 39 +++++++++++++++++++++++++++++++- CraftMagicItems/Main.cs | 29 +----------------------- 2 files changed, 39 insertions(+), 29 deletions(-) diff --git a/CraftMagicItems/CraftingLogic.cs b/CraftMagicItems/CraftingLogic.cs index 7f8ca3a..20a0f59 100644 --- a/CraftMagicItems/CraftingLogic.cs +++ b/CraftMagicItems/CraftingLogic.cs @@ -11,6 +11,8 @@ using Kingmaker.EntitySystem.Stats; using Kingmaker.UI.Log; using Kingmaker.UnitLogic; +using Kingmaker.UnitLogic.Abilities; +using Kingmaker.UnitLogic.Abilities.Blueprints; using Kingmaker.Utility; namespace CraftMagicItems @@ -123,7 +125,7 @@ public static void WorkOnProjects(UnitDescriptor caster, bool returningToCapital progressRate = Main.ModSettings.MagicCraftingRate; } - var missing = Main.CheckSpellPrerequisites(project, caster, isAdventuring, out var missingSpells, out var spellsToCast); + var missing = CheckSpellPrerequisites(project, caster, isAdventuring, out var missingSpells, out var spellsToCast); if (missing > 0) { var missingSpellNames = missingSpells @@ -352,5 +354,40 @@ public static int CheckFeatPrerequisites(BlueprintFeature[] prerequisites, bool return anyPrerequisite ? Math.Min(1, missingFeats.Count) : missingFeats.Count; } + + private static int CheckSpellPrerequisites(CraftingProjectData project, UnitDescriptor caster, bool mustPrepare, + out List missingSpells, out List spellsToCast) + { + return CheckSpellPrerequisites(project.SpellPrerequisites, project.AnyPrerequisite, caster, mustPrepare, out missingSpells, out spellsToCast); + } + + public static int CheckSpellPrerequisites(BlueprintAbility[] prerequisites, bool anyPrerequisite, UnitDescriptor caster, bool mustPrepare, + out List missingSpells, out List spellsToCast) + { + spellsToCast = new List(); + missingSpells = new List(); + if (prerequisites != null) + { + foreach (var spellBlueprint in prerequisites) + { + var spell = Main.FindCasterSpell(caster, spellBlueprint, mustPrepare, spellsToCast); + if (spell != null) + { + spellsToCast.Add(spell); + if (anyPrerequisite) + { + missingSpells.Clear(); + return 0; + } + } + else + { + missingSpells.Add(spellBlueprint); + } + } + } + + return anyPrerequisite ? Math.Min(1, missingSpells.Count) : missingSpells.Count; + } } } \ No newline at end of file diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 4b5eec9..358de9c 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -1590,7 +1590,7 @@ private static int RenderCraftingSkillInformation(UnitEntityData crafter, StatTy UmmUiRenderer.RenderLabelRow($"Base Crafting DC: {dc}"); } // ReSharper disable once UnusedVariable - var missing = CheckSpellPrerequisites(prerequisiteSpells, anyPrerequisite, crafter.Descriptor, false, out var missingSpells, + var missing = CraftingLogic.CheckSpellPrerequisites(prerequisiteSpells, anyPrerequisite, crafter.Descriptor, false, out var missingSpells, // ReSharper disable once UnusedVariable out var spellsToCast); missing += CraftingLogic.CheckFeatPrerequisites(prerequisiteFeats, anyPrerequisite, crafter.Descriptor, out var missingFeats); @@ -2729,33 +2729,6 @@ public static AbilityData FindCasterSpell(UnitDescriptor caster, BlueprintAbilit return fromItem?.Ability?.Data; } - public static int CheckSpellPrerequisites(CraftingProjectData project, UnitDescriptor caster, bool mustPrepare, - out List missingSpells, out List spellsToCast) { - return CheckSpellPrerequisites(project.SpellPrerequisites, project.AnyPrerequisite, caster, mustPrepare, out missingSpells, out spellsToCast); - } - - private static int CheckSpellPrerequisites(BlueprintAbility[] prerequisites, bool anyPrerequisite, UnitDescriptor caster, bool mustPrepare, - out List missingSpells, out List spellsToCast) { - spellsToCast = new List(); - missingSpells = new List(); - if (prerequisites != null) { - foreach (var spellBlueprint in prerequisites) { - var spell = FindCasterSpell(caster, spellBlueprint, mustPrepare, spellsToCast); - if (spell != null) { - spellsToCast.Add(spell); - if (anyPrerequisite) { - missingSpells.Clear(); - return 0; - } - } else { - missingSpells.Add(spellBlueprint); - } - } - } - - return anyPrerequisite ? Math.Min(1, missingSpells.Count) : missingSpells.Count; - } - public static List GetMissingCrafterPrerequisites(CrafterPrerequisiteType[] prerequisites, UnitDescriptor caster) { var missingCrafterPrerequisites = new List(); if (prerequisites != null) { From 1e6bed25837b75767ddb2109c811443c7d0d949c Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 20 Jul 2020 14:36:24 -0500 Subject: [PATCH 128/132] Moving GetMissingCrafterPrerequisites into CraftingLogic --- CraftMagicItems/CraftingLogic.cs | 23 ++++++++++++++++++++++- CraftMagicItems/Main.cs | 18 +----------------- 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/CraftMagicItems/CraftingLogic.cs b/CraftMagicItems/CraftingLogic.cs index 20a0f59..41be80e 100644 --- a/CraftMagicItems/CraftingLogic.cs +++ b/CraftMagicItems/CraftingLogic.cs @@ -4,15 +4,18 @@ using CraftMagicItems.Constants; using CraftMagicItems.Localization; using Kingmaker; +using Kingmaker.Blueprints; using Kingmaker.Blueprints.Classes; using Kingmaker.Blueprints.Classes.Spells; using Kingmaker.Blueprints.Items.Equipment; using Kingmaker.Blueprints.Root; using Kingmaker.EntitySystem.Stats; +using Kingmaker.Enums; using Kingmaker.UI.Log; using Kingmaker.UnitLogic; using Kingmaker.UnitLogic.Abilities; using Kingmaker.UnitLogic.Abilities.Blueprints; +using Kingmaker.UnitLogic.Alignments; using Kingmaker.Utility; namespace CraftMagicItems @@ -313,7 +316,7 @@ public static void WorkOnProjects(UnitDescriptor caster, bool returningToCapital private static int CheckCrafterPrerequisites(CraftingProjectData project, UnitDescriptor caster) { - var missing = Main.GetMissingCrafterPrerequisites(project.CrafterPrerequisites, caster); + var missing = GetMissingCrafterPrerequisites(project.CrafterPrerequisites, caster); foreach (var prerequisite in missing) { Main.AddBattleLogMessage(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-missing-crafter-prerequisite", @@ -389,5 +392,23 @@ public static int CheckSpellPrerequisites(BlueprintAbility[] prerequisites, bool return anyPrerequisite ? Math.Min(1, missingSpells.Count) : missingSpells.Count; } + + public static List GetMissingCrafterPrerequisites(CrafterPrerequisiteType[] prerequisites, UnitDescriptor caster) + { + var missingCrafterPrerequisites = new List(); + if (prerequisites != null) + { + missingCrafterPrerequisites.AddRange(prerequisites.Where(prerequisite => + prerequisite == CrafterPrerequisiteType.AlignmentLawful && (caster.Alignment.Value.ToMask() & AlignmentMaskType.Lawful) == 0 + || prerequisite == CrafterPrerequisiteType.AlignmentGood && (caster.Alignment.Value.ToMask() & AlignmentMaskType.Good) == 0 + || prerequisite == CrafterPrerequisiteType.AlignmentChaotic && (caster.Alignment.Value.ToMask() & AlignmentMaskType.Chaotic) == 0 + || prerequisite == CrafterPrerequisiteType.AlignmentEvil && (caster.Alignment.Value.ToMask() & AlignmentMaskType.Evil) == 0 + || prerequisite == CrafterPrerequisiteType.FeatureChannelEnergy && + caster.GetFeature(ResourcesLibrary.TryGetBlueprint(Features.ChannelEnergyFeatureGuid)) == null + )); + } + + return missingCrafterPrerequisites; + } } } \ No newline at end of file diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 358de9c..8d9c7b5 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -1594,7 +1594,7 @@ private static int RenderCraftingSkillInformation(UnitEntityData crafter, StatTy // ReSharper disable once UnusedVariable out var spellsToCast); missing += CraftingLogic.CheckFeatPrerequisites(prerequisiteFeats, anyPrerequisite, crafter.Descriptor, out var missingFeats); - missing += GetMissingCrafterPrerequisites(crafterPrerequisites, crafter.Descriptor).Count; + missing += CraftingLogic.GetMissingCrafterPrerequisites(crafterPrerequisites, crafter.Descriptor).Count; var crafterCasterLevel = CharacterCasterLevel(crafter.Descriptor); var casterLevelShortfall = Math.Max(0, casterLevel - crafterCasterLevel); if (casterLevelShortfall > 0 && ModSettings.CasterLevelIsSinglePrerequisite) { @@ -2729,22 +2729,6 @@ public static AbilityData FindCasterSpell(UnitDescriptor caster, BlueprintAbilit return fromItem?.Ability?.Data; } - public static List GetMissingCrafterPrerequisites(CrafterPrerequisiteType[] prerequisites, UnitDescriptor caster) { - var missingCrafterPrerequisites = new List(); - if (prerequisites != null) { - missingCrafterPrerequisites.AddRange(prerequisites.Where(prerequisite => - prerequisite == CrafterPrerequisiteType.AlignmentLawful && (caster.Alignment.Value.ToMask() & AlignmentMaskType.Lawful) == 0 - || prerequisite == CrafterPrerequisiteType.AlignmentGood && (caster.Alignment.Value.ToMask() & AlignmentMaskType.Good) == 0 - || prerequisite == CrafterPrerequisiteType.AlignmentChaotic && (caster.Alignment.Value.ToMask() & AlignmentMaskType.Chaotic) == 0 - || prerequisite == CrafterPrerequisiteType.AlignmentEvil && (caster.Alignment.Value.ToMask() & AlignmentMaskType.Evil) == 0 - || prerequisite == CrafterPrerequisiteType.FeatureChannelEnergy && - caster.GetFeature(ResourcesLibrary.TryGetBlueprint(Features.ChannelEnergyFeatureGuid)) == null - )); - } - - return missingCrafterPrerequisites; - } - public static bool EquipmentEnchantmentValid(ItemEntityWeapon weapon, ItemEntity owner) { if ((weapon == owner) || From f1d68243ddaba82effc537c12b25df6332d6717a Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 20 Jul 2020 14:42:13 -0500 Subject: [PATCH 129/132] Moving FindCasterSpell into CraftingLogic --- CraftMagicItems/CraftingLogic.cs | 79 +++++++++++++++++++++++++++++++- CraftMagicItems/Main.cs | 61 ------------------------ 2 files changed, 77 insertions(+), 63 deletions(-) diff --git a/CraftMagicItems/CraftingLogic.cs b/CraftMagicItems/CraftingLogic.cs index 41be80e..2f87957 100644 --- a/CraftMagicItems/CraftingLogic.cs +++ b/CraftMagicItems/CraftingLogic.cs @@ -11,6 +11,7 @@ using Kingmaker.Blueprints.Root; using Kingmaker.EntitySystem.Stats; using Kingmaker.Enums; +using Kingmaker.Items; using Kingmaker.UI.Log; using Kingmaker.UnitLogic; using Kingmaker.UnitLogic.Abilities; @@ -207,7 +208,7 @@ public static void WorkOnProjects(UnitDescriptor caster, bool returningToCapital if (itemSpell.SourceItem.Charges <= 0) { // This item is exhausted and we haven't finished crafting - find another item. - itemSpell = Main.FindCasterSpell(caster, spell.Blueprint, isAdventuring, spellsToCast); + itemSpell = FindCasterSpell(caster, spell.Blueprint, isAdventuring, spellsToCast); } if (itemSpell == null) @@ -373,7 +374,7 @@ public static int CheckSpellPrerequisites(BlueprintAbility[] prerequisites, bool { foreach (var spellBlueprint in prerequisites) { - var spell = Main.FindCasterSpell(caster, spellBlueprint, mustPrepare, spellsToCast); + var spell = FindCasterSpell(caster, spellBlueprint, mustPrepare, spellsToCast); if (spell != null) { spellsToCast.Add(spell); @@ -410,5 +411,79 @@ public static List GetMissingCrafterPrerequisites(Craft return missingCrafterPrerequisites; } + + private static AbilityData FindCasterSpell(UnitDescriptor caster, BlueprintAbility spellBlueprint, bool mustHavePrepared, + IReadOnlyCollection spellsToCast) + { + foreach (var spellbook in caster.Spellbooks) + { + var spellLevel = spellbook.GetSpellLevel(spellBlueprint); + if (spellLevel > spellbook.MaxSpellLevel || spellLevel < 0) + { + continue; + } + + if (mustHavePrepared && spellLevel > 0) + { + if (spellbook.Blueprint.Spontaneous) + { + // Count how many other spells of this class and level they're going to cast, to ensure they don't double-dip on spell slots. + var toCastCount = spellsToCast.Count(ability => ability.Spellbook == spellbook && spellbook.GetSpellLevel(ability) == spellLevel); + // Spontaneous spellcaster must have enough spell slots of the required level. + if (spellbook.GetSpontaneousSlots(spellLevel) <= toCastCount) + { + continue; + } + } + else + { + // Prepared spellcaster must have memorized the spell... + var spellSlot = spellbook.GetMemorizedSpells(spellLevel).FirstOrDefault(slot => + slot.Available && (slot.Spell?.Blueprint == spellBlueprint || + spellBlueprint.Parent && slot.Spell?.Blueprint == spellBlueprint.Parent)); + if (spellSlot == null && (spellbook.GetSpontaneousConversionSpells(spellLevel).Contains(spellBlueprint) || + (spellBlueprint.Parent && + spellbook.GetSpontaneousConversionSpells(spellLevel).Contains(spellBlueprint.Parent)))) + { + // ... or be able to convert, in which case any available spell of the given level will do. + spellSlot = spellbook.GetMemorizedSpells(spellLevel).FirstOrDefault(slot => slot.Available); + } + + if (spellSlot == null) + { + continue; + } + + return spellSlot.Spell; + } + } + + return spellbook.GetKnownSpells(spellLevel).Concat(spellbook.GetSpecialSpells(spellLevel)) + .First(known => known.Blueprint == spellBlueprint || + (spellBlueprint.Parent && known.Blueprint == spellBlueprint.Parent)); + } + + // Try casting the spell from an item + ItemEntity fromItem = null; + var fromItemCharges = 0; + foreach (var item in caster.Inventory) + { + // Check (non-potion) items wielded by the caster to see if they can cast the required spell + if (item.Wielder == caster && (!(item.Blueprint is BlueprintItemEquipmentUsable usable) || usable.Type != UsableItemType.Potion) + && (item.Ability?.Blueprint == spellBlueprint || + (spellBlueprint.Parent && item.Ability?.Blueprint == spellBlueprint.Parent))) + { + // Choose the item with the most available charges, with a multiplier if the item restores charges on rest. + var charges = item.Charges * (((BlueprintItemEquipment)item.Blueprint).RestoreChargesOnRest ? 50 : 1); + if (charges > fromItemCharges) + { + fromItem = item; + fromItemCharges = charges; + } + } + } + + return fromItem?.Ability?.Data; + } } } \ No newline at end of file diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 8d9c7b5..54bdf81 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -2668,67 +2668,6 @@ public static void AddBattleLogMessage(string message, object tooltip = null, Co } } - public static AbilityData FindCasterSpell(UnitDescriptor caster, BlueprintAbility spellBlueprint, bool mustHavePrepared, - IReadOnlyCollection spellsToCast) { - foreach (var spellbook in caster.Spellbooks) { - var spellLevel = spellbook.GetSpellLevel(spellBlueprint); - if (spellLevel > spellbook.MaxSpellLevel || spellLevel < 0) { - continue; - } - - if (mustHavePrepared && spellLevel > 0) { - if (spellbook.Blueprint.Spontaneous) { - // Count how many other spells of this class and level they're going to cast, to ensure they don't double-dip on spell slots. - var toCastCount = spellsToCast.Count(ability => ability.Spellbook == spellbook && spellbook.GetSpellLevel(ability) == spellLevel); - // Spontaneous spellcaster must have enough spell slots of the required level. - if (spellbook.GetSpontaneousSlots(spellLevel) <= toCastCount) { - continue; - } - } else { - // Prepared spellcaster must have memorized the spell... - var spellSlot = spellbook.GetMemorizedSpells(spellLevel).FirstOrDefault(slot => - slot.Available && (slot.Spell?.Blueprint == spellBlueprint || - spellBlueprint.Parent && slot.Spell?.Blueprint == spellBlueprint.Parent)); - if (spellSlot == null && (spellbook.GetSpontaneousConversionSpells(spellLevel).Contains(spellBlueprint) || - (spellBlueprint.Parent && - spellbook.GetSpontaneousConversionSpells(spellLevel).Contains(spellBlueprint.Parent)))) { - // ... or be able to convert, in which case any available spell of the given level will do. - spellSlot = spellbook.GetMemorizedSpells(spellLevel).FirstOrDefault(slot => slot.Available); - } - - if (spellSlot == null) { - continue; - } - - return spellSlot.Spell; - } - } - - return spellbook.GetKnownSpells(spellLevel).Concat(spellbook.GetSpecialSpells(spellLevel)) - .First(known => known.Blueprint == spellBlueprint || - (spellBlueprint.Parent && known.Blueprint == spellBlueprint.Parent)); - } - - // Try casting the spell from an item - ItemEntity fromItem = null; - var fromItemCharges = 0; - foreach (var item in caster.Inventory) { - // Check (non-potion) items wielded by the caster to see if they can cast the required spell - if (item.Wielder == caster && (!(item.Blueprint is BlueprintItemEquipmentUsable usable) || usable.Type != UsableItemType.Potion) - && (item.Ability?.Blueprint == spellBlueprint || - (spellBlueprint.Parent && item.Ability?.Blueprint == spellBlueprint.Parent))) { - // Choose the item with the most available charges, with a multiplier if the item restores charges on rest. - var charges = item.Charges * (((BlueprintItemEquipment) item.Blueprint).RestoreChargesOnRest ? 50 : 1); - if (charges > fromItemCharges) { - fromItem = item; - fromItemCharges = charges; - } - } - } - - return fromItem?.Ability?.Data; - } - public static bool EquipmentEnchantmentValid(ItemEntityWeapon weapon, ItemEntity owner) { if ((weapon == owner) || From e454ca32a9b897642d6556ed30ed4a3926f43da8 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 20 Jul 2020 14:44:54 -0500 Subject: [PATCH 130/132] Moving IsMundaneCraftingData into CraftingLogic --- CraftMagicItems/CraftingLogic.cs | 7 ++++++- CraftMagicItems/Main.cs | 10 +++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/CraftMagicItems/CraftingLogic.cs b/CraftMagicItems/CraftingLogic.cs index 2f87957..66ca82f 100644 --- a/CraftMagicItems/CraftingLogic.cs +++ b/CraftMagicItems/CraftingLogic.cs @@ -116,7 +116,7 @@ public static void WorkOnProjects(UnitDescriptor caster, bool returningToCapital dc = 10 + project.Crafter.Stats.GetStat(craftingSkill).ModifiedValue; progressRate = Main.ModSettings.MagicCraftingRate; } - else if (Main.IsMundaneCraftingData(craftingData)) + else if (IsMundaneCraftingData(craftingData)) { craftingSkill = StatType.SkillKnowledgeWorld; dc = Main.CalculateMundaneCraftingDC((RecipeBasedItemCraftingData)craftingData, project.ResultItem.Blueprint, caster); @@ -485,5 +485,10 @@ private static AbilityData FindCasterSpell(UnitDescriptor caster, BlueprintAbili return fromItem?.Ability?.Data; } + + public static bool IsMundaneCraftingData(ItemCraftingData craftingData) + { + return craftingData.FeatGuid == null; + } } } \ No newline at end of file diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 54bdf81..98a60df 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -370,7 +370,7 @@ private static void RenderCraftMagicItemsSection() { private static RecipeBasedItemCraftingData GetBondedItemCraftingData(BondedItemComponent bondedComponent) { // Find crafting data relevant to the bonded item return LoadedData.ItemCraftingData.OfType() - .First(data => data.Slots.Contains(bondedComponent.ownerItem.Blueprint.ItemType) && !IsMundaneCraftingData(data)); + .First(data => data.Slots.Contains(bondedComponent.ownerItem.Blueprint.ItemType) && !CraftingLogic.IsMundaneCraftingData(data)); } private static void UnBondFromCurrentBondedItem(UnitEntityData caster) { @@ -2187,7 +2187,7 @@ private static void CalculateProjectEstimate(CraftingProjectData project) { craftingSkill = StatType.SkillKnowledgeArcana; dc = 10 + project.Crafter.Stats.GetStat(craftingSkill).ModifiedValue; progressRate = ModSettings.MagicCraftingRate; - } else if (IsMundaneCraftingData(craftingData)) { + } else if (CraftingLogic.IsMundaneCraftingData(craftingData)) { craftingSkill = StatType.SkillKnowledgeWorld; var recipeBasedItemCraftingData = (RecipeBasedItemCraftingData) craftingData; dc = CalculateMundaneCraftingDC(recipeBasedItemCraftingData, project.ResultItem.Blueprint, project.Crafter.Descriptor); @@ -2314,10 +2314,6 @@ private static void BeginCraftingSpellBasedItem(UnitEntityData caster, SpellBase } } - public static bool IsMundaneCraftingData(ItemCraftingData craftingData) { - return craftingData.FeatGuid == null; - } - private static void RenderRecipeBasedCraftItemControl(UnitEntityData caster, ItemCraftingData craftingData, RecipeData recipe, int casterLevel, BlueprintItem itemBlueprint, ItemEntity upgradeItem = null) { int baseCost = (craftingData.Count != 0 ? craftingData.Count : 1) * itemBlueprint.Cost; @@ -2325,7 +2321,7 @@ private static void RenderRecipeBasedCraftItemControl(UnitEntityData caster, Ite int materialComponentCost = recipe?.MaterialComponentCost ?? 0; var requiredProgress = (baseCost - upgradeCost - materialComponentCost) / 4; var goldCost = materialComponentCost + (int)Mathf.Round(requiredProgress * ModSettings.CraftingPriceScale); - if (IsMundaneCraftingData(craftingData)) { + if (CraftingLogic.IsMundaneCraftingData(craftingData)) { // For mundane crafting, the gold cost is less, and the cost of the recipes don't increase the required progress. goldCost = Math.Max(1, (goldCost * 2 + 2) / 3); var recipeCost = 0; From c002da5de19f5a36550f982c47e1c26defb49298 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 20 Jul 2020 14:50:46 -0500 Subject: [PATCH 131/132] Moving CraftItem into CraftingLogic --- CraftMagicItems/CraftingLogic.cs | 75 +++++++++++++++++++++++++++++++- CraftMagicItems/Main.cs | 56 +----------------------- 2 files changed, 76 insertions(+), 55 deletions(-) diff --git a/CraftMagicItems/CraftingLogic.cs b/CraftMagicItems/CraftingLogic.cs index 66ca82f..f214fb6 100644 --- a/CraftMagicItems/CraftingLogic.cs +++ b/CraftMagicItems/CraftingLogic.cs @@ -12,6 +12,8 @@ using Kingmaker.EntitySystem.Stats; using Kingmaker.Enums; using Kingmaker.Items; +using Kingmaker.UI; +using Kingmaker.UI.Common; using Kingmaker.UI.Log; using Kingmaker.UnitLogic; using Kingmaker.UnitLogic.Abilities; @@ -280,7 +282,7 @@ public static void WorkOnProjects(UnitDescriptor caster, bool returningToCapital else { Main.AddBattleLogMessage(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-crafting-complete", project.ResultItem.Name), project.ResultItem); - Main.CraftItem(project.ResultItem, project.UpgradeItem); + CraftItem(project.ResultItem, project.UpgradeItem); } timer.CraftingProjects.Remove(project); if (project.UpgradeItem == null) @@ -490,5 +492,76 @@ public static bool IsMundaneCraftingData(ItemCraftingData craftingData) { return craftingData.FeatGuid == null; } + + public static void CraftItem(ItemEntity resultItem, ItemEntity upgradeItem = null) + { + var characters = UIUtility.GetGroup(true).Where(character => character.IsPlayerFaction && !character.Descriptor.IsPet); + foreach (var character in characters) + { + var bondedComponent = Main.GetBondedItemComponentForCaster(character.Descriptor); + if (bondedComponent && bondedComponent.ownerItem == upgradeItem) + { + bondedComponent.ownerItem = resultItem; + } + } + + using (new DisableBattleLog(!Main.ModSettings.CraftingTakesNoTime)) + { + var holdingSlot = upgradeItem?.HoldingSlot; + var slotIndex = upgradeItem?.InventorySlotIndex; + var inventory = true; + if (upgradeItem != null) + { + if (Game.Instance.Player.Inventory.Contains(upgradeItem)) + { + Game.Instance.Player.Inventory.Remove(upgradeItem); + } + else + { + Game.Instance.Player.SharedStash.Remove(upgradeItem); + inventory = false; + } + } + if (holdingSlot == null) + { + if (inventory) + { + Game.Instance.Player.Inventory.Add(resultItem); + } + else + { + Game.Instance.Player.SharedStash.Add(resultItem); + } + if (slotIndex is int value) + { + resultItem.SetSlotIndex(value); + } + } + else + { + holdingSlot.InsertItem(resultItem); + } + } + + if (resultItem is ItemEntityUsable usable) + { + switch (usable.Blueprint.Type) + { + case UsableItemType.Scroll: + Game.Instance.UI.Common.UISound.Play(UISoundType.NewInformation); + break; + case UsableItemType.Potion: + Game.Instance.UI.Common.UISound.PlayItemSound(SlotAction.Take, resultItem, false); + break; + default: + Game.Instance.UI.Common.UISound.Play(UISoundType.SettlementBuildStart); + break; + } + } + else + { + Game.Instance.UI.Common.UISound.Play(UISoundType.SettlementBuildStart); + } + } } } \ No newline at end of file diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 98a60df..3b3a97b 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -2071,58 +2071,6 @@ private static BlueprintItemEquipment RandomBaseBlueprintId(ItemCraftingData ite return blueprintIds[RandomGenerator.Next(blueprintIds.Length)]; } - public static void CraftItem(ItemEntity resultItem, ItemEntity upgradeItem = null) { - var characters = UIUtility.GetGroup(true).Where(character => character.IsPlayerFaction && !character.Descriptor.IsPet); - foreach (var character in characters) { - var bondedComponent = GetBondedItemComponentForCaster(character.Descriptor); - if (bondedComponent && bondedComponent.ownerItem == upgradeItem) { - bondedComponent.ownerItem = resultItem; - } - } - - using (new DisableBattleLog(!ModSettings.CraftingTakesNoTime)) { - var holdingSlot = upgradeItem?.HoldingSlot; - var slotIndex = upgradeItem?.InventorySlotIndex; - var inventory = true; - if (upgradeItem != null) { - if (Game.Instance.Player.Inventory.Contains(upgradeItem)) { - Game.Instance.Player.Inventory.Remove(upgradeItem); - } else { - Game.Instance.Player.SharedStash.Remove(upgradeItem); - inventory = false; - } - } - if (holdingSlot == null) { - if (inventory) { - Game.Instance.Player.Inventory.Add(resultItem); - } else { - Game.Instance.Player.SharedStash.Add(resultItem); - } - if (slotIndex is int value) { - resultItem.SetSlotIndex(value); - } - } else { - holdingSlot.InsertItem(resultItem); - } - } - - if (resultItem is ItemEntityUsable usable) { - switch (usable.Blueprint.Type) { - case UsableItemType.Scroll: - Game.Instance.UI.Common.UISound.Play(UISoundType.NewInformation); - break; - case UsableItemType.Potion: - Game.Instance.UI.Common.UISound.PlayItemSound(SlotAction.Take, resultItem, false); - break; - default: - Game.Instance.UI.Common.UISound.Play(UISoundType.SettlementBuildStart); - break; - } - } else { - Game.Instance.UI.Common.UISound.Play(UISoundType.SettlementBuildStart); - } - } - private static int CalculateSpellBasedGoldCost(SpellBasedItemCraftingData craftingData, int spellLevel, int casterLevel) { return spellLevel == 0 ? craftingData.BaseItemGoldCost * casterLevel / 8 : craftingData.BaseItemGoldCost * spellLevel * casterLevel / 4; } @@ -2302,7 +2250,7 @@ private static void BeginCraftingSpellBasedItem(UnitEntityData caster, SpellBase { AddBattleLogMessage(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-expend-spell", spell.Name)); spell.SpendFromSpellbook(); - CraftItem(resultItem); + CraftingLogic.CraftItem(resultItem); } else { @@ -2407,7 +2355,7 @@ private static void RenderRecipeBasedCraftItemControl(UnitEntityData caster, Ite var resultItem = BuildItemEntity(itemBlueprint, craftingData, caster); AddBattleLogMessage(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-begin-crafting", cost, itemBlueprint.Name), resultItem); if (ModSettings.CraftingTakesNoTime) { - CraftItem(resultItem, upgradeItem); + CraftingLogic.CraftItem(resultItem, upgradeItem); } else { var project = new CraftingProjectData(caster, requiredProgress, goldCost, casterLevel, resultItem, craftingData.Name, recipe?.Name, recipe?.PrerequisiteSpells ?? new BlueprintAbility[0], recipe?.PrerequisiteFeats ?? Array.Empty(), recipe?.PrerequisitesMandatory ?? false, From 13cf6eb75d22727c30ab9da59f69af993a449840 Mon Sep 17 00:00:00 2001 From: Erik Frantz Date: Mon, 20 Jul 2020 14:51:10 -0500 Subject: [PATCH 132/132] Moving CheckForOppositionSchool into CraftingLogic --- CraftMagicItems/CraftingLogic.cs | 18 +++++++++++++++++- CraftMagicItems/Main.cs | 14 +------------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/CraftMagicItems/CraftingLogic.cs b/CraftMagicItems/CraftingLogic.cs index f214fb6..9c18d59 100644 --- a/CraftMagicItems/CraftingLogic.cs +++ b/CraftMagicItems/CraftingLogic.cs @@ -171,7 +171,7 @@ public static void WorkOnProjects(UnitDescriptor caster, bool returningToCapital dc += casterLevelPenalty; Main.AddBattleLogMessage(LocalizationHelper.FormatLocalizedString("craftMagicItems-logMessage-low-caster-level", project.CasterLevel, casterLevelPenalty)); } - var oppositionSchool = Main.CheckForOppositionSchool(caster, project.SpellPrerequisites); + var oppositionSchool = CheckForOppositionSchool(caster, project.SpellPrerequisites); if (oppositionSchool != SpellSchool.None) { dc += DifficultyClass.OppositionSchoolDCModifier; @@ -396,6 +396,22 @@ public static int CheckSpellPrerequisites(BlueprintAbility[] prerequisites, bool return anyPrerequisite ? Math.Min(1, missingSpells.Count) : missingSpells.Count; } + public static SpellSchool CheckForOppositionSchool(UnitDescriptor crafter, BlueprintAbility[] prerequisiteSpells) + { + if (prerequisiteSpells != null) + { + foreach (var spell in prerequisiteSpells) + { + if (crafter.Spellbooks.Any(spellbook => spellbook.Blueprint.SpellList.Contains(spell) + && spellbook.OppositionSchools.Contains(spell.School))) + { + return spell.School; + } + } + } + return SpellSchool.None; + } + public static List GetMissingCrafterPrerequisites(CrafterPrerequisiteType[] prerequisites, UnitDescriptor caster) { var missingCrafterPrerequisites = new List(); diff --git a/CraftMagicItems/Main.cs b/CraftMagicItems/Main.cs index 3b3a97b..467be3c 100644 --- a/CraftMagicItems/Main.cs +++ b/CraftMagicItems/Main.cs @@ -1570,18 +1570,6 @@ public static int CharacterCasterLevel(UnitDescriptor character, Spellbook forSp return casterLevel; } - public static SpellSchool CheckForOppositionSchool(UnitDescriptor crafter, BlueprintAbility[] prerequisiteSpells) { - if (prerequisiteSpells != null) { - foreach (var spell in prerequisiteSpells) { - if (crafter.Spellbooks.Any(spellbook => spellbook.Blueprint.SpellList.Contains(spell) - && spellbook.OppositionSchools.Contains(spell.School))) { - return spell.School; - } - } - } - return SpellSchool.None; - } - private static int RenderCraftingSkillInformation(UnitEntityData crafter, StatType skill, int dc, int casterLevel = 0, BlueprintAbility[] prerequisiteSpells = null, BlueprintFeature[] prerequisiteFeats = null, bool anyPrerequisite = false, CrafterPrerequisiteType[] crafterPrerequisites = null, @@ -1611,7 +1599,7 @@ private static int RenderCraftingSkillInformation(UnitEntityData crafter, StatTy // Rob's ruling... if you're below the prerequisite caster level, you're considered to be missing a prerequisite for each // level you fall short. dc += DifficultyClass.MissingPrerequisiteDCModifier * (missing + casterLevelShortfall); - var oppositionSchool = CheckForOppositionSchool(crafter.Descriptor, prerequisiteSpells); + var oppositionSchool = CraftingLogic.CheckForOppositionSchool(crafter.Descriptor, prerequisiteSpells); if (oppositionSchool != SpellSchool.None) { dc += DifficultyClass.OppositionSchoolDCModifier; if (render) {