diff --git a/src/Json/EnumCacheJson.cs b/src/Json/EnumCacheJson.cs index 0aae58d..3a177d4 100644 --- a/src/Json/EnumCacheJson.cs +++ b/src/Json/EnumCacheJson.cs @@ -7,7 +7,7 @@ namespace PolyMod.Json; /// Converts an to and from a JSON string using the . /// /// The type of the enum. -internal class EnumCacheJson : JsonConverter where T : struct, Enum +public class EnumCacheJson : JsonConverter where T : struct, Enum { /// /// Reads and converts the JSON to an enum value. diff --git a/src/Json/JsonMerger.cs b/src/Json/JsonMerger.cs new file mode 100644 index 0000000..3db0e7c --- /dev/null +++ b/src/Json/JsonMerger.cs @@ -0,0 +1,124 @@ +using Il2CppSystem.IO; +using Newtonsoft.Json.Linq; + +namespace PolyMod.Json; + +/// +/// Provides functionality to merge JSON objects, with special handling for arrays. +/// +public static class JsonMerger +{ + /// + /// Merges a patch JObject into an original JObject. + /// + /// The original JObject. + /// The patch JObject to merge. + /// The merged JObject. + public static JObject Merge(JObject original, JObject patch) + { + foreach (var property in patch.Properties().ToArray().ToList()) + { + if (property == null || property.Name == null) + continue; + + string propName = property.Name; + JToken? originalValue = original[propName]; + JToken patchValue = property.Value; + + if (originalValue == null) + { + original[propName] = patchValue; + continue; + } + + JObject? originalObj = originalValue.TryCast(); + JObject? patchObj = patchValue.TryCast(); + if (originalObj != null && patchObj != null) + { + Merge(originalObj, patchObj); + continue; + } + + JArray? originalArr = originalValue.TryCast(); + JArray? patchArr = patchValue.TryCast(); + if (originalArr != null && patchArr != null) + { + bool isSkins = propName.Equals("skins", StringComparison.OrdinalIgnoreCase); + JArray merged = MergeArrays(originalArr, patchArr, isSkins); + original[propName] = merged; + continue; + } + original[propName] = patchValue; + } + + return original; + } + + /// + /// Merges two JArrays, with special handling for adding and removing elements. + /// + /// The original JArray. + /// The patch JArray to merge. + /// A flag to indicate if the array is a 'skins' array, which has special merging logic. + /// The merged JArray. + private static JArray MergeArrays(JArray original, JArray patch, bool isSkins) + { + var result = new JArray(original); + var patchList = patch._values.ToArray().ToList(); + + bool hasDirectValues = patchList.Any(v => + v.Type == JTokenType.String && + !v.ToString().StartsWith("+") && + !v.ToString().StartsWith("-")); + + if (!isSkins && hasDirectValues) + { + result = new JArray(); + } + + foreach (var token in patchList) + { + if (token.Type != JTokenType.String) + { + result.Add(token); + continue; + } + + string str = token.ToString(); + + if (str.StartsWith("+")) + { + string value = str.Substring(1); + if (!result._values.ToArray().Any(t => t.Type == JTokenType.String && t.ToString() == value)) + { + result.Add(value); + } + } + else if (str.StartsWith("-")) + { + string value = str.Substring(1); + var toRemove = result._values.ToArray() + .Where(t => t.Type == JTokenType.String && t.ToString() == value) + .ToList(); + foreach (var rem in toRemove) + result.Remove(rem); + } + else + { + if (isSkins) + { + if (!result._values.ToArray().Any(t => t.Type == JTokenType.String && t.ToString() == str)) + { + result.Add(str); + } + } + else + { + result.Add(str); + } + } + } + + return result; + } +} diff --git a/src/Json/Vector2Json.cs b/src/Json/Vector2Json.cs index 4764952..1957c4d 100644 --- a/src/Json/Vector2Json.cs +++ b/src/Json/Vector2Json.cs @@ -7,7 +7,7 @@ namespace PolyMod.Json; /// /// Converts a to and from a JSON array of two numbers. /// -internal class Vector2Json : JsonConverter +public class Vector2Json : JsonConverter { /// /// Reads and converts the JSON to a . diff --git a/src/Json/VersionJson.cs b/src/Json/VersionJson.cs index 54bb0a1..21c6c81 100644 --- a/src/Json/VersionJson.cs +++ b/src/Json/VersionJson.cs @@ -6,7 +6,7 @@ namespace PolyMod.Json; /// /// Converts a to and from a JSON string. /// -internal class VersionJson : JsonConverter +public class VersionJson : JsonConverter { /// /// Reads and converts the JSON to a . diff --git a/src/Loader.cs b/src/Loader.cs index de33248..dc3cd61 100644 --- a/src/Loader.cs +++ b/src/Loader.cs @@ -23,23 +23,26 @@ namespace PolyMod; /// public static class Loader { + internal record TypeMapping(Type type, bool shouldCreateCache = true); + /// /// Mappings from JSON data types to their corresponding C# types. /// - internal static Dictionary typeMappings = new() + internal static Dictionary typeMappings = new() { - { "tribeData", typeof(TribeData.Type) }, - { "techData", typeof(TechData.Type) }, - { "unitData", typeof(UnitData.Type) }, - { "improvementData", typeof(ImprovementData.Type) }, - { "terrainData", typeof(Polytopia.Data.TerrainData.Type) }, - { "resourceData", typeof(ResourceData.Type) }, - { "taskData", typeof(TaskData.Type) }, - { "tribeAbility", typeof(TribeAbility.Type) }, - { "unitAbility", typeof(UnitAbility.Type) }, - { "improvementAbility", typeof(ImprovementAbility.Type) }, - { "playerAbility", typeof(PlayerAbility.Type) }, - { "weaponData", typeof(UnitData.WeaponEnum) } + { "tribeData", new TypeMapping(typeof(TribeData.Type)) }, + { "techData", new TypeMapping(typeof(TechData.Type)) }, + { "unitData", new TypeMapping(typeof(UnitData.Type)) }, + { "improvementData", new TypeMapping(typeof(ImprovementData.Type)) }, + { "terrainData", new TypeMapping(typeof(Polytopia.Data.TerrainData.Type)) }, + { "resourceData", new TypeMapping(typeof(ResourceData.Type)) }, + { "taskData", new TypeMapping(typeof(TaskData.Type)) }, + { "tribeAbility", new TypeMapping(typeof(TribeAbility.Type)) }, + { "unitAbility", new TypeMapping(typeof(UnitAbility.Type)) }, + { "improvementAbility", new TypeMapping(typeof(ImprovementAbility.Type)) }, + { "playerAbility", new TypeMapping(typeof(PlayerAbility.Type)) }, + { "weaponData", new TypeMapping(typeof(UnitData.WeaponEnum)) }, + { "skinData", new TypeMapping(typeof(SkinData), false) } }; /// @@ -50,7 +53,7 @@ public static class Loader /// /// Handlers for processing specific data types during mod loading. /// - private static readonly Dictionary> typeHandlers = new() + internal static readonly Dictionary> typeHandlers = new() { [typeof(TribeData.Type)] = new((token, duringEnumCacheCreation) => { @@ -63,6 +66,38 @@ public static class Loader } else { + if (token["skins"] != null) + { + JArray skins = token["skins"].Cast(); + List skinValues = skins._values.ToArray().ToList(); + foreach (var skin in skinValues) + { + string skinValue = skin.ToString(); + if (!Enum.TryParse(CultureInfo.CurrentCulture.TextInfo.ToTitleCase(skinValue), out _)) + { + EnumCache.AddMapping(skinValue.ToLowerInvariant(), (SkinType)Registry.autoidx); + EnumCache.AddMapping(skinValue.ToLowerInvariant(), (SkinType)Registry.autoidx); + Registry.skinInfo.Add(new Visual.SkinInfo(Registry.autoidx, skinValue, null)); + Plugin.logger.LogInfo("Created mapping for skinType with id " + skinValue + " and index " + Registry.autoidx); + Registry.autoidx++; + } + } + Il2CppSystem.Collections.Generic.List modifiedSkins = skins._values; + foreach (var skin in Registry.skinInfo) + { + if (modifiedSkins.Contains(skin.id)) + { + modifiedSkins.Remove(skin.id); + modifiedSkins.Add(skin.idx.ToString()); + } + } + JArray newSkins = new JArray(); + foreach (var item in modifiedSkins) + { + newSkins.Add(item); + } + token["skins"] = newSkins; + } if (token["preview"] != null) { Visual.PreviewTile[] preview = JsonSerializer.Deserialize(token["preview"].ToString())!; @@ -142,7 +177,13 @@ public static class Loader } PrefabManager.resources.TryAdd((ResourceData.Type)Registry.autoidx, PrefabManager.resources[resourcePrefabType]); } - }) + }), + + [typeof(SkinData)] = new((token, duringEnumCacheCreation) => + { + var prop = token.Parent.Cast(); + prop.Replace(new JProperty(prop.Name.ToLower(), prop.Value)); + }), }; /// @@ -176,7 +217,13 @@ public static void AddGameModeButton(string id, UIButtonBase.ButtonAction action public static void AddPatchDataType(string typeId, Type type) { if (!typeMappings.ContainsKey(typeId)) - typeMappings.Add(typeId, type); + typeMappings.Add(typeId, new TypeMapping(type)); + } + + public static void AddPatchDataType(string typeId, Type type, bool shouldCreateCache) + { + if (!typeMappings.ContainsKey(typeId)) + typeMappings.Add(typeId, new TypeMapping(type, shouldCreateCache)); } /// @@ -428,7 +475,7 @@ public static void LoadLocalizationFile(Mod mod, Mod.File file) } catch (Exception e) { - Plugin.logger.LogError($"Error on loading locatization from {mod.id} mod: {e.Message}"); + Plugin.logger.LogError($"Error on loading locatization from {mod.id} mod: {e.StackTrace}"); } } @@ -509,7 +556,7 @@ public static void UpdateSprite(string name) } catch (Exception e) { - Plugin.logger.LogError($"Error on loading sprite data from {mod.id} mod: {e.Message}"); + Plugin.logger.LogError($"Error on loading sprite data from {mod.id} mod: {e.StackTrace}"); return null; } } @@ -568,7 +615,7 @@ public static void LoadPrefabInfoFile(Mod mod, Mod.File file) } catch (Exception e) { - Plugin.logger.LogError($"Error on loading prefab info from {mod.id} mod: {e.Message}"); + Plugin.logger.LogError($"Error on loading prefab info from {mod.id} mod: {e.StackTrace}"); } } @@ -675,60 +722,13 @@ public static void LoadGameLogicDataPatch(Mod mod, JObject gld, JObject patch) { try { - HandleSkins(gld, patch); - // First pass: add new enum values and create mappings - foreach (JToken jtoken in patch.SelectTokens("$.*.*").ToArray()) - { - JObject? token = jtoken.TryCast(); - if (token != null) - { - string dataType = Util.GetJTokenName(token, 2); - if (typeMappings.TryGetValue(dataType, out Type? targetType)) - { - if (token["idx"] != null && (int)token["idx"] == -1) - { - string id = Util.GetJTokenName(token); - token["idx"] = Registry.autoidx; - MethodInfo? methodInfo = typeof(EnumCache<>).MakeGenericType(targetType).GetMethod("AddMapping"); - if (methodInfo != null) - { - methodInfo.Invoke(null, new object[] { id, Registry.autoidx }); - methodInfo.Invoke(null, new object[] { id, Registry.autoidx }); - - if (typeHandlers.TryGetValue(targetType, out var handler)) - { - handler(token, true); - } - Plugin.logger.LogInfo("Created mapping for " + targetType.ToString() + " with id " + id + " and index " + Registry.autoidx); - Registry.autoidx++; - } - } - } - } - } - // Second pass: apply special handling - foreach (JToken jtoken in patch.SelectTokens("$.*.*").ToArray()) - { - JObject? token = jtoken.TryCast(); - if (token != null) - { - string dataType = Util.GetJTokenName(token, 2); - if (typeMappings.TryGetValue(dataType, out Type? targetType)) - { - if (typeHandlers.TryGetValue(targetType, out var handler)) - { - handler(token, false); - } - } - } - } - // Final pass: merge the patch into the game logic data - gld.Merge(patch, new() { MergeArrayHandling = MergeArrayHandling.Replace, MergeNullValueHandling = MergeNullValueHandling.Merge }); + // Merge the patch into the game logic data + gld = JsonMerger.Merge(gld, patch); Plugin.logger.LogInfo($"Registered patch from {mod.id} mod"); } catch (Exception e) { - Plugin.logger.LogError($"Error on loading patch from {mod.id} mod: {e.Message}"); + Plugin.logger.LogError($"Error on loading patch from {mod.id} mod: {e.StackTrace}"); mod.status = Mod.Status.Error; } } @@ -747,76 +747,160 @@ public static void LoadAssetBundle(Mod mod, Mod.File file) } /// - /// Handles skin data from a patch. + /// Processes the merged game logic data after all mods have been loaded and patched. /// - /// The original game logic data. - /// The patch to apply. - public static void HandleSkins(JObject gld, JObject patch) + /// The game logic data object to populate. + /// The root JObject of the merged game logic data. + internal static void ProcessGameLogicData(GameLogicData gameLogicData, JObject rootObject) { - foreach (JToken jtoken in patch.SelectTokens("$.tribeData.*").ToArray()) + try { - JObject token = jtoken.Cast(); + CreateMappings(rootObject); + ProcessPrefabs(); + ProcessEmbarkOverrides(); + ProcessAttractOverrides(); + } + catch (Exception e) + { + Plugin.logger.LogError($"Error on processing modified game logic data : {e.StackTrace}"); + } + } - if (token["skins"] != null) + /// + /// Creates EnumCache mappings for custom enum values and invokes type handlers. + /// + /// + internal static void CreateMappings(JObject rootObject) + { + foreach (JToken jtoken in rootObject.SelectTokens("$.*.*").ToArray()) + { + JObject? token = jtoken.TryCast(); + if (token != null) { - JArray skins = token["skins"].Cast(); - List skinsToRemove = new(); - List skinValues = skins._values.ToArray().ToList(); - foreach (var skin in skinValues) + string dataType = Util.GetJTokenName(token, 2); + if (Loader.typeMappings.TryGetValue(dataType, out Loader.TypeMapping? typeMapping)) { - string skinValue = skin.ToString(); - if (skinValue.StartsWith('-') && Enum.TryParse(skinValue.Substring(1), out _)) + if (token["idx"] != null && (int)token["idx"] == -1 && typeMapping.shouldCreateCache) { - skinsToRemove.Add(skinValue.Substring(1)); - } - else if (!Enum.TryParse(skinValue, out _)) - { - EnumCache.AddMapping(skinValue.ToLowerInvariant(), (SkinType)Registry.autoidx); - EnumCache.AddMapping(skinValue.ToLowerInvariant(), (SkinType)Registry.autoidx); - Registry.skinInfo.Add(new Visual.SkinInfo(Registry.autoidx, skinValue, null)); - Plugin.logger.LogInfo("Created mapping for skinType with id " + skinValue + " and index " + Registry.autoidx); - Registry.autoidx++; - } - } - foreach (var skin in Registry.skinInfo) - { - if (skins._values.Contains(skin.id)) - { - skins._values.Remove(skin.id); - skins._values.Add(skin.idx); + Type targetType = typeMapping.type; + string id = Util.GetJTokenName(token); + token["idx"] = Registry.autoidx; + MethodInfo? methodInfo = typeof(EnumCache<>).MakeGenericType(targetType).GetMethod("AddMapping"); + if (methodInfo != null) + { + methodInfo.Invoke(null, new object[] { id, Registry.autoidx }); + methodInfo.Invoke(null, new object[] { id, Registry.autoidx }); + + if (Loader.typeHandlers.TryGetValue(targetType, out var handler)) + { + handler(token, true); + } + Plugin.logger.LogInfo("Created mapping for " + targetType.ToString() + " with id " + id + " and index " + Registry.autoidx); + Registry.autoidx++; + } } } - JToken originalSkins = gld.SelectToken(skins.Path, false); - if (originalSkins != null) + } + } + foreach (JToken jtoken in rootObject.SelectTokens("$.*.*").ToArray()) + { + JObject? token = jtoken.TryCast(); + if (token != null) + { + string dataType = Util.GetJTokenName(token, 2); + if (Loader.typeMappings.TryGetValue(dataType, out Loader.TypeMapping? typeMapping)) { - skins.Merge(originalSkins); - foreach (var skin in skinsToRemove) + if (Loader.typeHandlers.TryGetValue(typeMapping.type, out var handler)) { - skins._values.Remove(skin); - skins._values.Remove("-" + skin); + handler(token, false); } } } } - foreach (JToken jtoken in patch.SelectTokens("$.skinData.*").ToArray()) + } + + /// + /// Processes the prefab registry and populates the PrefabManager with custom prefabs. + /// + internal static void ProcessPrefabs() + { + foreach (System.Collections.Generic.KeyValuePair item in Registry.prefabNames) { - JObject token = jtoken.Cast(); - string id = Util.GetJTokenName(token); - int index = Registry.skinInfo.FindIndex(t => t.id == id); - if (Registry.skinInfo.ElementAtOrDefault(index) != null) + UnitData.Type unitPrefabType = UnitData.Type.Scout; + string prefabId = item.Value; + if (Enum.TryParse(prefabId, out UnitData.Type parsedType)) { - SkinData skinData = new(); - if (token["color"] != null) + unitPrefabType = parsedType; + PrefabManager.units.TryAdd(item.Key, PrefabManager.units[(int)unitPrefabType]); + } + else + { + KeyValuePair prefabInfo = Registry.unitPrefabs.FirstOrDefault(kv => kv.Key.name == prefabId); + if (!EqualityComparer.Default.Equals(prefabInfo.Key, default)) { - skinData.color = (int)token["color"]; + PrefabManager.units.TryAdd(item.Key, prefabInfo.Value); } - if (token["language"] != null) + else { - skinData.language = token["language"].ToString(); + PrefabManager.units.TryAdd(item.Key, PrefabManager.units[(int)unitPrefabType]); } - Registry.skinInfo[index] = new Visual.SkinInfo(Registry.skinInfo[index].idx, Registry.skinInfo[index].id, skinData); } } - patch.Remove("skinData"); + } + + /// + /// Processes embark overrides by mapping original embark unit types to configured overrides. + /// + internal static void ProcessEmbarkOverrides() + { + foreach (KeyValuePair entry in Main.embarkNames) + { + try + { + UnitData.Type unit = EnumCache.GetType(entry.Key); + UnitData.Type newUnit = EnumCache.GetType(entry.Value); + Main.embarkOverrides[unit] = newUnit; + Plugin.logger.LogInfo($"Embark unit type for {entry.Key} is now {entry.Value}"); + } + catch + { + Plugin.logger.LogError($"Embark unit type for {entry.Key} is not valid: {entry.Value}"); + } + } + } + + /// + /// Processes attract overrides by mapping improvements to resources and terrain types based on configured overrides. + /// + internal static void ProcessAttractOverrides() + { + foreach (KeyValuePair entry in Main.attractsResourceNames) + { + try + { + ImprovementData.Type improvement = EnumCache.GetType(entry.Key); + ResourceData.Type resource = EnumCache.GetType(entry.Value); + Main.attractsResourceOverrides[improvement] = resource; + Plugin.logger.LogInfo($"Improvement {entry.Key} now attracts {entry.Value}"); + } + catch + { + Plugin.logger.LogError($"Improvement {entry.Key} resource type is not valid: {entry.Value}"); + } + } + foreach (KeyValuePair entry in Main.attractsTerrainNames) + { + try + { + ImprovementData.Type improvement = EnumCache.GetType(entry.Key); + Polytopia.Data.TerrainData.Type terrain = EnumCache.GetType(entry.Value); + Main.attractsTerrainOverrides[improvement] = terrain; + Plugin.logger.LogInfo($"Improvement {entry.Key} now attracts on {entry.Value}"); + } + catch + { + Plugin.logger.LogError($"Improvement {entry.Key} terrain type is not valid: {entry.Value}"); + } + } } } diff --git a/src/Managers/Loc.cs b/src/Managers/Loc.cs index 37d7767..cf6c5d3 100644 --- a/src/Managers/Loc.cs +++ b/src/Managers/Loc.cs @@ -63,9 +63,9 @@ private static bool Localization_Get(ref string key, Il2CppReferenceArray).MakeGenericType(targetType).GetMethod("TryGetName"); + MethodInfo? methodInfo = typeof(EnumCache<>).MakeGenericType(typeMapping.type).GetMethod("TryGetName"); if (methodInfo != null) { object?[] parameters = { idx, null }; diff --git a/src/Managers/Main.cs b/src/Managers/Main.cs index acc8c5e..b9e19f4 100644 --- a/src/Managers/Main.cs +++ b/src/Managers/Main.cs @@ -1,8 +1,10 @@ using BepInEx.Unity.IL2CPP.Logging; using HarmonyLib; +using Il2CppSystem.Linq; using Newtonsoft.Json.Linq; using Polytopia.Data; using PolytopiaBackendBase.Game; +using System.Reflection; using System.Diagnostics; using System.Text; using System.Text.Json; @@ -76,80 +78,11 @@ public static class Main /// [HarmonyPrefix] [HarmonyPatch(typeof(GameLogicData), nameof(GameLogicData.AddGameLogicPlaceholders))] - private static void GameLogicData_Parse(GameLogicData __instance, JObject rootObject) + private static void GameLogicData_AddGameLogicPlaceholders(GameLogicData __instance, ref JObject rootObject) { if (!fullyInitialized) { - Load(rootObject); - foreach (System.Collections.Generic.KeyValuePair item in Registry.prefabNames) - { - UnitData.Type unitPrefabType = UnitData.Type.Scout; - string prefabId = item.Value; - if (Enum.TryParse(prefabId, out UnitData.Type parsedType)) - { - unitPrefabType = parsedType; - PrefabManager.units.TryAdd(item.Key, PrefabManager.units[(int)unitPrefabType]); - } - else - { - KeyValuePair prefabInfo = Registry.unitPrefabs.FirstOrDefault(kv => kv.Key.name == prefabId); - if (!EqualityComparer.Default.Equals(prefabInfo.Key, default)) - { - PrefabManager.units.TryAdd(item.Key, prefabInfo.Value); - } - else - { - PrefabManager.units.TryAdd(item.Key, PrefabManager.units[(int)unitPrefabType]); - } - } - } - foreach (Visual.SkinInfo skin in Registry.skinInfo) - { - if (skin.skinData != null) - __instance.skinData[(SkinType)skin.idx] = skin.skinData; - } - foreach (KeyValuePair entry in embarkNames) - { - try - { - UnitData.Type unit = EnumCache.GetType(entry.Key); - UnitData.Type newUnit = EnumCache.GetType(entry.Value); - embarkOverrides[unit] = newUnit; - Plugin.logger.LogInfo($"Embark unit type for {entry.Key} is now {entry.Value}"); - } - catch - { - Plugin.logger.LogError($"Embark unit type for {entry.Key} is not valid: {entry.Value}"); - } - } - foreach (KeyValuePair entry in attractsResourceNames) - { - try - { - ImprovementData.Type improvement = EnumCache.GetType(entry.Key); - ResourceData.Type resource = EnumCache.GetType(entry.Value); - attractsResourceOverrides[improvement] = resource; - Plugin.logger.LogInfo($"Improvement {entry.Key} now attracts {entry.Value}"); - } - catch - { - Plugin.logger.LogError($"Improvement {entry.Key} resource type is not valid: {entry.Value}"); - } - } - foreach (KeyValuePair entry in attractsTerrainNames) - { - try - { - ImprovementData.Type improvement = EnumCache.GetType(entry.Key); - Polytopia.Data.TerrainData.Type terrain = EnumCache.GetType(entry.Value); - attractsTerrainOverrides[improvement] = terrain; - Plugin.logger.LogInfo($"Improvement {entry.Key} now attracts on {entry.Value}"); - } - catch - { - Plugin.logger.LogError($"Improvement {entry.Key} terrain type is not valid: {entry.Value}"); - } - } + Load(__instance, rootObject); fullyInitialized = true; } } @@ -452,7 +385,8 @@ internal static void Init() /// Loads all mod content. /// /// The game logic data to patch. - internal static void Load(JObject gameLogicdata) + /// The JSON object representing the game logic data. + internal static void Load(GameLogicData gameLogicData, JObject json) { stopwatch.Start(); Loc.BuildAndLoadLocalization( @@ -476,7 +410,7 @@ internal static void Load(JObject gameLogicdata) { Loader.LoadGameLogicDataPatch( mod, - gameLogicdata, + json, JObject.Parse(new StreamReader(new MemoryStream(file.bytes)).ReadToEnd()) ); continue; @@ -509,6 +443,7 @@ internal static void Load(JObject gameLogicdata) { TechItem.techTierFirebaseId.Add($"tech_research_{i}"); } + Loader.ProcessGameLogicData(gameLogicData, json); stopwatch.Stop(); Plugin.logger.LogInfo($"Loaded all mods in {stopwatch.ElapsedMilliseconds}ms"); } diff --git a/src/Managers/Visual.cs b/src/Managers/Visual.cs index f2ae379..acd12ba 100644 --- a/src/Managers/Visual.cs +++ b/src/Managers/Visual.cs @@ -643,12 +643,16 @@ private static void UpdateVisualPart(SkinVisualsReference.VisualPart visualPart, { if (visualPart.outlineRenderer.spriteRenderer != null) visualPart.outlineRenderer.spriteRenderer.sprite = outlineSprite; - else if (visualpart.outlineRenderer.polytopiaSpriteRenderer != null) + else if (visualPart.outlineRenderer.polytopiaSpriteRenderer != null) visualPart.outlineRenderer.polytopiaSpriteRenderer.sprite = outlineSprite; } } /// Builds a sprite from raw byte data. + /// The raw byte data of the image. + /// The pivot point of the sprite. + /// The number of pixels per unit for the sprite. + /// The created sprite. public static Sprite BuildSprite(byte[] data, Vector2? pivot = null, float pixelsPerUnit = 2112f) { Texture2D texture = new(1, 1, TextureFormat.RGBA32, true); @@ -665,6 +669,10 @@ public static Sprite BuildSprite(byte[] data, Vector2? pivot = null, float pixel } /// Builds a sprite from a texture. + /// The texture to create the sprite from. + /// The pivot point of the sprite. + /// The number of pixels per unit for the sprite. + /// The created sprite. public static Sprite BuildSpriteWithTexture(Texture2D texture, Vector2? pivot = null, float? pixelsPerUnit = 2112f) { return Sprite.Create(