diff --git a/CharmHelper.cs b/CharmHelper.cs index 6336283..c671cb3 100644 --- a/CharmHelper.cs +++ b/CharmHelper.cs @@ -23,16 +23,26 @@ public static class CharmHelper /// List of sprites to use for the charms. /// private static readonly List CustomSprites = new List(); - + + /// + /// A detour for a private method that has no body. + /// + private static MonoMod.RuntimeDetour.Detour BuildEquippedCharms_Start_hook; + /// /// Constructs the mod and hooks important functions. /// static CharmHelper() { On.PlayerData.CalculateNotchesUsed += OnPlayerDataCalculateNotchesUsed; - On.BuildEquippedCharms.Start += OnBuildEquippedCharmsStart; + // i hate this, can't have shit in detroid + //On.BuildEquippedCharms.Start += OnBuildEquippedCharmsStart; + // workaround, since above isn't working + BuildEquippedCharms_Start_hook = new MonoMod.RuntimeDetour.Detour(typeof(BuildEquippedCharms).GetMethod("Start", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic), typeof(CharmHelper).GetMethod(nameof(OnBuildEquippedCharmsStart_single), System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic)); + BuildEquippedCharms_Start_hook.Apply(); On.GameManager.Start += OnGameManagerStart; } + /// /// Used for static initialization. /// @@ -420,7 +430,17 @@ private static void InitBuildEquippedCharms(BuildEquippedCharms self) self.gameObjectList = tmplist; } - + + /// + /// On hook to initialize charms and equipped charms. + /// + private static void OnBuildEquippedCharmsStart_single() + { + Init(); + + InitBuildEquippedCharms(GameObject.FindObjectOfType(true)); + } + /// /// On hook to initialize charms and equipped charms. /// diff --git a/SFCore.cs b/SFCore.cs index 7ab2ae4..c4ef19e 100644 --- a/SFCore.cs +++ b/SFCore.cs @@ -1,4 +1,5 @@ -using System.Reflection; +using System.IO; +using System.Reflection; using Modding; using SFCore.Utils; @@ -12,9 +13,9 @@ public class SFCoreMod : Mod static SFCoreMod() { // to load all components - SFCore.AchievementHelper.unusedInit(); - SFCore.CharmHelper.unusedInit(); - SFCore.EnviromentParticleHelper.unusedInit(); + //SFCore.AchievementHelper.unusedInit(); + //SFCore.CharmHelper.unusedInit(); + //SFCore.EnviromentParticleHelper.unusedInit(); //SFCore.ItemHelper.unusedInit(); SFCore.MenuStyleHelper.unusedInit(); SFCore.TitleLogoHelper.unusedInit(); diff --git a/SFCore.csproj b/SFCore.csproj index 5104aca..3412c63 100644 --- a/SFCore.csproj +++ b/SFCore.csproj @@ -7,8 +7,8 @@ SFCore A Hollow Knight Mod Library Copyright © SFGrenade 2019-2021 - 1.5.2.1 - 1.5.2.1 + 1.5.2.2 + 1.5.2.2 bin\$(Configuration)\ latest 8002 diff --git a/Util/SpriteUtil.cs b/Util/SpriteUtil.cs new file mode 100644 index 0000000..540b80e --- /dev/null +++ b/Util/SpriteUtil.cs @@ -0,0 +1,136 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using UnityEngine; +using UObject = UnityEngine.Object; + +namespace SFCore.Utils +{ + /// + /// Utils specifically for Sprites and Textures. + /// + public static class SpriteUtil + { + private delegate Color Texture2D_GetPixelImpl(int image, int x, int y); + private static MethodInfo tex_GetPixelImpl = typeof(Texture2D).GetMethod("GetPixelImpl", BindingFlags.NonPublic | BindingFlags.Instance); + + private static Texture2D_GetPixelImpl getGetPixel(Texture2D tex) + { + return (Texture2D_GetPixelImpl) tex_GetPixelImpl.CreateDelegate(typeof(Texture2D_GetPixelImpl), tex); + } + + /// + /// Calculates the area between 3 integer points. + /// + /// A point of the triangle + /// A point of the triangle + /// A point of the triangle + /// The area of the triangle. + public static float CalcTriangleArea(Vector2Int a, Vector2Int b, Vector2Int c) + { + return Mathf.Abs(((a.x * (b.y - c.y)) + (b.x * (c.y - a.y)) + (c.x * (a.y - b.y))) / 2f); + } + + /// + /// Returns a new Texture2D that is cropped. + /// + /// The texture to crop + /// Left offset + /// Top offset + /// Width of the new texture + /// Height of the new texture + /// The cropped Texture2D. + public static Texture2D GetReadableCroppedTexture(Texture2D tex, int x, int y, int width, int height) + { + float xScale = (float)width / tex.width; + float yScale = (float)height / tex.height; + float xOffset = (float)x / tex.width; + float yOffset = (float)y / tex.height; + Texture2D ret = new Texture2D(width, height); + RenderTexture tempRt = RenderTexture.GetTemporary(width, height, 0, RenderTextureFormat.Default, RenderTextureReadWrite.Linear); + Graphics.Blit(tex, tempRt, new Vector2(xScale, yScale), new Vector2(xOffset, yOffset)); + RenderTexture tmpActiveRt = RenderTexture.active; + RenderTexture.active = tempRt; + ret.ReadPixels(new Rect(0, 0, ret.width, ret.height), 0, 0); + ret.Apply(); + RenderTexture.active = tmpActiveRt; + RenderTexture.ReleaseTemporary(tempRt); + return ret; + } + + /// + /// Extracts a sprite from a texture, returns a new texture which is cropped to only the sprite according to its UV values. + /// + /// The sprite to extract + /// A texture with just the sprite on it. + public static Texture2D ExtractTextureFromSprite(Sprite sprite) + { + var spriteRect = (sprite.texture.width, sprite.texture.height); + List texUVs = new List(); + List<(Vector2Int, Vector2Int, Vector2Int)> tmpTriangles = new List<(Vector2Int, Vector2Int, Vector2Int)>(); + int i; + bool[][] contents; + float triangleArea; + float pab, pbc, pac; + Vector2Int p; + int x, y; + int minX, maxX, minY, maxY; + int width, height; + Texture2D outTex; + + foreach (var item in sprite.uv) + { + texUVs.Add(new Vector2Int(Mathf.RoundToInt(item.x * (spriteRect.width - 1)), Mathf.RoundToInt(item.y * (spriteRect.height - 1)))); + } + for (i = 0; i < sprite.triangles.Length; i += 3) + { + tmpTriangles.Add((texUVs[sprite.triangles[i]], texUVs[sprite.triangles[i + 1]], texUVs[sprite.triangles[i + 2]])); + } + + minX = texUVs.Min(uv => uv.x); + maxX = texUVs.Max(uv => uv.x); + minY = texUVs.Min(uv => uv.y); + maxY = texUVs.Max(uv => uv.y); + width = maxX - minX + 1; + height = maxY - minY + 1; + + contents = new bool[height][]; + for (i = 0; i < contents.Length; i++) + contents[i] = new bool[width]; + foreach (var item in tmpTriangles) + { + triangleArea = CalcTriangleArea(item.Item1, item.Item2, item.Item3); + for (x = 0; x < width; x++) + { + for (y = 0; y < height; y++) + { + p = new Vector2Int(minX + x, minY + y); + pab = CalcTriangleArea(item.Item1, item.Item2, p); + pbc = CalcTriangleArea(p, item.Item2, item.Item3); + pac = CalcTriangleArea(item.Item1, p, item.Item3); + if ((pab + pbc + pac) == triangleArea) + { + contents[y][x] = true; + } + } + } + + } + + outTex = GetReadableCroppedTexture(sprite.texture, minX, minY, width, height); + + for (x = 0; x < width; x++) + { + for (y = 0; y < height; y++) + { + if (!contents[y][x]) + outTex.SetPixel(x, y, new Color(0, 0, 0, 0)); + } + } + outTex.Apply(); + + return outTex; + } + } +}