Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of CustomMonsterReplacer v1.4.3
CustomMonsterReplacer.dll
Decompiled 2 weeks ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Diagnostics; using System.IO; using System.Linq; using System.Net; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using System.Threading; using BepInEx; using BepInEx.Logging; using HarmonyLib; using Jotunn.Managers; using Jotunn.Utils; using Microsoft.CodeAnalysis; using MyMods.Web; using Newtonsoft.Json; using UnityEngine; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("CustomMonsterReplacer")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.4.3.0")] [assembly: AssemblyInformationalVersion("1.4.3+901b84ab23f2f10926b13865f9b40bb283490874")] [assembly: AssemblyProduct("CustomMonsterReplacer")] [assembly: AssemblyTitle("CustomMonsterReplacer")] [assembly: AssemblyVersion("1.4.3.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace MyMods.Web { public static class WebNav { public sealed class Link { public readonly string Id; public readonly string Label; public readonly int Port; public string Url => "http://127.0.0.1:" + Port + "/"; public Link(string id, string label, int port) { Id = id; Label = label; Port = port; } } public static readonly Link[] Links = new Link[13] { new Link("monsters", "Monsters", 8777), new Link("searoute", "SeaRoute", 8778), new Link("longboat", "Harpoon Longboat", 8779), new Link("tradewind", "TradeWind", 8780), new Link("safeharbor", "Safe Harbor", 8781), new Link("hub", "DFG Hub", 8782), new Link("vault", "Viking Vault", 8783), new Link("qolvault", "QoL Vault Buttons", 8784), new Link("qolinventory", "QoL Inventory Buttons", 8785), new Link("cacophony", "Cacophony", 8787), new Link("ominous", "Ominous", 8788), new Link("pebble", "Pebble", 8789), new Link("trolls", "Trolls", 8790) }; public const string Style = "nav.top{position:sticky;top:0;z-index:20;font-family:Segoe UI,Arial,sans-serif;background:#0d1219;border-bottom:1px solid #2b3b4c;padding:6px 14px;display:flex;align-items:center;gap:6px;flex-wrap:wrap}\nnav.top .brand{color:#7f8fa0;font-size:11px;text-transform:uppercase;letter-spacing:.6px;margin-right:8px}\nnav.top a{color:#9fc3dd;text-decoration:none;font-size:13px;padding:4px 9px;border-radius:5px}\nnav.top a:hover{background:#1a2530}\nnav.top a.here{background:#3a2b4c;border:1px solid #55406b;color:#efe0ff}\n"; public static string Html(string currentId) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("<nav class='top'><span class='brand'>MyMods</span>"); Link[] links = Links; foreach (Link link in links) { stringBuilder.Append("<a class='"); if (link.Id == currentId) { stringBuilder.Append("here"); } stringBuilder.Append("' href='").Append(link.Url).Append("'>") .Append(link.Label) .Append("</a>"); } stringBuilder.Append("</nav>"); return stringBuilder.ToString(); } public static Link Find(string id) { Link[] links = Links; foreach (Link link in links) { if (link.Id == id) { return link; } } return null; } } } namespace CustomMonsterReplacer { [BepInPlugin("com.custom.monsterreplacer", "Custom Monster Replacer", "1.4.3")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class CustomMonsterReplacer : BaseUnityPlugin { private class SystemDump { public string Path; public int Folded; public int Count; public List<SpawnDataDump> Entries = new List<SpawnDataDump>(); } private class SpawnDataDump { public string Name; public bool Enabled; public bool DevDisabled; public string Prefab; public string Biome; public string BiomeArea; public int MinLevel; public int MaxLevel; public int MaxSpawned; public float SpawnChance; public float SpawnInterval; public int GroupMin; public int GroupMax; public float GroupRadius; public bool SpawnAtDay; public bool SpawnAtNight; public bool HuntPlayer; public float SpawnDistance; public float SpawnRadiusMin; public float SpawnRadiusMax; public bool CanSpawnCloseToPlayer; public bool InsidePlayerBase; public string RequiredGlobalKey; public int RequiredEnvironments; public bool Foldout; } [Serializable] private class MonsterConfig { public bool EnableWebServer = true; public int WebPort = 8777; public List<MonsterEntry> Monsters = new List<MonsterEntry>(); } private class LegacyConfig { public string TargetPath = ""; public string ModelFile = ""; public bool UseTargetHeight = true; public bool EnableSkeletonBinding = true; public float TargetHeight = 2f; public float ManualScale = 0.01f; public float PositionX; public float PositionY; public float PositionZ; public float RotationX; public float RotationY; public float RotationZ; public List<string> DisabledMeshes = new List<string>(); public float CullDistance = 60f; public CreatureConfig Creature = new CreatureConfig(); } private class CreatureConfig { public bool Enabled; public string Name = "DFGBear"; public string SourcePrefab = "Greydwarf"; public float Health = 120f; public string Faction = "ForestMonsters"; public float CullDistance = 60f; public float? AlertRange; public float? ViewRange; public float? MaxChaseDistance; public float? FleeIfLowHealth; public bool RegisterSpawns; public List<string> SpawnBiomes = new List<string> { "BlackForest" }; public int MinLevel = 1; public int MaxLevel = 2; public int MaxSpawned = 6; public float SpawnInterval = 300f; public float SpawnChance = 1f; public int GroupSizeMin = 1; public int GroupSizeMax = 2; public bool SpawnAtNight; public bool HuntPlayer; public bool DumpSpawnSystems = true; } private class MeshInfo { public string Name; public Vector3 LocalSize; public Vector3 ScaledSize; } private class PartData { public string Name; public Mesh Mesh; public Material[] Materials; public Vector3 LocalPosition; public Quaternion LocalRotation; public Vector3 LocalScale; public bool FromSkinnedSource; public string[] JointNames; } private class RigPart { public string Name; public Mesh Mesh; public Material[] Materials; public int DominantBoneIndex = -1; } private class RigCache { public string[] BoneNames; public Vector3[] BoneLocalPositions; public int[] BoneParentIndices; public Matrix4x4[] BindPoses; public float Radius; public string RootBoneName; public List<RigPart> Parts = new List<RigPart>(); } [Serializable] private class GLTFRoot { public GLTFMesh[] meshes; public GLTFNode[] nodes; public GLTFSkin[] skins; public GLTFMaterial[] materials; public GLTFAccessor[] accessors; public GLTFBufferView[] bufferViews; public GLTFImage[] images; public GLTFTexture[] textures; public GLTFScene[] scenes; } [Serializable] private class GLTFSkin { public int[] joints; public int inverseBindMatrices = -1; public string name; } [Serializable] private class GLTFMesh { public string name; public GLTFPrimitive[] primitives; } [Serializable] private class GLTFPrimitive { public GLTFAttributes attributes; public int indices = -1; public int material = -1; } [Serializable] private class GLTFAttributes { public int POSITION = -1; public int NORMAL = -1; public int TEXCOORD_0 = -1; public int JOINTS_0 = -1; public int WEIGHTS_0 = -1; } [Serializable] private class GLTFNode { public string name; public int mesh = -1; public int skin = -1; public float[] translation; public float[] rotation; public float[] scale; public int[] children; } [Serializable] private class GLTFMaterial { public string name; public GLTFPBR pbrMetallicRoughness; } [Serializable] private class GLTFPBR { public float[] baseColorFactor; public float? metallicFactor; public float? roughnessFactor; public GLTFTextureInfo baseColorTexture; } [Serializable] private class GLTFTextureInfo { public int index = -1; } [Serializable] private class GLTFImage { public string mimeType; public int bufferView = -1; public string name; public string uri; } [Serializable] private class GLTFTexture { public int source = -1; } [Serializable] private class GLTFAccessor { public int bufferView; public int byteOffset; public int componentType; public int count; public string type; } [Serializable] private class GLTFBufferView { public int buffer; public int byteOffset; public int byteLength; public int byteStride; } [Serializable] private class GLTFScene { public int[] nodes; } private class MonsterRequest { public string id; public string name; public string kind; public bool? enabled; public string targetPrefab; public string modelFile; public string bundleFile; public string bundlePrefab; public bool? matchSourceHeight; public bool? useTargetHeight; public float? targetHeight; public float? manualScale; public float[] positionOffset; public float[] rotationEuler; public bool? enableSkeletonBinding; public string[] disabledMeshes; public float? cullDistance; public float? health; public string faction; public float? alertRange; public float? viewRange; public float? maxChaseDistance; public float? fleeIfLowHealth; public bool? registerSpawns; public bool? copySourceSpawns; public string[] spawnBiomes; public int? minLevel; public int? maxLevel; public int? maxSpawned; public float? spawnInterval; public float? spawnChance; public int? groupSizeMin; public int? groupSizeMax; public bool? spawnAtNight; public bool? huntPlayer; public bool? dumpSpawnSystems; public bool? apply; public void ApplyTo(MonsterEntry e) { if (!string.IsNullOrEmpty(targetPrefab)) { e.TargetPrefab = targetPrefab; } if (!string.IsNullOrEmpty(name)) { e.Name = name; } if (enabled.HasValue) { e.Enabled = enabled.Value; } if (!string.IsNullOrEmpty(modelFile)) { e.ModelFile = modelFile; } if (bundleFile != null) { e.BundleFile = bundleFile; } if (bundlePrefab != null) { e.BundlePrefab = bundlePrefab; } if (matchSourceHeight.HasValue) { e.MatchSourceHeight = matchSourceHeight.Value; } if (useTargetHeight.HasValue) { e.UseTargetHeight = useTargetHeight.Value; } if (targetHeight.HasValue) { e.TargetHeight = targetHeight.Value; } if (manualScale.HasValue) { e.ManualScale = manualScale.Value; } if (positionOffset != null && positionOffset.Length == 3) { e.PositionX = positionOffset[0]; e.PositionY = positionOffset[1]; e.PositionZ = positionOffset[2]; } if (rotationEuler != null && rotationEuler.Length == 3) { e.RotationX = rotationEuler[0]; e.RotationY = rotationEuler[1]; e.RotationZ = rotationEuler[2]; } if (enableSkeletonBinding.HasValue) { e.EnableSkeletonBinding = enableSkeletonBinding.Value; } if (disabledMeshes != null) { e.DisabledMeshes = disabledMeshes.ToList(); } if (cullDistance.HasValue) { e.CullDistance = cullDistance.Value; } if (health.HasValue) { e.Health = health.Value; } if (faction != null) { e.Faction = faction; } if (alertRange.HasValue) { e.AlertRange = alertRange.Value; } if (viewRange.HasValue) { e.ViewRange = viewRange.Value; } if (maxChaseDistance.HasValue) { e.MaxChaseDistance = maxChaseDistance.Value; } if (fleeIfLowHealth.HasValue) { e.FleeIfLowHealth = fleeIfLowHealth.Value; } if (registerSpawns.HasValue) { e.RegisterSpawns = registerSpawns.Value; } if (copySourceSpawns.HasValue) { e.CopySourceSpawns = copySourceSpawns.Value; } if (spawnBiomes != null) { e.SpawnBiomes = spawnBiomes.ToList(); } if (minLevel.HasValue) { e.MinLevel = minLevel.Value; } if (maxLevel.HasValue) { e.MaxLevel = maxLevel.Value; } if (maxSpawned.HasValue) { e.MaxSpawned = maxSpawned.Value; } if (spawnInterval.HasValue) { e.SpawnInterval = spawnInterval.Value; } if (spawnChance.HasValue) { e.SpawnChance = spawnChance.Value; } if (groupSizeMin.HasValue) { e.GroupSizeMin = groupSizeMin.Value; } if (groupSizeMax.HasValue) { e.GroupSizeMax = groupSizeMax.Value; } if (spawnAtNight.HasValue) { e.SpawnAtNight = spawnAtNight.Value; } if (huntPlayer.HasValue) { e.HuntPlayer = huntPlayer.Value; } if (dumpSpawnSystems.HasValue) { e.DumpSpawnSystems = dumpSpawnSystems.Value; } } } private static class CustomCreatureNames { public const string DefaultBundleFile = "valheimbear"; public const string DefaultBundlePrefab = "BearPlayer"; public const string OgreBundleFile = "valheimogre"; public const string OgreBundlePrefab = "OgrePlayer"; public const string OgreCreatureName = "DFGOgre"; public const string OgreSourcePrefab = "Troll"; } private class MonsterEntry { public string Id = "monster"; public string Name = "Greydwarf"; public string Kind = "Replace"; public bool Enabled = true; public string TargetPrefab = "Greydwarf"; public string ModelFile = ""; public string BundleFile = ""; public string BundlePrefab = ""; public bool MatchSourceHeight; public bool UseTargetHeight = true; public float TargetHeight = 2f; public float ManualScale = 0.01f; public float PositionX; public float PositionY; public float PositionZ; public float RotationX; public float RotationY; public float RotationZ; public bool EnableSkeletonBinding = true; public List<string> DisabledMeshes = new List<string>(); public float CullDistance = 60f; public float Health; public string Faction = ""; public float? AlertRange; public float? ViewRange; public float? MaxChaseDistance; public float? FleeIfLowHealth; public bool RegisterSpawns; public bool CopySourceSpawns; public List<string> SpawnBiomes = new List<string> { "BlackForest" }; public int MinLevel = 1; public int MaxLevel = 2; public int MaxSpawned = 6; public float SpawnInterval = 300f; public float SpawnChance = 1f; public int GroupSizeMin = 1; public int GroupSizeMax = 2; public bool SpawnAtNight; public bool HuntPlayer; public bool DumpSpawnSystems = true; public bool IsCreature => string.Equals(Kind, "Creature", StringComparison.OrdinalIgnoreCase); public bool UsesBundle => !string.IsNullOrEmpty(BundleFile); public void Normalize() { if (string.IsNullOrEmpty(Id)) { Id = Slug(Name); } if (string.IsNullOrEmpty(Name)) { Name = Id; } if (string.IsNullOrEmpty(TargetPrefab)) { TargetPrefab = Name; } if (string.IsNullOrEmpty(ModelFile) && !UsesBundle) { List<string> list = AvailableModelsStatic(); if (list.Count > 0) { ModelFile = list[0]; } } Kind = (IsCreature ? "Creature" : "Replace"); if (DisabledMeshes == null) { DisabledMeshes = new List<string>(); } if (SpawnBiomes == null) { SpawnBiomes = new List<string>(); } HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase); List<string> list2 = new List<string>(); foreach (string spawnBiome in SpawnBiomes) { if (!string.IsNullOrEmpty(spawnBiome)) { string item = spawnBiome.Trim(); if (hashSet.Add(item)) { list2.Add(item); } } } SpawnBiomes = list2; if (CopySourceSpawns) { RegisterSpawns = true; } if (CullDistance <= 0.5f) { CullDistance = 60f; } if (TargetHeight <= 0f) { TargetHeight = 2f; } if (ManualScale <= 0f) { ManualScale = 0.01f; } if (GroupSizeMin < 1) { GroupSizeMin = 1; } if (GroupSizeMax < GroupSizeMin) { GroupSizeMax = GroupSizeMin; } if (MaxLevel < MinLevel) { MaxLevel = MinLevel; } } public static string Slug(string value) { if (string.IsNullOrEmpty(value)) { return "monster"; } char[] array = value.Trim().ToLowerInvariant().ToCharArray(); for (int i = 0; i < array.Length; i++) { if (!char.IsLetterOrDigit(array[i])) { array[i] = '_'; } } return new string(array); } } private class MonsterRuntime { public MonsterEntry Entry; public GameObject Prefab; public RigCache Rig; public List<MeshInfo> LastMeshes = new List<MeshInfo>(); public Vector3 LayoutCenter; public Vector3 LayoutPosition; public Quaternion LayoutRotation; public float LayoutScale = 1f; public float MeasuredHeight; public int MeshCount; public bool Attempted; public bool ProbeDone; public bool ZNetRegistered; public bool SpawnDumpDone; public bool SpawnsRegistered; public string Stage = "pending"; public void Reset() { Prefab = null; Rig = null; LastMeshes.Clear(); MeshCount = 0; MeasuredHeight = 0f; Attempted = false; ProbeDone = false; ZNetRegistered = false; SpawnDumpDone = false; SpawnsRegistered = false; Stage = "pending"; } } private readonly Dictionary<string, AssetBundle> _loadedBundles = new Dictionary<string, AssetBundle>(); private const float PrefabCacheInterval = 2f; private float _prefabCacheAt = -999f; private int _prefabCountSeen = -1; private bool _znetSeen; private float _spawnRetryAt; private bool _spawnMissLogged; public const string PluginGUID = "com.custom.monsterreplacer"; public const string PluginName = "Custom Monster Replacer"; public const string PluginVersion = "1.4.3"; internal static ManualLogSource Log; private GameObject _customModel; private MonsterConfig _config; private SynchronizationContext _syncContext; private HttpListener _httpListener; private Thread _httpThread; private volatile bool _httpRunning; private Shader _sourceShader; private Dictionary<Mesh, string[]> _glbSkinJoints = new Dictionary<Mesh, string[]>(); private Dictionary<int, string[]> _skinJointNameByMesh = new Dictionary<int, string[]>(); private const string KindReplace = "Replace"; private const string KindCreature = "Creature"; private readonly object _monsterLock = new object(); private readonly List<MonsterRuntime> _runtimes = new List<MonsterRuntime>(); private MonsterEntry _activeEntry; private MonsterRuntime _activeRt; private bool _spawnDumpDone; private List<string> _creaturePrefabCache = new List<string>(); private readonly List<MonsterRuntime> _frameRuntimes = new List<MonsterRuntime>(); private static readonly List<string> _modelCache = new List<string>(); private static string _modelCacheDir = null; private string PluginDir => Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location); private string SpawnDumpPath => Path.Combine(PluginDir, "spawn_dump.json"); private string ConfigPath => Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location), "monsterreplacer_config.json"); public void TryRegisterPrefab(GameObject prefab, ref bool registered) { if (((Object)(object)prefab == (Object)null) | registered) { return; } ZNetScene instance = ZNetScene.instance; if ((Object)(object)instance == (Object)null || instance.m_prefabs == null) { return; } if ((Object)(object)instance.GetPrefab(((Object)prefab).name) != (Object)null) { registered = true; return; } try { PrefabManager.Instance.RegisterToZNetScene(prefab); registered = true; Log.LogInfo((object)("Registered '" + ((Object)prefab).name + "' with ZNetScene (in-game).")); } catch (Exception ex) { Log.LogWarning((object)("ZNetScene registration failed: " + ex.Message)); } } private bool TryAttachBundleCreatureModel(GameObject clone, MonsterEntry entry) { //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Expected O, but got Unknown //IL_01ce: Unknown result type (might be due to invalid IL or missing references) //IL_01d5: Expected O, but got Unknown //IL_0276: Unknown result type (might be due to invalid IL or missing references) //IL_0287: Unknown result type (might be due to invalid IL or missing references) //IL_0298: Unknown result type (might be due to invalid IL or missing references) if (entry == null || !entry.UsesBundle) { return false; } string text = Path.Combine(PluginDir, entry.BundleFile); if (!File.Exists(text)) { return false; } try { if (!_loadedBundles.TryGetValue(entry.BundleFile, out AssetBundle value) || (Object)(object)value == (Object)null) { Log.LogInfo((object)("Loading creature AssetBundle from: " + text)); try { value = AssetUtils.LoadAssetBundle(text); } catch (Exception ex) { Log.LogWarning((object)("Jotunn LoadAssetBundle failed (" + ex.Message + "); trying AssetBundle.LoadFromFile.")); } if ((Object)(object)value == (Object)null) { value = AssetBundle.LoadFromFile(text); } if ((Object)(object)value != (Object)null) { _loadedBundles[entry.BundleFile] = value; } } if ((Object)(object)value == (Object)null) { Log.LogError((object)"AssetBundle failed to load (Unity version mismatch?); falling back to GLB path."); return false; } GameObject val = value.LoadAsset<GameObject>(entry.BundlePrefab); if ((Object)(object)val == (Object)null) { Log.LogError((object)("Prefab '" + entry.BundlePrefab + "' not found in bundle; falling back to GLB path.")); return false; } float num = 0f; if (entry.MatchSourceHeight) { num = RendererHeight(clone); if (num < 0.5f) { Log.LogWarning((object)$" could not measure '{entry.TargetPrefab}' renderers ({num:F2}m); using 6m as the troll height."); num = 6f; } } foreach (Transform item in clone.transform) { Transform val2 = item; if (((Object)val2).name == "CustomModel") { Object.Destroy((Object)(object)((Component)val2).gameObject); } } ResolveSourceShader(clone); foreach (Transform item2 in clone.transform) { Transform val3 = item2; if ((Object)(object)((Component)val3).GetComponentInChildren<Renderer>(true) != (Object)null || (Object)(object)((Component)val3).GetComponentInChildren<Animator>(true) != (Object)null) { ((Component)val3).gameObject.SetActive(false); } } Renderer[] components = clone.GetComponents<Renderer>(); for (int i = 0; i < components.Length; i++) { components[i].enabled = false; } GameObject val4 = Object.Instantiate<GameObject>(val); ((Object)val4).name = "CustomModel"; val4.transform.SetParent(clone.transform, false); val4.transform.localPosition = Vector3.zero; val4.transform.localRotation = Quaternion.identity; val4.transform.localScale = Vector3.one; Component component = val4.GetComponent("CharacterController"); if ((Object)(object)component != (Object)null) { Object.Destroy((Object)(object)component); } ApplyBundleShaders(val4, value); Animator componentInChildren = val4.GetComponentInChildren<Animator>(true); if ((Object)(object)componentInChildren != (Object)null) { componentInChildren.cullingMode = (AnimatorCullingMode)1; componentInChildren.applyRootMotion = false; WireAnimator(clone, componentInChildren); componentInChildren.Update(0f); } SkinnedMeshRenderer[] componentsInChildren = val4.GetComponentsInChildren<SkinnedMeshRenderer>(true); for (int i = 0; i < componentsInChildren.Length; i++) { componentsInChildren[i].updateWhenOffscreen = false; } AttachRenderCuller(val4, entry.CullDistance); ApplyBundleTransform(val4, componentInChildren, entry, num); SnapFeetToGround(val4); val4.AddComponent<BundleFeetSnap>(); Log.LogInfo((object)(" bundle model '" + entry.BundlePrefab + "' attached to creature clone.")); return true; } catch (Exception ex2) { Log.LogError((object)("Bundle creature attach failed: " + ex2)); return false; } } private void ApplyBundleTransform(GameObject model, Animator animator, MonsterEntry entry, float sourceHeight) { //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)animator != (Object)null) { animator.Update(0f); } float num = RendererHeight(model); float num2 = 1f; string text; if (num <= 0.2f) { text = "MEASUREMENT FAILED, left at 1.0 - check the bundle is not empty"; } else if (entry.MatchSourceHeight && sourceHeight > 0.5f) { num2 = sourceHeight / num; text = "matched to the source height"; } else if (entry.UseTargetHeight) { num2 = entry.TargetHeight / num; text = "matched to the target height"; } else { num2 = entry.ManualScale; text = "manual scale"; } model.transform.localScale = Vector3.one * num2; model.transform.localRotation = Quaternion.Euler(entry.RotationX, entry.RotationY, entry.RotationZ); model.transform.localPosition = new Vector3(entry.PositionX, entry.PositionY, entry.PositionZ); float num3 = RendererHeight(model); Log.LogInfo((object)(" '" + entry.BundlePrefab + "' raw " + num.ToString("F2") + "m, " + ((sourceHeight > 0.5f) ? ("source " + sourceHeight.ToString("F2") + "m, ") : "") + (entry.MatchSourceHeight ? "matchSourceHeight=on, " : "matchSourceHeight=off, ") + (entry.UseTargetHeight ? ("targetHeight=" + entry.TargetHeight.ToString("F2") + "m") : ("manualScale=" + num2.ToString("F3"))) + " -> scale " + num2.ToString("F3") + " (" + text + "), final height " + num3.ToString("F2") + "m.")); } private void ApplyBundleShaders(GameObject model, AssetBundle bundle) { //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Expected O, but got Unknown //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) Shader val = (Shader)(((Object)(object)_sourceShader != (Object)null) ? ((object)_sourceShader) : ((object)Shader.Find("Standard"))); if ((Object)(object)val == (Object)null) { Log.LogWarning((object)" no replacement shader found; bundle materials may render magenta."); return; } Renderer[] componentsInChildren = model.GetComponentsInChildren<Renderer>(true); foreach (Renderer val2 in componentsInChildren) { Material[] sharedMaterials = val2.sharedMaterials; for (int j = 0; j < sharedMaterials.Length; j++) { Material val3 = sharedMaterials[j]; if ((Object)(object)val3 == (Object)null) { continue; } Material val4 = new Material(val); Texture val5 = FindAlbedoTexture(val3, bundle); if ((Object)(object)val5 != (Object)null) { val4.mainTexture = val5; if (val4.HasProperty("_MainTex")) { val4.SetTexture("_MainTex", val5); } } if (val4.HasProperty("_Color")) { Color val6 = Color.white; if (val3.HasProperty("_BaseColor")) { val6 = val3.GetColor("_BaseColor"); } else if (val3.HasProperty("baseColorFactor")) { val6 = val3.GetColor("baseColorFactor"); } else if (val3.HasProperty("_Color")) { val6 = val3.GetColor("_Color"); } val4.SetColor("_Color", val6); val4.color = val6; } if (val4.HasProperty("_Metallic")) { val4.SetFloat("_Metallic", 0f); } if (val4.HasProperty("_Glossiness")) { val4.SetFloat("_Glossiness", 0.15f); } sharedMaterials[j] = val4; Log.LogInfo((object)(" material '" + ((Object)val3).name + "' albedo=" + (((Object)(object)val5 != (Object)null) ? (((Object)val5).name + " " + val5.width + "x" + val5.height) : "MISSING"))); } val2.sharedMaterials = sharedMaterials; } Log.LogInfo((object)(" re-shadered bundle materials onto '" + ((Object)val).name + "'.")); } private static Texture FindAlbedoTexture(Material src, AssetBundle bundle) { string[] array = new string[5] { "baseColorTexture", "_BaseMap", "_MainTex", "_BaseColorMap", "_BaseColorTexture" }; if ((Object)(object)src != (Object)null) { string[] array2 = array; foreach (string text in array2) { if (src.HasProperty(text)) { Texture texture = src.GetTexture(text); if ((Object)(object)texture != (Object)null) { return texture; } } } if ((Object)(object)src.mainTexture != (Object)null) { return src.mainTexture; } } if ((Object)(object)bundle == (Object)null) { return null; } Texture result = null; int num = 0; Texture[] array3 = bundle.LoadAllAssets<Texture>(); foreach (Texture val in array3) { if ((Object)(object)val == (Object)null) { continue; } string text2 = ((((Object)val).name != null) ? ((Object)val).name.ToLowerInvariant() : ""); if (!text2.Contains("normal") && !text2.Contains("rough") && !text2.Contains("metal") && !text2.Contains("occlusion")) { int num2 = val.width * val.height; if (num2 > num) { num = num2; result = val; } } } return result; } private void WireAnimator(GameObject clone, Animator animator) { BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; int num = 0; MonoBehaviour[] components = clone.GetComponents<MonoBehaviour>(); foreach (MonoBehaviour val in components) { if (!((Object)(object)val == (Object)null)) { FieldInfo field = ((object)val).GetType().GetField("m_animator", bindingAttr); if (!(field == null) && !(field.FieldType != typeof(Animator))) { field.SetValue(val, animator); num++; Log.LogInfo((object)(" " + ((object)val).GetType().Name + ".m_animator now drives the model.")); } } } if (num == 0) { Log.LogWarning((object)" no m_animator field found; locomotion bridge will drive speed from movement."); } ((Component)animator).gameObject.AddComponent<ModelLocomotion>(); } private static float RendererHeight(GameObject root) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)root == (Object)null) { return 0f; } bool flag = false; Bounds val = default(Bounds); Renderer[] componentsInChildren = root.GetComponentsInChildren<Renderer>(true); foreach (Renderer val2 in componentsInChildren) { if ((Object)(object)val2 == (Object)null) { continue; } Bounds val3 = RendererBounds(val2); Vector3 size = ((Bounds)(ref val3)).size; if (!(((Vector3)(ref size)).sqrMagnitude < 1E-06f)) { if (!flag) { val = val3; flag = true; } else { ((Bounds)(ref val)).Encapsulate(val3); } } } if (!flag) { return 0f; } return ((Bounds)(ref val)).size.y; } private static Bounds RendererBounds(Renderer r) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) Bounds bounds = r.bounds; Vector3 size = ((Bounds)(ref bounds)).size; if (((Vector3)(ref size)).sqrMagnitude > 1E-06f) { return bounds; } SkinnedMeshRenderer val = (SkinnedMeshRenderer)(object)((r is SkinnedMeshRenderer) ? r : null); if (val != null && (Object)(object)val.sharedMesh != (Object)null) { return val.sharedMesh.bounds; } MeshFilter component = ((Component)r).GetComponent<MeshFilter>(); if ((Object)(object)component != (Object)null && (Object)(object)component.sharedMesh != (Object)null) { return component.sharedMesh.bounds; } return bounds; } private void SnapFeetToGround(GameObject model) { if (BundleFeetSnap.TrySnap(model)) { Log.LogInfo((object)" feet snap: mesh seated on the ground."); } } private void Update() { SyncRuntimes(); RefreshPrefabCache(); List<MonsterRuntime> list = SnapshotRuntimes(_frameRuntimes); for (int i = 0; i < list.Count; i++) { MonsterRuntime monsterRuntime = list[i]; if (monsterRuntime?.Entry != null && monsterRuntime.Entry.Enabled) { if (monsterRuntime.Entry.IsCreature) { EnsureCreature(monsterRuntime); } else { EnsureReplaceRegistration(monsterRuntime); } } } if (!_spawnDumpDone && list.Any((MonsterRuntime r) => r.Entry != null && r.Entry.Enabled && r.Entry.DumpSpawnSystems)) { TryDumpSpawnSystems(); } } private void RefreshPrefabCache() { ZNetScene instance = ZNetScene.instance; bool flag = (Object)(object)instance != (Object)null && instance.m_prefabs != null; int num = (flag ? instance.m_prefabs.Count : 0); if ((_znetSeen != flag || _prefabCountSeen != num) && !(Time.unscaledTime < _prefabCacheAt)) { _prefabCacheAt = Time.unscaledTime + 2f; _znetSeen = flag; _prefabCountSeen = num; _creaturePrefabCache = AvailableCreaturePrefabs(); } } private void EnsureReplaceRegistration(MonsterRuntime rt) { if (!((Object)(object)rt.Prefab == (Object)null)) { TryRegisterPrefab(rt.Prefab, ref rt.ZNetRegistered); } } private void EnsureCreature(MonsterRuntime rt) { if ((Object)(object)rt.Prefab != (Object)null) { TryRegisterPrefab(rt.Prefab, ref rt.ZNetRegistered); TrySpawnWork(rt); } else { if (rt.Attempted) { return; } if (!rt.ProbeDone) { if (rt.Entry.UsesBundle && !File.Exists(Path.Combine(PluginDir, rt.Entry.BundleFile))) { rt.Attempted = true; rt.Stage = "bundle missing: " + rt.Entry.BundleFile; Log.LogWarning((object)("Creature '" + rt.Entry.Name + "': AssetBundle '" + rt.Entry.BundleFile + "' not found next to the DLL; entry skipped.")); return; } if (!rt.Entry.UsesBundle && !File.Exists(Path.Combine(PluginDir, rt.Entry.ModelFile))) { rt.Attempted = true; rt.Stage = "model missing: " + rt.Entry.ModelFile; Log.LogWarning((object)("Creature '" + rt.Entry.Name + "': model '" + rt.Entry.ModelFile + "' not found next to the DLL; entry skipped.")); return; } rt.ProbeDone = true; } if (PrefabManager.Instance == null || (Object)(object)PrefabManager.Instance.GetPrefab(rt.Entry.TargetPrefab) == (Object)null) { return; } rt.Attempted = true; try { rt.Prefab = CreateCustomCreature(rt); if (!((Object)(object)rt.Prefab == (Object)null)) { TryRegisterPrefab(rt.Prefab, ref rt.ZNetRegistered); TrySpawnWork(rt); } } catch (Exception ex) { rt.Stage = "failed: " + ex.Message; Log.LogError((object)$"Custom creature '{rt.Entry.Name}' creation threw: {ex}"); } } } private GameObject CreateCustomCreature(MonsterRuntime rt) { //IL_01b6: Unknown result type (might be due to invalid IL or missing references) MonsterEntry entry = rt.Entry; GameObject prefab = PrefabManager.Instance.GetPrefab(entry.TargetPrefab); if ((Object)(object)prefab == (Object)null) { rt.Stage = "source prefab missing"; Log.LogError((object)("Source prefab '" + entry.TargetPrefab + "' not found; custom creature '" + entry.Name + "' not created.")); return null; } Log.LogInfo((object)("Cloning '" + ((Object)prefab).name + "' into custom creature '" + entry.Name + "'...")); GameObject val = PrefabManager.Instance.CreateClonedPrefab(entry.Name, prefab); if ((Object)(object)val == (Object)null) { rt.Stage = "CreateClonedPrefab returned null"; Log.LogError((object)"CreateClonedPrefab returned null; custom creature not created."); return null; } ((Object)val).name = entry.Name; rt.Prefab = val; Character component = val.GetComponent<Character>(); if ((Object)(object)component == (Object)null) { rt.Stage = "no Character on clone"; Log.LogError((object)("Clone '" + ((Object)val).name + "' has no Character component; aborting.")); rt.Prefab = null; return null; } ApplyCreatureData(component, entry); AttachCreatureModel(val, rt, entry); try { PrefabManager.Instance.RegisterToZNetScene(val); } catch (Exception ex) { Log.LogWarning((object)("RegisterToZNetScene('" + ((Object)val).name + "') failed: " + ex.Message)); } rt.Stage = "created"; Log.LogInfo((object)$"Custom creature ready: '{((Object)val).name}' health(base)={component.GetMaxHealthBase()} faction={component.GetFaction()} AI={((object)component.GetBaseAI())?.GetType().Name}"); Log.LogInfo((object)(" spawn it with the console command: spawn " + entry.Name + " (NOT 'spawn " + entry.TargetPrefab + "', which is the vanilla one)")); return val; } private string ReapplyCreature(MonsterRuntime rt) { //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Expected O, but got Unknown MonsterEntry entry = rt.Entry; if ((Object)(object)rt.Prefab == (Object)null) { rt.Reset(); return "no prefab yet; will build on the next frame"; } Character component = rt.Prefab.GetComponent<Character>(); if ((Object)(object)component == (Object)null) { rt.Reset(); return "prefab has no Character; rebuilding"; } ApplyCreatureData(component, entry); foreach (Transform item in rt.Prefab.transform) { Transform val = item; if (((Object)val).name == "CustomModel") { Object.Destroy((Object)(object)((Component)val).gameObject); } } AttachCreatureModel(rt.Prefab, rt, entry); if (entry.RegisterSpawns && (Object)(object)ZNetScene.instance != (Object)null) { RefreshSpawns(rt); } TryRegisterPrefab(rt.Prefab, ref rt.ZNetRegistered); rt.Stage = "updated"; return "updated in place"; } private void RefreshSpawns(MonsterRuntime rt) { int num = 0; foreach (SpawnSystemList item in FindSpawnLists()) { if (item.m_spawners != null) { num += item.m_spawners.RemoveAll((SpawnData d) => d != null && string.Equals(d.m_name, rt.Entry.Name, StringComparison.Ordinal)); } } if (num > 0) { Log.LogInfo((object)$"RefreshSpawns: removed {num} previous SpawnData entries named '{rt.Entry.Name}'."); } rt.SpawnsRegistered = RegisterSpawns(rt); } private void ApplyCreatureData(Character character, MonsterEntry entry) { //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) if (entry.Health > 0f) { character.m_health = entry.Health; Log.LogInfo((object)$" health -> {entry.Health}"); } if (!string.IsNullOrEmpty(entry.Faction)) { if (Enum.TryParse(typeof(Faction), entry.Faction, ignoreCase: true, out object result) && result is Faction val) { character.m_faction = val; Log.LogInfo((object)$" faction -> {val}"); } else { Log.LogWarning((object)$" faction '{entry.Faction}' is not a Character.Faction member; left as {character.m_faction}."); } } BaseAI baseAI = character.GetBaseAI(); if ((Object)(object)baseAI == (Object)null) { Log.LogWarning((object)" no BaseAI on clone; AI knobs skipped."); return; } Log.LogInfo((object)(" AI component: " + ((object)baseAI).GetType().Name)); if (entry.ViewRange.HasValue) { baseAI.m_viewRange = entry.ViewRange.Value; Log.LogInfo((object)$" BaseAI.m_viewRange -> {entry.ViewRange.Value}"); } MonsterAI val2 = (MonsterAI)(object)((baseAI is MonsterAI) ? baseAI : null); if (val2 == null) { Log.LogInfo((object)" AI is not MonsterAI; MonsterAI knobs skipped."); return; } if (entry.AlertRange.HasValue) { val2.m_alertRange = entry.AlertRange.Value; Log.LogInfo((object)$" MonsterAI.m_alertRange -> {entry.AlertRange.Value}"); } if (entry.MaxChaseDistance.HasValue) { val2.m_maxChaseDistance = entry.MaxChaseDistance.Value; Log.LogInfo((object)$" MonsterAI.m_maxChaseDistance -> {entry.MaxChaseDistance.Value}"); } if (entry.FleeIfLowHealth.HasValue) { val2.m_fleeIfLowHealth = entry.FleeIfLowHealth.Value; Log.LogInfo((object)$" MonsterAI.m_fleeIfLowHealth -> {entry.FleeIfLowHealth.Value}"); } } private void AttachCreatureModel(GameObject clone, MonsterRuntime rt, MonsterEntry entry) { if (TryAttachBundleCreatureModel(clone, entry)) { Log.LogInfo((object)" custom creature uses animated AssetBundle model."); rt.MeasuredHeight = CustomModelHeight(clone); Log.LogInfo((object)$" '{entry.Name}' custom model is {rt.MeasuredHeight:F2}m tall."); return; } string text = Path.Combine(PluginDir, entry.ModelFile); if (!File.Exists(text)) { Log.LogWarning((object)("Model '" + entry.ModelFile + "' not found; creature keeps vanilla visuals (looked in " + text + ").")); return; } _activeEntry = entry; _activeRt = rt; ResolveSourceShader(clone); DestroyRigCache(); _customModel = LoadCustomModel(entry.ModelFile); if ((Object)(object)_customModel == (Object)null) { Log.LogError((object)"Custom model failed to load; creature keeps vanilla visuals."); return; } ReplaceOnTarget(clone, isPrefab: true, rt); rt.MeasuredHeight = CustomModelHeight(clone); Log.LogInfo((object)" custom model attached to creature clone."); } private static float CustomModelHeight(GameObject clone) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown GameObject val = null; foreach (Transform item in clone.transform) { Transform val2 = item; if (((Object)val2).name == "CustomModel") { val = ((Component)val2).gameObject; } } if (!((Object)(object)val != (Object)null)) { return 0f; } return RendererHeight(val); } private void TrySpawnWork(MonsterRuntime rt) { if (rt.SpawnsRegistered || !rt.Entry.RegisterSpawns || Time.unscaledTime < _spawnRetryAt) { return; } ZNetScene instance = ZNetScene.instance; if (!((Object)(object)instance == (Object)null) && instance.m_prefabs != null && instance.m_prefabs.Count != 0) { if (RegisterSpawns(rt)) { rt.SpawnsRegistered = true; _spawnMissLogged = false; } else { _spawnRetryAt = Time.unscaledTime + 2f; } } } private void TryDumpSpawnSystems() { ZNetScene instance = ZNetScene.instance; if ((Object)(object)instance == (Object)null || instance.m_prefabs == null || instance.m_prefabs.Count == 0) { return; } _spawnDumpDone = true; foreach (MonsterRuntime runtime in _runtimes) { runtime.SpawnDumpDone = true; } DumpSpawnSystems(); } private static List<SpawnSystemList> FindSpawnLists() { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) List<SpawnSystemList> found = new List<SpawnSystemList>(); HashSet<int> seen = new HashSet<int>(); SpawnSystem[] array = Resources.FindObjectsOfTypeAll<SpawnSystem>(); foreach (SpawnSystem val in array) { if ((Object)(object)val == (Object)null || val.m_spawnLists == null) { continue; } Scene scene = ((Component)val).gameObject.scene; if (!((Scene)(ref scene)).IsValid()) { continue; } foreach (SpawnSystemList spawnList in val.m_spawnLists) { Add(spawnList); } } SpawnSystemList[] array2 = Resources.FindObjectsOfTypeAll<SpawnSystemList>(); for (int i = 0; i < array2.Length; i++) { Add(array2[i]); } return found; void Add(SpawnSystemList sl) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)sl == (Object)null) && sl.m_spawners != null) { Scene scene2 = ((Component)sl).gameObject.scene; if (((Scene)(ref scene2)).IsValid() && seen.Add(((Object)sl).GetInstanceID())) { found.Add(sl); } } } } private static string HierarchyPath(Transform t) { List<string> list = new List<string>(); while ((Object)(object)t != (Object)null) { list.Add(((Object)t).name); t = t.parent; } list.Reverse(); return string.Join("/", list); } private void DumpSpawnSystems() { List<SpawnSystemList> list = FindSpawnLists(); Log.LogInfo((object)$"--- SpawnSystemList dump: {list.Count} component(s) found ---"); if (list.Count == 0) { Log.LogWarning((object)"No SpawnSystemList components in the loaded scene; spawn registration cannot work as written."); return; } List<SystemDump> list2 = new List<SystemDump>(); int num = 0; foreach (SpawnSystemList item in list) { List<SpawnData> spawners = item.m_spawners; int num2 = spawners?.Count ?? 0; num += num2; Log.LogInfo((object)$" SpawnSystemList on '{HierarchyPath(((Component)item).transform)}': {num2} SpawnData"); List<SpawnDataDump> list3 = new List<SpawnDataDump>(); if (spawners != null) { for (int i = 0; i < spawners.Count; i++) { SpawnDataDump spawnDataDump = DescribeSpawnData(spawners[i]); list3.Add(spawnDataDump); Log.LogInfo((object)$" [{i}] {spawnDataDump.Name} prefab={spawnDataDump.Prefab} biome={spawnDataDump.Biome}/{spawnDataDump.BiomeArea} lvl {spawnDataDump.MinLevel}-{spawnDataDump.MaxLevel} max={spawnDataDump.MaxSpawned} chance={spawnDataDump.SpawnChance} interval={spawnDataDump.SpawnInterval} group {spawnDataDump.GroupMin}-{spawnDataDump.GroupMax} enabled={spawnDataDump.Enabled}"); } } list2.Add(new SystemDump { Path = HierarchyPath(((Component)item).transform), Folded = ((item.m_biomeFolded != null) ? item.m_biomeFolded.Count : 0), Count = num2, Entries = list3 }); } Log.LogInfo((object)$"--- {num} SpawnData entries total ---"); try { File.WriteAllText(SpawnDumpPath, JsonConvert.SerializeObject((object)list2, (Formatting)1)); Log.LogInfo((object)("Spawn dump written to " + SpawnDumpPath)); } catch (Exception ex) { Log.LogWarning((object)("Could not write spawn dump: " + ex.Message)); } } private SpawnDataDump DescribeSpawnData(SpawnData d) { if (d == null) { return new SpawnDataDump { Name = "<null>" }; } return new SpawnDataDump { Name = d.m_name, Enabled = d.m_enabled, DevDisabled = d.m_devDisabled, Prefab = (((Object)(object)d.m_prefab != (Object)null) ? ((Object)d.m_prefab).name : null), Biome = ((object)Unsafe.As<Biome, Biome>(ref d.m_biome)/*cast due to .constrained prefix*/).ToString(), BiomeArea = ((object)Unsafe.As<BiomeArea, BiomeArea>(ref d.m_biomeArea)/*cast due to .constrained prefix*/).ToString(), MinLevel = d.m_minLevel, MaxLevel = d.m_maxLevel, MaxSpawned = d.m_maxSpawned, SpawnChance = d.m_spawnChance, SpawnInterval = d.m_spawnInterval, GroupMin = d.m_groupSizeMin, GroupMax = d.m_groupSizeMax, GroupRadius = d.m_groupRadius, SpawnAtDay = d.m_spawnAtDay, SpawnAtNight = d.m_spawnAtNight, HuntPlayer = d.m_huntPlayer, SpawnDistance = d.m_spawnDistance, SpawnRadiusMin = d.m_spawnRadiusMin, SpawnRadiusMax = d.m_spawnRadiusMax, CanSpawnCloseToPlayer = d.m_canSpawnCloseToPlayer, InsidePlayerBase = d.m_insidePlayerBase, RequiredGlobalKey = d.m_requiredGlobalKey, RequiredEnvironments = ((d.m_requiredEnvironments != null) ? d.m_requiredEnvironments.Count : 0), Foldout = d.m_foldout }; } private bool RegisterSpawns(MonsterRuntime rt) { //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_019a: Expected O, but got Unknown //IL_01be: Unknown result type (might be due to invalid IL or missing references) //IL_01c3: Unknown result type (might be due to invalid IL or missing references) //IL_028c: Unknown result type (might be due to invalid IL or missing references) MonsterEntry entry = rt.Entry; if ((Object)(object)rt.Prefab == (Object)null) { Log.LogWarning((object)("RegisterSpawns: creature '" + entry.Name + "' prefab is not built yet; skipping.")); return false; } List<SpawnSystemList> list = FindSpawnLists(); if (list.Count == 0) { if (!_spawnMissLogged) { Log.LogWarning((object)"RegisterSpawns: no SpawnSystemList yet; will retry once the world is loaded."); _spawnMissLogged = true; } return false; } if (entry.CopySourceSpawns) { return RegisterCopiedSpawns(rt, list); } HashSet<string> hashSet = new HashSet<string>(entry.SpawnBiomes ?? new List<string>(), StringComparer.OrdinalIgnoreCase); int num = 0; int num2 = 0; int num3 = 0; int num4 = 0; Log.LogInfo((object)string.Format("RegisterSpawns: creature='{0}' biomes=[{1}] across {2} SpawnSystemList(s)", ((Object)rt.Prefab).name, string.Join(",", hashSet), list.Count)); foreach (SpawnSystemList item in list) { if (item.m_spawners == null) { continue; } foreach (string item2 in hashSet) { if (Enum.TryParse(typeof(Biome), item2, ignoreCase: true, out object result) && result is Biome) { Biome biome = (Biome)result; SpawnData val = ((IEnumerable<SpawnData>)item.m_spawners).FirstOrDefault((Func<SpawnData, bool>)((SpawnData d) => d != null && d.m_enabled && d.m_biome == biome)); SpawnData val2; if (val != null) { val2 = val.Clone(); num2++; } else { val2 = new SpawnData(); num3++; } val2.m_name = entry.Name; val2.m_prefab = rt.Prefab; val2.m_biome = biome; val2.m_enabled = true; val2.m_devDisabled = false; val2.m_minLevel = entry.MinLevel; val2.m_maxLevel = entry.MaxLevel; val2.m_maxSpawned = entry.MaxSpawned; val2.m_spawnInterval = entry.SpawnInterval; val2.m_spawnChance = entry.SpawnChance; val2.m_groupSizeMin = entry.GroupSizeMin; val2.m_groupSizeMax = entry.GroupSizeMax; val2.m_spawnAtNight = entry.SpawnAtNight; val2.m_huntPlayer = entry.HuntPlayer; item.m_spawners.Add(val2); num++; Log.LogInfo((object)string.Format(" '{0}' -> added SpawnData '{1}' biome={2} ({3})", HierarchyPath(((Component)item).transform), val2.m_name, val2.m_biome, (val != null) ? ("from template '" + val.m_name + "'") : "fresh")); } else { Log.LogWarning((object)(" '" + item2 + "' is not a Heightmap.Biome member; skipped.")); num4++; } } } Log.LogInfo((object)$"RegisterSpawns: added {num} SpawnData entries ({num2} from template, {num3} fresh, {num4} skipped)."); if (num == 0) { Log.LogWarning((object)"RegisterSpawns: nothing added. Check the dump - either no SpawnSystemList holds those biomes, or the biome names do not match Heightmap.Biome."); return false; } return true; } private bool RegisterCopiedSpawns(MonsterRuntime rt, List<SpawnSystemList> lists) { //IL_0126: Unknown result type (might be due to invalid IL or missing references) MonsterEntry entry = rt.Entry; int num = 0; foreach (SpawnSystemList list2 in lists) { List<SpawnData> list = new List<SpawnData>(); foreach (SpawnData spawner in list2.m_spawners) { if (spawner != null && spawner.m_enabled && !spawner.m_devDisabled && !((Object)(object)spawner.m_prefab == (Object)null) && string.Equals(((Object)spawner.m_prefab).name, entry.TargetPrefab, StringComparison.Ordinal)) { list.Add(spawner); } } foreach (SpawnData item in list) { SpawnData val = item.Clone(); val.m_name = entry.Name; val.m_prefab = rt.Prefab; val.m_enabled = true; list2.m_spawners.Add(val); num++; Log.LogInfo((object)$" '{HierarchyPath(((Component)list2).transform)}' -> '{entry.Name}' copies '{item.m_name}' biome={val.m_biome} interval={val.m_spawnInterval} chance={val.m_spawnChance} group {val.m_groupSizeMin}-{val.m_groupSizeMax} max={val.m_maxSpawned} night={val.m_spawnAtNight} day={val.m_spawnAtDay}"); } } if (num == 0) { if (!_spawnMissLogged) { Log.LogWarning((object)("RegisterSpawns: no enabled '" + entry.TargetPrefab + "' rows to copy yet; will retry.")); _spawnMissLogged = true; } return false; } Log.LogInfo((object)$"RegisterSpawns: '{entry.Name}' copied {num} '{entry.TargetPrefab}' spawn row(s). Trolls keep their own rows."); return true; } private void AttachRenderCuller(GameObject model, float cullDistance) { if (!((Object)(object)model == (Object)null) && !((Object)(object)MonsterRenderCuller.Attach(model, cullDistance) == (Object)null)) { Log.LogInfo((object)((cullDistance > 0.5f) ? $" distance culling: model hidden past {cullDistance:F0}m." : " distance culling off.")); } } private void Awake() { //IL_0049: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; Log.LogInfo((object)"Custom Monster Replacer loading..."); _syncContext = SynchronizationContext.Current; if (_syncContext == null) { _syncContext = new SynchronizationContext(); } LoadConfig(); SaveConfig(); new Harmony("com.custom.monsterreplacer").PatchAll(); PrefabManager.OnPrefabsRegistered += ApplyAllReplaceEntries; StartWebServer(); } private void OnDestroy() { PrefabManager.OnPrefabsRegistered -= ApplyAllReplaceEntries; StopWebServer(); } private void LoadConfig() { string text = null; try { if (File.Exists(ConfigPath)) { text = File.ReadAllText(ConfigPath); _config = JsonConvert.DeserializeObject<MonsterConfig>(text); Log.LogInfo((object)("Loaded config from: " + ConfigPath)); } } catch (Exception ex) { Log.LogWarning((object)("Failed to load config: " + ex.Message)); } if (_config == null) { _config = new MonsterConfig(); } if (_config.Monsters == null || _config.Monsters.Count == 0) { MigrateLegacyConfig(text); if (_config.Monsters.Count == 0) { AddDefaultEntry(); } } SyncRuntimes(); Log.LogInfo((object)$"Config: webPort={_config.WebPort}, monsters={_config.Monsters.Count}"); foreach (MonsterEntry monster in _config.Monsters) { MonsterRuntime monsterRuntime = RuntimeForEntry(monster); Log.LogInfo((object)($" - [{monster.Id}] {monster.Kind} '{monster.Name}' on '{monster.TargetPrefab}' enabled={monster.Enabled}" + (monster.UsesBundle ? (" bundle=" + monster.BundleFile + "/" + monster.BundlePrefab) : (" model=" + monster.ModelFile)) + string.Format(" cull={0:F0}m stage={1}", monster.CullDistance, monsterRuntime?.Stage ?? "pending"))); } } private void MigrateLegacyConfig(string raw) { LegacyConfig legacyConfig = null; if (!string.IsNullOrEmpty(raw)) { try { legacyConfig = JsonConvert.DeserializeObject<LegacyConfig>(raw); } catch (Exception ex) { Log.LogWarning((object)("Could not read legacy config: " + ex.Message)); } } if (legacyConfig != null && !string.IsNullOrEmpty(legacyConfig.TargetPath)) { Log.LogInfo((object)"Migrating the legacy single-target config into the Monsters list."); _config.Monsters.Add(new MonsterEntry { Id = MonsterEntry.Slug(legacyConfig.TargetPath), Name = legacyConfig.TargetPath, Kind = "Replace", Enabled = true, TargetPrefab = legacyConfig.TargetPath, ModelFile = legacyConfig.ModelFile, UseTargetHeight = legacyConfig.UseTargetHeight, TargetHeight = legacyConfig.TargetHeight, ManualScale = legacyConfig.ManualScale, PositionX = legacyConfig.PositionX, PositionY = legacyConfig.PositionY, PositionZ = legacyConfig.PositionZ, RotationX = legacyConfig.RotationX, RotationY = legacyConfig.RotationY, RotationZ = legacyConfig.RotationZ, EnableSkeletonBinding = legacyConfig.EnableSkeletonBinding, DisabledMeshes = (legacyConfig.DisabledMeshes ?? new List<string>()), CullDistance = legacyConfig.CullDistance }); CreatureConfig creatureConfig = legacyConfig.Creature ?? new CreatureConfig(); _config.Monsters.Add(new MonsterEntry { Id = MonsterEntry.Slug(creatureConfig.Name), Name = creatureConfig.Name, Kind = "Creature", Enabled = creatureConfig.Enabled, TargetPrefab = creatureConfig.SourcePrefab, BundleFile = "valheimbear", BundlePrefab = "BearPlayer", UseTargetHeight = false, ManualScale = 1f, EnableSkeletonBinding = false, CullDistance = creatureConfig.CullDistance, Health = creatureConfig.Health, Faction = creatureConfig.Faction, AlertRange = creatureConfig.AlertRange, ViewRange = creatureConfig.ViewRange, MaxChaseDistance = creatureConfig.MaxChaseDistance, FleeIfLowHealth = creatureConfig.FleeIfLowHealth, RegisterSpawns = creatureConfig.RegisterSpawns, SpawnBiomes = (creatureConfig.SpawnBiomes ?? new List<string> { "BlackForest" }), MinLevel = creatureConfig.MinLevel, MaxLevel = creatureConfig.MaxLevel, MaxSpawned = creatureConfig.MaxSpawned, SpawnInterval = creatureConfig.SpawnInterval, SpawnChance = creatureConfig.SpawnChance, GroupSizeMin = creatureConfig.GroupSizeMin, GroupSizeMax = creatureConfig.GroupSizeMax, SpawnAtNight = creatureConfig.SpawnAtNight, HuntPlayer = creatureConfig.HuntPlayer, DumpSpawnSystems = creatureConfig.DumpSpawnSystems }); if (File.Exists(Path.Combine(PluginDir, "valheimogre"))) { _config.Monsters.Add(new MonsterEntry { Id = MonsterEntry.Slug("DFGOgre"), Name = "DFGOgre", Kind = "Creature", Enabled = true, TargetPrefab = "Troll", BundleFile = "valheimogre", BundlePrefab = "OgrePlayer", MatchSourceHeight = true, UseTargetHeight = false, ManualScale = 1f, EnableSkeletonBinding = false, CullDistance = 60f, Health = 0f, Faction = "", ModelFile = "", DumpSpawnSystems = false }); } } } private void AddDefaultEntry() { List<string> list = AvailableModels(); _config.Monsters.Add(new MonsterEntry { Id = "greydwarf", Name = "Greydwarf", Kind = "Replace", TargetPrefab = "Greydwarf", ModelFile = ((list.Count > 0) ? list[0] : "smallbear.glb") }); } private void SaveConfig() { try { File.WriteAllText(ConfigPath, JsonConvert.SerializeObject((object)_config, (Formatting)1)); } catch (Exception ex) { Log.LogWarning((object)("Failed to save config: " + ex.Message)); } } private void ApplyAllReplaceEntries() { int num = 0; foreach (MonsterRuntime item in SnapshotRuntimes()) { if (item?.Entry != null && item.Entry.Enabled && !item.Entry.IsCreature && ApplyEntry(item)) { num++; } } Log.LogInfo((object)((num > 0) ? $"Applied {num} visual replacement(s)." : "No visual replacements applied.")); } private void ApplyEntryById(string id) { MonsterRuntime monsterRuntime = RuntimeFor(id); if (monsterRuntime == null) { Log.LogWarning((object)("Unknown monster id '" + id + "'; nothing applied.")); } else { ApplyEntry(monsterRuntime); } } private bool ApplyEntry(MonsterRuntime rt) { MonsterEntry entry = rt.Entry; GameObject val = ((PrefabManager.Instance != null) ? PrefabManager.Instance.GetPrefab(entry.TargetPrefab) : null); if ((Object)(object)val == (Object)null) { rt.Stage = "target prefab missing"; Log.LogError((object)("Target prefab '" + entry.TargetPrefab + "' not found; model not applied.")); return false; } rt.Prefab = val; if (entry.UsesBundle) { if (ApplyBundleReplace(rt, val)) { return true; } if (string.IsNullOrEmpty(entry.ModelFile)) { return false; } Log.LogWarning((object)("Bundle attach failed for '" + entry.Name + "'; trying GLB '" + entry.ModelFile + "'.")); } _activeEntry = entry; _activeRt = rt; ResolveSourceShader(val); DestroyRigCache(); _customModel = LoadCustomModel(entry.ModelFile); if ((Object)(object)_customModel == (Object)null) { rt.Stage = "model failed to load"; Log.LogError((object)("Failed to load '" + entry.ModelFile + "'; original renderers left untouched.")); return false; } ReplaceOnTarget(val, isPrefab: true, rt); RefreshLiveInstances(rt); Object.Destroy((Object)(object)_customModel); _customModel = null; rt.Stage = "applied"; return true; } private bool ApplyBundleReplace(MonsterRuntime rt, GameObject prefab) { MonsterEntry entry = rt.Entry; string text = Path.Combine(PluginDir, entry.BundleFile); if (!File.Exists(text)) { rt.Stage = "model failed to load"; Log.LogError((object)("Bundle '" + entry.BundleFile + "' not found at " + text + "; original renderers left untouched.")); return false; } if (!TryAttachBundleCreatureModel(prefab, entry)) { rt.Stage = "model failed to load"; Log.LogError((object)("Failed to attach '" + entry.BundlePrefab + "' from '" + entry.BundleFile + "' onto '" + entry.TargetPrefab + "'.")); return false; } RememberBundleMeshes(rt, prefab); int num = RefreshBundleInstances(rt); rt.Stage = "applied"; Log.LogInfo((object)$"Replaced '{entry.TargetPrefab}' with '{entry.BundleFile}/{entry.BundlePrefab}' ({rt.MeasuredHeight:F2}m, {num} live)."); return true; } private void RememberBundleMeshes(MonsterRuntime rt, GameObject host) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) rt.LastMeshes.Clear(); rt.Rig = null; GameObject val = null; foreach (Transform item in host.transform) { Transform val2 = item; if (((Object)val2).name == "CustomModel") { val = ((Component)val2).gameObject; } } if ((Object)(object)val == (Object)null) { return; } Renderer[] componentsInChildren = val.GetComponentsInChildren<Renderer>(true); foreach (Renderer val3 in componentsInChildren) { if ((Object)(object)val3 == (Object)null) { continue; } Vector3 val4 = Vector3.zero; SkinnedMeshRenderer val5 = (SkinnedMeshRenderer)(object)((val3 is SkinnedMeshRenderer) ? val3 : null); Bounds bounds; if (val5 != null && (Object)(object)val5.sharedMesh != (Object)null) { bounds = val5.sharedMesh.bounds; val4 = ((Bounds)(ref bounds)).size; } else { MeshFilter component = ((Component)val3).GetComponent<MeshFilter>(); if ((Object)(object)component != (Object)null && (Object)(object)component.sharedMesh != (Object)null) { bounds = component.sharedMesh.bounds; val4 = ((Bounds)(ref bounds)).size; } } rt.LastMeshes.Add(new MeshInfo { Name = ((Object)((Component)val3).gameObject).name, LocalSize = val4, ScaledSize = val4 }); } rt.MeshCount = rt.LastMeshes.Count; rt.MeasuredHeight = RendererHeight(val); } private int RefreshBundleInstances(MonsterRuntime rt) { GameObject[] array = Resources.FindObjectsOfTypeAll<GameObject>(); int num = 0; string targetPrefab = rt.Entry.TargetPrefab; GameObject[] array2 = array; foreach (GameObject val in array2) { if (IsLiveInstanceOf(val, targetPrefab) && TryAttachBundleCreatureModel(val, rt.Entry)) { num++; } } if (num > 0) { Log.LogInfo((object)$"Refreshed {num} live '{targetPrefab}' instances"); } return num; } private static bool IsLiveInstanceOf(GameObject go, string prefabName) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)go == (Object)null)) { Scene scene = go.scene; if (((Scene)(ref scene)).IsValid() && !string.IsNullOrEmpty(((Object)go).name) && !string.IsNullOrEmpty(prefabName)) { if (((Object)go).name == prefabName) { return true; } return ((Object)go).name.StartsWith(prefabName + "(", StringComparison.Ordinal); } } return false; } private void DestroyRigCache() { if (_activeRt?.Rig == null) { return; } foreach (RigPart part in _activeRt.Rig.Parts) { if ((Object)(object)part.Mesh != (Object)null) { Object.Destroy((Object)(object)part.Mesh); } } _activeRt.Rig = null; } private void ResolveSourceShader(GameObject target) { if ((Object)(object)_sourceShader != (Object)null) { return; } Renderer[] componentsInChildren = target.GetComponentsInChildren<Renderer>(true); for (int i = 0; i < componentsInChildren.Length; i++) { Material sharedMaterial = componentsInChildren[i].sharedMaterial; if ((Object)(object)sharedMaterial != (Object)null && (Object)(object)sharedMaterial.shader != (Object)null) { string name = ((Object)sharedMaterial.shader).name; if (name.IndexOf("InternalError", StringComparison.Ordinal) < 0) { _sourceShader = sharedMaterial.shader; Log.LogInfo((object)("Using '" + ((Object)target).name + "' material shader for custom model: " + name)); return; } } } Log.LogWarning((object)("No usable shader found on '" + ((Object)target).name + "'; custom materials may render magenta.")); } private GameObject LoadCustomModel(string modelFile) { try { string text = Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location), modelFile); if (!File.Exists(text)) { Log.LogError((object)("Model file not found at: " + text)); return null; } _glbSkinJoints.Clear(); _skinJointNameByMesh.Clear(); Log.LogInfo((object)("Loading GLB from: " + text)); GameObject val = LoadGLB(text, _sourceShader); if ((Object)(object)val == (Object)null) { Log.LogError((object)"Failed to load GameObject from GLB"); } else { Log.LogInfo((object)("Loaded custom model: " + ((Object)val).name)); } return val; } catch (Exception arg) { Log.LogError((object)$"Error loading custom model: {arg}"); return null; } } private void ReplaceOnTarget(GameObject target, bool isPrefab, MonsterRuntime rt) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Expected O, but got Unknown //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Expected O, but got Unknown //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_020b: Unknown result type (might be due to invalid IL or missing references) //IL_0212: Expected O, but got Unknown //IL_022f: Unknown result type (might be due to invalid IL or missing references) //IL_0242: Unknown result type (might be due to invalid IL or missing references) //IL_0255: Unknown result type (might be due to invalid IL or missing references) SkinnedMeshRenderer[] componentsInChildren = target.GetComponentsInChildren<SkinnedMeshRenderer>(true); MeshRenderer[] componentsInChildren2 = target.GetComponentsInChildren<MeshRenderer>(true); foreach (Transform item in target.transform) { Transform val = item; if (((Object)val).name == "CustomModel") { Object.Destroy((Object)(object)((Component)val).gameObject); } } if ((Object)(object)_customModel == (Object)null) { Log.LogError((object)"No custom model available; leaving original renderers untouched."); return; } SkinnedMeshRenderer[] componentsInChildren3 = _customModel.GetComponentsInChildren<SkinnedMeshRenderer>(true); MeshRenderer[] componentsInChildren4 = _customModel.GetComponentsInChildren<MeshRenderer>(true); List<PartData> list = new List<PartData>(); rt.LastMeshes.Clear(); Renderer[] customRenderers = (Renderer[])(object)componentsInChildren3; CollectPartData(customRenderers, list, fromSkinned: true); customRenderers = (Renderer[])(object)componentsInChildren4; CollectPartData(customRenderers, list, fromSkinned: false); if (list.Count == 0) { Log.LogError((object)"No enabled custom meshes; leaving original renderers untouched."); return; } GameObject val2 = new GameObject("CustomModel"); val2.transform.SetParent(target.transform, false); val2.transform.localPosition = Vector3.zero; val2.transform.localRotation = Quaternion.identity; val2.transform.localScale = Vector3.one; ApplyModelTransform(val2, list, rt); bool flag = _activeEntry.EnableSkeletonBinding; if (flag && isPrefab) { rt.Rig = BuildRigCache(val2, target, list); if (rt.Rig == null) { Log.LogWarning((object)("Skeleton binding requested but '" + ((Object)target).name + "' has no usable skeleton; falling back to static attach.")); flag = false; } } else if (flag && rt.Rig == null) { flag = false; } int num = 0; GameObject val3 = val2; if (flag && rt.Rig != null) { val3 = AttachRigged(val2, target, rt.Rig); if ((Object)(object)val3 == (Object)null) { return; } num = rt.Rig.Parts.Count; } else { foreach (PartData item2 in list) { GameObject val4 = new GameObject(item2.Name); val4.transform.SetParent(val2.transform, false); val4.transform.localPosition = item2.LocalPosition; val4.transform.localRotation = item2.LocalRotation; val4.transform.localScale = item2.LocalScale; if (item2.FromSkinnedSource) { val4.AddComponent<SkinnedMeshRenderer>().sharedMesh = item2.Mesh; SkinnedMeshRenderer component = val4.GetComponent<SkinnedMeshRenderer>(); component.sharedMesh = item2.Mesh; ((Renderer)component).sharedMaterials = item2.Materials; component.updateWhenOffscreen = false; component.quality = (SkinQuality)2; } else { val4.AddComponent<MeshFilter>().sharedMesh = item2.Mesh; ((Renderer)val4.AddComponent<MeshRenderer>()).sharedMaterials = item2.Materials; } num++; } } MonsterRenderCuller.Attach(val3, _activeEntry.CullDistance); SkinnedMeshRenderer[] array = componentsInChildren; for (int i = 0; i < array.Length; i++) { ((Renderer)array[i]).enabled = false; } MeshRenderer[] array2 = componentsInChildren2; for (int i = 0; i < array2.Length; i++) { ((Renderer)array2[i]).enabled = false; } rt.MeshCount = num; rt.MeasuredHeight = RendererHeight(target); Log.LogInfo((object)string.Format("Attached {0} custom meshes to '{1}' (disabled {2} original renderers, mode: {3}, cull {4:F0}m)", num, ((Object)target).name, componentsInChildren.Length + componentsInChildren2.Length, (flag && rt.Rig != null) ? "skeleton-bound" : "static", _activeEntry.CullDistance)); } private void CollectPartData(Renderer[] customRenderers, List<PartData> parts, bool fromSkinned) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected O, but got Unknown //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) foreach (Renderer val in customRenderers) { Mesh val2 = null; if (fromSkinned) { SkinnedMeshRenderer val3 = (SkinnedMeshRenderer)val; if ((Object)(object)val3.sharedMesh == (Object)null) { continue; } val2 = val3.sharedMesh; } else { MeshFilter component = ((Component)val).GetComponent<MeshFilter>(); if ((Object)(object)component == (Object)null || (Object)(object)component.sharedMesh == (Object)null) { continue; } val2 = component.sharedMesh; } if (!_activeEntry.DisabledMeshes.Contains<string>(((Object)val).name, StringComparer.OrdinalIgnoreCase)) { parts.Add(new PartData { Name = ((Object)val).name, Mesh = val2, Materials = val.sharedMaterials, LocalPosition = ((Component)val).transform.localPosition, LocalRotation = ((Component)val).transform.localRotation, LocalScale = ((Component)val).transform.localScale, FromSkinnedSource = fromSkinned, JointNames = (((Object)(object)val2 != (Object)null && _glbSkinJoints.TryGetValue(val2, out string[] value)) ? value : null) }); List<MeshInfo> lastMeshes = _activeRt.LastMeshes; MeshInfo obj = new MeshInfo { Name = ((Object)val).name }; Bounds bounds = val2.bounds; obj.LocalSize = ((Bounds)(ref bounds)).size; lastMeshes.Add(obj); } } } private SkinnedMeshRenderer FindBestSkeleton(GameObject target) { SkinnedMeshRenderer val = null; SkinnedMeshRenderer[] componentsInChildren = target.GetComponentsInChildren<SkinnedMeshRenderer>(true); foreach (SkinnedMeshRenderer val2 in componentsInChildren) { if (val2.bones != null && val2.bones.Length != 0 && ((Object)(object)val == (Object)null || val2.bones.Length > val.bones.Length)) { val = val2; } } return val; } private RigCache BuildRigCache(GameObject customRoot, GameObject target, List<PartData> parts) { //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) SkinnedMeshRenderer val = FindBestSkeleton(target); if ((Object)(object)val == (Object)null) { return null; } Transform[] bones = val.bones; int num = bones.Length; string[] array = new string[num]; Vector3[] array2 = (Vector3[])(object)new Vector3[num]; Matrix4x4[] array3 = (Matrix4x4[])(object)new Matrix4x4[num]; for (int i = 0; i < num; i++) { if ((Object)(object)bones[i] == (Object)null) { Log.LogWarning((object)$"Skeleton bone {i} on '{((Object)target).name}' is null; cannot build rig."); return null; } array[i] = ((Object)bones[i]).name; array2[i] = target.transform.InverseTransformPoint(bones[i].position); array3[i] = bones[i].worldToLocalMatrix * target.transform.localToWorldMatrix; } int[] array4 = new int[num]; for (int j = 0; j < num; j++) { array4[j] = -1; Transform parent = bones[j].parent; if ((Object)(object)parent == (Object)null) { continue; } for (int k = 0; k < num; k++) { if ((Object)(object)bones[k] == (Object)(object)parent) { array4[j] = k; break; } } } float num2 = ComputeBoneSpacing(array2); float num3 = Mathf.Max(0.25f, num2 * 2.2f); RigCache rigCache = new RigCache(); rigCache.BoneNames = array; rigCache.BoneLocalPositions = array2; rigCache.BoneParentIndices = array4; rigCache.BindPoses = array3; rigCache.Radius = num3; rigCache.RootBoneName = (((Object)(object)val.rootBone != (Object)null) ? ((Object)val.rootBone).name : array[0]); foreach (PartData part in parts) { RigPart rigPart = BakePart(part, customRoot, target, array3, array2, array, num3); if (rigPart != null) { rigPart.DominantBoneIndex = ComputeDominantBone(rigPart.Mesh); rigCache.Parts.Add(rigPart); } } if (rigCache.Parts.Count == 0) { return null; } Log.LogInfo((object)$"Built rig: {num} bones bound to '{((Object)target).name}', radius {num3:F2}, {rigCache.Parts.Count} parts"); return rigCache; } private float ComputeBoneSpacing(Vector3[] bonePositions) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) float num = 0f; int num2 = 0; for (int i = 0; i < bonePositions.Length; i++) { float num3 = float.MaxValue; for (int j = 0; j < bonePositions.Length; j++) { if (i != j) { float num4 = Vector3.Distance(bonePositions[i], bonePositions[j]); if (num4 < num3) { num3 = num4; } } } if (num3 < float.MaxValue) { num += num3; num2++; } } if (num2 <= 0) { return 0.4f; } return num / (float)num2; } private RigPart BakePart(PartData p, GameObject customRoot, GameObject target, Matrix4x4[] bindPoses, Vector3[] bonePositions, string[] boneNames, float radius) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0274: Unknown result type (might be due to invalid IL or missing references) //IL_027b: Expected O, but got Unknown //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Unknown result type (might be due to invalid IL or missing references) //IL_0205: Unknown result type (might be due to invalid IL or missing references) //IL_020e: Unknown result type (might be due to invalid IL or missing references) //IL_0213: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_01e1: Unknown result type (might be due to invalid IL or missing references) //IL_01ea: Unknown result type (might be due to invalid IL or missing references) //IL_01ef: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)p.Mesh == (Object)null) { return null; } Vector3[] vertices = p.Mesh.vertices; Vector3[] normals = p.Mesh.normals; Vector2[] uv = p.Mesh.uv; int[] triangles = p.Mesh.triangles; if (vertices == null || vertices.Length == 0) { return null; } Matrix4x4 val = Matrix4x4.TRS(p.LocalPosition, p.LocalRotation, p.LocalScale); Matrix4x4 val2 = target.transform.worldToLocalMatrix * customRoot.transform.localToWorldMatrix * val; Vector3[] array = (Vector3[])(object)new Vector3[vertices.Length]; Vector3[] array2 = (Vector3[])(object)((normals != null && normals.Length == vertices.Length) ? new Vector3[vertices.Length] : null); BoneWeight[] array3 = (BoneWeight[])(object)new BoneWeight[vertices.Length]; bool flag = p.JointNames != null && p.JointNames.Length != 0 && p.Mesh.boneWeights != null && p.Mesh.boneWeights.Length == vertices.Length; Dictionary<int, int> jointToBone = (flag ? MapJointNamesToBones(p.JointNames, boneNames) : null); int num = 0; for (int i = 0; i < vertices.Length; i++) { array[i] = ((Matrix4x4)(ref val2)).MultiplyPoint3x4(vertices[i]); if (array2 != null) { int num2 = i; Vector3 val3 = ((Matrix4x4)(ref val2)).MultiplyVector(normals[i]); array2[num2] = ((Vector3)(ref val3)).normalized; } BoneWeight src = (BoneWeight)(flag ? p.Mesh.boneWeights[i] : default(BoneWeight)); if (flag) { array3[i] = RemapGlbWeightToBones(src, jointToBone); if (((BoneWeight)(ref array3[i])).weight0 <= 0f && ((BoneWeight)(ref array3[i])).weight1 <= 0f && ((BoneWeight)(ref array3[i])).weight2 <= 0f && ((BoneWeight)(ref array3[i])).weight3 <= 0f) { array3[i] = ComputeBoneWeight(array[i], bonePositions, radius); num++; } } else { array3[i] = ComputeBoneWeight(array[i], bonePositions, radius); } } if (flag) { Log.LogInfo((object)$"Part '{p.Name}': using GLB skin weights ({p.JointNames.Length} joints), {num}/{vertices.Length} verts fell back to proximity"); } Mesh val4 = new Mesh(); ((Object)val4).name = p.Name; val4.vertices = array; val4.uv = ((uv != null && uv.Length == vertices.Length) ? uv : null); val4.triangles = triangles; if (array2 != null) { val4.normals = array2; } val4.boneWeights = array3; val4.bindposes = bindPoses; val4.RecalculateBounds(); return new RigPart { Name = p.Name, Mesh = val4, Materials = p.Materials }; } private Dictionary<int, int> MapJointNamesToBones(string[] jointNames, string[] boneNames) { if (jointNames == null || boneNames == null) { return null; } Dictionary<string, int> dictionary = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase); for (int i = 0; i < boneNames.Length; i++) { if (boneNames[i] != null) { dictionary[boneNames[i]] = i; } } Dictionary<int, int> dictionary2 = new Dictionary<int, int>(); for (int j = 0; j < jointNames.Length; j++) { if (jointNames[j] != null && dictionary.TryGetValue(jointNames[j], out var value)) { dictionary2[j] = value; } } return dictionary2; } private BoneWeight RemapGlbWeightToBones(BoneWeight src, Dictionary<int, int> jointToBone) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) if (jointToBone == null) { return default(BoneWeight); } BoneWeight result = default(BoneWeight); int num = 0; int[] array = new int[4] { ((BoneWeight)(ref src)).boneIndex0, ((BoneWeight)(ref src)).boneIndex1, ((BoneWeight)(ref src)).boneIndex2, ((BoneWeight)(ref src)).boneIndex3 }; float[] array2 = new float[4] { ((BoneWeight)(ref src)).weight0, ((BoneWeight)(ref src)).weight1, ((BoneWeight)(ref src)).weight2, ((BoneWeight)(ref src)).weight3 }; float num2 = 0f; for (int i = 0; i < 4; i++) { if (array[i] >= 0 && !(array2[i] <= 0f) && jointToBone.TryGetValue(array[i], out var value)) { switch (num) { case 0: ((BoneWeight)(ref result)).boneIndex0 = value; ((BoneWeight)(ref result)).weight0 = array2[i]; break; case 1: ((BoneWeight)(ref result)).boneIndex1 = value; ((BoneWeight)(ref result)).weight1 = array2[i]; break; case 2: ((BoneWeight)(ref result)).boneIndex2 = value; ((BoneWeight)(ref result)).weight2 = array2[i]; break; default: ((BoneWeight)(ref result)).boneIndex3 = value; ((BoneWeight)(ref result)).weight3 = array2[i]; break; } num++; num2 += array2[i]; } } if (num == 0 || num2 <= 0f) { return default(BoneWeight); } float num3 = 1f / num2; ((BoneWeight)(ref result)).weight0 = ((BoneWeight)(ref result)).weight0 * num3; ((BoneWeight)(ref result)).weight1 = ((BoneWeight)(ref result)).weight1 * num3; ((BoneWeight)(ref result)).weight2 = ((BoneWeight)(ref result)).weight2 * num3; ((BoneWeight)(ref result)).weight3 = ((BoneWeight)(ref result)).weight3 * num3; return result; } private BoneWeight ComputeBoneWeight(Vector3 pos, Vector3[] bonePositions, float radius) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_020e: Unknown result type (might be due to invalid IL or missing references) BoneWeight result = default(BoneWeight); List<KeyValuePair<int, float>> list = new List<KeyValuePair<int, float>>(); for (int i = 0; i < bonePositions.Length; i++) { float num = Vector3.Distance(pos, bonePositions[i]); if (num < radius) { list.Add(new KeyValuePair<int, float>(i, num)); } } list.Sort((KeyValuePair<int, float> a, KeyValuePair<int, float> b) => a.Value.CompareTo(b.Value)); if (list.Count == 0) { int boneIndex = 0; float num2 = float.MaxValue; for (int num3 = 0; num3 < bonePositions.Length; num3++) { float num4 = Vector3.Distance(pos, bonePositions[num3]); if (num4 < num2) { num2 = num4; boneIndex = num3; } } ((BoneWeight)(ref result)).boneIndex0 = boneIndex; ((BoneWeight)(ref result)).weight0 = 1f; return result; } int num5 = Mathf.Min(4, list.Count); float[] array = new float[4]; int[] array2 = new int[4]; float[] array3 = new float[4]; for (int num6 = 0; num6 < num5; num6++) { array[num6] = list[num6].Value; array2[num6] = list[num6].Key; array3[num6] = 1f - array[num6] / radius; } float num7 = 0f; for (int num8 = 0; num8 < num5; num8++) { array3[num8] *= array3[num8]; num7 += array3[num8]; } if (num7 <= 0.0001f) { for (int num9 = 0; num9 < num5; num9++) { array3[num9] = 1f / (float)num5; } } else { for (int num10 = 0; num10 < num5; num10++) { array3[num10] /= num7; } } ((BoneWeight)(ref result)).boneIndex0 = array2[0]; ((BoneWeight)(ref result)).weight0 = array3[0]; if (num5 > 1) { ((BoneWeight)(ref result)).boneIndex1 = array2[1]; ((BoneWeight)(ref result)).weight1 = array3[1]; } if (num5 > 2) { ((BoneWeight)(ref result)).boneIndex2 = array2[2]; ((BoneWeight)(ref result)).weight2 = array3[2]; } if (num5 > 3) { ((BoneWeight)(ref result)).boneIndex3 = array2[3]; ((BoneWeight)(ref result)).weight3 = array3[3]; } return result; } private int ComputeDominantBone(Mesh mesh) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)mesh == (Object)null || mesh.boneWeights == null || mesh.boneWeights.Length == 0) { return -1; } Dictionary<int, float> dictionary = new Dictionary<int, float>(); BoneWeight[] boneWeights = mesh.boneWeights; for (int i = 0; i < boneWeights.Length; i++) { BoneWeight val = boneWeights[i]; AddWeight(dictionary, ((BoneWeight)(ref val)).boneIndex0, ((BoneWeight)(ref val)).weight0); if (((BoneWeight)(ref val)).weight1 > 0f) { AddWeight(dictionary, ((BoneWeight)(ref val)).boneIndex1, ((BoneWeight)(ref val)).weight1); } if (((BoneWeight)(ref val)).weight2 > 0f) { AddWeight(dictionary, ((BoneWeight)(ref val)).boneIndex2, ((BoneWeight)(ref val)).weight2); } if (((BoneWeight)(ref val)).weight3 > 0f) { AddWeight(dictionary, ((BoneWeight)(ref val)).boneIndex3, ((BoneWeight)(ref val)).weight3); } } int result = -1; float num = -1f; foreach (KeyValuePair<int, float> item in dictionary) { if (item.Value > num) { num = item.Value; result = item.Key; } } return result; } private void AddWeight(Dictionary<int, float> sums, int index, float w) { if (index >= 0 && !(w <= 0f)) { if (sums.TryGetValue(index, out var value)) { sums[index] = value + w; } else { sums[index] = w; } } } private GameObject AttachRigged(GameObject customRoot, GameObject target, RigCache cache) { //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Expected O, but got Unknown //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) SkinnedMeshRenderer val = FindBestSkeleton(target); if ((Object)(object)val == (Object)null) { return null; } Transform[] array = ResolveBonesByIndex(target, val.bones, cache); if (array == null) { Log.LogWarning((object)$"Could not match all {cache.BoneNames.Length} skeleton bones on live/target '{((Object)target).name}'; rig skipped for this object."); return null; } Transform rootBone = ((array.Length != 0) ? array[0] : null); if (!string.IsNullOrEmpty(cache.RootBoneName)) { Transform[] componentsInChildren = ((Component)((Component)val).transform).GetComponentsInChildren<Transform>(true); foreach (Transform val2 in componentsInChildren) { if (((Object)val2).name == cache.RootBoneName) { rootBone = val2; break; } } } GameObject val3 = new GameObject("CustomModel"); val3.transform.SetParent(target.transform, false); val3.transform.localPosition = Vector3.zero; val3.transform.localRotation = Quaternion.identity; val3.transform.localScale = Vector3.one; foreach (RigPart part in cache.Parts) { GameObject val4 = new GameObject(part.Name); val4.transform.SetParent(val3.transform, false); SkinnedMeshRenderer obj = val4.AddComponent<SkinnedMeshRenderer>(); obj.sharedMesh = part.Mesh; ((Renderer)obj).sharedMaterials = part.Materials; obj.bones = array; obj.rootBone = rootBone; obj.updateWhenOffscreen = false; obj.quality = (SkinQuality)2; } Object.Destroy((Object)(object)customRoot); return val3; } private Transform[] ResolveBonesByIndex(GameObject target, Transform[] targetBones, RigCache cache) { if (targetBones != null && targetBones.Length == cache.BoneNames.Length) { bool flag = true; for (int i = 0; i < targetBones.Length; i++) { if ((Object)(object)targetBones[i] == (Object)null || ((Object)targetBones[i]).name != cache.BoneNames[i]) { flag = false; break; } } if (flag) { return targetBones; } } Dictionary<string, Transform> dictionary = new Dictionary<string, Transform>(); Transform[] componentsInChildren = target.GetComponentsInChildren<Transform>(true); foreach (Transform val in componentsInChildren) { if ((Object)(object)val != (Object)null && !dictionary.ContainsKey(((Object)val).name)) { dictionary[((Object)val).name] = val; } } Transform[] array = (Transform[])(object)new Transform[cache.BoneNames.Length]; for (int k = 0; k < cache.BoneNames.Length; k++) { if (!dictionary.TryGetValue(cache.BoneNames[k], out array[k])) { return null; } } return array; } private void ApplyModelTransform(GameObject customRoot, List<PartData> parts, MonsterRuntime rt) { //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing