using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.AI; using UnityEngine.Animations; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; using UnityEngine.Playables; using UnityEngine.Rendering.HighDefinition; [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("DrifterBracken")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.18.0.0")] [assembly: AssemblyInformationalVersion("1.18.0")] [assembly: AssemblyProduct("DrifterBracken")] [assembly: AssemblyTitle("DrifterBracken")] [assembly: AssemblyVersion("1.18.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [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 DrifterBracken { public class BrackenAnimTester : MonoBehaviour { private static BrackenAnimTester _primary; private Animator _animator; private AnimationClip[] _clips = (AnimationClip[])(object)new AnimationClip[0]; private RuntimeAnimatorController _originalController; private readonly List _suspended = new List(); private PlayableGraph _graph; private AnimationClipPlayable _playable; private bool _graphLive; private Transform[] _recBones = (Transform[])(object)new Transform[0]; private readonly List _recClips = new List(); private readonly List _recFrames = new List(); private float _recAccum; private const float RecFps = 20f; private int _index; private float _timer; private bool _running; private bool _paused; private bool _held; private float _heldSpeed = 1f; private void Awake() { _animator = ((Component)this).GetComponentInChildren(true); if ((Object)(object)_animator == (Object)null) { ((Behaviour)this).enabled = false; return; } _clips = (AnimationClip[])(((Object)(object)_animator.runtimeAnimatorController == (Object)null) ? ((Array)new AnimationClip[0]) : ((Array)(from c in _animator.runtimeAnimatorController.animationClips where (Object)(object)c != (Object)null group c by ((Object)c).name into grp select grp.First() into c orderby ((Object)c).name select c).ToArray())); SkinnedMeshRenderer val = ((IEnumerable)((Component)this).GetComponentsInChildren(true)).FirstOrDefault((Func)((SkinnedMeshRenderer r) => r.bones != null && r.bones.Length > 60)); if ((Object)(object)val != (Object)null) { _recBones = val.bones.Where((Transform b) => (Object)(object)b != (Object)null).ToArray(); } if ((Object)(object)_primary == (Object)null) { _primary = this; } Plugin.Log.LogInfo((object)("Test mode: " + _clips.Length + " clips on '" + ((Object)this).name + "' -> " + string.Join(", ", _clips.Select((AnimationClip c) => ((Object)c).name).ToArray()))); } private void OnDestroy() { Stop(); Release(); if ((Object)(object)_primary == (Object)(object)this) { _primary = null; } } private void Update() { bool flag = Plugin.HoldActive && !Plugin.TestActive; if (flag && !_held) { HoldStill(); } else if (!flag && _held) { Release(); } if (Plugin.TestActive && !_running) { Begin(); } else if (!Plugin.TestActive && _running) { Stop(); } if (!_running || _clips.Length == 0) { return; } if (Plugin.ConsumeStep(out var step)) { Show(_index + step); return; } if (Plugin.ConsumePause()) { _paused = !_paused; } if (!_paused) { if (Plugin.RecordPoses.Value && (Object)(object)_primary == (Object)(object)this) { Sample(); } _timer += Time.deltaTime; float num = _clips[_index].length; if (num <= 0.01f) { num = 2f; } if (_timer >= num + Plugin.TestHold.Value) { Show(_index + 1); } } } private void Begin() { //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) _running = true; Suspend(); _originalController = _animator.runtimeAnimatorController; _animator.runtimeAnimatorController = null; _graph = PlayableGraph.Create("DrifterBrackenTest:" + ((Object)this).GetInstanceID()); ((PlayableGraph)(ref _graph)).SetTimeUpdateMode((DirectorUpdateMode)1); AnimationPlayableOutput val = AnimationPlayableOutput.Create(_graph, "anim", _animator); _playable = AnimationClipPlayable.Create(_graph, (_clips.Length != 0) ? _clips[0] : null); PlayableOutputExtensions.SetSourcePlayable(val, _playable); _graphLive = true; ((PlayableGraph)(ref _graph)).Play(); _index = -1; Show(0); Plugin.Log.LogInfo((object)("Test mode ON - frozen, cycling " + _clips.Length + " clips.")); } private void Stop() { if (_running) { _running = false; _paused = false; if (_graphLive && ((PlayableGraph)(ref _graph)).IsValid()) { ((PlayableGraph)(ref _graph)).Destroy(); } _graphLive = false; if ((Object)(object)_animator != (Object)null) { _animator.runtimeAnimatorController = _originalController; } Resume(); Plugin.Log.LogInfo((object)"Test mode OFF - Bracken released."); } } private void Suspend() { //IL_0081: Unknown result type (might be due to invalid IL or missing references) _suspended.Clear(); MonoBehaviour[] components = ((Component)this).GetComponents(); foreach (MonoBehaviour val in components) { if (!((Object)(object)val == (Object)null) && !((Object)(object)val == (Object)(object)this) && ((Behaviour)val).enabled && val is EnemyAI) { ((Behaviour)val).enabled = false; _suspended.Add((Behaviour)(object)val); } } NavMeshAgent component = ((Component)this).GetComponent(); if ((Object)(object)component != (Object)null && ((Behaviour)component).enabled) { if (component.isOnNavMesh) { component.isStopped = true; } component.velocity = Vector3.zero; ((Behaviour)component).enabled = false; _suspended.Add((Behaviour)(object)component); } components = ((Component)this).GetComponentsInChildren(true); foreach (MonoBehaviour val2 in components) { if (!((Object)(object)val2 == (Object)null) && ((Behaviour)val2).enabled && !(((object)val2).GetType().Name != "RigBuilder")) { ((Behaviour)val2).enabled = false; _suspended.Add((Behaviour)(object)val2); } } } private void Resume() { foreach (Behaviour item in _suspended) { if ((Object)(object)item != (Object)null) { item.enabled = true; } } _suspended.Clear(); } private void HoldStill() { _held = true; Suspend(); if ((Object)(object)_animator != (Object)null) { _heldSpeed = ((_animator.speed <= 0f) ? 1f : _animator.speed); _animator.speed = 0f; } Plugin.Log.LogInfo((object)("Hold still ON - '" + ((Object)this).name + "' frozen on its current pose.")); } private void Release() { if (_held) { _held = false; if ((Object)(object)_animator != (Object)null) { _animator.speed = _heldSpeed; } Resume(); Plugin.Log.LogInfo((object)("Hold still OFF - '" + ((Object)this).name + "' released.")); } } private void Sample() { //IL_0051: 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_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0082: 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_00a5: 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_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) if (_recBones.Length == 0) { return; } _recAccum += Time.deltaTime; if (!(_recAccum < 0.05f)) { _recAccum = 0f; float[] array = new float[_recBones.Length * 4]; for (int i = 0; i < _recBones.Length; i++) { Quaternion localRotation = _recBones[i].localRotation; array[i * 4] = localRotation.x; array[i * 4 + 1] = localRotation.y; array[i * 4 + 2] = localRotation.z; array[i * 4 + 3] = localRotation.w; } Vector3 localPosition = _recBones[0].localPosition; _recFrames.Add(new PoseFrame { r = array, p = new float[3] { localPosition.x, localPosition.y, localPosition.z } }); } } private void FlushClip() { if (!Plugin.RecordPoses.Value || (Object)(object)_primary != (Object)(object)this) { return; } if (_index < 0 || _index >= _clips.Length || _recFrames.Count == 0) { _recFrames.Clear(); return; } string name = ((Object)_clips[_index]).name; if (_recClips.Any((PoseClip c) => c.name == name)) { _recFrames.Clear(); return; } _recClips.Add(new PoseClip { name = name, fps = 20f, frames = _recFrames.ToArray() }); _recFrames.Clear(); if (_recClips.Count >= _clips.Length) { WriteRecording(); } } private void WriteRecording() { try { PoseRecording poseRecording = new PoseRecording { bones = _recBones.Select((Transform b) => ((Object)b).name).ToArray(), clips = _recClips.ToArray() }; string text = Path.Combine(Path.GetDirectoryName(typeof(Plugin).Assembly.Location), "bracken_anim.json"); File.WriteAllText(text, JsonUtility.ToJson((object)poseRecording)); Plugin.Log.LogInfo((object)($"Recorded {_recClips.Count} clips, " + $"{_recClips.Sum((PoseClip c) => c.frames.Length)} frames -> {text}")); } catch (Exception ex) { Plugin.Log.LogError((object)("Could not write recording: " + ex.Message)); } } private void Show(int i) { //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005f: 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_0084: 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_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: 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) FlushClip(); if (_clips.Length == 0) { return; } _index = (i % _clips.Length + _clips.Length) % _clips.Length; _timer = 0f; if (_graphLive && ((PlayableGraph)(ref _graph)).IsValid()) { AnimationPlayableOutput val = (AnimationPlayableOutput)((PlayableGraph)(ref _graph)).GetOutput(0); if (PlayableExtensions.IsValid(_playable)) { ((PlayableGraph)(ref _graph)).DestroyPlayable(_playable); } _playable = AnimationClipPlayable.Create(_graph, _clips[_index]); ((AnimationClipPlayable)(ref _playable)).SetApplyFootIK(false); PlayableOutputExtensions.SetSourcePlayable(val, _playable); PlayableExtensions.SetTime(_playable, 0.0); PlayableExtensions.SetSpeed(_playable, 1.0); Plugin.Log.LogInfo((object)($"Test clip {_index + 1}/{_clips.Length}: '{((Object)_clips[_index]).name}' " + $"({_clips[_index].length:F2}s, loop={((Motion)_clips[_index]).isLooping})")); } } private void OnGUI() { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: 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_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Expected O, but got Unknown //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Expected O, but got Unknown //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_0159: 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_0177: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Unknown result type (might be due to invalid IL or missing references) if (_running && !((Object)(object)_primary != (Object)(object)this) && _clips.Length != 0) { GUIStyle val = new GUIStyle(GUI.skin.label) { fontSize = 18 }; val.normal.textColor = Color.white; GUIStyle val2 = val; GUIStyle val3 = new GUIStyle(GUI.skin.box); GUI.Box(new Rect(10f, 10f, 430f, 104f), GUIContent.none, val3); GUI.Label(new Rect(20f, 16f, 420f, 26f), "DRIFTER BRACKEN - TEST MODE" + (_paused ? " [PAUSED]" : ""), val2); GUI.Label(new Rect(20f, 42f, 420f, 26f), $"{_index + 1}/{_clips.Length} {((Object)_clips[_index]).name} ({_clips[_index].length:F2}s)", val2); GUI.Label(new Rect(20f, 68f, 420f, 26f), $"{Plugin.TestToggle.Value} exit {Plugin.TestNext.Value}/{Plugin.TestPrev.Value} step" + $" {Plugin.TestPause.Value} pause", val2); } } } [HarmonyPatch(typeof(FlowermanAI), "OnCollideWithPlayer")] internal static class HeldBrackenCannotGrabPatch { private static bool Prefix() { if (!Plugin.HoldActive) { return !Plugin.TestActive; } return false; } } public class DrifterBrackenSkin : MonoBehaviour { private readonly List _owned = new List(); private SkinnedMeshRenderer _lod1; private SkinnedMeshRenderer[] _mine = (SkinnedMeshRenderer[])(object)new SkinnedMeshRenderer[0]; private Renderer[] _hidden = (Renderer[])(object)new Renderer[0]; private bool _ready; private EnemyAI _ai; private float _voiceTimer; private static readonly string[] StripSlots = new string[19] { "_MaskMap", "_NormalMap", "_NormalMapOS", "_BentNormalMap", "_BentNormalMapOS", "_DetailMap", "_HeightMap", "_EmissiveColorMap", "_SpecularColorMap", "_AnisotropyMap", "_CoatMaskMap", "_SubsurfaceMaskMap", "_ThicknessMap", "_TangentMap", "_TangentMapOS", "_IridescenceMaskMap", "_IridescenceThicknessMap", "_TransmissionMaskMap", "_TransmittanceColorMap" }; private static readonly string[] StripKeywords = new string[14] { "_NORMALMAP", "_NORMALMAP_TANGENT_SPACE", "_MASKMAP", "_DETAIL_MAP", "_HEIGHTMAP", "_EMISSIVE_COLOR_MAP", "_EMISSION", "_BENTNORMALMAP", "_MATERIAL_FEATURE_SUBSURFACE_SCATTERING", "_MATERIAL_FEATURE_TRANSMISSION", "_MATERIAL_FEATURE_ANISOTROPY", "_MATERIAL_FEATURE_IRIDESCENCE", "_MATERIAL_FEATURE_CLEAR_COAT", "_MATERIAL_FEATURE_SPECULAR_COLOR" }; private static bool _loggedShader; private static bool _loggedValidate; private static bool _loggedEyes; private void Start() { try { Setup(); } catch (Exception ex) { Plugin.Log.LogError((object)("Setup failed on '" + ((Object)this).name + "': " + ex)); } } private void Setup() { //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_0175: 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_0185: Unknown result type (might be due to invalid IL or missing references) //IL_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_01d9: Unknown result type (might be due to invalid IL or missing references) if (Plugin.Meshes.Count == 0) { return; } SkinnedMeshRenderer[] componentsInChildren = ((Component)this).GetComponentsInChildren(true); _lod1 = ((IEnumerable)componentsInChildren).FirstOrDefault((Func)((SkinnedMeshRenderer r) => ((Object)r).name == "LOD1")) ?? componentsInChildren.FirstOrDefault(); if ((Object)(object)_lod1 == (Object)null) { Plugin.Log.LogError((object)"No Bracken renderer found."); return; } Transform[] bones = _lod1.bones; Transform rootBone = _lod1.rootBone; Material template = ((IEnumerable)((Renderer)_lod1).sharedMaterials).FirstOrDefault((Func)((Material m) => (Object)(object)m != (Object)null)); List list = new List(); foreach (KeyValuePair mesh in Plugin.Meshes) { Mesh value = mesh.Value; if (value.bindposes.Length != bones.Length) { Plugin.Log.LogError((object)($"'{mesh.Key}' has {value.bindposes.Length} bindposes but the Bracken " + $"has {bones.Length} bones - skipping.")); continue; } GameObject val = new GameObject("Drifter_" + mesh.Key); val.transform.SetParent(((Component)_lod1).transform.parent, false); val.transform.localPosition = Vector3.zero; val.transform.localRotation = Quaternion.identity; val.transform.localScale = Vector3.one; SkinnedMeshRenderer val2 = val.AddComponent(); val2.sharedMesh = value; val2.bones = bones; val2.rootBone = rootBone; ((Renderer)val2).localBounds = ((Renderer)_lod1).localBounds; val2.quality = (SkinQuality)4; val2.updateWhenOffscreen = false; ((Renderer)val2).shadowCastingMode = ((Renderer)_lod1).shadowCastingMode; ((Renderer)val2).receiveShadows = ((Renderer)_lod1).receiveShadows; string[] value2; string[] array = (Plugin.MaterialNames.TryGetValue(mesh.Key, out value2) ? value2 : new string[0]); Material[] array2 = (Material[])(object)new Material[value.subMeshCount]; for (int num = 0; num < array2.Length; num++) { array2[num] = BuildMaterial(template, (num < array.Length) ? array[num] : ""); } ((Renderer)val2).sharedMaterials = array2; list.Add(val2); Plugin.Log.LogInfo((object)($"Attached '{mesh.Key}': {value.vertexCount} verts, " + $"{value.subMeshCount} submeshes, {bones.Length} bones.")); } _mine = list.ToArray(); if (_mine.Length == 0) { Plugin.Log.LogError((object)"Nothing attached."); return; } List list2 = new List { (Renderer)(object)_lod1 }; Transform[] componentsInChildren2 = ((Component)this).GetComponentsInChildren(true); foreach (Transform val3 in componentsInChildren2) { if (((Object)val3).name == "LeftEye" || ((Object)val3).name == "RightEye") { list2.AddRange(((Component)val3).GetComponents()); } } _hidden = list2.Where((Renderer r) => (Object)(object)r != (Object)null).ToArray(); _ready = true; } private void LateUpdate() { Plugin.PollHotkeys(); _voiceTimer += Time.deltaTime; if (_voiceTimer >= 1f) { _voiceTimer = 0f; if (DrifterVoice.Enabled) { if ((Object)(object)_ai == (Object)null) { _ai = ((Component)this).GetComponent(); } DrifterVoice.FillState(_ai); } } if (!_ready) { return; } bool enabled = (Object)(object)_lod1 != (Object)null && ((Renderer)_lod1).enabled; for (int i = 0; i < _mine.Length; i++) { if ((Object)(object)_mine[i] != (Object)null) { ((Renderer)_mine[i]).enabled = enabled; } } bool value = Plugin.HideOriginal.Value; for (int j = 0; j < _hidden.Length; j++) { if ((Object)(object)_hidden[j] != (Object)null) { _hidden[j].forceRenderingOff = value; } } } private void OnDestroy() { SkinnedMeshRenderer[] mine = _mine; foreach (SkinnedMeshRenderer val in mine) { if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)((Component)val).gameObject); } } foreach (Material item in _owned) { if ((Object)(object)item != (Object)null) { Object.Destroy((Object)(object)item); } } _owned.Clear(); } private Material BuildMaterial(Material template, string slotName) { //IL_002e: 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) //IL_0034: Expected O, but got Unknown //IL_0530: Unknown result type (might be due to invalid IL or missing references) //IL_0535: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0212: Unknown result type (might be due to invalid IL or missing references) //IL_033e: Unknown result type (might be due to invalid IL or missing references) //IL_03e9: Unknown result type (might be due to invalid IL or missing references) //IL_03f0: Unknown result type (might be due to invalid IL or missing references) Shader val = (((Object)(object)template != (Object)null) ? template.shader : Shader.Find("HDRP/Lit")); Material val2 = (((Object)(object)val != (Object)null) ? new Material(val) : new Material(template)); ((Object)val2).name = "Drifter_" + slotName; _owned.Add(val2); if (!_loggedShader) { _loggedShader = true; Plugin.Log.LogInfo((object)("Materials built on shader '" + (((Object)(object)val != (Object)null) ? ((Object)val).name : "") + "' (template '" + (((Object)(object)template != (Object)null) ? ((Object)template).name : "") + "')")); } string[] stripSlots = StripSlots; foreach (string text in stripSlots) { if (val2.HasProperty(text)) { val2.SetTexture(text, (Texture)null); } } stripSlots = StripKeywords; foreach (string text2 in stripSlots) { val2.DisableKeyword(text2); } SetColor(val2, Color.black, "_EmissiveColor", "_EmissionColor"); if (val2.HasProperty("_NormalMapSpace")) { val2.SetFloat("_NormalMapSpace", 0f); } val2.renderQueue = (((Object)(object)val != (Object)null) ? val.renderQueue : (-1)); string text3 = slotName.ToLowerInvariant(); string text4 = (text3.Contains("teeth") ? "drifter_teeth" : (text3.Contains("eye") ? "drifter_eyes" : (text3.Contains("hair") ? "drifter_hair" : (text3.Contains("lower") ? "drifter_lower_body" : (text3.Contains("head") ? "drifter_head" : "drifter_upper_body"))))); Texture2D val3 = Tex(text4); bool flag = SetTex(val2, val3, "_BaseColorMap", "_MainTex", "_BaseMap"); SetColor(val2, Color.white, "_BaseColor", "_Color"); Plugin.Log.LogInfo((object)(" slot '" + slotName + "' -> " + text4 + (((Object)(object)val3 == (Object)null) ? " [TEXTURE MISSING FROM BUNDLE]" : (flag ? $" ({((Texture)val3).width}x{((Texture)val3).height})" : " [NO BASE COLOUR PROPERTY ON SHADER]")))); if (text4 == "drifter_head") { Texture2D val4 = Tex("drifter_head_normal"); if ((Object)(object)val4 != (Object)null && SetTex(val2, val4, "_NormalMap", "_BumpMap")) { val2.EnableKeyword("_NORMALMAP"); } } if (val2.HasProperty("_Smoothness")) { val2.SetFloat("_Smoothness", Plugin.Smoothness.Value); } if (val2.HasProperty("_Metallic")) { val2.SetFloat("_Metallic", 0f); } if (text4 == "drifter_eyes") { SetColor(val2, Color.black, "_BaseColor", "_Color"); if (val2.HasProperty("_Smoothness")) { val2.SetFloat("_Smoothness", 0f); } if (val2.HasProperty("_Metallic")) { val2.SetFloat("_Metallic", 0f); } float value = Plugin.EyeBrightness.Value; Texture2D val5 = Tex("drifter_eyes"); if (value > 0f && (Object)(object)val5 != (Object)null) { SetTex(val2, val5, "_EmissiveColorMap", "_EmissionMap"); SetColor(val2, Color.white * value, "_EmissiveColor", "_EmissionColor"); if (val2.HasProperty("_UseEmissiveIntensity")) { val2.SetFloat("_UseEmissiveIntensity", 0f); } if (val2.HasProperty("_EmissiveExposureWeight")) { val2.SetFloat("_EmissiveExposureWeight", 0f); } val2.EnableKeyword("_EMISSIVE_COLOR_MAP"); val2.EnableKeyword("_EMISSION"); } } try { HDMaterial.ValidateMaterial(val2); } catch (Exception ex) { if (!_loggedValidate) { _loggedValidate = true; Plugin.Log.LogWarning((object)("HDMaterial.ValidateMaterial unavailable: " + ex.Message)); } } if (text4 == "drifter_eyes" && !_loggedEyes) { _loggedEyes = true; Texture val6 = (val2.HasProperty("_EmissiveColorMap") ? val2.GetTexture("_EmissiveColorMap") : null); Plugin.Log.LogInfo((object)(" eyes: emissive map = " + (((Object)(object)val6 == (Object)null) ? "NONE" : ((Object)val6).name) + ", _EMISSIVE_COLOR_MAP keyword = " + val2.IsKeywordEnabled("_EMISSIVE_COLOR_MAP") + ", emissive colour = " + ((object)val2.GetColor("_EmissiveColor")/*cast due to .constrained prefix*/).ToString())); } return val2; } private static Texture2D Tex(string key) { if (!Plugin.Textures.TryGetValue(key, out var value)) { return null; } return value; } private static bool SetTex(Material m, Texture2D t, params string[] props) { if ((Object)(object)t == (Object)null) { return false; } bool result = false; foreach (string text in props) { if (m.HasProperty(text)) { m.SetTexture(text, (Texture)(object)t); result = true; } } return result; } private static void SetColor(Material m, Color c, params string[] props) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) foreach (string text in props) { if (m.HasProperty(text)) { m.SetColor(text, c); } } } } internal static class DrifterVoice { private static readonly FieldInfo FAngerVoice = AccessTools.Field(typeof(FlowermanAI), "creatureAngerVoice"); private static readonly FieldInfo FCrackNeck = AccessTools.Field(typeof(FlowermanAI), "crackNeckSFX"); private static readonly FieldInfo FStunSFX = AccessTools.Field(typeof(EnemyType), "stunSFX"); private static readonly FieldInfo FClips = AccessTools.Field(typeof(EnemyType), "audioClips"); private static readonly FieldInfo FAngerMode = AccessTools.Field(typeof(FlowermanAI), "isInAngerMode"); private static bool _stunDone; private static bool _loggedClips; internal static bool Enabled { get { if (Plugin.ReplaceVoice != null && Plugin.ReplaceVoice.Value) { return Plugin.Voice.Count > 0; } return false; } } internal static AudioClip Pick(string category) { if (!Plugin.Voice.TryGetValue(category, out var value) || value.Count == 0) { return null; } return value[Random.Range(0, value.Count)]; } internal static void FillState(EnemyAI ai) { if ((Object)(object)ai == (Object)null || (Object)(object)ai.enemyType == (Object)null || FClips == null || !(FClips.GetValue(ai.enemyType) is AudioClip[] array)) { return; } bool flag = FAngerMode != null && ai is FlowermanAI && (bool)FAngerMode.GetValue(ai); string category = (flag ? "enraged" : "spotted"); for (int i = 0; i < array.Length; i++) { AudioClip val = Pick(category) ?? Pick("spotted") ?? Pick("enraged"); if ((Object)(object)val != (Object)null) { array[i] = val; } } if (!_loggedClips) { _loggedClips = true; Plugin.Log.LogInfo((object)("Voice: enemyType.audioClips has " + array.Length + " slot(s), all filled from state (angry=" + flag + ")")); } if (!_stunDone && FStunSFX != null) { AudioClip val2 = Pick("stunned"); if ((Object)(object)val2 != (Object)null) { FStunSFX.SetValue(ai.enemyType, val2); _stunDone = true; } } } internal static void SetAngerVoice(FlowermanAI f) { if (!((Object)(object)f == (Object)null) && !(FAngerVoice == null)) { object? value = FAngerVoice.GetValue(f); AudioSource val = (AudioSource)((value is AudioSource) ? value : null); AudioClip val2 = Pick("enraged"); if ((Object)(object)val != (Object)null && (Object)(object)val2 != (Object)null) { val.clip = val2; } } } internal static void SetKillVoice(FlowermanAI f) { if (!((Object)(object)f == (Object)null) && !(FCrackNeck == null)) { AudioClip val = Pick("kill"); if ((Object)(object)val != (Object)null) { FCrackNeck.SetValue(f, val); } } } } [HarmonyPatch(typeof(EnemyAI), "PlayAudioOfCurrentState")] internal static class VoiceStatePatch { private static void Prefix(EnemyAI __instance) { if (DrifterVoice.Enabled && __instance is FlowermanAI) { DrifterVoice.FillState(__instance); } } } [HarmonyPatch(typeof(FlowermanAI), "EnterAngerModeClientRpc")] internal static class VoiceAngerPatch { private static void Prefix(FlowermanAI __instance) { if (DrifterVoice.Enabled) { DrifterVoice.FillState((EnemyAI)(object)__instance); DrifterVoice.SetAngerVoice(__instance); } } } [HarmonyPatch(typeof(FlowermanAI), "KillPlayerAnimationClientRpc")] internal static class VoiceKillPatch { private static void Prefix(FlowermanAI __instance) { if (DrifterVoice.Enabled) { DrifterVoice.SetKillVoice(__instance); } } } [HarmonyPatch(typeof(EnemyAI), "SetEnemyStunned")] internal static class VoiceStunPatch { private static void Prefix(EnemyAI __instance) { if (DrifterVoice.Enabled && __instance is FlowermanAI) { DrifterVoice.FillState(__instance); } } } [BepInPlugin("drifterbracken", "Drifter Bracken", "1.18.0")] public class Plugin : BaseUnityPlugin { public const string Guid = "drifterbracken"; internal static ManualLogSource Log; internal static readonly Dictionary Meshes = new Dictionary(); internal static readonly Dictionary MaterialNames = new Dictionary(); internal static readonly Dictionary Textures = new Dictionary(); internal static readonly Dictionary> Voice = new Dictionary>(); internal static ConfigEntry EyeBrightness; internal static ConfigEntry Smoothness; internal static ConfigEntry HideOriginal; internal static ConfigEntry ReplaceVoice; internal static ConfigEntry TestAutoStart; internal static ConfigEntry RecordPoses; internal static ConfigEntry TestHold; internal static ConfigEntry TestToggle; internal static ConfigEntry TestNext; internal static ConfigEntry TestPrev; internal static ConfigEntry TestPause; internal static ConfigEntry HoldKey; internal static bool TestActive; internal static bool HoldActive; private static int _step; private static int _stepFrame = -1; private static int _pauseFrame = -1; private static readonly Dictionary KeyFixups = new Dictionary { { (KeyCode)13, (Key)2 }, { (KeyCode)271, (Key)77 }, { (KeyCode)306, (Key)55 }, { (KeyCode)305, (Key)56 }, { (KeyCode)96, (Key)4 }, { (KeyCode)316, (Key)74 } }; private static readonly Dictionary _held = new Dictionary(); private static bool _kbLogged; private static float _kbNag; private static int _polledFrame = -1; private static int _tick; internal static bool ConsumeStep(out int step) { step = _step; if (_stepFrame == Time.frameCount) { return _step != 0; } return false; } internal static bool ConsumePause() { return _pauseFrame == Time.frameCount; } private unsafe static Key ToKey(KeyCode c) { //IL_0005: 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) //IL_006b: Unknown result type (might be due to invalid IL or missing references) if (KeyFixups.TryGetValue(c, out var value)) { return value; } string text = ((object)(*(KeyCode*)(&c))/*cast due to .constrained prefix*/).ToString(); if (text.StartsWith("Alpha")) { text = "Digit" + text.Substring(5); } else if (text.StartsWith("Keypad")) { text = "Numpad" + text.Substring(6); } if (!Enum.TryParse(text, out value)) { return (Key)0; } return value; } private static Keyboard Kb() { return Keyboard.current ?? InputSystem.GetDevice(); } internal static bool Pressed(ConfigEntry e) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0020: 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) //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_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: 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) //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) Keyboard val = Kb(); if (val == null) { return false; } KeyboardShortcut value = e.Value; Key val2 = ToKey(((KeyboardShortcut)(ref value)).MainKey); if ((int)val2 == 0) { return false; } bool flag = ((ButtonControl)val[val2]).isPressed; if (flag) { value = e.Value; foreach (KeyCode modifier in ((KeyboardShortcut)(ref value)).Modifiers) { Key val3 = ToKey(modifier); if ((int)val3 != 0 && !((ButtonControl)val[val3]).isPressed) { flag = false; break; } } } string key = ((ConfigEntryBase)e).Definition.Section + "/" + ((ConfigEntryBase)e).Definition.Key; _held.TryGetValue(key, out var value2); _held[key] = flag; if (flag) { return !value2; } return false; } private static void KeyboardDiagnostics() { //IL_009f: 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) //IL_0047: Unknown result type (might be due to invalid IL or missing references) Keyboard val = Kb(); if (val != null) { if (!_kbLogged) { _kbLogged = true; Log.LogInfo((object)("Keyboard found: '" + ((InputControl)val).name + "', Input System updateMode=" + ((object)InputSystem.settings.updateMode/*cast due to .constrained prefix*/).ToString() + ". Hotkeys are live.")); } return; } _kbNag += Time.unscaledDeltaTime; if (!(_kbNag < 15f)) { _kbNag = 0f; Log.LogWarning((object)("Input System has no keyboard device yet, so hotkeys cannot fire. Devices: " + string.Join(", ", ((IEnumerable)(object)InputSystem.devices).Select((InputDevice d) => ((InputControl)d).name).ToArray()))); } } private void Awake() { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Expected O, but got Unknown //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Expected O, but got Unknown //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Expected O, but got Unknown //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_01b8: Unknown result type (might be due to invalid IL or missing references) //IL_01e6: Unknown result type (might be due to invalid IL or missing references) //IL_0214: Unknown result type (might be due to invalid IL or missing references) //IL_0245: Unknown result type (might be due to invalid IL or missing references) //IL_026e: Unknown result type (might be due to invalid IL or missing references) //IL_0273: Unknown result type (might be due to invalid IL or missing references) //IL_0291: Unknown result type (might be due to invalid IL or missing references) //IL_0296: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; Smoothness = ((BaseUnityPlugin)this).Config.Bind("Look", "Smoothness", 0.35f, new ConfigDescription("Surface smoothness. The Drifter's roughness maps aren't shipped, so this is a flat value.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); EyeBrightness = ((BaseUnityPlugin)this).Config.Bind("Look", "EyeBrightness", 2f, new ConfigDescription("How bright the eyes render. They are unlit, so this is their brightness in any light. 1 is the texture as authored, higher blooms, 0 goes black.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 8f), Array.Empty())); ReplaceVoice = ((BaseUnityPlugin)this).Config.Bind("Look", "ReplaceVoice", true, "Give the Bracken the Drifter's voice - stalking, anger, the neck snap and being stunned. Turn off to keep the vanilla sounds."); HideOriginal = ((BaseUnityPlugin)this).Config.Bind("Debug", "HideOriginalBracken", true, "Hide the vanilla Bracken mesh. Turn off to see both models overlaid while tuning."); TestAutoStart = ((BaseUnityPlugin)this).Config.Bind("TestMode", "AutoStart", false, "Start every Bracken frozen and cycling its animations, without pressing the toggle key."); RecordPoses = ((BaseUnityPlugin)this).Config.Bind("TestMode", "RecordPoses", true, "While cycling, sample the Bracken's skeleton and write bracken_anim.json next to the plugin. The standalone retarget test scene replays that file, so the animations can be inspected without launching the game."); TestHold = ((BaseUnityPlugin)this).Config.Bind("TestMode", "HoldSeconds", 1f, new ConfigDescription("Extra pause after each clip finishes before the next one starts.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 10f), Array.Empty())); HoldKey = ((BaseUnityPlugin)this).Config.Bind("TestMode", "HoldStillKey", new KeyboardShortcut((KeyCode)288, Array.Empty()), "Freeze every Bracken where it stands, on its pose, without cycling animations. Its AI is off while held, so it will not chase or kill - walk up and look at it."); TestToggle = ((BaseUnityPlugin)this).Config.Bind("TestMode", "ToggleKey", new KeyboardShortcut((KeyCode)289, Array.Empty()), "Freeze every Bracken in place and cycle through all of its animations."); TestNext = ((BaseUnityPlugin)this).Config.Bind("TestMode", "NextKey", new KeyboardShortcut((KeyCode)290, Array.Empty()), "Step to the next animation."); TestPrev = ((BaseUnityPlugin)this).Config.Bind("TestMode", "PrevKey", new KeyboardShortcut((KeyCode)291, Array.Empty()), "Step to the previous animation."); TestPause = ((BaseUnityPlugin)this).Config.Bind("TestMode", "PauseKey", new KeyboardShortcut((KeyCode)292, Array.Empty()), "Hold the current animation instead of advancing."); if (LoadBundle()) { TestActive = TestAutoStart.Value; new Harmony("drifterbracken").PatchAll(Assembly.GetExecutingAssembly()); Log.LogInfo((object)("Drifter Bracken ready. Hold still: " + ((object)HoldKey.Value/*cast due to .constrained prefix*/).ToString() + ", test mode: " + ((object)TestToggle.Value/*cast due to .constrained prefix*/).ToString() + ". Waiting for the Input System to report a keyboard.")); } } private void Update() { PollHotkeys(); } internal static void PollHotkeys() { //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) if (_polledFrame == Time.frameCount) { return; } _polledFrame = Time.frameCount; if (_tick < 3) { _tick++; Keyboard val = Kb(); Log.LogInfo((object)("Hotkey poll " + _tick + ": keyboard=" + ((val == null) ? "null" : ((InputControl)val).name) + ", devices=" + InputSystem.devices.Count)); } KeyboardDiagnostics(); if (Pressed(HoldKey)) { HoldActive = !HoldActive; Log.LogInfo((object)("Hold still " + (HoldActive ? "ON" : "OFF"))); } if (Pressed(TestToggle)) { TestActive = !TestActive; Log.LogInfo((object)("Test mode " + (TestActive ? "ON" : "OFF"))); } if (TestActive) { _step = 0; if (Pressed(TestNext)) { _step = 1; _stepFrame = Time.frameCount; } else if (Pressed(TestPrev)) { _step = -1; _stepFrame = Time.frameCount; } if (Pressed(TestPause)) { _pauseFrame = Time.frameCount; } } } private bool LoadBundle() { string text = Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location), "drifterbracken"); if (!File.Exists(text)) { Log.LogError((object)("Asset bundle missing - expected it next to the plugin at '" + text + "'.")); return false; } AssetBundle val = AssetBundle.LoadFromFile(text); if ((Object)(object)val == (Object)null) { Log.LogError((object)"Asset bundle failed to load (built for the wrong Unity version?)."); return false; } Mesh[] array = val.LoadAllAssets(); foreach (Mesh val2 in array) { if (((Object)val2).name.EndsWith("_rebound")) { Meshes[((Object)val2).name.Substring(0, ((Object)val2).name.Length - "_rebound".Length)] = val2; } } GameObject[] array2 = val.LoadAllAssets(); for (int i = 0; i < array2.Length; i++) { SkinnedMeshRenderer[] componentsInChildren = array2[i].GetComponentsInChildren(true); foreach (SkinnedMeshRenderer val3 in componentsInChildren) { MaterialNames[((Object)val3).name] = ((Renderer)val3).sharedMaterials.Select((Material x) => (!((Object)(object)x != (Object)null)) ? "" : ((Object)x).name).ToArray(); } } Texture2D[] array3 = val.LoadAllAssets(); foreach (Texture2D val4 in array3) { Textures[((Object)val4).name] = val4; } AudioClip[] array4 = val.LoadAllAssets(); foreach (AudioClip val5 in array4) { string key = ((Object)val5).name.Split('_')[0]; if (!Voice.TryGetValue(key, out var value)) { value = (Voice[key] = new List()); } value.Add(val5); } if (Voice.Count > 0) { Log.LogInfo((object)("Voice lines: " + string.Join(", ", (from kv in Voice orderby kv.Key select kv.Key + " x" + kv.Value.Count).ToArray()))); } if (Meshes.Count == 0) { Log.LogError((object)"No pre-bound meshes in the bundle."); return false; } Log.LogInfo((object)($"Loaded {Meshes.Count} pre-bound meshes and {Textures.Count} textures: " + string.Join(", ", Meshes.Keys.ToArray()))); return true; } } [HarmonyPatch] internal static class FlowermanStartPatch { private static MethodBase TargetMethod() { return AccessTools.DeclaredMethod(typeof(FlowermanAI), "Start", (Type[])null, (Type[])null) ?? AccessTools.Method(typeof(EnemyAI), "Start", (Type[])null, (Type[])null); } private static void Postfix(EnemyAI __instance) { if (__instance is FlowermanAI && !((Object)(object)((Component)__instance).GetComponent() != (Object)null)) { ((Component)__instance).gameObject.AddComponent(); ((Component)__instance).gameObject.AddComponent(); } } } [Serializable] public class PoseFrame { public float[] r; public float[] p; } [Serializable] public class PoseClip { public string name; public float fps; public PoseFrame[] frames; } [Serializable] public class PoseRecording { public string[] bones; public PoseClip[] clips; } }