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 TheMimic v1.0.1
MimicMonster.dll
Decompiled 14 hours agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using HarmonyLib; using Microsoft.CodeAnalysis; using Photon.Pun; using Photon.Voice; using UnityEngine; using UnityEngine.AI; using UnityEngine.Events; using UnityEngine.Rendering; 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: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: AssemblyCompany("MimicMonster")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("MimicMonster")] [assembly: AssemblyTitle("MimicMonster")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [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; } } } public class EnemyMimic : MonoBehaviour { public enum State { Spawn, Idle, Roam, Stalk, SeekCorpse, Impersonate, Hunt, Jumpscare, Leave, Despawn, Stun } public bool debugSpawn; [Space] public State currentState; private bool stateImpulse; internal float stateTimer; [Space] public Enemy enemy; private PhotonView photonView; [Space] public SpringQuaternion horizontalRotationSpring; private Quaternion horizontalRotationTarget = Quaternion.identity; private float pitCheckTimer; private Vector3 leavePosition; [Header("Roam / Stalk")] public float roamSpeed = 3.5f; public float roamAcceleration = 8f; public float stalkSpeed = 2f; public float stalkAcceleration = 4f; public float lureHoldDistance = 8.5f; public float stalkTimeoutMax = 20f; private PlayerAvatar stalkTargetPlayer; private Vector3 stalkPoint; public float isolationWeight = 1f; public float proximityWeight = 0.6f; public float isolationDistanceCap = 30f; private float stalkTimer; [Header("SeekCorpse / Impersonate")] public float corpseDetectionRadius = 20f; public float corpseReachDistance = 1.5f; public float impersonateDuration = 2f; public Transform mimicVisualsRoot; public Transform disguiseRigParent; public GameObject disguiseRigTemplate; private GameObject disguiseRigInstance; private PlayerCosmetics disguiseCosmetics; private SkinnedMeshRenderer[] mimicRenderers; public float disguiseSwayCycle = 1.6f; public float disguiseSwayLeanDegrees = 3f; public float disguiseSwayBobMeters = 0.02f; private float disguiseSwayTime; private PlayerDeathHead corpseTarget; private static readonly HashSet<PlayerDeathHead> ClaimedHeads = new HashSet<PlayerDeathHead>(); [Header("Hunt")] public float huntSpeed = 4.5f; public float huntAcceleration = 10f; public float huntProximityRevealDistance = 2.5f; [Header("Voice Mimicry")] public float voiceLureMinInterval = 12f; public float voiceLureMaxInterval = 24f; private float voiceLureTimer; private PlayerAvatar huntTarget; [Header("Jumpscare")] public Transform jumpscareFaceTransform; public HurtCollider hurtCollider; private float hurtColliderTimer; public Sound soundJumpscareSting; public AudioClip[] footstepClips; public AudioClip[] heavyFootstepClips; public AudioClip[] servoClips; private AudioSource footstepSource; private float footstepTimer; private bool footstepAltServo; private Vector3 footLastPos; private bool footPosInit; private Vector3 jumpscareLockPos; private bool jumpscareLockSet; private Vector3 mimicJumpscareLockPos; private Coroutine jumpscareVictimCo; private Transform footBoneL; private Transform footBoneR; private float footPrevYL; private float footPrevYR; private bool footDescL; private bool footDescR; private float footCdL; private float footCdR; public float jumpscareAnimDuration = 1.2f; public float jumpscareWindupDuration = 1f; public string jumpscareAnimatorTrigger = "Jumpscare"; public Animator mimicAnimator; private PlayerAvatar jumpscareTarget; private bool jumpscareKillDone; private static bool loggedAnimParams; private Vector3 nativeLastPos; private bool nativeLastPosInit; private Transform jumpscareHeadBone; private static bool loggedAimBone; public bool debugForceSpawn; public float pounceRange = 7f; public float stalkDetectionRange = 22f; public static void ClearRunStatics() { ClaimedHeads.Clear(); } private void Awake() { photonView = ((Component)this).GetComponent<PhotonView>(); if (Object.op_Implicit((Object)(object)mimicVisualsRoot)) { mimicRenderers = ((Component)mimicVisualsRoot).GetComponentsInChildren<SkinnedMeshRenderer>(true); } } private void Start() { //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Expected O, but got Unknown //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Expected O, but got Unknown //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Expected O, but got Unknown //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Expected O, but got Unknown //IL_01b8: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)enemy == (Object)null) { enemy = ((Component)this).GetComponent<Enemy>(); } if ((Object)(object)enemy == (Object)null) { return; } if (enemy.HasStateSpawn && (Object)(object)enemy.StateSpawn != (Object)null && enemy.StateSpawn.OnSpawn != null) { enemy.StateSpawn.OnSpawn.AddListener(new UnityAction(OnSpawn)); } if (enemy.HasHealth && (Object)(object)enemy.Health != (Object)null) { if (enemy.Health.onDeath != null) { enemy.Health.onDeath.AddListener(new UnityAction(OnDeath)); } if (enemy.Health.onHurt != null) { enemy.Health.onHurt.AddListener(new UnityAction(OnHurt)); } } if (enemy.HasStateInvestigate && (Object)(object)enemy.StateInvestigate != (Object)null && enemy.StateInvestigate.onInvestigateTriggered != null) { enemy.StateInvestigate.onInvestigateTriggered.AddListener(new UnityAction(OnInvestigate)); } if (!loggedAnimParams && Object.op_Implicit((Object)(object)mimicAnimator) && (Object)(object)mimicAnimator.runtimeAnimatorController != (Object)null) { loggedAnimParams = true; StringBuilder stringBuilder = new StringBuilder("[EnemyMimic][AnimParams] native mimicAnimator params: "); AnimatorControllerParameter[] parameters = mimicAnimator.parameters; foreach (AnimatorControllerParameter val in parameters) { stringBuilder.Append(val.name).Append('(').Append(val.type) .Append(") "); } Debug.Log((object)stringBuilder.ToString(), (Object)(object)this); } if (Object.op_Implicit((Object)(object)mimicAnimator)) { mimicAnimator.cullingMode = (AnimatorCullingMode)0; } jumpscareWindupDuration = 3f; jumpscareAnimDuration = 0.6f; } private void Update() { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Invalid comparison between Unknown and I4 HurtColliderTimer(); DisguiseSwayLogic(); NativeLocomotionTick(); FootstepTick(); if (SemiFunc.IsMasterClientOrSingleplayer()) { if (enemy.IsStunned()) { UpdateState(State.Stun); } if ((int)enemy.CurrentState == 11 && !enemy.IsStunned()) { UpdateState(State.Despawn); } HorizontalRotationLogic(); VoiceLureTick(); switch (currentState) { case State.Spawn: StateSpawn(); break; case State.Idle: StateIdle(); break; case State.Roam: StateRoam(); break; case State.Stalk: StateStalk(); break; case State.SeekCorpse: StateSeekCorpse(); break; case State.Impersonate: StateImpersonate(); break; case State.Hunt: StateHunt(); break; case State.Jumpscare: StateJumpscare(); break; case State.Leave: StateLeave(); break; case State.Despawn: StateDespawn(); break; case State.Stun: StateStun(); break; } } } private void StateSpawn() { if (stateImpulse) { stateImpulse = false; stateTimer = 1f; } stateTimer -= Time.deltaTime; if (stateTimer <= 0f) { UpdateState(State.Idle); } } private void StateIdle() { //IL_003f: Unknown result type (might be due to invalid IL or missing references) if (stateImpulse) { stateImpulse = false; stateTimer = Random.Range(14f, 28f); enemy.NavMeshAgent.Warp(((Component)enemy.Rigidbody).transform.position, false); enemy.NavMeshAgent.ResetPath(); } if (SemiFunc.EnemySpawnIdlePause()) { return; } PlayerDeathHead val = TryFindCorpse(corpseDetectionRadius); if ((Object)(object)val != (Object)null) { ClaimCorpse(val); UpdateState(State.SeekCorpse); } else { if (PounceCheck()) { return; } PlayerAvatar val2 = DetectStalkTarget(); if ((Object)(object)val2 != (Object)null) { stalkTargetPlayer = val2; UpdateState(State.Stalk); return; } stateTimer -= Time.deltaTime; if (stateTimer <= 0f) { UpdateState(State.Roam); } if (SemiFunc.EnemyForceLeave(enemy)) { UpdateState(State.Leave); } } } private void StateRoam() { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0127: 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_0136: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: 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_009b: Unknown result type (might be due to invalid IL or missing references) if (stateImpulse) { stateImpulse = false; stateTimer = 1f; bool flag = false; LevelPoint val = null; float num = -1f; for (int i = 0; i < 6; i++) { LevelPoint val2 = SemiFunc.LevelPointGet(((Component)this).transform.position, 8f, 40f); if (!Object.op_Implicit((Object)(object)val2)) { continue; } float num2 = float.MaxValue; foreach (PlayerAvatar item in SemiFunc.PlayerGetList()) { if (!((Object)(object)item == (Object)null) && !item.isDisabled) { float num3 = Vector3.Distance(((Component)val2).transform.position, ((Component)item).transform.position); if (num3 < num2) { num2 = num3; } } } if (num2 == float.MaxValue) { num2 = 999f; } if (num2 > num) { num = num2; val = val2; } } if (!Object.op_Implicit((Object)(object)val)) { val = SemiFunc.LevelPointGet(((Component)this).transform.position, 0f, 999f); } NavMeshHit val3 = default(NavMeshHit); if (Object.op_Implicit((Object)(object)val) && NavMesh.SamplePosition(((Component)val).transform.position + Random.insideUnitSphere * 3f, ref val3, 5f, -1) && Physics.Raycast(((NavMeshHit)(ref val3)).position, Vector3.down, 5f, LayerMask.GetMask(new string[1] { "Default" }))) { enemy.NavMeshAgent.UpdateAgent(roamSpeed, roamAcceleration); enemy.NavMeshAgent.SetDestination(((NavMeshHit)(ref val3)).position); flag = true; } if (!flag) { return; } pitCheckTimer = 0.1f; enemy.Rigidbody.notMovingTimer = 0f; } else { PlayerDeathHead val4 = TryFindCorpse(corpseDetectionRadius); if ((Object)(object)val4 != (Object)null) { ClaimCorpse(val4); UpdateState(State.SeekCorpse); return; } if (enemy.Rigidbody.notMovingTimer > 1f) { stateTimer -= Time.deltaTime; } if (PitCheckLogic()) { enemy.NavMeshAgent.ResetPath(); } if (stateTimer <= 0f || !enemy.NavMeshAgent.HasPath()) { UpdateState(State.Idle); } } if (SemiFunc.EnemyForceLeave(enemy)) { UpdateState(State.Leave); } } private void StateStalk() { //IL_0129: 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) //IL_01d3: Unknown result type (might be due to invalid IL or missing references) //IL_01d8: Unknown result type (might be due to invalid IL or missing references) //IL_01e0: Unknown result type (might be due to invalid IL or missing references) //IL_01e5: Unknown result type (might be due to invalid IL or missing references) //IL_01e7: Unknown result type (might be due to invalid IL or missing references) //IL_01ec: Unknown result type (might be due to invalid IL or missing references) //IL_0215: Unknown result type (might be due to invalid IL or missing references) //IL_0219: Unknown result type (might be due to invalid IL or missing references) //IL_0224: Unknown result type (might be due to invalid IL or missing references) //IL_0229: Unknown result type (might be due to invalid IL or missing references) //IL_022e: 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_018a: Unknown result type (might be due to invalid IL or missing references) //IL_023a: Unknown result type (might be due to invalid IL or missing references) //IL_01c0: Unknown result type (might be due to invalid IL or missing references) //IL_01b3: Unknown result type (might be due to invalid IL or missing references) //IL_01c5: Unknown result type (might be due to invalid IL or missing references) if (stateImpulse) { stateImpulse = false; stateTimer = 1f; stalkTimer = stalkTimeoutMax; enemy.NavMeshAgent.UpdateAgent(stalkSpeed, stalkAcceleration); pitCheckTimer = 0.1f; enemy.Rigidbody.notMovingTimer = 0f; voiceLureTimer = Random.Range(2.5f, 4.5f); } if (PounceCheck()) { return; } if (!Object.op_Implicit((Object)(object)stalkTargetPlayer) || stalkTargetPlayer.isDisabled) { PlayerDeathHead val = TryFindCorpse(corpseDetectionRadius); if ((Object)(object)val != (Object)null) { ClaimCorpse(val); UpdateState(State.SeekCorpse); } else { UpdateState(State.Roam); } return; } PlayerDeathHead val2 = TryFindCorpse(corpseDetectionRadius); if ((Object)(object)val2 != (Object)null) { ClaimCorpse(val2); UpdateState(State.SeekCorpse); return; } stalkTimer -= Time.deltaTime; if (stalkTimer <= 0f) { UpdateState(State.Roam); return; } if (SemiFunc.PerSecond(1f, (Object)(object)this)) { if (Vector3.Distance(((Component)this).transform.position, ((Component)stalkTargetPlayer).transform.position) <= stalkDetectionRange && IsIsolated(stalkTargetPlayer)) { stalkTimer = stalkTimeoutMax; } Vector3 destination; if (enemy.HasOnScreen && enemy.OnScreen.OnScreenAny) { LevelPoint val3 = SemiFunc.LevelPointGetPlayerDistance(((Component)this).transform.position, 8f, 20f, false); destination = (Object.op_Implicit((Object)(object)val3) ? ((Component)val3).transform.position : ((Component)stalkTargetPlayer).transform.position); } else { Vector3 position = ((Component)stalkTargetPlayer).transform.position; Vector3 val4 = ((Component)this).transform.position - position; val4.y = 0f; if (((Vector3)(ref val4)).sqrMagnitude < 0.01f) { val4 = ((Component)this).transform.forward; } destination = position + ((Vector3)(ref val4)).normalized * lureHoldDistance; } enemy.NavMeshAgent.SetDestination(destination); } if (enemy.Rigidbody.notMovingTimer > 1f) { stateTimer -= Time.deltaTime; } PitCheckLogic(); if (SemiFunc.EnemyForceLeave(enemy)) { UpdateState(State.Leave); } } private void StateSeekCorpse() { //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_0184: Unknown result type (might be due to invalid IL or missing references) if (stateImpulse) { stateImpulse = false; stateTimer = 8f; if ((Object)(object)corpseTarget == (Object)null) { corpseTarget = TryFindCorpse(corpseDetectionRadius); if ((Object)(object)corpseTarget != (Object)null) { ClaimCorpse(corpseTarget); } } if ((Object)(object)corpseTarget == (Object)null) { UpdateState(State.Idle); return; } enemy.NavMeshAgent.UpdateAgent(huntSpeed, huntAcceleration); enemy.NavMeshAgent.SetDestination(((Component)corpseTarget).transform.position); pitCheckTimer = 0.1f; enemy.Rigidbody.notMovingTimer = 0f; return; } if ((Object)(object)corpseTarget == (Object)null || !corpseTarget.triggered || corpseTarget.overrideSpectated) { ReleaseCorpseClaim(); UpdateState(State.Idle); return; } if (SemiFunc.PerSecond(2f, (Object)(object)this)) { enemy.NavMeshAgent.SetDestination(((Component)corpseTarget).transform.position); } if (enemy.Rigidbody.notMovingTimer > 1f) { stateTimer -= Time.deltaTime; } if (PitCheckLogic()) { enemy.NavMeshAgent.ResetPath(); } if (Vector3.Distance(((Component)this).transform.position, ((Component)corpseTarget).transform.position) <= corpseReachDistance) { UpdateState(State.Impersonate); } else if (stateTimer <= 0f || !enemy.NavMeshAgent.HasPath()) { ReleaseCorpseClaim(); UpdateState(State.Idle); } } private void StateImpersonate() { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) if (stateImpulse) { stateImpulse = false; stateTimer = impersonateDuration; enemy.NavMeshAgent.Warp(((Component)enemy.Rigidbody).transform.position, false); enemy.NavMeshAgent.ResetPath(); if ((Object)(object)corpseTarget == (Object)null || (Object)(object)corpseTarget.playerAvatar == (Object)null || (Object)(object)corpseTarget.playerCosmetics == (Object)null) { ReleaseCorpseClaim(); UpdateState(State.Idle); return; } List<int> cosmeticEquippedRaw = corpseTarget.playerCosmetics.cosmeticEquippedRaw; int[] colorsEquipped = corpseTarget.playerCosmetics.colorsEquipped; if (SemiFunc.IsMultiplayer()) { photonView.RPC("ImpersonateRPC", (RpcTarget)0, new object[2] { cosmeticEquippedRaw.ToArray(), colorsEquipped }); } else { ImpersonateRPC(cosmeticEquippedRaw.ToArray(), colorsEquipped); } } else { stateTimer -= Time.deltaTime; if (stateTimer <= 0f) { UpdateState(State.Hunt); } } } [PunRPC] private void ImpersonateRPC(int[] cosmeticIds, int[] colorIds, PhotonMessageInfo info = default(PhotonMessageInfo)) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) if (SemiFunc.MasterOnlyRPC(info)) { if (Object.op_Implicit((Object)(object)mimicVisualsRoot)) { ((Component)mimicVisualsRoot).gameObject.SetActive(false); } SetupDisguiseRig(new List<int>(cosmeticIds), colorIds); } } private void SetupDisguiseRig(List<int> cosmeticIds, int[] colorIds) { //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) Transform val = (((Object)(object)disguiseRigParent != (Object)null) ? disguiseRigParent : (((Object)(object)mimicVisualsRoot != (Object)null && (Object)(object)mimicVisualsRoot.parent != (Object)null) ? mimicVisualsRoot.parent : ((Component)this).transform)); GameObject val2 = disguiseRigTemplate; if ((Object)(object)val2 == (Object)null) { val2 = FindLiveAvatarVisualsRig(); } if ((Object)(object)val2 == (Object)null || (Object)(object)val == (Object)null) { Debug.LogWarning((object)"EnemyMimic: no disguise rig template and no live player rig to clone - Impersonate cannot build a disguise.", (Object)(object)this); return; } try { if (!Object.op_Implicit((Object)(object)disguiseRigInstance)) { disguiseRigParent = val; ((Component)disguiseRigParent).gameObject.SetActive(false); disguiseRigInstance = Object.Instantiate<GameObject>(val2, disguiseRigParent); disguiseRigInstance.transform.localPosition = Vector3.zero; disguiseRigInstance.transform.localRotation = Quaternion.identity; PhotonView[] componentsInChildren = disguiseRigInstance.GetComponentsInChildren<PhotonView>(true); for (int i = 0; i < componentsInChildren.Length; i++) { Object.Destroy((Object)(object)componentsInChildren[i]); } PlayerNameChecker[] componentsInChildren2 = disguiseRigInstance.GetComponentsInChildren<PlayerNameChecker>(true); foreach (PlayerNameChecker val3 in componentsInChildren2) { if ((Object)(object)val3 != (Object)null) { Object.Destroy((Object)(object)val3); } } PlayerAvatarVisuals[] componentsInChildren3 = disguiseRigInstance.GetComponentsInChildren<PlayerAvatarVisuals>(true); foreach (PlayerAvatarVisuals val4 in componentsInChildren3) { if ((Object)(object)val4 != (Object)null) { ((Behaviour)val4).enabled = false; val4.playerAvatar = null; } } disguiseCosmetics = disguiseRigInstance.GetComponentInChildren<PlayerCosmetics>(true); if ((Object)(object)disguiseCosmetics != (Object)null) { disguiseCosmetics.firstSetup = false; } ((Component)disguiseRigParent).gameObject.SetActive(true); if ((Object)(object)disguiseRigInstance.GetComponent<MimicDisguiseAnimDriver>() == (Object)null) { disguiseRigInstance.AddComponent<MimicDisguiseAnimDriver>(); } } if (!Object.op_Implicit((Object)(object)disguiseCosmetics)) { Debug.LogError((object)"EnemyMimic: disguiseRigTemplate has no PlayerCosmetics component anywhere in its hierarchy - cannot apply corpse identity.", (Object)(object)this); return; } if (disguiseCosmetics.cosmeticParents == null || disguiseCosmetics.cosmeticParents.Count == 0) { Debug.LogError((object)"EnemyMimic: disguiseRigTemplate's PlayerCosmetics.cosmeticParents is empty - SetupCosmetics will NRE inside InstantiateCosmetic() (confirmed failure mode, DECISIONS.md 21:5x). Wire cosmeticParents on the template prefab in the Editor before Impersonate can work.", (Object)(object)this); return; } disguiseCosmetics.SetupCosmetics(false, true, cosmeticIds); disguiseCosmetics.SetupColors(false, colorIds); Renderer[] componentsInChildren4 = disguiseRigInstance.GetComponentsInChildren<Renderer>(true); foreach (Renderer val5 in componentsInChildren4) { if (!((Object)(object)val5 == (Object)null)) { val5.shadowCastingMode = (ShadowCastingMode)1; val5.forceRenderingOff = false; } } if (Object.op_Implicit((Object)(object)disguiseRigInstance)) { disguiseRigInstance.SetActive(true); } } catch (Exception ex) { Debug.LogError((object)("EnemyMimic: disguise rig setup threw (cloned-rig detached-state?) - disguise skipped this attempt: " + ex)); } } private GameObject FindLiveAvatarVisualsRig() { PlayerAvatar[] array = Object.FindObjectsOfType<PlayerAvatar>(); foreach (PlayerAvatar val in array) { if (!((Object)(object)val == (Object)null)) { PlayerCosmetics componentInChildren = ((Component)val).GetComponentInChildren<PlayerCosmetics>(true); if ((Object)(object)componentInChildren != (Object)null && componentInChildren.cosmeticParents != null && componentInChildren.cosmeticParents.Count > 0 && (Object)(object)componentInChildren.playerAvatarVisuals != (Object)null) { return ((Component)componentInChildren.playerAvatarVisuals).gameObject; } } } return null; } public void DebugForceVoiceLure() { //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) PlayerAvatar instance = PlayerAvatar.instance; if ((Object)(object)instance == (Object)null || (Object)(object)instance.photonView == (Object)null) { Debug.LogError((object)"[EnemyMimic] DebugForceVoiceLure: no local PlayerAvatar."); return; } int num = 0; foreach (MimicVoiceCapture item in MimicVoiceCapture.All) { if ((Object)(object)item != (Object)null && item.IsLocalOwned) { num = item.SnippetCount + item.ChatLineCount; break; } } Debug.Log((object)$"[EnemyMimic] DebugForceVoiceLure: local='{((Object)instance).name}' has {num} captured snippet(s)/chat-line(s). Requesting playback from Mimic view {photonView.ViewID}. If 0, talk into your mic (or send a TTS-chat line) first, then press again."); if (num == 0) { foreach (MimicVoiceCapture item2 in MimicVoiceCapture.All) { if ((Object)(object)item2 != (Object)null && item2.IsLocalOwned) { item2.InjectDebugSnippet(); break; } } Debug.Log((object)"[EnemyMimic] DebugForceVoiceLure: no real captured voice -> injected a synthetic test snippet so the Mimic-body playback can be verified without a mic."); } int viewID = photonView.ViewID; int viewID2 = instance.photonView.ViewID; MimicVoicePlayback.SoloFallbackMimic = ((Component)this).gameObject; if (SemiFunc.IsMultiplayer()) { photonView.RPC("VoiceLureRequestRPC", (RpcTarget)0, new object[2] { viewID2, viewID }); } else { VoiceLureRequestRPC(viewID2, viewID); } } public void DebugForceTTSLure() { //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) PlayerAvatar instance = PlayerAvatar.instance; if ((Object)(object)instance == (Object)null || (Object)(object)instance.photonView == (Object)null) { Debug.LogError((object)"[EnemyMimic] DebugForceTTSLure: no local PlayerAvatar."); return; } foreach (MimicVoiceCapture item in MimicVoiceCapture.All) { if ((Object)(object)item != (Object)null && item.IsLocalOwned) { item.InjectDebugChatLine(); break; } } int viewID = photonView.ViewID; int viewID2 = instance.photonView.ViewID; MimicVoicePlayback.SoloFallbackMimic = ((Component)this).gameObject; Debug.Log((object)$"[EnemyMimic] DebugForceTTSLure: forcing a TTS-chat fallback lure from Mimic view {viewID}."); if (SemiFunc.IsMultiplayer()) { photonView.RPC("VoiceLureRequestRPC", (RpcTarget)0, new object[2] { viewID2, viewID }); } else { VoiceLureRequestRPC(viewID2, viewID); } } public void DebugForceImpersonate(PlayerAvatar targetPlayer) { if ((Object)(object)targetPlayer == (Object)null || (Object)(object)targetPlayer.playerCosmetics == (Object)null) { Debug.LogError((object)"EnemyMimic.DebugForceImpersonate: targetPlayer or its playerCosmetics is null - cannot force a disguise.", (Object)(object)this); return; } List<int> cosmeticEquippedRaw = targetPlayer.playerCosmetics.cosmeticEquippedRaw; int[] colorsEquipped = targetPlayer.playerCosmetics.colorsEquipped; if (Object.op_Implicit((Object)(object)mimicVisualsRoot)) { ((Component)mimicVisualsRoot).gameObject.SetActive(false); } SetupDisguiseRig(cosmeticEquippedRaw, colorsEquipped); currentState = State.Impersonate; stateImpulse = false; stateTimer = impersonateDuration; string text = "(no animator)"; if (Object.op_Implicit((Object)(object)mimicAnimator)) { AnimatorClipInfo[] currentAnimatorClipInfo = mimicAnimator.GetCurrentAnimatorClipInfo(0); text = ((currentAnimatorClipInfo.Length != 0 && (Object)(object)((AnimatorClipInfo)(ref currentAnimatorClipInfo[0])).clip != (Object)null) ? ((Object)((AnimatorClipInfo)(ref currentAnimatorClipInfo[0])).clip).name : "(no clip)"); } int num = (((Object)(object)disguiseCosmetics != (Object)null && disguiseCosmetics.cosmeticEquipped != null) ? disguiseCosmetics.cosmeticEquipped.Count : 0); Debug.Log((object)$"[EnemyMimic][DebugForceImpersonate] target='{((Object)targetPlayer).name}' rigInstantiated={(Object)(object)disguiseRigInstance != (Object)null} cosmeticsComponentPresent={(Object)(object)disguiseCosmetics != (Object)null} cosmeticIdsRead={cosmeticEquippedRaw?.Count ?? 0} cosmeticsActuallyApplied={num} animatorClip={text} nextTransition=Hunt in {impersonateDuration:F1}s (then proximity<={huntProximityRevealDistance:F1}m auto-triggers Jumpscare)", (Object)(object)this); } private void StateHunt() { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0037: 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_00c3: 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_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Unknown result type (might be due to invalid IL or missing references) if (stateImpulse) { stateImpulse = false; stateTimer = 1f; huntTarget = SemiFunc.PlayerGetNearestPlayerAvatarWithinRange(999f, ((Component)this).transform.position, false, default(LayerMask)); if ((Object)(object)huntTarget == (Object)null) { UpdateState(State.Leave); return; } enemy.NavMeshAgent.UpdateAgent(huntSpeed, huntAcceleration); pitCheckTimer = 0.1f; enemy.Rigidbody.notMovingTimer = 0f; } if ((Object)(object)huntTarget == (Object)null || huntTarget.isDisabled) { huntTarget = SemiFunc.PlayerGetNearestPlayerAvatarWithinRange(999f, ((Component)this).transform.position, false, default(LayerMask)); if ((Object)(object)huntTarget == (Object)null) { UpdateState(State.Leave); return; } } enemy.NavMeshAgent.SetDestination(((Component)huntTarget).transform.position); if (enemy.Rigidbody.notMovingTimer > 1f) { stateTimer -= Time.deltaTime; } if (PitCheckLogic()) { enemy.NavMeshAgent.ResetPath(); } if (Vector3.Distance(((Component)this).transform.position, ((Component)huntTarget).transform.position) <= huntProximityRevealDistance) { jumpscareTarget = huntTarget; UpdateState(State.Jumpscare); } else if (SemiFunc.EnemyForceLeave(enemy)) { UpdateState(State.Leave); } } private void StateJumpscare() { //IL_023c: Unknown result type (might be due to invalid IL or missing references) //IL_024e: Unknown result type (might be due to invalid IL or missing references) //IL_0040: 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_00b8: 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) //IL_02e7: Unknown result type (might be due to invalid IL or missing references) //IL_02ed: 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_011d: 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_0127: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_0158: 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_0160: Unknown result type (might be due to invalid IL or missing references) //IL_0170: 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_0176: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_01af: Unknown result type (might be due to invalid IL or missing references) //IL_0200: Unknown result type (might be due to invalid IL or missing references) //IL_0206: Unknown result type (might be due to invalid IL or missing references) if (stateImpulse) { stateImpulse = false; stateTimer = jumpscareWindupDuration; jumpscareKillDone = false; enemy.NavMeshAgent.Warp(((Component)enemy.Rigidbody).transform.position, false); enemy.NavMeshAgent.ResetPath(); if ((Object)(object)jumpscareTarget == (Object)null || jumpscareTarget.isDisabled) { UpdateState(State.Hunt); return; } Vector3 val = ((jumpscareTarget.isLocal && Object.op_Implicit((Object)(object)Camera.main)) ? ((Component)Camera.main).transform.forward : ((Component)jumpscareTarget).transform.forward); val.y = 0f; if (((Vector3)(ref val)).sqrMagnitude < 0.01f) { val = ((Component)this).transform.position - ((Component)jumpscareTarget).transform.position; } val.y = 0f; ((Vector3)(ref val)).Normalize(); Vector3 val2 = ((Component)jumpscareTarget).transform.position + val * 1.7f; NavMeshHit val3 = default(NavMeshHit); if (NavMesh.SamplePosition(val2, ref val3, 2.5f, -1)) { val2 = ((NavMeshHit)(ref val3)).position; } enemy.NavMeshAgent.Warp(val2, false); ((Component)this).transform.position = val2; mimicJumpscareLockPos = val2; Vector3 val4 = ((Component)jumpscareTarget).transform.position - val2; val4.y = 0f; if (((Vector3)(ref val4)).sqrMagnitude > 0.001f) { horizontalRotationTarget = Quaternion.LookRotation(((Vector3)(ref val4)).normalized); ((Component)this).transform.rotation = horizontalRotationTarget; } jumpscareLockSet = false; int viewID = jumpscareTarget.photonView.ViewID; if (SemiFunc.IsMultiplayer()) { photonView.RPC("JumpscarePresentationRPC", (RpcTarget)0, new object[1] { viewID }); } else { JumpscarePresentationRPC(viewID); } return; } stateTimer -= Time.deltaTime; enemy.NavMeshAgent.ResetPath(); enemy.NavMeshAgent.Warp(mimicJumpscareLockPos, false); ((Component)this).transform.position = mimicJumpscareLockPos; if (stateTimer > 0f) { return; } if (!jumpscareKillDone) { jumpscareKillDone = true; stateTimer = jumpscareAnimDuration; if ((Object)(object)jumpscareTarget == (Object)null || jumpscareTarget.isDisabled) { UpdateState(State.Hunt); return; } int viewID2 = jumpscareTarget.photonView.ViewID; if (SemiFunc.IsMultiplayer()) { photonView.RPC("JumpscareKillRPC", (RpcTarget)0, new object[1] { viewID2 }); } else { JumpscareKillRPC(viewID2); } } else { if (Object.op_Implicit((Object)(object)hurtCollider)) { ((Component)hurtCollider).gameObject.SetActive(false); } ReleaseCorpseClaim(); corpseTarget = null; UpdateState(State.SeekCorpse); } } [PunRPC] private void JumpscarePresentationRPC(int targetViewId, PhotonMessageInfo info = default(PhotonMessageInfo)) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: 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_020d: Unknown result type (might be due to invalid IL or missing references) //IL_01e8: Unknown result type (might be due to invalid IL or missing references) if (!SemiFunc.MasterOnlyRPC(info)) { return; } PlayerAvatar val = null; foreach (PlayerAvatar item in SemiFunc.PlayerGetList()) { if (item.photonView.ViewID == targetViewId) { val = item; break; } } jumpscareTarget = val; if ((Object)(object)val == (Object)null) { return; } Vector3 jumpscareAimPoint = GetJumpscareAimPoint(); if (val.isLocal && Object.op_Implicit((Object)(object)CameraAim.Instance)) { CameraAim.Instance.AimTargetSet(jumpscareAimPoint, jumpscareWindupDuration + jumpscareAnimDuration + 0.3f, 90f, ((Component)this).gameObject, 100); CameraAim.Instance.OverrideAimStop(); if (jumpscareVictimCo != null) { ((MonoBehaviour)this).StopCoroutine(jumpscareVictimCo); } jumpscareLockSet = false; jumpscareVictimCo = ((MonoBehaviour)this).StartCoroutine(JumpscareVictimDriveCo()); } if (Object.op_Implicit((Object)(object)mimicVisualsRoot)) { ((Component)mimicVisualsRoot).gameObject.SetActive(true); } if (Object.op_Implicit((Object)(object)disguiseRigInstance)) { disguiseRigInstance.SetActive(false); } if (Object.op_Implicit((Object)(object)mimicAnimator) && !string.IsNullOrEmpty(jumpscareAnimatorTrigger)) { mimicAnimator.cullingMode = (AnimatorCullingMode)0; mimicAnimator.speed = 1f; mimicAnimator.ResetTrigger(jumpscareAnimatorTrigger); mimicAnimator.Play("Jumpscare", 0, 0f); mimicAnimator.Update(0f); mimicAnimator.SetTrigger(jumpscareAnimatorTrigger); string arg = "(none)"; AnimatorClipInfo[] currentAnimatorClipInfo = mimicAnimator.GetCurrentAnimatorClipInfo(0); if (currentAnimatorClipInfo != null && currentAnimatorClipInfo.Length != 0 && (Object)(object)((AnimatorClipInfo)(ref currentAnimatorClipInfo[0])).clip != (Object)null) { arg = ((Object)((AnimatorClipInfo)(ref currentAnimatorClipInfo[0])).clip).name; } Debug.Log((object)$"[EnemyMimic][Jumpscare] revealed native model, played Jumpscare (resulting clip='{arg}') trigger='{jumpscareAnimatorTrigger}' aimPoint={jumpscareAimPoint}", (Object)(object)this); } if (soundJumpscareSting != null) { soundJumpscareSting.Play(((Component)this).transform.position, 1f, 1f, 1f, 1f); } } private Vector3 GetJumpscareAimPoint() { //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)jumpscareHeadBone == (Object)null) { jumpscareHeadBone = FindDeepChild(((Object)(object)mimicVisualsRoot != (Object)null) ? mimicVisualsRoot : ((Component)this).transform, "head_jnt"); if ((Object)(object)jumpscareHeadBone == (Object)null) { jumpscareHeadBone = FindDeepChild(((Component)this).transform, "head_jnt"); } } if (!loggedAimBone) { loggedAimBone = true; Debug.Log((object)($"[EnemyMimic][AimBone] head_jnt found={(Object)(object)jumpscareHeadBone != (Object)null} " + (((Object)(object)jumpscareHeadBone != (Object)null) ? $"pos={jumpscareHeadBone.position}" : "(falling back)"))); } if (Object.op_Implicit((Object)(object)jumpscareHeadBone)) { return jumpscareHeadBone.position; } if (Object.op_Implicit((Object)(object)jumpscareFaceTransform)) { return jumpscareFaceTransform.position; } return ((Component)this).transform.position + Vector3.up * 1.7f; } private static Transform FindDeepChild(Transform root, string name) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Expected O, but got Unknown if ((Object)(object)root == (Object)null) { return null; } if (((Object)root).name == name) { return root; } foreach (Transform item in root) { Transform val = FindDeepChild(item, name); if ((Object)(object)val != (Object)null) { return val; } } return null; } private IEnumerator JumpscareVictimDriveCo() { float t = jumpscareWindupDuration + jumpscareAnimDuration + 0.1f; while (t > 0f) { t -= Time.deltaTime; if (Object.op_Implicit((Object)(object)CameraAim.Instance)) { CameraAim.Instance.OverrideAimStop(); CameraAim.Instance.AimTargetSet(GetJumpscareAimPoint(), 0.25f, 90f, ((Component)this).gameObject, 100); } if (Object.op_Implicit((Object)(object)PlayerController.instance) && Object.op_Implicit((Object)(object)PlayerController.instance.rb)) { Rigidbody rb = PlayerController.instance.rb; if (!jumpscareLockSet) { jumpscareLockPos = rb.position; jumpscareLockSet = true; } Vector3 position = rb.position; position.x = jumpscareLockPos.x; position.z = jumpscareLockPos.z; if (Object.op_Implicit((Object)(object)jumpscareHeadBone) && Object.op_Implicit((Object)(object)jumpscareTarget) && Object.op_Implicit((Object)(object)jumpscareTarget.PlayerVisionTarget)) { float y = jumpscareTarget.PlayerVisionTarget.VisionTransform.position.y; float num = jumpscareHeadBone.position.y - y; if (num > 0.03f) { position.y += Mathf.Min(num, 2f) * 0.4f; } } rb.position = position; rb.velocity = Vector3.zero; } yield return null; } jumpscareVictimCo = null; } [PunRPC] private void JumpscareKillRPC(int targetViewId, PhotonMessageInfo info = default(PhotonMessageInfo)) { //IL_0000: 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_0096: 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_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: 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) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) if (!SemiFunc.MasterOnlyRPC(info)) { return; } PlayerAvatar val = null; foreach (PlayerAvatar item in SemiFunc.PlayerGetList()) { if (item.photonView.ViewID == targetViewId) { val = item; break; } } if (!((Object)(object)val == (Object)null)) { if (Object.op_Implicit((Object)(object)hurtCollider)) { ((Component)hurtCollider).transform.position = val.PlayerVisionTarget.VisionTransform.position; Transform transform = ((Component)hurtCollider).transform; Vector3 val2 = ((Component)val).transform.position - ((Component)this).transform.position; transform.rotation = Quaternion.LookRotation(((Vector3)(ref val2)).normalized); ((Component)hurtCollider).gameObject.SetActive(true); hurtColliderTimer = jumpscareAnimDuration; Debug.Log((object)$"[EnemyMimic][JumpscareKill] hurtCollider ENABLED at {((Component)hurtCollider).transform.position} vs target '{((Object)val).name}' (playerKill collider fired; expect PlayerHurt/death this frame)", (Object)(object)this); } else { Debug.LogError((object)"[EnemyMimic][JumpscareKill] hurtCollider is NULL - prefab has no playerKill HurtCollider child, so NO KILL will land. This is the C3 gap."); } } } private void StateLeave() { //IL_0115: 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_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_0171: 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_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_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) if (stateImpulse) { enemy.NavMeshAgent.Warp(((Component)enemy.Rigidbody).transform.position, false); enemy.NavMeshAgent.ResetPath(); if (!enemy.EnemyParent.playerClose) { UpdateState(State.Idle); return; } bool flag = false; LevelPoint val = SemiFunc.LevelPointGetPlayerDistance(((Component)this).transform.position, 25f, 50f, false); if (!Object.op_Implicit((Object)(object)val)) { val = SemiFunc.LevelPointGetFurthestFromPlayer(((Component)this).transform.position, 5f); } NavMeshHit val2 = default(NavMeshHit); if (Object.op_Implicit((Object)(object)val) && NavMesh.SamplePosition(((Component)val).transform.position, ref val2, 5f, -1) && Physics.Raycast(((NavMeshHit)(ref val2)).position, Vector3.down, 5f, LayerMask.GetMask(new string[1] { "Default" }))) { leavePosition = ((NavMeshHit)(ref val2)).position; flag = true; } if (!flag) { return; } stateImpulse = false; stateTimer = 5f; } enemy.NavMeshAgent.SetDestination(leavePosition); if (enemy.Rigidbody.notMovingTimer > 2f) { stateTimer -= Time.deltaTime; } if (PitCheckLogic()) { stateImpulse = true; } else if (stateTimer <= 0f || Vector3.Distance(((Component)this).transform.position, leavePosition) < 1f) { UpdateState(State.Idle); } } private void StateDespawn() { //IL_002a: Unknown result type (might be due to invalid IL or missing references) if (stateImpulse) { stateImpulse = false; enemy.NavMeshAgent.Warp(((Component)enemy.Rigidbody).transform.position, false); enemy.NavMeshAgent.ResetPath(); ReleaseCorpseClaim(); } } private void StateStun() { if (!enemy.IsStunned()) { UpdateState(State.Idle); } } public void OnSpawn() { //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) if (!debugForceSpawn && SemiFunc.IsMasterClientOrSingleplayer() && SemiFunc.PlayerGetAll().Count < 2) { Debug.Log((object)"[EnemyMimic] no-solo-spawn: <2 players, despawning this natural spawn (design: no solo spawn).", (Object)(object)this); enemy.EnemyParent.Despawn(); } else if (SemiFunc.IsMasterClientOrSingleplayer() && SemiFunc.EnemySpawn(enemy)) { if (SemiFunc.IsMultiplayer()) { photonView.RPC("ResetVisualsRPC", (RpcTarget)0, Array.Empty<object>()); } else { ResetVisualsRPC(); } ReleaseCorpseClaim(); UpdateState(State.Spawn); } } [PunRPC] private void ResetVisualsRPC(PhotonMessageInfo info = default(PhotonMessageInfo)) { if (Object.op_Implicit((Object)(object)mimicVisualsRoot)) { ((Component)mimicVisualsRoot).gameObject.SetActive(true); } if (Object.op_Implicit((Object)(object)disguiseRigInstance)) { Object.Destroy((Object)(object)disguiseRigInstance); disguiseRigInstance = null; disguiseCosmetics = null; } } public void OnHurt() { if (Object.op_Implicit((Object)(object)enemy) && enemy.HasHealth && (Object)(object)enemy.Health != (Object)null) { Debug.Log((object)$"[EnemyMimic][OnHurt] healthCurrent={enemy.Health.healthCurrent}/{enemy.Health.health} (damage landed - Mimic IS weapon-vulnerable)", (Object)(object)this); } if (SemiFunc.IsMasterClientOrSingleplayer() && currentState == State.Leave) { UpdateState(State.Idle); } } public void OnDeath() { if (SemiFunc.IsMasterClientOrSingleplayer()) { ReleaseCorpseClaim(); enemy.EnemyParent.Despawn(); } } public void OnInvestigate() { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) if (SemiFunc.IsMasterClientOrSingleplayer() && enemy.Rigidbody.timeSinceStun > 1.5f && (currentState == State.Idle || currentState == State.Roam || currentState == State.Stalk)) { stalkPoint = enemy.StateInvestigate.onInvestigateTriggeredPosition; stalkTargetPlayer = FindPlayerNearPoint(stalkPoint); if ((Object)(object)stalkTargetPlayer != (Object)null) { UpdateState(State.Stalk); } } } private void UpdateState(State _state) { //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) if (SemiFunc.IsMasterClientOrSingleplayer() && _state != currentState) { Debug.Log((object)$"[EnemyMimic][State] {currentState} -> {_state} (view {photonView.ViewID}, disguised={Object.op_Implicit((Object)(object)disguiseRigInstance)})"); currentState = _state; stateImpulse = true; stateTimer = 0f; if (GameManager.Multiplayer()) { photonView.RPC("UpdateStateRPC", (RpcTarget)0, new object[1] { currentState }); } else { UpdateStateRPC(currentState); } } } [PunRPC] private void UpdateStateRPC(State _state, PhotonMessageInfo _info = default(PhotonMessageInfo)) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) if (SemiFunc.MasterOnlyRPC(_info)) { currentState = _state; } } private void HorizontalRotationLogic() { //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: 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_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_005e: Unknown result type (might be due to invalid IL or missing references) //IL_017b: 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_012f: 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) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) if (currentState == State.Jumpscare && Object.op_Implicit((Object)(object)jumpscareTarget)) { Vector3 val = ((Component)jumpscareTarget).transform.position - ((Component)this).transform.position; val.y = 0f; if (((Vector3)(ref val)).sqrMagnitude > 0.0001f) { horizontalRotationTarget = Quaternion.LookRotation(((Vector3)(ref val)).normalized); } horizontalRotationSpring.speed = 25f; horizontalRotationSpring.damping = 0.85f; } else { Vector3 val2 = Vector3.zero; if ((Object)(object)enemy.NavMeshAgent != (Object)null && (Object)(object)enemy.NavMeshAgent.Agent != (Object)null && enemy.NavMeshAgent.Agent.isOnNavMesh) { val2 = enemy.NavMeshAgent.Agent.desiredVelocity; } if (((Vector3)(ref val2)).magnitude < 0.5f && enemy.HasRigidbody) { val2 = enemy.Rigidbody.velocity; } val2.y = 0f; if (((Vector3)(ref val2)).magnitude > 0.6f) { horizontalRotationTarget = Quaternion.LookRotation(((Vector3)(ref val2)).normalized); horizontalRotationSpring.speed = ((currentState == State.Stalk) ? 4f : 6f); horizontalRotationSpring.damping = 0.7f; } } ((Component)this).transform.rotation = SemiFunc.SpringQuaternionGet(horizontalRotationSpring, horizontalRotationTarget, -1f); } private bool PitCheckLogic() { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0052: 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_006a: 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_0075: 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_0089: Unknown result type (might be due to invalid IL or missing references) //IL_0093: 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_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) if (pitCheckTimer <= 0f) { Vector3 normalized = ((Vector3)(ref enemy.NavMeshAgent.AgentVelocity)).normalized; if (((Vector3)(ref normalized)).magnitude > 0.1f) { pitCheckTimer = 0.5f; Vector3 normalized2 = ((Vector3)(ref enemy.NavMeshAgent.AgentVelocity)).normalized; normalized2.y = 0f; bool num = Physics.Raycast(((Component)this).transform.position + normalized2 + Vector3.up * 1f, Vector3.down, 5f, LayerMask.op_Implicit(SemiFunc.LayerMaskGetVisionObstruct())); if (!num) { enemy.NavMeshAgent.Warp(((Component)this).transform.position - normalized2 * 0.5f, false); enemy.NavMeshAgent.ResetPath(); enemy.NavMeshAgent.Agent.velocity = Vector3.zero; } return !num; } } pitCheckTimer -= Time.deltaTime; return false; } private void FootstepTick() { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: 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_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: 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_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) if (footstepClips == null || footstepClips.Length == 0 || currentState == State.Jumpscare) { return; } Vector3 position = ((Component)this).transform.position; if (!footPosInit) { footLastPos = position; footPosInit = true; return; } Vector3 val = position - footLastPos; val.y = 0f; footLastPos = position; float num = Mathf.Max(Time.deltaTime, 1E-05f); float num2 = ((Vector3)(ref val)).magnitude / num; if (num2 > 20f) { return; } footCdL -= num; footCdR -= num; if (!(num2 < 0.4f)) { Transform root = (((Object)(object)mimicVisualsRoot != (Object)null) ? mimicVisualsRoot : ((Component)this).transform); if ((Object)(object)footBoneL == (Object)null) { footBoneL = FindDeepChild(root, "L_foot_jnt"); } if ((Object)(object)footBoneR == (Object)null) { footBoneR = FindDeepChild(root, "R_foot_jnt"); } ProcessFootPlant(footBoneL, ref footPrevYL, ref footDescL, ref footCdL, num2); ProcessFootPlant(footBoneR, ref footPrevYR, ref footDescR, ref footCdR, num2); } } private void ProcessFootPlant(Transform foot, ref float prevY, ref bool descending, ref float cooldown, float spd) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)foot == (Object)null) { return; } float y = foot.position.y; float num = y - prevY; prevY = y; if (num < -0.0004f) { descending = true; } else if (descending && num > 0.0004f && cooldown <= 0f) { descending = false; cooldown = 0.14f; EnsureFootstepSource(); bool flag = spd > 3.2f && heavyFootstepClips != null && heavyFootstepClips.Length != 0; AudioClip[] array = (flag ? heavyFootstepClips : footstepClips); footstepSource.PlayOneShot(array[Random.Range(0, array.Length)], flag ? 0.3f : 0.2f); if (servoClips != null && servoClips.Length != 0) { footstepSource.PlayOneShot(servoClips[Random.Range(0, servoClips.Length)], 0.15f); } } } private void EnsureFootstepSource() { if (!((Object)(object)footstepSource != (Object)null)) { footstepSource = ((Component)this).gameObject.AddComponent<AudioSource>(); footstepSource.spatialBlend = 1f; footstepSource.minDistance = 2f; footstepSource.maxDistance = 32f; footstepSource.rolloffMode = (AudioRolloffMode)1; footstepSource.playOnAwake = false; footstepSource.dopplerLevel = 0f; } } private void NativeLocomotionTick() { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006e: 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_007d: 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_0055: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)mimicAnimator) || currentState == State.Jumpscare) { return; } if (!Object.op_Implicit((Object)(object)mimicVisualsRoot) || !((Component)mimicVisualsRoot).gameObject.activeInHierarchy) { nativeLastPosInit = false; return; } Vector3 position = ((Component)this).transform.position; if (!nativeLastPosInit) { nativeLastPos = position; nativeLastPosInit = true; return; } Vector3 val = position - nativeLastPos; val.y = 0f; nativeLastPos = position; float num = Mathf.Max(Time.deltaTime, 1E-05f); float num2 = ((Vector3)(ref val)).magnitude / num; if (!(num2 > 20f)) { mimicAnimator.SetFloat("Speed", num2, 0.3f, num); if (Object.op_Implicit((Object)(object)enemy)) { mimicAnimator.SetBool("Stunned", enemy.IsStunned()); } } } private void DisguiseSwayLogic() { //IL_0076: 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) if (Object.op_Implicit((Object)(object)disguiseRigInstance) && disguiseRigInstance.activeInHierarchy) { disguiseSwayTime += Time.deltaTime; float num = disguiseSwayTime / disguiseSwayCycle * (MathF.PI * 2f); float num2 = Mathf.Sin(num) * disguiseSwayLeanDegrees; float num3 = Mathf.Abs(Mathf.Sin(num)) * disguiseSwayBobMeters; disguiseRigInstance.transform.localRotation = Quaternion.Euler(0f, 0f, num2); disguiseRigInstance.transform.localPosition = new Vector3(0f, num3, 0f); } } private void HurtColliderTimer() { if (hurtColliderTimer > 0f) { hurtColliderTimer -= Time.deltaTime; if (hurtColliderTimer <= 0f && Object.op_Implicit((Object)(object)hurtCollider)) { ((Component)hurtCollider).gameObject.SetActive(false); } } } private PlayerDeathHead TryFindCorpse(float maxDistance) { //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) PlayerDeathHead result = null; float num = maxDistance; foreach (PlayerAvatar item in SemiFunc.PlayerGetList()) { if (!item.isDisabled) { continue; } PlayerDeathHead playerDeathHead = item.playerDeathHead; if (Object.op_Implicit((Object)(object)playerDeathHead) && playerDeathHead.triggered && !playerDeathHead.overrideSpectated && !ClaimedHeads.Contains(playerDeathHead)) { float num2 = Vector3.Distance(((Component)enemy.Rigidbody).transform.position, ((Component)playerDeathHead).transform.position); if (num2 < num) { num = num2; result = playerDeathHead; } } } return result; } private void ClaimCorpse(PlayerDeathHead head) { corpseTarget = head; ClaimedHeads.Add(head); } private void ReleaseCorpseClaim() { if ((Object)(object)corpseTarget != (Object)null) { ClaimedHeads.Remove(corpseTarget); } corpseTarget = null; } private PlayerAvatar FindPlayerNearPoint(Vector3 point) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) Collider[] array = Physics.OverlapSphere(point, 1.5f, LayerMask.GetMask(new string[1] { "Player" }) + LayerMask.GetMask(new string[1] { "PhysGrabObject" })); foreach (Collider val in array) { if (((Component)val).CompareTag("Player")) { PlayerController componentInParent = ((Component)val).GetComponentInParent<PlayerController>(); if (Object.op_Implicit((Object)(object)componentInParent)) { return componentInParent.playerAvatarScript; } PlayerAvatar componentInParent2 = ((Component)val).GetComponentInParent<PlayerAvatar>(); if (Object.op_Implicit((Object)(object)componentInParent2)) { return componentInParent2; } } else { PlayerTumble componentInParent3 = ((Component)val).GetComponentInParent<PlayerTumble>(); if (Object.op_Implicit((Object)(object)componentInParent3)) { return componentInParent3.playerAvatar; } } } return null; } private PlayerAvatar SelectBestLureTarget() { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0099: 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) List<PlayerAvatar> list = SemiFunc.PlayerGetList(); PlayerAvatar result = null; float num = float.MinValue; foreach (PlayerAvatar item in list) { if ((Object)(object)item == (Object)null || item.isDisabled) { continue; } float num2 = Vector3.Distance(((Component)this).transform.position, ((Component)item).transform.position); float num3 = float.MaxValue; foreach (PlayerAvatar item2 in list) { if (!((Object)(object)item2 == (Object)null) && !((Object)(object)item2 == (Object)(object)item) && !item2.isDisabled) { float num4 = Vector3.Distance(((Component)item).transform.position, ((Component)item2).transform.position); if (num4 < num3) { num3 = num4; } } } if (num3 == float.MaxValue) { num3 = isolationDistanceCap; } float num5 = Mathf.Min(num3, isolationDistanceCap); float num6 = 0f - num2; float num7 = isolationWeight * num5 + proximityWeight * num6; if (num7 > num) { num = num7; result = item; } } return result; } private PlayerAvatar DetectStalkTarget() { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) PlayerAvatar val = SelectBestLureTarget(); if ((Object)(object)val == (Object)null) { return null; } if (Vector3.Distance(((Component)this).transform.position, ((Component)val).transform.position) > stalkDetectionRange) { return null; } if (!IsIsolated(val)) { return null; } return val; } private bool PounceCheck() { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) PlayerAvatar val = SelectBestLureTarget(); if ((Object)(object)val == (Object)null) { return false; } if (Vector3.Distance(((Component)this).transform.position, ((Component)val).transform.position) > pounceRange) { return false; } if (!IsIsolated(val)) { return false; } huntTarget = val; UpdateState(State.Hunt); return true; } private bool IsIsolated(PlayerAvatar p) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)p == (Object)null) { return false; } float num = float.MaxValue; foreach (PlayerAvatar item in SemiFunc.PlayerGetList()) { if (!((Object)(object)item == (Object)null) && !((Object)(object)item == (Object)(object)p) && !item.isDisabled) { float num2 = Vector3.Distance(((Component)p).transform.position, ((Component)item).transform.position); if (num2 < num) { num = num2; } } } return num > isolationDistanceCap * 0.5f; } private void VoiceLureTick() { //IL_0255: Unknown result type (might be due to invalid IL or missing references) //IL_025b: Unknown result type (might be due to invalid IL or missing references) if (voiceLureTimer > 0f) { voiceLureTimer -= Time.deltaTime; return; } voiceLureTimer = Random.Range(voiceLureMinInterval, voiceLureMaxInterval); PlayerAvatar val = null; val = ((currentState == State.Impersonate && (Object)(object)corpseTarget != (Object)null && (Object)(object)corpseTarget.playerAvatar != (Object)null) ? corpseTarget.playerAvatar : ((currentState == State.Hunt && (Object)(object)huntTarget != (Object)null) ? huntTarget : ((currentState != State.Stalk || !((Object)(object)stalkTargetPlayer != (Object)null)) ? SelectBestLureTarget() : stalkTargetPlayer))); if ((Object)(object)val == (Object)null || (Object)(object)val.photonView == (Object)null) { return; } if (debugForceSpawn && !MimicVoiceCapture.VoiceViablePlayers.Contains(val.photonView.ViewID)) { foreach (MimicVoiceCapture item in MimicVoiceCapture.All) { if ((Object)(object)item != (Object)null && item.IsLocalOwned && (Object)(object)item.PlayerAvatarRef == (Object)(object)val) { item.InjectDebugSnippet(); break; } } } if (currentState != State.Impersonate && currentState != State.Hunt && !MimicVoiceCapture.VoiceViablePlayers.Contains(val.photonView.ViewID)) { PlayerAvatar val2 = null; foreach (PlayerAvatar item2 in SemiFunc.PlayerGetList()) { if (!((Object)(object)item2 == (Object)null) && !item2.isDisabled && !((Object)(object)item2.photonView == (Object)null) && MimicVoiceCapture.VoiceViablePlayers.Contains(item2.photonView.ViewID)) { val2 = item2; break; } } if ((Object)(object)val2 == (Object)null) { return; } val = val2; } int viewID = photonView.ViewID; int viewID2 = val.photonView.ViewID; MimicVoicePlayback.SoloFallbackMimic = ((Component)this).gameObject; if (SemiFunc.IsMultiplayer()) { photonView.RPC("VoiceLureRequestRPC", (RpcTarget)0, new object[2] { viewID2, viewID }); } else { VoiceLureRequestRPC(viewID2, viewID); } } [PunRPC] private void VoiceLureRequestRPC(int playerViewId, int enemyViewId, PhotonMessageInfo info = default(PhotonMessageInfo)) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) if (SemiFunc.MasterOnlyRPC(info)) { MimicVoiceCapture.RequestCaptureForMimic(playerViewId, enemyViewId); } } } public class MimicDisguiseAnimDriver : MonoBehaviour { private Animator anim; private Vector3 lastPos; private bool moving; private bool sprinting; private static readonly string[] ClearBools = new string[10] { "Crouching", "Crawling", "Grabbing", "Jumping", "Falling", "Sliding", "Tumbling", "TumblingMove", "Turning", "Sprinting" }; private void OnEnable() { //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) anim = ((Component)this).GetComponent<Animator>(); if ((Object)(object)anim == (Object)null) { anim = ((Component)this).GetComponentInChildren<Animator>(true); } if ((Object)(object)anim == (Object)null) { ((Behaviour)this).enabled = false; return; } anim.cullingMode = (AnimatorCullingMode)0; anim.speed = 1f; anim.Rebind(); anim.Update(0f); string[] clearBools = ClearBools; foreach (string text in clearBools) { anim.SetBool(text, false); } moving = false; sprinting = false; lastPos = ((Component)this).transform.position; } private void Update() { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: 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_0025: 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) //IL_003e: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)anim == (Object)null) { return; } Vector3 val = ((Component)this).transform.position - lastPos; val.y = 0f; lastPos = ((Component)this).transform.position; float num = Mathf.Max(Time.deltaTime, 1E-05f); float num2 = ((Vector3)(ref val)).magnitude / num; if (num2 > 20f) { return; } if (moving) { if (num2 < 0.15f) { moving = false; } } else if (num2 > 0.35f) { moving = true; } anim.SetBool("Moving", moving); bool flag = moving && num2 > 4f; if (flag && !sprinting) { anim.SetTrigger("SprintingImpulse"); } if (!flag && sprinting) { anim.ResetTrigger("SprintingImpulse"); } sprinting = flag; anim.SetBool("Sprinting", flag); } } public class MimicVoiceCapture : MonoBehaviour { private PlayerAvatar playerAvatar; private PhotonView view; private const int SampleRate = 48000; private const float SnippetSeconds = 2.5f; private const int MaxSnippets = 6; private float[] captureBuffer; private int capturePos; private bool capturing; private readonly List<float[]> snippets = new List<float[]>(); private float recaptureCooldown; private readonly List<byte[]> rxChunks = new List<byte[]>(); private int rxExpected; private int rxEnemyViewId; internal static readonly List<MimicVoiceCapture> All = new List<MimicVoiceCapture>(); private const int MaxChatLines = 6; private readonly List<string> chatLines = new List<string>(); internal static readonly HashSet<int> VoiceViablePlayers = new HashSet<int>(); private bool reportedViable; internal bool IsLocalOwned { get { if ((Object)(object)view != (Object)null) { if (SemiFunc.IsMultiplayer()) { return view.IsMine; } return true; } return false; } } internal int SnippetCount => snippets.Count; internal PlayerAvatar PlayerAvatarRef => playerAvatar; internal int ChatLineCount => chatLines.Count; private void Awake() { playerAvatar = ((Component)this).GetComponent<PlayerAvatar>(); view = ((Component)this).GetComponent<PhotonView>(); All.Add(this); } internal static void RequestCaptureForMimic(int playerViewId, int enemyViewId) { foreach (MimicVoiceCapture item in All) { if ((Object)(object)item != (Object)null && (Object)(object)item.view != (Object)null && item.view.ViewID == playerViewId) { item.BroadcastRandomSnippet(enemyViewId); return; } } Debug.LogWarning((object)$"[MimicVoice] No capture instance found for player view {playerViewId}."); } private void OnDestroy() { All.Remove(this); } private void Update() { if (recaptureCooldown > 0f) { recaptureCooldown -= Time.deltaTime; } } internal void InjectDebugSnippet() { int num = 96000; float[] array = new float[num]; for (int i = 0; i < num; i++) { float num2 = (float)i / 48000f; float num3 = 180f + 40f * Mathf.Sin(num2 * 6f); array[i] = 0.45f * (Mathf.Sin(MathF.PI * 2f * num3 * num2) + 0.4f * Mathf.Sin(MathF.PI * 2f * num3 * 2f * num2)) * (0.6f + 0.4f * Mathf.Sin(num2 * 10f)); } if (snippets.Count >= 6) { snippets.RemoveAt(0); } snippets.Add(array); ReportViable(); Debug.Log((object)"[MimicVoice] DEBUG: injected a synthetic 2s voice snippet (no mic needed) - press the lure to hear it from the Mimic's body."); } internal void InjectDebugChatLine() { snippets.Clear(); CaptureChatLine("hey, I found a bunch of loot back here - come help me carry it"); Debug.Log((object)"[MimicVoice] DEBUG: seeded a synthetic TTS-chat line + cleared the voice ring - press the lure to hear the Mimic SPEAK it (TTS fallback rung)."); } internal void CaptureChatLine(string text) { if (!string.IsNullOrWhiteSpace(text)) { if (chatLines.Count >= 6) { chatLines.RemoveAt(0); } chatLines.Add(text); Debug.Log((object)string.Format("[MimicVoice] captured TTS-chat line #{0} of local player (\"{1}\").", chatLines.Count, (text.Length > 40) ? (text.Substring(0, 40) + "...") : text)); ReportViable(); } } private void ReportViable() { if (!reportedViable && !((Object)(object)view == (Object)null) && (snippets.Count != 0 || chatLines.Count != 0)) { reportedViable = true; int viewID = view.ViewID; VoiceViablePlayers.Add(viewID); if (SemiFunc.IsMultiplayer()) { view.RPC("VoiceViableRPC", (RpcTarget)0, new object[1] { viewID }); } } } [PunRPC] private void VoiceViableRPC(int playerViewId, PhotonMessageInfo info = default(PhotonMessageInfo)) { VoiceViablePlayers.Add(playerViewId); } internal void ProcessVoiceData(short[] voiceData) { if (!IsLocalOwned || (Object)(object)playerAvatar == (Object)null || (Object)(object)playerAvatar.voiceChat == (Object)null) { return; } if (playerAvatar.voiceChat.isTalking && !capturing && recaptureCooldown <= 0f) { capturing = true; captureBuffer = new float[120000]; capturePos = 0; } if (!capturing) { return; } int num = Mathf.Min(voiceData.Length, captureBuffer.Length - capturePos); for (int i = 0; i < num; i++) { captureBuffer[capturePos + i] = (float)voiceData[i] / 32768f; } capturePos += num; if (capturePos >= captureBuffer.Length) { capturing = false; recaptureCooldown = 8f; if (snippets.Count >= 6) { snippets.RemoveAt(0); } snippets.Add(captureBuffer); captureBuffer = null; Debug.Log((object)$"[MimicVoice] captured snippet #{snippets.Count} of local player voice ({2.5f:F1}s)."); ReportViable(); } } internal void BroadcastRandomSnippet(int enemyViewId) { //IL_0115: 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) if (snippets.Count == 0) { if (chatLines.Count > 0) { BroadcastRandomChatLine(enemyViewId); } else { Debug.Log((object)"[MimicVoice] lure requested for this player but no mic snippets OR TTS-chat lines captured yet (normal in solo / before anyone talks or types)."); } return; } float[] array = snippets[Random.Range(0, snippets.Count)]; byte[] array2 = new byte[array.Length * 2]; for (int i = 0; i < array.Length; i++) { short num = (short)(Mathf.Clamp(array[i], -1f, 1f) * 32767f); array2[i * 2] = (byte)(num & 0xFF); array2[i * 2 + 1] = (byte)((num >> 8) & 0xFF); } int num2 = (array2.Length + 8192 - 1) / 8192; if (SemiFunc.IsMultiplayer()) { ((MonoBehaviour)this).StartCoroutine(SendChunksPaced(array2, num2, 8192, enemyViewId)); return; } for (int j = 0; j < num2; j++) { int num3 = Mathf.Min(8192, array2.Length - j * 8192); byte[] array3 = new byte[num3]; Array.Copy(array2, j * 8192, array3, 0, num3); VoiceChunkRPC(array3, j, num2, 48000, enemyViewId); } Debug.Log((object)$"[MimicVoice] broadcast snippet in {num2} chunk(s) for Mimic view {enemyViewId} (solo, inline)."); } private IEnumerator SendChunksPaced(byte[] bytes, int total, int chunkSize, int enemyViewId) { for (int c = 0; c < total; c++) { int num = Mathf.Min(chunkSize, bytes.Length - c * chunkSize); byte[] array = new byte[num]; Array.Copy(bytes, c * chunkSize, array, 0, num); if ((Object)(object)view == (Object)null) { yield break; } view.RPC("VoiceChunkRPC", (RpcTarget)0, new object[5] { array, c, total, 48000, enemyViewId }); if ((c & 1) == 1) { yield return (object)new WaitForSecondsRealtime(0.09f); } } Debug.Log((object)$"[MimicVoice] broadcast snippet in {total} paced chunk(s) for Mimic view {enemyViewId}."); } private void BroadcastRandomChatLine(int enemyViewId) { //IL_0069: 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) if (chatLines.Count != 0) { string text = chatLines[Random.Range(0, chatLines.Count)]; int num = 0; if (SemiFunc.IsMultiplayer()) { view.RPC("TTSChatRPC", (RpcTarget)0, new object[3] { text, num, enemyViewId }); } else { TTSChatRPC(text, num, enemyViewId); } Debug.Log((object)$"[MimicVoice] broadcast TTS-chat line for Mimic view {enemyViewId}: {((text.Length > 40) ? text.Substring(0, 40) : text)}"); } } [PunRPC] private void TTSChatRPC(string text, int voiceIndex, int enemyViewId, PhotonMessageInfo info = default(PhotonMessageInfo)) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) if (!SemiFunc.IsMultiplayer() || SemiFunc.OwnerOnlyRPC(info, view)) { MimicTTSPlayback.Speak(text, voiceIndex, enemyViewId); } } [PunRPC] private void VoiceChunkRPC(byte[] chunk, int chunkIndex, int totalChunks, int sampleRate, int enemyViewId, PhotonMessageInfo info = default(PhotonMessageInfo)) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) if (SemiFunc.IsMultiplayer() && !SemiFunc.OwnerOnlyRPC(info, view)) { return; } if (chunkIndex == 0) { rxChunks.Clear(); rxExpected = totalChunks; rxEnemyViewId = enemyViewId; } if (chunkIndex >= rxExpected) { return; } while (rxChunks.Count <= chunkIndex) { rxChunks.Add(null); } rxChunks[chunkIndex] = chunk; if (rxChunks.Count < rxExpected) { return; } foreach (byte[] rxChunk in rxChunks) { if (rxChunk == null) { return; } } int num = 0; foreach (byte[] rxChunk2 in rxChunks) { num += rxChunk2.Length; } byte[] array = new byte[num]; int num2 = 0; foreach (byte[] rxChunk3 in rxChunks) { Array.Copy(rxChunk3, 0, array, num2, rxChunk3.Length); num2 += rxChunk3.Length; } rxChunks.Clear(); MimicVoicePlayback.PlayOnMimic(array, sampleRate, rxEnemyViewId, playerAvatar); } } public static class MimicVoicePlayback { public static GameObject SoloFallbackMimic; public static void PlayOnMimic(byte[] audioData, int sampleRate, int enemyViewId, PlayerAvatar anyAvatarForMixer) { //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) PhotonView val = PhotonView.Find(enemyViewId); GameObject val2 = (((Object)(object)val != (Object)null) ? ((Component)val).gameObject : SoloFallbackMimic); if ((Object)(object)val2 == (Object)null) { Debug.LogWarning((object)$"[MimicVoice] playback: Mimic view {enemyViewId} not found and no solo fallback - dropping snippet."); return; } int num = audioData.Length / 2; float[] array = new float[num]; for (int i = 0; i < num; i++) { array[i] = (float)(short)(audioData[i * 2] | (audioData[i * 2 + 1] << 8)) / 32768f; } array = LowPass(array, 4500f, sampleRate); EdgeFade(array, (int)((float)sampleRate * 0.02f)); AudioClip val3 = AudioClip.Create("MimicVoiceLure", array.Length, 1, sampleRate, false); val3.SetData(array, 0); Transform val4 = val2.transform.Find("MimicVoiceSource"); if ((Object)(object)val4 == (Object)null) { GameObject val5 = new GameObject("MimicVoiceSource"); val5.transform.SetParent(val2.transform, false); val4 = val5.transform; } AudioSource val6 = ((Component)val4).GetComponent<AudioSource>(); if ((Object)(object)val6 == (Object)null) { val6 = ((Component)val4).gameObject.AddComponent<AudioSource>(); } val6.clip = val3; val6.volume = 0.9f; val6.spatialBlend = 1f; val6.dopplerLevel = 0.5f; val6.minDistance = 1f; val6.maxDistance = 20f; val6.rolloffMode = (AudioRolloffMode)1; if ((Object)(object)anyAvatarForMixer != (Object)null && (Object)(object)anyAvatarForMixer.voiceChat != (Object)null && (Object)(object)anyAvatarForMixer.voiceChat.mixerMicrophoneSound != (Object)null) { val6.outputAudioMixerGroup = anyAvatarForMixer.voiceChat.mixerMicrophoneSound; } val6.Play(); Debug.Log((object)$"[MimicVoice] playing {(float)array.Length / (float)sampleRate:F1}s lure from Mimic view {enemyViewId}."); } private static float[] LowPass(float[] input, float cutoff, int sampleRate) { float num = 1f / (MathF.PI * 2f * cutoff); float num2 = 1f / (float)sampleRate; float num3 = num2 / (num + num2); float[] array = new float[input.Length]; float num4 = 0f; for (int i = 0; i < input.Length; i++) { num4 = (array[i] = num4 + num3 * (input[i] - num4)); } return array; } private static void EdgeFade(float[] data, int fadeSamples) { for (int i = 0; i < fadeSamples && i < data.Length; i++) { float num = (float)i / (float)fadeSamples; data[i] *= num; data[data.Length - 1 - i] *= num; } } } [HarmonyPatch(typeof(PlayerAvatar), "Awake")] internal static class MimicVoice_PlayerAvatarAwakePatch { private static void Postfix(PlayerAvatar __instance) { if ((Object)(object)((Component)__instance).GetComponent<MimicVoiceCapture>() == (Object)null) { ((Component)__instance).gameObject.AddComponent<MimicVoiceCapture>(); } } } [HarmonyPatch(typeof(LocalVoiceFramed<short>), "PushDataAsync")] internal static class MimicVoice_PushDataPatch { private static void Prefix(short[] buf) { foreach (MimicVoiceCapture item in MimicVoiceCapture.All) { if ((Object)(object)item != (Object)null && item.IsLocalOwned) { item.ProcessVoiceData(buf); break; } } } } [HarmonyPatch(typeof(TTSVoice), "TTSSpeakNow")] internal static class MimicVoice_TTSCapturePatch { private static void Postfix(TTSVoice __instance, string text) { if ((Object)(object)__instance == (Object)null || (Object)(object)__instance.playerAvatar == (Object)null || string.IsNullOrWhiteSpace(text)) { return; } foreach (MimicVoiceCapture item in MimicVoiceCapture.All) { if ((Object)(object)item != (Object)null && item.IsLocalOwned && (Object)(object)item.PlayerAvatarRef == (Object)(object)__instance.playerAvatar) { item.CaptureChatLine(text); break; } } } } public static class MimicTTSPlayback { private static readonly Dictionary<int, TTSVoice> perMimic = new Dictionary<int, TTSVoice>(); public static void ClearRunStatics() { perMimic.Clear(); } public static void Speak(string text, int voiceIndex, int enemyViewId) { if (string.IsNullOrWhiteSpace(text)) { return; } PhotonView val = PhotonView.Find(enemyViewId); GameObject val2 = (((Object)(object)val != (Object)null) ? ((Component)val).gameObject : MimicVoicePlayback.SoloFallbackMimic); if ((Object)(object)val2 == (Object)null) { Debug.LogWarning((object)$"[MimicVoice] TTS-chat: Mimic view {enemyViewId} not found and no solo fallback."); return; } TTSVoice orCreate = GetOrCreate(val2, enemyViewId); if ((Object)(object)orCreate == (Object)null) { Debug.LogWarning((object)"[MimicVoice] TTS-chat: no TTSVoice template to clone (no live player TTSVoice found)."); return; } orCreate.setVoice(Mathf.Clamp(voiceIndex, 0, 1)); orCreate.TTSSpeakNow(text, false); Debug.Log((object)string.Format("[MimicVoice] Mimic speaking TTS-chat line from its body (view {0}): \"{1}\".", enemyViewId, (text.Length > 40) ? (text.Substring(0, 40) + "...") : text)); } private static TTSVoice GetOrCreate(GameObject mimic, int enemyViewId) { //IL_016d: 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) if (perMimic.TryGetValue(enemyViewId, out var value) && (Object)(object)value != (Object)null) { return value; } TTSVoice val = null; TTSVoice[] array = Object.FindObjectsOfType<TTSVoice>(); TTSVoice[] array2 = array; foreach (TTSVoice val2 in array2) { if ((Object)(object)val2 != (Object)null && (Object)(object)val2.playerAvatar != (Object)null && val2.voices != null && val2.voices.Length != 0) { val = val2; break; } } if ((Object)(object)val == (Object)null) { Debug.LogWarning((object)$"[MimicVoice] TTS template scan: {array.Length} TTSVoice object(s) in scene."); array2 = array; foreach (TTSVoice val3 in array2) { if ((Object)(object)val3 != (Object)null) { Debug.LogWarning((object)$"[MimicVoice] TTSVoice on '{((Object)((Component)val3).gameObject).name}': playerAvatar={(Object)(object)val3.playerAvatar != (Object)null}, voices={((val3.voices != null) ? val3.voices.Length : (-1))}"); } } array2 = array; foreach (TTSVoice val4 in array2) { if ((Object)(object)val4 != (Object)null && val4.voices != null && val4.voices.Length != 0) { val = val4; break; } } if ((Object)(object)val == (Object)null) { return null; } Debug.Log((object)"[MimicVoice] TTS template: relaxed match (voices-only, playerAvatar ignored) succeeded."); } GameObject val5 = Object.Instantiate<GameObject>(((Component)val).gameObject, mimic.transform); val5.transform.localPosition = Vector3.up * 1.8f; PhotonView[] componentsInChildren = val5.GetComponentsInChildren<PhotonView>(true); for (int i = 0; i < componentsInChildren.Length; i++) { Object.Destroy((Object)(object)componentsInChildren[i]); } TTSVoice val6 = val5.GetComponent<TTSVoice>(); if ((Object)(object)val6 == (Object)null) { val6 = val5.GetComponentInChildren<TTSVoice>(true); } if ((Object)(object)val6 == (Object)null) { Object.Destroy((Object)(object)val5); return null; } val6.playerAvatar = null; AudioSource component = ((Component)val6).GetComponent<AudioSource>(); if ((Object)(object)component != (Object)null) { component.spatialBlend = 1f; component.minDistance = 1f; component.maxDistance = 20f; component.rolloffMode = (AudioRolloffMode)1; } perMimic[enemyViewId] = val6; return val6; } } [HarmonyPatch(typeof(TTSVoice), "Update")] internal static class MimicVoice_TTSUpdateGuard { private static bool Prefix(TTSVoice __instance) { if (!((Object)(object)__instance == (Object)null)) { return (Object)(object)__instance.playerAvatar != (Object)null; } return true; } } namespace MimicMonster { [BepInPlugin("jonathan.mimicmonster", "MimicMonster", "1.0.0")] public class MimicMonsterPlugin : BaseUnityPlugin { private void Awake() { //IL_0015: Unknown result type (might be due to invalid IL or missing references) ((BaseUnityPlugin)this).Logger.LogInfo((object)"MimicMonster script assembly loaded (EnemyMimic available for bundle deserialization)."); try { new Harmony("jonathan.mimicmonster").PatchAll(Assembly.GetExecutingAssembly()); ((BaseUnityPlugin)this).Logger.LogInfo((object)"MimicMonster Harmony patches applied (voice capture + frame feed)."); SceneManager.sceneLoaded += delegate { MimicVoiceCapture.VoiceViablePlayers.Clear(); EnemyMimic.ClearRunStatics(); MimicTTSPlayback.ClearRunStatics(); }; } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("MimicMonster Harmony PatchAll failed - voice mimicry will be inert: " + ex.Message)); } } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { internal IgnoresAccessChecksToAttribute(string assemblyName) { } } }