diff --git a/SkillFramework/BaseSkill/BaseSkill.cs b/SkillFramework/BaseSkill/BaseSkill.cs new file mode 100644 index 0000000..a1dddc1 --- /dev/null +++ b/SkillFramework/BaseSkill/BaseSkill.cs @@ -0,0 +1,167 @@ +using BepInEx; +using BepInEx.Configuration; +using System.Collections.Generic; +using System.IO; +using UnityEngine; + +namespace SkillFramework.BaseSkill +{ + public abstract class BaseSkill : ISkill + { + #region Default properties + protected BaseUnityPlugin plugin; + // name and description of the skill used in Localization added in order: + // english, chinese, russian, japanese, ... + protected List skillName; + protected List skillDescription; + // represent the category the skill will be in + public int skillCategory = SkillCategories.Combat; + // plugin section name + protected string configSection; + // skill icon texture + protected Texture2D skillIcon = new Texture2D(1, 1); + // skill icon file name (without file extension, we only use .png) + public string iconName = "frame"; + // config default properties + public int defaultMaxPoints = 5; + public int defaultReqLevel = 2; + // default config entries + public static ConfigEntry maxPoints; + public static ConfigEntry reqLevel; + #endregion + + #region Getters/Setters + protected abstract string skillId + { + get; + set; + } + public string GetSkillID() + { + return skillId; + } + + public override string ToString() + { + return iconName; + } + #endregion + + public ISkill Build(BaseUnityPlugin defaultPlugin, string sectionName) + { + plugin = defaultPlugin; + configSection = sectionName; + + // bind default skill settings + maxPoints = plugin.Config.Bind(configSection, "MaxPoints", defaultMaxPoints, "Maximum skill points for this skill"); + reqLevel = plugin.Config.Bind(configSection, "RequiredLevel", defaultReqLevel, "Character level required for this skill"); + + // bind skill information + SetConfig(); + SetSkillDescription(); + SetSkillName(); + SetSettingChanged(); + SetSkillIcon(); + + BepInExPlugin.Log($"Setup Skill using: {configSection} - {skillCategory} - {defaultMaxPoints} - {defaultReqLevel} - {iconName}"); + + // add skill to the list + AddSkill(); + + return this; + } + + /// + /// This method is called whenever the BepInEx configuration is updated + /// + public virtual void Update() + { + SetSkillDescription(); + + // initialize increase/decrease delegate + SkillInfo skillInfo = SkillAPI.GetSkill(skillId); + + // setup delegate handler if skill is defined + if (skillInfo != null) + { + skillInfo.SetOnDecreaseSkillLevel = OnDecreaseSkillLevel; + skillInfo.SetOnIncreaseSkillLevel = OnIncreaseSkillLevel; + skillInfo.SetPostfixLoadCharacterCustomization = PostfixLoadCharacterCustomization; + skillInfo.SetPrefixSaveCharacterCustomization = PrefixSaveCharacterCustomization; + skillInfo.SetPrefixCharacterCustomizationUpdateStats = PrefixCharacterCustomizationUpdateStats; + skillInfo.SetPostfixCharacterCustomizationUpdateStats = PostfixCharacterCustomizationUpdateStats; + } + } + + /// + /// This method is called when the skill should be added to the game + /// + public virtual void AddSkill() + { + // initialize skill using SkillFramework API + SkillAPI.AddSkill(skillId, skillName, skillDescription, skillCategory, skillIcon, maxPoints.Value, reqLevel.Value, false); + } + + public virtual void SetSkillIcon() + { + string path = Path.Combine(AedenthornUtils.GetAssetPath(plugin), $"{iconName}.png"); + BepInExPlugin.Log($"Setup skill icon {path}"); + if (File.Exists(path)) + skillIcon.LoadImage(File.ReadAllBytes(path)); + } + + #region BepInEx & Basic Skill configuration method to override + public abstract void SetConfig(); + public abstract void SetSkillDescription(); + public abstract void SetSkillName(); + public abstract void SetSettingChanged(); + #endregion + + #region Events Delegates + + public virtual bool OnDecreaseSkillLevel(SkillBox skillBox, SkillInfo skillInfo) + { + // cannot handle skill increase + if (!CanHandleSkillIncreaseDecrease(skillBox, skillId)) + return true; + + // TODO Custom code here + + return true; + } + public virtual bool OnIncreaseSkillLevel(SkillBox skillBox, SkillInfo skillInfo) + { + // cannot handle skill increase + if (!CanHandleSkillIncreaseDecrease(skillBox, skillId)) + return true; + + // TODO Custom code here + + return true; + } + public virtual void PrefixSaveCharacterCustomization(SkillInfo skillInfo, Mainframe mainFrame, CharacterCustomization characterCustomization) + { } + public virtual void PostfixLoadCharacterCustomization(SkillInfo skillInfo, Mainframe mainFrame, CharacterCustomization characterCustomization) + { } + public virtual void PrefixCharacterCustomizationUpdateStats(CharacterCustomization characterCustomization, SkillInfo skillInfo) + { } + public virtual void PostfixCharacterCustomizationUpdateStats(CharacterCustomization characterCustomization, SkillInfo skillInfo) + { } + + #endregion + + #region Helpers + + public virtual bool CanHandleSkillIncreaseDecrease(SkillBox skillBox, string skillId) + { + // procceed to next call if not in the current skill + if (skillId != skillBox.name) + return false; + + return true; + } + + #endregion + + } +} diff --git a/SkillFramework/BaseSkill/ISkill.cs b/SkillFramework/BaseSkill/ISkill.cs new file mode 100644 index 0000000..d5cbdb3 --- /dev/null +++ b/SkillFramework/BaseSkill/ISkill.cs @@ -0,0 +1,42 @@ +using BepInEx; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SkillFramework.BaseSkill +{ + public interface ISkill + { + + ISkill Build(BaseUnityPlugin defaultPlugin, string sectionName); + + void SetConfig(); + + void SetSettingChanged(); + + void SetSkillDescription(); + + void SetSkillIcon(); + + void SetSkillName(); + + void AddSkill(); + + void Update(); + + bool OnIncreaseSkillLevel(SkillBox skillBox, SkillInfo skillInfo); + + bool OnDecreaseSkillLevel(SkillBox skillBox, SkillInfo skillInfo); + + void PrefixSaveCharacterCustomization(SkillInfo skillInfo, Mainframe mainFrame, CharacterCustomization characterCustomization); + + void PostfixLoadCharacterCustomization(SkillInfo skillInfo, Mainframe mainFrame, CharacterCustomization characterCustomization); + + void PrefixCharacterCustomizationUpdateStats(CharacterCustomization characterCustomization, SkillInfo skillInfo); + + void PostfixCharacterCustomizationUpdateStats(CharacterCustomization characterCustomization, SkillInfo skillInfo); + + } +} diff --git a/SkillFramework/BepInExPlugin.cs b/SkillFramework/BepInExPlugin.cs index 61cb311..65f1eb3 100644 --- a/SkillFramework/BepInExPlugin.cs +++ b/SkillFramework/BepInExPlugin.cs @@ -11,8 +11,8 @@ namespace SkillFramework { - [BepInPlugin("aedenthorn.SkillFramework", "Skill Framework", "0.2.1")] - public class BepInExPlugin: BaseUnityPlugin + [BepInPlugin("aedenthorn.SkillFramework", "Skill Framework", "0.3.0")] + public class BepInExPlugin : BaseUnityPlugin { public static ConfigEntry modEnabled; public static ConfigEntry isDebug; @@ -23,11 +23,12 @@ public class BepInExPlugin: BaseUnityPlugin public static Dictionary> characterSkillLevels = new Dictionary>(); public static Dictionary customSkills = new Dictionary(); - public static void Dbgl(string str = "", bool pref = true) + public static void Log(string message) { if (isDebug.Value) - Debug.Log((pref ? typeof(BepInExPlugin).Namespace + " " : "") + str); + Debug.Log(typeof(BepInExPlugin).Namespace + " - " + message); } + private void Awake() { context = this; @@ -39,6 +40,18 @@ private void Awake() Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), null); } + + public static Transform[] GetUICharacterSkillCategoriesParents(UICharacter uICharacter) + { + return new Transform[] + { + uICharacter.daggerproficiency.transform.parent, + uICharacter.ignorpain.transform.parent, + uICharacter.healaura.transform.parent, + uICharacter.goldenhand.transform.parent + }; + } + public static int GetCharacterSkillLevel(string charName, string skillname) { if (!characterSkillLevels.ContainsKey(charName)) @@ -59,30 +72,34 @@ static class UICharacter_Refresh_Patch { static void Postfix(UICharacter __instance) { + // this draw the custom skills in their respective categories + // after each time the UICharacter.Refresh method is called in the game + if (!modEnabled.Value || !customSkills.Any()) return; - Transform[] parents = new Transform[] - { - __instance.daggerproficiency.transform.parent, - __instance.ignorpain.transform.parent, - __instance.healaura.transform.parent, - __instance.goldenhand.transform.parent - }; - foreach (SkillInfo info in customSkills.Values) + + // get categories transform elements + Transform[] parents = GetUICharacterSkillCategoriesParents(__instance); + + foreach (SkillInfo skillInfo in customSkills.Values) { - Transform t = parents[info.category].Find(info.id); - if (!t) + // get current skill transform element + Transform skillTransform = parents[skillInfo.category].Find(skillInfo.id); + + // check if it's defined + if (!skillTransform) { - if (parents[info.category].childCount % 6 == 0) + // check if we should place the skill in a new line + if (parents[skillInfo.category].childCount % 6 == 0) { - // decrease y of lower cats - float height = parents[info.category].GetComponent().spacing.y + __instance.daggerproficiency.transform.GetComponent().sizeDelta.y; - for (int i = info.category + 1; i < parents.Length; i++) + // decrease y of lower categories + float height = parents[skillInfo.category].GetComponent().spacing.y + __instance.daggerproficiency.transform.GetComponent().sizeDelta.y; + for (int i = skillInfo.category + 1; i < parents.Length; i++) { parents[i].parent.GetComponent().anchoredPosition -= new Vector2(0, height); - foreach(Transform c in parents[i]) + foreach (Transform category in parents[i]) { - foreach(Transform cc in c) + foreach (Transform cc in category) { if (cc.name.StartsWith("plus")) cc.GetComponent().anchoredPosition = Vector2.zero; @@ -91,97 +108,71 @@ static void Postfix(UICharacter __instance) } parents[0].parent.parent.GetComponent().sizeDelta += new Vector2(0, height); } - t = Instantiate(__instance.daggerproficiency.transform, parents[info.category]); - t.name = info.id; - t.GetComponentInChildren