Critical damage renamed to marked for death and all share the same mechanic now Revelant now slows down to speed over a fixed short amount instead the nonworking jank before Moved item/enemy injection to DunGenPlus
466 lines
15 KiB
C#
466 lines
15 KiB
C#
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<NavMeshAgent>();
|
|
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);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|