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 rendererInfoBuffer = new List(); 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(); 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(); 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(); if (component3 is MeshRenderer) { component3.GetComponent().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(); public GameObjectActivation[] gameObjectActivations = Array.Empty(); public MeshReplacement[] meshReplacements = Array.Empty(); public ProjectileGhostReplacement[] projectileGhostReplacements = Array.Empty(); public MinionSkinReplacement[] minionSkinReplacements = Array.Empty(); private RuntimeSkin runtimeSkin; public SkinIndex skinIndex { get; set; } = SkinIndex.None; private void Bake() { if (runtimeSkin != null) { return; } runtimeSkin = new RuntimeSkin(); List rendererInfoTemplates = new List(); List gameObjectActivationTemplates = new List(); List meshReplacementTemplates = new List(); 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("UnlockableDefs/" + unlockableName); if ((bool)unlockableDef) { this.unlockableDef = unlockableDef; unlockableName = null; } } EditorUtil.SetDirty(this); } }