Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion PolyMod.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
</RestoreAdditionalProjectSources>
<Configurations>IL2CPP</Configurations>
<RootNamespace>PolyMod</RootNamespace>
<Version>1.2.10-pre</Version>
<Version>1.2.10-pre.1</Version>
<PolytopiaVersion>2.16.3.15581</PolytopiaVersion>
<Authors>PolyModdingTeam</Authors>
<Description>The Battle of Polytopia's mod loader.</Description>
Expand Down
226 changes: 129 additions & 97 deletions src/Loader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,85 +54,89 @@ internal record TypeMapping(Type type, bool shouldCreateCache = true);
/// <summary>
/// Handlers for processing specific data types during mod loading.
/// </summary>
internal static readonly Dictionary<Type, Action<JObject, bool>> typeHandlers = new()
internal static readonly Dictionary<Type, List<Action<JObject, bool>>> 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<Action<JObject, bool>>() {
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<JArray>();
List<JToken> skinValues = skins._values.ToArray().ToList();
foreach (var skin in skinValues)
if (token["skins"] != null)
{
string skinValue = skin.ToString();
if (!Enum.TryParse<SkinType>(skinValue, ignoreCase: true, out _))
JArray skins = token["skins"].Cast<JArray>();
List<JToken> skinValues = skins._values.ToArray().ToList();
foreach (var skin in skinValues)
{
EnumCache<SkinType>.AddMapping(skinValue.ToLowerInvariant(), (SkinType)Registry.autoidx);
EnumCache<SkinType>.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<SkinType>(skinValue, ignoreCase: true, out _))
{
EnumCache<SkinType>.AddMapping(skinValue.ToLowerInvariant(), (SkinType)Registry.autoidx);
EnumCache<SkinType>.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<JToken> modifiedSkins = skins._values;
foreach (var skin in Registry.skinInfo)
{
if (modifiedSkins.Contains(skin.id))
Il2CppSystem.Collections.Generic.List<JToken> 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<Visual.PreviewTile[]>(token["preview"].ToString())!;
Registry.tribePreviews[Util.GetJTokenName(token)] = preview;
}
token["skins"] = newSkins;
}
if (token["preview"] != null)
{
Visual.PreviewTile[] preview = JsonSerializer.Deserialize<Visual.PreviewTile[]>(token["preview"].ToString())!;
Registry.tribePreviews[Util.GetJTokenName(token)] = preview;
}
}
}),

[typeof(UnitData.Type)] = new((token, duringEnumCacheCreation) =>
{
if (!duringEnumCacheCreation)
})
},
[typeof(UnitData.Type)] = new List<Action<JObject, bool>>() {
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<UnitData.WeaponEnum>.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<UnitData.WeaponEnum>.TryGetType(weaponString, out UnitData.WeaponEnum type))
{
token["weapon"] = (int)type;
}
}
}
}
}),
})
},

[typeof(ImprovementData.Type)] = new((token, duringEnumCacheCreation) =>
[typeof(ImprovementData.Type)] = new List<Action<JObject, bool>>() {
new((token, duringEnumCacheCreation) =>
{
if (duringEnumCacheCreation)
{
Expand Down Expand Up @@ -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<Action<JObject, bool>>() {
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<JProperty>();
prop.Replace(new JProperty(prop.Name.ToLower(), prop.Value));
}),
[typeof(SkinData)] = new List<Action<JObject, bool>>() {
new((token, duringEnumCacheCreation) =>
{
var prop = token.Parent.Cast<JProperty>();
prop.Replace(new JProperty(prop.Name.ToLower(), prop.Value));
})
},
};

/// <summary>
Expand Down Expand Up @@ -217,7 +226,7 @@ public static void AddGameMode(string id, UIButtonBase.ButtonAction action, bool
/// Adds a new data type for patching.
/// </summary>
/// <param name="typeId">The identifier for the data type in JSON.</param>
/// <param name="type">The C# type corresponding to the identifier.</param>
/// <param name="type">"The C# type corresponding to the identifier.</param>
public static void AddPatchDataType(string typeId, Type type)
{
if (!typeMappings.ContainsKey(typeId))
Expand All @@ -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<JObject, bool> 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()}.");
}

/// <summary>
/// Loads all mods from the mods directory.
/// </summary>
Expand Down Expand Up @@ -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"]);
}
Expand All @@ -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);
}
}
}
}
Expand All @@ -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<Visual.PrefabInfo, Unit> prefabInfo = Registry.unitPrefabs.FirstOrDefault(kv => kv.Key.name == prefabId);
int hashKey = PrefabManager.GetSkinnedHashKey((UnitData.Type)item.Key, TribeType.None, SkinType.Default);
KeyValuePair<Visual.PrefabInfo, Unit> prefabInfo = Registry.unitPrefabs.FirstOrDefault(
kv => kv.Key.name == prefabId
);
if (!EqualityComparer<Visual.PrefabInfo>.Default.Equals(prefabInfo.Key, default))
{
Visual.UnitPrefabInfo unitPrefabInfo = new(
EnumCache<UnitData.Type>.GetName((UnitData.Type)item.Key),
EnumCache<TribeType>.GetName(TribeType.None),
EnumCache<SkinType>.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)]
);
}
}

Expand Down
Loading