diff --git a/PolyMod.csproj b/PolyMod.csproj index fc5d85d..99a2248 100644 --- a/PolyMod.csproj +++ b/PolyMod.csproj @@ -11,7 +11,7 @@ IL2CPP PolyMod - 1.2.10-pre + 1.2.10-pre.1 2.16.3.15581 PolyModdingTeam The Battle of Polytopia's mod loader. diff --git a/src/Loader.cs b/src/Loader.cs index 838e491..32bbc96 100644 --- a/src/Loader.cs +++ b/src/Loader.cs @@ -54,85 +54,89 @@ internal record TypeMapping(Type type, bool shouldCreateCache = true); /// /// Handlers for processing specific data types during mod loading. /// - internal static readonly Dictionary> typeHandlers = new() + internal static readonly Dictionary>> typeHandlers = new() { - [typeof(TribeType)] = new((token, duringEnumCacheCreation) => - { - if (duringEnumCacheCreation) - { - Registry.customTribes.Add((TribeType)Registry.autoidx); - token["style"] = Registry.climateAutoidx; - token["climate"] = Registry.climateAutoidx; - Registry.climateAutoidx++; - } - else + [typeof(TribeType)] = new List>() { + new((token, duringEnumCacheCreation) => { - if (token["skins"] != null) + if (duringEnumCacheCreation) + { + Registry.customTribes.Add((TribeType)Registry.autoidx); + token["style"] = Registry.climateAutoidx; + token["climate"] = Registry.climateAutoidx; + Registry.climateAutoidx++; + } + else { - JArray skins = token["skins"].Cast(); - List skinValues = skins._values.ToArray().ToList(); - foreach (var skin in skinValues) + if (token["skins"] != null) { - string skinValue = skin.ToString(); - if (!Enum.TryParse(skinValue, ignoreCase: true, out _)) + JArray skins = token["skins"].Cast(); + List skinValues = skins._values.ToArray().ToList(); + foreach (var skin in skinValues) { - 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++; + string skinValue = skin.ToString(); + if (!Enum.TryParse(skinValue, ignoreCase: true, 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)) + 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) { - modifiedSkins.Remove(skin.id); - modifiedSkins.Add(skin.idx.ToString()); + newSkins.Add(item); } + token["skins"] = newSkins; } - JArray newSkins = new JArray(); - foreach (var item in modifiedSkins) + if (token["preview"] != null) { - newSkins.Add(item); + Visual.PreviewTile[] preview = JsonSerializer.Deserialize(token["preview"].ToString())!; + Registry.tribePreviews[Util.GetJTokenName(token)] = preview; } - token["skins"] = newSkins; - } - if (token["preview"] != null) - { - Visual.PreviewTile[] preview = JsonSerializer.Deserialize(token["preview"].ToString())!; - Registry.tribePreviews[Util.GetJTokenName(token)] = preview; } - } - }), - - [typeof(UnitData.Type)] = new((token, duringEnumCacheCreation) => - { - if (!duringEnumCacheCreation) + }) + }, + [typeof(UnitData.Type)] = new List>() { + new((token, duringEnumCacheCreation) => { - if (token["prefab"] != null) + if (!duringEnumCacheCreation) { - Registry.prefabNames.Add((int)(UnitData.Type)(int)token["idx"], CultureInfo.CurrentCulture.TextInfo.ToTitleCase(token["prefab"]!.ToString())); - } - if (token["embarksTo"] != null) - { - string unitId = Util.GetJTokenName(token); - string embarkUnitId = token["embarksTo"].ToString(); - Main.embarkNames[unitId] = embarkUnitId; - } - if (token["weapon"] != null) - { - string weaponString = token["weapon"].ToString(); - if (EnumCache.TryGetType(weaponString, out UnitData.WeaponEnum type)) + if (token["prefab"] != null) + { + Registry.prefabNames.Add((int)(UnitData.Type)(int)token["idx"], token["prefab"]!.ToString()); + } + if (token["embarksTo"] != null) + { + string unitId = Util.GetJTokenName(token); + string embarkUnitId = token["embarksTo"].ToString(); + Main.embarkNames[unitId] = embarkUnitId; + } + if (token["weapon"] != null) { - token["weapon"] = (int)type; + string weaponString = token["weapon"].ToString(); + if (EnumCache.TryGetType(weaponString, out UnitData.WeaponEnum type)) + { + token["weapon"] = (int)type; + } } } - } - }), + }) + }, - [typeof(ImprovementData.Type)] = new((token, duringEnumCacheCreation) => + [typeof(ImprovementData.Type)] = new List>() { + new((token, duringEnumCacheCreation) => { if (duringEnumCacheCreation) { @@ -161,29 +165,34 @@ internal record TypeMapping(Type type, bool shouldCreateCache = true); Main.attractsTerrainNames[improvementId] = attractsId; } } - }), + }) + }, - [typeof(ResourceData.Type)] = new((token, duringEnumCacheCreation) => - { - if (duringEnumCacheCreation) + [typeof(ResourceData.Type)] = new List>() { + new((token, duringEnumCacheCreation) => { - ResourceData.Type resourcePrefabType = ResourceData.Type.Game; - if (token["prefab"] != null) + if (duringEnumCacheCreation) { - string prefabId = CultureInfo.CurrentCulture.TextInfo.ToTitleCase(token["prefab"]!.ToString()); - if (Enum.TryParse(prefabId, out ResourceData.Type parsedType)) - resourcePrefabType = parsedType; + ResourceData.Type resourcePrefabType = ResourceData.Type.Game; + if (token["prefab"] != null) + { + string prefabId = CultureInfo.CurrentCulture.TextInfo.ToTitleCase(token["prefab"]!.ToString()); + if (Enum.TryParse(prefabId, out ResourceData.Type parsedType)) + resourcePrefabType = parsedType; + } + if(token["idx"] != null) + PrefabManager.resources.TryAdd((ResourceData.Type)(int)token["idx"], PrefabManager.resources[resourcePrefabType]); } - if(token["idx"] != null) - PrefabManager.resources.TryAdd((ResourceData.Type)(int)token["idx"], PrefabManager.resources[resourcePrefabType]); - } - }), + }) + }, - [typeof(SkinData)] = new((token, duringEnumCacheCreation) => - { - var prop = token.Parent.Cast(); - prop.Replace(new JProperty(prop.Name.ToLower(), prop.Value)); - }), + [typeof(SkinData)] = new List>() { + new((token, duringEnumCacheCreation) => + { + var prop = token.Parent.Cast(); + prop.Replace(new JProperty(prop.Name.ToLower(), prop.Value)); + }) + }, }; /// @@ -217,7 +226,7 @@ public static void AddGameMode(string id, UIButtonBase.ButtonAction action, bool /// Adds a new data type for patching. /// /// The identifier for the data type in JSON. - /// The C# type corresponding to the identifier. + /// "The C# type corresponding to the identifier. public static void AddPatchDataType(string typeId, Type type) { if (!typeMappings.ContainsKey(typeId)) @@ -230,6 +239,21 @@ public static void AddPatchDataType(string typeId, Type type, bool shouldCreateC typeMappings.Add(typeId, new TypeMapping(type, shouldCreateCache)); } + public static void AddTypeHandler(Type type, Action handler) + { + if(!typeMappings.ContainsValue(new TypeMapping(type, true)) && !typeMappings.ContainsValue(new TypeMapping(type, false))) + { + Plugin.logger.LogWarning($"Tried adding TypeHandler for type: {type.ToString()} with missing TypeMapping. Please, add TypeMapping first."); + return; + } + if(!typeHandlers.ContainsKey(type)) + typeHandlers[type] = new(); + + typeHandlers[type].Add(handler); + + Plugin.logger.LogWarning($"Added TypeHandler for type: {type.ToString()}."); + } + /// /// Loads all mods from the mods directory. /// @@ -889,9 +913,12 @@ internal static void CreateMappings(JObject rootObject) methodInfo.Invoke(null, new object[] { id, (int)token["idx"] }); methodInfo.Invoke(null, new object[] { id, (int)token["idx"] }); - if (typeHandlers.TryGetValue(targetType, out var handler)) + if (typeHandlers.TryGetValue(targetType, out var handlers)) { - handler(token, true); + foreach(var handler in handlers) + { + handler(token, true); + } } Plugin.logger.LogInfo("Created mapping for " + targetType.ToString() + " with id " + id + " and index " + (int)token["idx"]); } @@ -903,9 +930,12 @@ internal static void CreateMappings(JObject rootObject) string dataType = Util.GetJTokenName(token, 2); if (typeMappings.TryGetValue(dataType, out TypeMapping? typeMapping)) { - if (typeHandlers.TryGetValue(typeMapping.type, out var handler)) + if (typeHandlers.TryGetValue(typeMapping.type, out var handlers)) { - handler(token, false); + foreach(var handler in handlers) + { + handler(token, false); + } } } } @@ -921,25 +951,27 @@ internal static void ProcessPrefabs() { UnitData.Type unitPrefabType = UnitData.Type.Scout; string prefabId = item.Value; - if (Enum.TryParse(prefabId, out UnitData.Type parsedType)) + int hashKey = PrefabManager.GetSkinnedHashKey((UnitData.Type)item.Key, TribeType.None, SkinType.Default); + if (!Enum.TryParse(CultureInfo.CurrentCulture.TextInfo.ToTitleCase(prefabId), out unitPrefabType)) // Existing unit prefabs { - unitPrefabType = parsedType; - PrefabManager.units.TryAdd(item.Key, PrefabManager.units[(int)unitPrefabType]); - } - else - { - KeyValuePair prefabInfo = Registry.unitPrefabs.FirstOrDefault(kv => kv.Key.name == prefabId); - int hashKey = PrefabManager.GetSkinnedHashKey((UnitData.Type)item.Key, TribeType.None, SkinType.Default); + KeyValuePair prefabInfo = Registry.unitPrefabs.FirstOrDefault( + kv => kv.Key.name == prefabId + ); if (!EqualityComparer.Default.Equals(prefabInfo.Key, default)) { + Visual.UnitPrefabInfo unitPrefabInfo = new( + EnumCache.GetName((UnitData.Type)item.Key), + EnumCache.GetName(TribeType.None), + EnumCache.GetName(SkinType.Default) + ); PrefabManager.units.TryAdd(hashKey, prefabInfo.Value); - } - else - { - int existingPrefabKey = PrefabManager.GetSkinnedHashKey(unitPrefabType, TribeType.None, SkinType.Default); - PrefabManager.units.TryAdd(hashKey, PrefabManager.units[existingPrefabKey]); + return; } } + PrefabManager.units.TryAdd( + hashKey, + PrefabManager.units[PrefabManager.GetSkinnedHashKey(unitPrefabType, TribeType.None, SkinType.Default)] + ); } } diff --git a/src/Managers/Visual.cs b/src/Managers/Visual.cs index bade10b..e3e7c71 100644 --- a/src/Managers/Visual.cs +++ b/src/Managers/Visual.cs @@ -54,8 +54,19 @@ public record SkinInfo(int idx, string id, SkinData? skinData); /// A dictionary of custom widths for basic popups. public static Dictionary basicPopupWidths = new(); /// Represents information about a unit prefab. - public record UnitPrefabInfo(string type, string tribe, string skin); - public static Dictionary skinnedHashKeys = new(); + public struct UnitPrefabInfo + { + public UnitPrefabInfo(string type, string tribe, string skin) + { + this.type = type; + this.tribe = tribe; + this.skin = skin; + } + public string type; + public string tribe; + public string skin; + } + public static Dictionary customPrefabs = new(); private static bool firstTimeOpeningPreview = true; private static UnitData.Type currentUnitTypeUI = UnitData.Type.None; private static TribeType attackerTribe = TribeType.None; @@ -234,118 +245,22 @@ private static void SkinVisualsRenderer_SkinWorldObject( } [HarmonyPostfix] - [HarmonyPatch(typeof(PrefabManager), nameof(PrefabManager.SetupData))] - private static void PrefabManager_SetupData(PrefabManager __instance) - { - PrefabManager.units.Clear(); - PrefabManager.unitUIOverrides.Clear(); - - foreach (PrefabManager.UnitPrefabData unitPrefabData in __instance.unitPrefabs) - { - PrefabManager.units.Add( - PrefabManager.GetSkinnedHashKey(unitPrefabData.type, unitPrefabData.tribe, unitPrefabData.skin), - unitPrefabData.prefab - ); - } - - foreach (PrefabManager.UnitPrefabData unitUIPrefabData in __instance.unitUIOverridePrefabs) - { - PrefabManager.unitUIOverrides.Add( - PrefabManager.GetSkinnedHashKey(unitUIPrefabData.type, unitUIPrefabData.tribe, unitUIPrefabData.skin), - unitUIPrefabData.prefab - ); - } - } - - [HarmonyPrefix] - [HarmonyPatch(typeof(PrefabManager), nameof(PrefabManager.GetSkinnedHashKey))] - private static bool PrefabManager_GetSkinnedHashKey(ref int __result, UnitData.Type type, TribeType tribe, SkinType skin) + [HarmonyPatch(typeof(PrefabManager), nameof(PrefabManager.GetPrefab), + typeof(UnitData.Type), typeof(TribeType), typeof(SkinType) + )] + private static void PrefabManager_GetPrefab(ref Unit __result, UnitData.Type type, TribeType tribe, SkinType skin) { UnitPrefabInfo unitPrefabInfo = new( EnumCache.GetName(type), EnumCache.GetName(tribe), EnumCache.GetName(skin) ); - if(!skinnedHashKeys.ContainsKey(unitPrefabInfo)) - { - int key = skinnedHashKeys.Keys.Count; - skinnedHashKeys.Add(unitPrefabInfo, key); - } - __result = skinnedHashKeys[unitPrefabInfo]; - return false; - } - - [HarmonyPrefix] - [HarmonyPatch(typeof(PrefabManager), nameof(PrefabManager.HasUIUnitOverride))] - public static bool PrefabManager_HasUIUnitOverride(ref bool __result, UnitData.Type unitType, TribeType tribe, SkinType skin) - { - int skinnedHashKey = PrefabManager.GetSkinnedHashKey(unitType, tribe, skin); - __result = PrefabManager.unitUIOverrides.ContainsKey(skinnedHashKey); - return false; - } - - [HarmonyPrefix] - [HarmonyPatch(typeof(PrefabManager), nameof(PrefabManager.GetUnitOrUiOverride))] - public static bool PrefabManager_GetUnitOrUiOverride(ref Unit __result, UnitData.Type unitType, TribeType tribe, SkinType skin) - { - int skinnedHashKey = PrefabManager.GetSkinnedHashKey(unitType, tribe, skin); - if (PrefabManager.unitUIOverrides.TryGetValue(skinnedHashKey, out var prefab)) - { - __result = prefab; - return false; - } - __result = PrefabManager.GetPrefab(unitType, tribe, skin); - return false; - } - - [HarmonyPrefix] - [HarmonyPatch(typeof(PrefabManager), nameof(PrefabManager.GetPrefab), typeof(UnitData.Type), typeof(TribeType), typeof(SkinType))] - public static bool GetPrefab(ref Unit __result, UnitData.Type type, TribeType tribe, SkinType skin) - { - if (SeasonManager.IsChristmas()) - { - Unit prefab = PrefabManager.GetPrefab(type, OverrideCondition.Christmas); - if (prefab != null) - { - __result = prefab; - return false; - } - } - int skinnedHashKey = PrefabManager.GetSkinnedHashKey(type, tribe, skin); - if (PrefabManager.units.TryGetValue(skinnedHashKey, out var value)) - { - __result = value; - return false; - } - if (skin != SkinType.Default || tribe != TribeType.None) + if(customPrefabs.ContainsKey( + unitPrefabInfo + )) { - int skinnedHashKey2 = PrefabManager.GetSkinnedHashKey(type, TribeType.None, skin); - int skinnedHashKey3 = PrefabManager.GetSkinnedHashKey(type, tribe, SkinType.Default); - int skinnedHashKey4 = PrefabManager.GetSkinnedHashKey(type, TribeType.None, SkinType.Default); - int num = 0; - if (PrefabManager.units.ContainsKey(skinnedHashKey2)) - { - num = skinnedHashKey2; - } - else if (PrefabManager.units.ContainsKey(skinnedHashKey3)) - { - num = skinnedHashKey3; - } - else if (PrefabManager.units.ContainsKey(skinnedHashKey4)) - { - num = skinnedHashKey4; - } - if (num != 0) - { - Unit unit = PrefabManager.units[num]; - PrefabManager.units.Add(skinnedHashKey, unit); - __result = unit; - return false; - } + __result = customPrefabs[unitPrefabInfo]; } - Plugin.logger.LogInfo($"Couldn't find prefab for type: {type}"); - __result = PrefabManager.units[0]; - return false; } #endregion