using GameNetcodeStuff; using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Unity.Netcode; using UnityEngine; using UnityEngine.AI; namespace ScarletMansion.GamePatch.Enemies { public class KnightV2Variant : EnemyAI { public AISearchRoutine searchForPlayers; private float checkLineOfSightInterval; private bool hasEnteredChaseMode; private bool stoppingMovement; private bool hasStopped; public AnimationStopPoints animStopPoints; private float currentChaseSpeed = 13f; private float currentAnimSpeed = 1f; private PlayerControllerB previousTarget; private bool wasOwnerLastFrame; private float stopAndGoMinimumInterval; private float hitPlayerTimer; public AudioClip[] springNoises; public AudioClip enterCooldownSFX; public Collider mainCollider; private float loseAggroTimer; private float timeSinceHittingPlayer; private bool movingOnOffMeshLink; private Coroutine offMeshLinkCoroutine; private float stopMovementTimer; public float timeSpentMoving; public float onCooldownPhase; private bool setOnCooldown; public float timeAtLastCooldown; private Vector3 previousPosition; private float checkPositionInterval; private bool isMakingDistance; // Token: 0x04000F2A RID: 3882 private bool inCooldownAnimation; public override void Start(){ base.Start(); currentChaseSpeed = PluginConfig.Instance.knightEnemyValue.speed; if (IsOwner && KnightSpawnManager.Instance) { var index = KnightSpawnManager.Instance.GetSpawnPointIndex(); if (index == -1) return; SyncKnightReplacementClientRpc(index); } } [ServerRpc(RequireOwnership = false)] public void SetCoilheadOnCooldownServerRpc(float cooldown) { SetCoilheadOnCooldownClientRpc(cooldown); } // Token: 0x06000F6F RID: 3951 RVA: 0x000942FC File Offset: 0x000924FC [ClientRpc] public void SetCoilheadOnCooldownClientRpc(float cooldown) { timeSpentMoving = 0f; if (cooldown > 0f) { onCooldownPhase = cooldown; setOnCooldown = true; inCooldownAnimation = true; SwitchToBehaviourStateOnLocalClient(0); creatureVoice.PlayOneShot(enterCooldownSFX); if (animStopPoints.animationPosition == 1) creatureAnimator.SetTrigger("springBoing"); else creatureAnimator.SetTrigger("springBoingPosition2"); creatureAnimator.SetFloat("walkSpeed", 0f); return; } onCooldownPhase = 0f; setOnCooldown = false; timeAtLastCooldown = Time.realtimeSinceStartup; } // Token: 0x06000F70 RID: 3952 RVA: 0x0009443C File Offset: 0x0009263C public override void DoAIInterval() { base.DoAIInterval(); if (StartOfRound.Instance.allPlayersDead) return; if (isEnemyDead) return; var currentBehaviourStateIndex = this.currentBehaviourStateIndex; if (currentBehaviourStateIndex != 0) { if (currentBehaviourStateIndex != 1) return; if (searchForPlayers.inProgress) StopSearch(searchForPlayers, true); if (TargetClosestPlayer(1.5f, false, 70f)) { if (previousTarget != targetPlayer) { previousTarget = targetPlayer; ChangeOwnershipOfEnemy(targetPlayer.actualClientId); } if (Time.realtimeSinceStartup - timeSinceHittingPlayer > 7f && !stoppingMovement) { if (Vector3.Distance(targetPlayer.transform.position, transform.position) <= 40f || CheckLineOfSightForPosition(targetPlayer.gameplayCamera.transform.position, 180f, 140, -1f, null)) { loseAggroTimer = 0f; return; } loseAggroTimer += AIIntervalTime; if (loseAggroTimer > 4.5f) { SwitchToBehaviourState(0); ChangeOwnershipOfEnemy(StartOfRound.Instance.allPlayerScripts[0].actualClientId); return; } } } else { var flag = false; for (var i = 0; i < StartOfRound.Instance.allPlayerScripts.Length; i++) { if (StartOfRound.Instance.allPlayerScripts[i].HasLineOfSightToPosition(transform.position + Vector3.up * 0.3f, 68f, 60, -1f) || StartOfRound.Instance.allPlayerScripts[i].HasLineOfSightToPosition(transform.position + Vector3.up * 1.6f, 68f, 60, -1f)) { flag = true; break; } } if (!flag) { loseAggroTimer += AIIntervalTime; if (loseAggroTimer > 1f) { SwitchToBehaviourState(0); ChangeOwnershipOfEnemy(StartOfRound.Instance.allPlayerScripts[0].actualClientId); } } } } else { if (!IsServer) { ChangeOwnershipOfEnemy(StartOfRound.Instance.allPlayerScripts[0].actualClientId); return; } if (onCooldownPhase > 0f) { agent.speed = 0f; SetDestinationToPosition(transform.position, false); onCooldownPhase -= AIIntervalTime; return; } if (setOnCooldown) { setOnCooldown = false; SetCoilheadOnCooldownClientRpc(0f); } loseAggroTimer = 0f; for (var j = 0; j < StartOfRound.Instance.allPlayerScripts.Length; j++) { if (PlayerIsTargetable(StartOfRound.Instance.allPlayerScripts[j], false, false)) { if (StartOfRound.Instance.allPlayerScripts[j].HasLineOfSightToPosition(transform.position + Vector3.up * 0.3f, 60f, 20, -1f)) { targetPlayer = StartOfRound.Instance.allPlayerScripts[j]; SwitchToBehaviourState(1); } if (!PathIsIntersectedByLineOfSight(StartOfRound.Instance.allPlayerScripts[j].transform.position, false, false, false) && !Physics.Linecast(transform.position + Vector3.up * 0.5f, StartOfRound.Instance.allPlayerScripts[j].gameplayCamera.transform.position, StartOfRound.Instance.collidersAndRoomMaskAndDefault) && Vector3.Distance(transform.position, StartOfRound.Instance.allPlayerScripts[j].transform.position) < 30f) { SwitchToBehaviourState(1); return; } } } agent.speed = 6f; if (!searchForPlayers.inProgress) { movingTowardsTargetPlayer = false; SetDestinationToPosition(transform.position, false); StartSearch(transform.position, searchForPlayers); return; } } } // Token: 0x06000F71 RID: 3953 RVA: 0x00094882 File Offset: 0x00092A82 private IEnumerator Parabola(NavMeshAgent agent, float height, float duration) { var data = agent.currentOffMeshLinkData; var startPos = agent.transform.position; var endPos = data.endPos + Vector3.up * agent.baseOffset; var normalizedTime = 0f; while (normalizedTime < 1f && data.valid && data.activated && IsOwner) { var num = height * 4f * (normalizedTime - normalizedTime * normalizedTime); agent.transform.position = Vector3.Lerp(startPos, endPos, normalizedTime) + num * Vector3.up; normalizedTime += Time.deltaTime / duration; yield return null; } agent.CompleteOffMeshLink(); offMeshLinkCoroutine = null; yield break; } // Token: 0x06000F72 RID: 3954 RVA: 0x000948A8 File Offset: 0x00092AA8 private void StopOffMeshLinkMovement() { if (offMeshLinkCoroutine != null) { StopCoroutine(offMeshLinkCoroutine); offMeshLinkCoroutine = null; var currentOffMeshLinkData = agent.currentOffMeshLinkData; agent.CompleteOffMeshLink(); if (currentOffMeshLinkData.valid) { if (Vector3.SqrMagnitude(transform.position - currentOffMeshLinkData.startPos) < Vector3.SqrMagnitude(transform.position - currentOffMeshLinkData.endPos)) { agent.Warp(currentOffMeshLinkData.startPos); return; } agent.Warp(currentOffMeshLinkData.endPos); return; } } } // Token: 0x06000F73 RID: 3955 RVA: 0x000949B0 File Offset: 0x00092BB0 private void DoSpringAnimation(bool springPopUp = false) { if (GameNetworkManager.Instance.localPlayerController.HasLineOfSightToPosition(transform.position, 70f, 25, -1f)) { var num = Vector3.Distance(transform.position, GameNetworkManager.Instance.localPlayerController.transform.position); if (num < 4f) { GameNetworkManager.Instance.localPlayerController.JumpToFearLevel(0.9f, true); } else if (num < 9f) { GameNetworkManager.Instance.localPlayerController.JumpToFearLevel(0.4f, true); } } if (currentAnimSpeed > 2f || springPopUp) { RoundManager.PlayRandomClip(creatureVoice, springNoises, false, 1f, 0, 1000); if (animStopPoints.animationPosition == 1) creatureAnimator.SetTrigger("springBoing"); else creatureAnimator.SetTrigger("springBoingPosition2"); } } // Token: 0x06000F74 RID: 3956 RVA: 0x00094AA4 File Offset: 0x00092CA4 public override void Update() { base.Update(); if (isEnemyDead) return; if (hitPlayerTimer >= 0f) hitPlayerTimer -= Time.deltaTime; if (!IsOwner) { stopMovementTimer = 5f; loseAggroTimer = 0f; if (offMeshLinkCoroutine != null) StopCoroutine(offMeshLinkCoroutine); } if (IsOwner) { if (agent.isOnOffMeshLink) { if (!stoppingMovement && agent.currentOffMeshLinkData.activated) { if (offMeshLinkCoroutine == null) offMeshLinkCoroutine = StartCoroutine(Parabola(agent, 0.6f, 0.5f)); } else { StopOffMeshLinkMovement(); } } else if (offMeshLinkCoroutine != null) { StopOffMeshLinkMovement(); } } creatureAnimator.SetBool("OnCooldown", setOnCooldown); if (setOnCooldown) { mainCollider.isTrigger = true; stoppingMovement = true; hasStopped = true; return; } var currentBehaviourStateIndex = this.currentBehaviourStateIndex; if (currentBehaviourStateIndex == 0) { agent.autoTraverseOffMeshLink = false; creatureAnimator.SetFloat("walkSpeed", 2.5f); stoppingMovement = false; hasStopped = false; return; } if (currentBehaviourStateIndex != 1) return; if (IsOwner) { if (onCooldownPhase > 0f) { SwitchToBehaviourState(0); return; } if (stopAndGoMinimumInterval > 0f) stopAndGoMinimumInterval -= Time.deltaTime; if (!wasOwnerLastFrame) { wasOwnerLastFrame = true; if (!stoppingMovement && hitPlayerTimer < 0.12f) agent.speed = currentChaseSpeed; else agent.speed = 0f; } var flag = false; for (int i = 0; i < StartOfRound.Instance.allPlayerScripts.Length; i++) { if (PlayerIsTargetable(StartOfRound.Instance.allPlayerScripts[i], false, false) && (StartOfRound.Instance.allPlayerScripts[i].HasLineOfSightToPosition(transform.position + Vector3.up * 0.25f, 68f, 60, -1f) || StartOfRound.Instance.allPlayerScripts[i].HasLineOfSightToPosition(transform.position + Vector3.up * 1.6f, 68f, 60, -1f)) && Vector3.Distance(StartOfRound.Instance.allPlayerScripts[i].gameplayCamera.transform.position, eye.position) > 0.3f) { flag = true; } } if (stunNormalizedTimer > 0f) flag = true; if (flag != stoppingMovement && stopAndGoMinimumInterval <= 0f) { stopAndGoMinimumInterval = 0.15f; if (flag) SetAnimationStopServerRpc(); else SetAnimationGoServerRpc(); stoppingMovement = flag; } } var num = 0f; if (stoppingMovement) { if (animStopPoints.canAnimationStop || stopMovementTimer > 0.27f) { if (!hasStopped) { hasStopped = true; DoSpringAnimation(false); } else if (inCooldownAnimation) { inCooldownAnimation = false; DoSpringAnimation(true); } if (RoundManager.Instance.currentMineshaftElevator != null && Vector3.Distance(transform.position, RoundManager.Instance.currentMineshaftElevator.elevatorInsidePoint.position) < 1f) { num = 0.5f; } if (mainCollider.isTrigger && Vector3.Distance(GameNetworkManager.Instance.localPlayerController.transform.position, transform.position) > 0.3f) { mainCollider.isTrigger = false; } creatureAnimator.SetFloat("walkSpeed", 0f); currentAnimSpeed = 0f; if (IsOwner) { agent.speed = 0f; movingTowardsTargetPlayer = false; SetDestinationToPosition(transform.position, false); } } else { stopMovementTimer += Time.deltaTime; } } else { stopMovementTimer = 0f; if (hasStopped) { hasStopped = false; mainCollider.isTrigger = true; isMakingDistance = true; } currentAnimSpeed = Mathf.Lerp(currentAnimSpeed, currentChaseSpeed * 0.4597f, 5f * Time.deltaTime); creatureAnimator.SetFloat("walkSpeed", currentAnimSpeed); inCooldownAnimation = false; if (IsServer) { if (checkPositionInterval <= 0f) { checkPositionInterval = 0.65f; isMakingDistance = (Vector3.Distance(transform.position, previousPosition) > 0.5f); previousPosition = transform.position; } else { checkPositionInterval -= Time.deltaTime; } if (isMakingDistance) { num = 1f; } else { num = 0.2f; } } if (IsOwner) { agent.speed = Mathf.Lerp(agent.speed, currentChaseSpeed, 4.5f * Time.deltaTime); movingTowardsTargetPlayer = true; } } if (IsServer) { if (num > 0f) { timeSpentMoving += Time.deltaTime * num; } if (timeSpentMoving > 9f) { onCooldownPhase = 11f; setOnCooldown = true; inCooldownAnimation = true; SetCoilheadOnCooldownClientRpc(25f); SwitchToBehaviourStateOnLocalClient(0); } } } // Token: 0x06000F75 RID: 3957 RVA: 0x00094F98 File Offset: 0x00093198 [ServerRpc] public void SetAnimationStopServerRpc() { SetAnimationStopClientRpc(); } // Token: 0x06000F76 RID: 3958 RVA: 0x000950AC File Offset: 0x000932AC [ClientRpc] public void SetAnimationStopClientRpc() { stoppingMovement = true; } // Token: 0x06000F77 RID: 3959 RVA: 0x0009517C File Offset: 0x0009337C [ServerRpc] public void SetAnimationGoServerRpc() { SetAnimationGoClientRpc(); } // Token: 0x06000F78 RID: 3960 RVA: 0x00095290 File Offset: 0x00093490 [ClientRpc] public void SetAnimationGoClientRpc() { stoppingMovement = false; } // Token: 0x06000F79 RID: 3961 RVA: 0x00095360 File Offset: 0x00093560 public override void OnCollideWithPlayer(Collider other) { base.OnCollideWithPlayer(other); if (stoppingMovement || currentBehaviourStateIndex != 1 || hitPlayerTimer >= 0f || setOnCooldown || (Time.realtimeSinceStartup - timeAtLastCooldown) < 0.45f) { return; } var playerControllerB = MeetsStandardPlayerCollisionConditions(other, false, false); if (playerControllerB != null) { hitPlayerTimer = 0.2f; playerControllerB.DamagePlayer(90, true, true, CauseOfDeath.Mauling, 0); playerControllerB.JumpToFearLevel(1f, true); timeSinceHittingPlayer = Time.realtimeSinceStartup; if (playerControllerB.isPlayerDead) { Assets.onPlayerDeath.Call(new ModPatch.CoronerParameters(playerControllerB, ModPatch.CoronerDeathEnum.Knight)); } SetCoilheadOnCooldownServerRpc(5f); } } [ClientRpc] public void SyncKnightReplacementClientRpc(int index){ SyncKnightReplacement(index); } public void SyncKnightReplacement(int index){ Plugin.logger.LogDebug($"Spawning knight at {index}"); try { var target = KnightSpawnManager.Instance.GetSpawnPointTransform(index); target.gameObject.SetActive(false); transform.position = target.position; transform.rotation = target.rotation; serverPosition = target.position; if (agent == null) agent = GetComponentInChildren(); agent.Warp(target.position); if (IsOwner) { SyncPositionToClients(); } } catch (Exception e){ Plugin.logger.LogError($"Tried to knight spawn at {index}, but completely failed"); Plugin.logger.LogError(e); } } } }