334 lines
9.8 KiB
C#
334 lines
9.8 KiB
C#
|
using System;
|
||
|
using System.Collections.Generic;
|
||
|
using UnityEngine;
|
||
|
|
||
|
namespace RoR2;
|
||
|
|
||
|
[CreateAssetMenu(menuName = "RoR2/SkinDef")]
|
||
|
public class SkinDef : ScriptableObject
|
||
|
{
|
||
|
[Serializable]
|
||
|
public struct GameObjectActivation
|
||
|
{
|
||
|
[PrefabReference]
|
||
|
public GameObject gameObject;
|
||
|
|
||
|
public bool shouldActivate;
|
||
|
}
|
||
|
|
||
|
[Serializable]
|
||
|
public struct MeshReplacement
|
||
|
{
|
||
|
[PrefabReference]
|
||
|
public Renderer renderer;
|
||
|
|
||
|
public Mesh mesh;
|
||
|
}
|
||
|
|
||
|
[Serializable]
|
||
|
public struct ProjectileGhostReplacement
|
||
|
{
|
||
|
public GameObject projectilePrefab;
|
||
|
|
||
|
public GameObject projectileGhostReplacementPrefab;
|
||
|
}
|
||
|
|
||
|
[Serializable]
|
||
|
public struct MinionSkinReplacement
|
||
|
{
|
||
|
public GameObject minionBodyPrefab;
|
||
|
|
||
|
public SkinDef minionSkin;
|
||
|
}
|
||
|
|
||
|
private struct RendererInfoTemplate
|
||
|
{
|
||
|
public string path;
|
||
|
|
||
|
public CharacterModel.RendererInfo data;
|
||
|
}
|
||
|
|
||
|
private struct GameObjectActivationTemplate
|
||
|
{
|
||
|
public string path;
|
||
|
|
||
|
public bool shouldActivate;
|
||
|
}
|
||
|
|
||
|
private struct MeshReplacementTemplate
|
||
|
{
|
||
|
public string path;
|
||
|
|
||
|
public Mesh mesh;
|
||
|
}
|
||
|
|
||
|
private class RuntimeSkin
|
||
|
{
|
||
|
public RendererInfoTemplate[] rendererInfoTemplates;
|
||
|
|
||
|
public GameObjectActivationTemplate[] gameObjectActivationTemplates;
|
||
|
|
||
|
public MeshReplacementTemplate[] meshReplacementTemplates;
|
||
|
|
||
|
private static readonly List<CharacterModel.RendererInfo> rendererInfoBuffer = new List<CharacterModel.RendererInfo>();
|
||
|
|
||
|
public void Apply(GameObject modelObject)
|
||
|
{
|
||
|
Transform transform = modelObject.transform;
|
||
|
for (int i = 0; i < rendererInfoTemplates.Length; i++)
|
||
|
{
|
||
|
ref RendererInfoTemplate reference = ref rendererInfoTemplates[i];
|
||
|
CharacterModel.RendererInfo data = reference.data;
|
||
|
Transform transform2 = transform.Find(reference.path);
|
||
|
if ((bool)transform2)
|
||
|
{
|
||
|
Renderer component = transform2.GetComponent<Renderer>();
|
||
|
if ((bool)component)
|
||
|
{
|
||
|
data.renderer = component;
|
||
|
rendererInfoBuffer.Add(data);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Debug.LogWarningFormat("No renderer at {0}/{1}", transform.name, reference.path);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Debug.LogWarningFormat("Could not find transform \"{0}\" relative to \"{1}\".", reference.path, transform.name);
|
||
|
}
|
||
|
}
|
||
|
CharacterModel component2 = modelObject.GetComponent<CharacterModel>();
|
||
|
component2.baseRendererInfos = rendererInfoBuffer.ToArray();
|
||
|
component2.forceUpdate = true;
|
||
|
rendererInfoBuffer.Clear();
|
||
|
foreach (Transform gameObjectActivationTransform in component2.gameObjectActivationTransforms)
|
||
|
{
|
||
|
if ((bool)gameObjectActivationTransform.gameObject)
|
||
|
{
|
||
|
gameObjectActivationTransform.gameObject.SetActive(value: false);
|
||
|
}
|
||
|
}
|
||
|
component2.gameObjectActivationTransforms.Clear();
|
||
|
for (int j = 0; j < gameObjectActivationTemplates.Length; j++)
|
||
|
{
|
||
|
ref GameObjectActivationTemplate reference2 = ref gameObjectActivationTemplates[j];
|
||
|
bool shouldActivate = reference2.shouldActivate;
|
||
|
Transform transform3 = transform.Find(reference2.path);
|
||
|
transform3.gameObject.SetActive(shouldActivate);
|
||
|
component2.gameObjectActivationTransforms.Add(transform3);
|
||
|
}
|
||
|
for (int k = 0; k < meshReplacementTemplates.Length; k++)
|
||
|
{
|
||
|
ref MeshReplacementTemplate reference3 = ref meshReplacementTemplates[k];
|
||
|
Mesh mesh = reference3.mesh;
|
||
|
Renderer component3 = transform.Find(reference3.path).GetComponent<Renderer>();
|
||
|
if (component3 is MeshRenderer)
|
||
|
{
|
||
|
component3.GetComponent<MeshFilter>().sharedMesh = mesh;
|
||
|
}
|
||
|
else if (component3 is SkinnedMeshRenderer skinnedMeshRenderer)
|
||
|
{
|
||
|
skinnedMeshRenderer.sharedMesh = mesh;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Tooltip("The skins which will be applied before this one.")]
|
||
|
public SkinDef[] baseSkins;
|
||
|
|
||
|
[ShowThumbnail]
|
||
|
public Sprite icon;
|
||
|
|
||
|
public string nameToken;
|
||
|
|
||
|
[Obsolete("Use 'unlockableDef' instead.")]
|
||
|
public string unlockableName;
|
||
|
|
||
|
public UnlockableDef unlockableDef;
|
||
|
|
||
|
[PrefabReference]
|
||
|
public GameObject rootObject;
|
||
|
|
||
|
public CharacterModel.RendererInfo[] rendererInfos = Array.Empty<CharacterModel.RendererInfo>();
|
||
|
|
||
|
public GameObjectActivation[] gameObjectActivations = Array.Empty<GameObjectActivation>();
|
||
|
|
||
|
public MeshReplacement[] meshReplacements = Array.Empty<MeshReplacement>();
|
||
|
|
||
|
public ProjectileGhostReplacement[] projectileGhostReplacements = Array.Empty<ProjectileGhostReplacement>();
|
||
|
|
||
|
public MinionSkinReplacement[] minionSkinReplacements = Array.Empty<MinionSkinReplacement>();
|
||
|
|
||
|
private RuntimeSkin runtimeSkin;
|
||
|
|
||
|
public SkinIndex skinIndex { get; set; } = SkinIndex.None;
|
||
|
|
||
|
|
||
|
private void Bake()
|
||
|
{
|
||
|
if (runtimeSkin != null)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
runtimeSkin = new RuntimeSkin();
|
||
|
List<RendererInfoTemplate> rendererInfoTemplates = new List<RendererInfoTemplate>();
|
||
|
List<GameObjectActivationTemplate> gameObjectActivationTemplates = new List<GameObjectActivationTemplate>();
|
||
|
List<MeshReplacementTemplate> meshReplacementTemplates = new List<MeshReplacementTemplate>();
|
||
|
SkinDef[] array = baseSkins;
|
||
|
foreach (SkinDef skinDef in array)
|
||
|
{
|
||
|
if (!skinDef)
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
skinDef.Bake();
|
||
|
if (skinDef.runtimeSkin == null)
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
if (skinDef.runtimeSkin.rendererInfoTemplates != null)
|
||
|
{
|
||
|
RendererInfoTemplate[] rendererInfoTemplates2 = skinDef.runtimeSkin.rendererInfoTemplates;
|
||
|
for (int j = 0; j < rendererInfoTemplates2.Length; j++)
|
||
|
{
|
||
|
AddRendererInfoTemplate(rendererInfoTemplates2[j]);
|
||
|
}
|
||
|
}
|
||
|
if (skinDef.runtimeSkin.gameObjectActivationTemplates != null)
|
||
|
{
|
||
|
GameObjectActivationTemplate[] gameObjectActivationTemplates2 = skinDef.runtimeSkin.gameObjectActivationTemplates;
|
||
|
for (int j = 0; j < gameObjectActivationTemplates2.Length; j++)
|
||
|
{
|
||
|
AddGameObjectActivationTemplate(gameObjectActivationTemplates2[j]);
|
||
|
}
|
||
|
}
|
||
|
if (skinDef.runtimeSkin.meshReplacementTemplates != null)
|
||
|
{
|
||
|
MeshReplacementTemplate[] meshReplacementTemplates2 = skinDef.runtimeSkin.meshReplacementTemplates;
|
||
|
for (int j = 0; j < meshReplacementTemplates2.Length; j++)
|
||
|
{
|
||
|
AddMeshReplacementTemplate(meshReplacementTemplates2[j]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
for (int k = 0; k < rendererInfos.Length; k++)
|
||
|
{
|
||
|
ref CharacterModel.RendererInfo reference = ref rendererInfos[k];
|
||
|
if (!reference.renderer)
|
||
|
{
|
||
|
Debug.LogErrorFormat("Skin {0} has an empty renderer field in its rendererInfos at index {1}.", this, k);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
RendererInfoTemplate rendererInfoTemplate2 = default(RendererInfoTemplate);
|
||
|
rendererInfoTemplate2.data = reference;
|
||
|
rendererInfoTemplate2.path = Util.BuildPrefabTransformPath(rootObject.transform, reference.renderer.transform);
|
||
|
AddRendererInfoTemplate(rendererInfoTemplate2);
|
||
|
}
|
||
|
}
|
||
|
runtimeSkin.rendererInfoTemplates = rendererInfoTemplates.ToArray();
|
||
|
for (int l = 0; l < gameObjectActivations.Length; l++)
|
||
|
{
|
||
|
ref GameObjectActivation reference2 = ref gameObjectActivations[l];
|
||
|
if (!reference2.gameObject)
|
||
|
{
|
||
|
Debug.LogErrorFormat("Skin {0} has an empty gameObject field in its gameObjectActivations at index {1}.", this, l);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
GameObjectActivationTemplate gameObjectActivationTemplate2 = default(GameObjectActivationTemplate);
|
||
|
gameObjectActivationTemplate2.shouldActivate = reference2.shouldActivate;
|
||
|
gameObjectActivationTemplate2.path = Util.BuildPrefabTransformPath(rootObject.transform, reference2.gameObject.transform);
|
||
|
AddGameObjectActivationTemplate(gameObjectActivationTemplate2);
|
||
|
}
|
||
|
}
|
||
|
runtimeSkin.gameObjectActivationTemplates = gameObjectActivationTemplates.ToArray();
|
||
|
for (int m = 0; m < meshReplacements.Length; m++)
|
||
|
{
|
||
|
ref MeshReplacement reference3 = ref meshReplacements[m];
|
||
|
if (!reference3.renderer)
|
||
|
{
|
||
|
Debug.LogErrorFormat("Skin {0} has an empty renderer field in its meshReplacements at index {1}.", this, m);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
MeshReplacementTemplate meshReplacementTemplate2 = default(MeshReplacementTemplate);
|
||
|
meshReplacementTemplate2.mesh = reference3.mesh;
|
||
|
meshReplacementTemplate2.path = Util.BuildPrefabTransformPath(rootObject.transform, reference3.renderer.transform);
|
||
|
AddMeshReplacementTemplate(meshReplacementTemplate2);
|
||
|
}
|
||
|
}
|
||
|
runtimeSkin.meshReplacementTemplates = meshReplacementTemplates.ToArray();
|
||
|
void AddGameObjectActivationTemplate(GameObjectActivationTemplate gameObjectActivationTemplate)
|
||
|
{
|
||
|
int num = 0;
|
||
|
for (int count2 = gameObjectActivationTemplates.Count; num < count2; num++)
|
||
|
{
|
||
|
if (gameObjectActivationTemplates[num].path == gameObjectActivationTemplate.path)
|
||
|
{
|
||
|
gameObjectActivationTemplates[num] = gameObjectActivationTemplate;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
gameObjectActivationTemplates.Add(gameObjectActivationTemplate);
|
||
|
}
|
||
|
void AddMeshReplacementTemplate(MeshReplacementTemplate meshReplacementTemplate)
|
||
|
{
|
||
|
int n = 0;
|
||
|
for (int count = meshReplacementTemplates.Count; n < count; n++)
|
||
|
{
|
||
|
if (meshReplacementTemplates[n].path == meshReplacementTemplate.path)
|
||
|
{
|
||
|
meshReplacementTemplates[n] = meshReplacementTemplate;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
meshReplacementTemplates.Add(meshReplacementTemplate);
|
||
|
}
|
||
|
void AddRendererInfoTemplate(RendererInfoTemplate rendererInfoTemplate)
|
||
|
{
|
||
|
int num2 = 0;
|
||
|
for (int count3 = rendererInfoTemplates.Count; num2 < count3; num2++)
|
||
|
{
|
||
|
if (rendererInfoTemplates[num2].path == rendererInfoTemplate.path)
|
||
|
{
|
||
|
rendererInfoTemplates[num2] = rendererInfoTemplate;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
rendererInfoTemplates.Add(rendererInfoTemplate);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void Apply(GameObject modelObject)
|
||
|
{
|
||
|
Bake();
|
||
|
runtimeSkin.Apply(modelObject);
|
||
|
}
|
||
|
|
||
|
private void Awake()
|
||
|
{
|
||
|
if (Application.IsPlaying(this))
|
||
|
{
|
||
|
Bake();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[ContextMenu("Upgrade unlockableName to unlockableDef")]
|
||
|
public void UpgradeUnlockableNameToUnlockableDef()
|
||
|
{
|
||
|
if (!string.IsNullOrEmpty(unlockableName) && !this.unlockableDef)
|
||
|
{
|
||
|
UnlockableDef unlockableDef = LegacyResourcesAPI.Load<UnlockableDef>("UnlockableDefs/" + unlockableName);
|
||
|
if ((bool)unlockableDef)
|
||
|
{
|
||
|
this.unlockableDef = unlockableDef;
|
||
|
unlockableName = null;
|
||
|
}
|
||
|
}
|
||
|
EditorUtil.SetDirty(this);
|
||
|
}
|
||
|
}
|