Redid vent code to work better and like work in general. Knight animation now pauses for a second. Knights no longer trigger the default scarlet vent animation. Revenants now spawn at a vent if no knights exist.
200 lines
6.3 KiB
C#
200 lines
6.3 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using UnityEngine;
|
|
using Unity.Netcode;
|
|
using UnityEngine.AI;
|
|
using GameNetcodeStuff;
|
|
using ScarletMansion.GamePatch.Components;
|
|
using ScarletMansion.Configs;
|
|
|
|
namespace ScarletMansion {
|
|
|
|
public class KnightGhostVariant : EnemyAI {
|
|
|
|
public float maxChaseSpeed = 14.5f;
|
|
public float slowChaseSpeed = 6f;
|
|
private float currentAnimSpeed = 1f;
|
|
|
|
public Collider mainCollider;
|
|
|
|
public MeshRenderer[] knightMeshRenderers;
|
|
|
|
public AudioClip[] ramAudioClips;
|
|
|
|
public KnightSpawnPoint spawnPoint;
|
|
public bool activated;
|
|
|
|
public override void Start(){
|
|
base.Start();
|
|
|
|
maxChaseSpeed = ConfigMain.Instance.revEnemyValue.speed;
|
|
|
|
if (IsOwner && KnightSpawnManager.Instance) {
|
|
var index = KnightSpawnManager.Instance.GetSpawnPointIndex();
|
|
if (index == -1) {
|
|
// grab position instead
|
|
var allVents = RoundManager.Instance.allEnemyVents;
|
|
var anyEnemyVent = allVents[UnityEngine.Random.Range(0, allVents.Length)];
|
|
SyncKnightAtPositionClientRpc(anyEnemyVent.floorNode.position, Quaternion.Euler(0f, anyEnemyVent.floorNode.eulerAngles.y, 0f));
|
|
return;
|
|
}
|
|
|
|
SyncKnightReplacementClientRpc(index);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
public override void DoAIInterval() {
|
|
base.DoAIInterval();
|
|
|
|
movingTowardsTargetPlayer = true;
|
|
|
|
if (targetPlayer == null || !targetPlayer.IsOwner) return;
|
|
if (isEnemyDead) return;
|
|
if (timeSinceSpawn < 1f) return;
|
|
|
|
var targetOutOfMap = !PlayerIsTargetable(targetPlayer);
|
|
if (targetOutOfMap){
|
|
CallDisappear();
|
|
return;
|
|
}
|
|
|
|
// whoops i didn't copy from knight code properly lmaoz
|
|
var targetLookingAtMe = targetPlayer.HasLineOfSightToPosition(transform.position + Vector3.up * 1.6f, 68f, 60);
|
|
var newBehaviourState = targetLookingAtMe ? 1 : 0;
|
|
if (targetLookingAtMe) {
|
|
targetPlayer.JumpToFearLevel(0.75f, true);
|
|
}
|
|
|
|
if (newBehaviourState != currentBehaviourStateIndex) {
|
|
SwitchToBehaviourState(newBehaviourState);
|
|
}
|
|
|
|
}
|
|
|
|
public override void Update() {
|
|
base.Update();
|
|
if (isEnemyDead) return;
|
|
if (timeSinceSpawn < 1f) return;
|
|
|
|
if (!activated) {
|
|
activated = true;
|
|
creatureAnimator.SetBool("Activate", true);
|
|
}
|
|
|
|
var trueSpeed = currentBehaviourStateIndex == 0 ? maxChaseSpeed : slowChaseSpeed;
|
|
if (IsOwner) {
|
|
agent.speed = Mathf.MoveTowards(agent.speed, trueSpeed, (maxChaseSpeed - slowChaseSpeed) * 4f * Time.deltaTime);
|
|
}
|
|
|
|
currentAnimSpeed = Mathf.Lerp(currentAnimSpeed, trueSpeed * 0.4597f, 4f * Time.deltaTime);
|
|
creatureAnimator.SetFloat("walkSpeed", currentAnimSpeed);
|
|
}
|
|
|
|
public override void OnCollideWithPlayer(Collider other) {
|
|
base.OnCollideWithPlayer(other);
|
|
|
|
if (timeSinceSpawn < 1f) return;
|
|
|
|
var playerControllerB = MeetsStandardPlayerCollisionConditions(other, false, false);
|
|
if (playerControllerB != null && playerControllerB.IsOwner && playerControllerB == targetPlayer) {
|
|
var damage = ScarletNetworkManagerUtility.GetMarkedDamageToPlayer(playerControllerB);
|
|
playerControllerB.DamagePlayer(damage, true, true, CauseOfDeath.Mauling, 1, false, default(Vector3));
|
|
playerControllerB.JumpToFearLevel(1f, true);
|
|
|
|
ScarletNetworkManagerUtility.MarkPlayerForDeath(playerControllerB);
|
|
|
|
if (playerControllerB.isPlayerDead) {
|
|
Assets.onPlayerDeath.Call(new ModPatch.CoronerParameters(playerControllerB, ModPatch.CoronerDeathEnum.GhostKnight));
|
|
}
|
|
|
|
RoundManager.PlayRandomClip(creatureVoice, ramAudioClips, false, 1f, 1);
|
|
CallDisappear();
|
|
}
|
|
}
|
|
|
|
[ClientRpc]
|
|
public void SyncKnightReplacementClientRpc(int index){
|
|
Plugin.logger.LogDebug($"Spawning ghost knight at {index}");
|
|
|
|
try {
|
|
spawnPoint = KnightSpawnManager.Instance.GetSpawnPoint(index);
|
|
KnightSpawnManager.Instance.SpawnEnemyOnSpawnPoint(this, spawnPoint);
|
|
} catch (Exception e){
|
|
Plugin.logger.LogError($"Tried to ghost knight spawn at {index}, but completely failed");
|
|
Plugin.logger.LogError(e);
|
|
}
|
|
}
|
|
|
|
[ClientRpc]
|
|
public void SyncKnightAtPositionClientRpc(Vector3 position, Quaternion rotation){
|
|
Plugin.logger.LogDebug($"Spawning ghost knight at {position}");
|
|
|
|
try {
|
|
KnightSpawnManager.Instance.SpawnEnemyOnSpawnPoint(this, position, rotation);
|
|
} catch (Exception e){
|
|
Plugin.logger.LogError($"Tried to ghost knight spawn at {position}, but completely failed");
|
|
Plugin.logger.LogError(e);
|
|
}
|
|
}
|
|
|
|
|
|
public void FindAndTunnelPlayer(Vector3 searchPosition){
|
|
var validPlayers = StartOfRound.Instance.allPlayerScripts.Where(p => p != null && PlayerIsTargetable(p));
|
|
if (validPlayers.Count() == 0) {
|
|
Plugin.logger.LogWarning("Could not find valid target to tunnel");
|
|
return;
|
|
}
|
|
|
|
var closestPlayer = validPlayers
|
|
.OrderBy(p => Vector3.SqrMagnitude(p.transform.position - searchPosition))
|
|
.FirstOrDefault();
|
|
|
|
if (closestPlayer != null) {
|
|
ChangeOwnershipOfEnemy(closestPlayer.actualClientId);
|
|
TunnelPlayerClientRpc(closestPlayer);
|
|
}
|
|
}
|
|
|
|
[ClientRpc]
|
|
public void TunnelPlayerClientRpc(NetworkBehaviourReference playerRef){
|
|
if (playerRef.TryGet<PlayerControllerB>(out var player)){
|
|
targetPlayer = player;
|
|
Plugin.logger.LogDebug($"Targeting {player.playerUsername} for death");
|
|
|
|
if (targetPlayer.IsOwner) return;
|
|
|
|
// other clients will only see eyes of death
|
|
foreach(var m in knightMeshRenderers) {
|
|
m.enabled = false;
|
|
}
|
|
} else {
|
|
Plugin.logger.LogWarning("Could not find target player through reference");
|
|
}
|
|
}
|
|
|
|
private bool calledDisappear = false;
|
|
public void CallDisappear(){
|
|
if (calledDisappear) return;
|
|
|
|
Plugin.logger.LogDebug("Killing ghost knight");
|
|
|
|
calledDisappear = true;
|
|
mainCollider.enabled = false;
|
|
mainCollider.gameObject.SetActive(false);
|
|
|
|
ScarletNetworkManager.Instance.CreateSpawnAudioPrefab(transform.position, targetPlayer.actualClientId);
|
|
DisappearServerRpc();
|
|
}
|
|
|
|
[ServerRpc(RequireOwnership = false)]
|
|
public void DisappearServerRpc(){
|
|
KillEnemy(true);
|
|
}
|
|
}
|
|
}
|