LadyAliceMargatroid 056cac8df1 Moved dungen code to DunGenPlus
Added critical damage as a mechanic
Maid knife feed deals critical damage, then kills on second time
Added concept of treasure room with kitchen
Frames now only start at the player when not being looked
New attempt at snowglobe animations, it broke though
Added concept of indoor weathers
LethalConfig compability now changes the color of configs that my presets touch
Added jukebox
Changed modGUID
Removed AC compability
Falling into void deals critical damage and teleports, then kills on second time
Maid spawns revenant ghost on death
2024-08-02 08:56:09 -07:00

166 lines
5.1 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;
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 override void Start(){
base.Start();
if (IsOwner && KnightSpawnManager.Instance) {
var index = KnightSpawnManager.Instance.GetSpawnPointIndex();
if (index == -1) return;
SyncKnightReplacementClientRpc(index);
}
}
public override void DoAIInterval() {
base.DoAIInterval();
movingTowardsTargetPlayer = true;
if (targetPlayer == null || !targetPlayer.IsOwner) return;
if (isEnemyDead) return;
var targetOutOfMap = !PlayerIsTargetable(targetPlayer);
if (targetOutOfMap){
CallDisappear();
return;
}
var targetLookingAtMe = !Physics.Linecast(transform.position + Vector3.up * 0.5f, targetPlayer.gameplayCamera.transform.position, StartOfRound.Instance.collidersAndRoomMaskAndDefault) && Vector3.Distance(base.transform.position, targetPlayer.transform.position) < 30f;
var newBehaviourState = targetLookingAtMe ? 1 : 0;
if (newBehaviourState != currentBehaviourStateIndex) {
SwitchToBehaviourState(newBehaviourState);
}
}
public override void Update() {
base.Update();
if (isEnemyDead) return;
var trueSpeed = currentBehaviourStateIndex == 0 ? maxChaseSpeed : slowChaseSpeed;
if (IsOwner) {
agent.speed = Mathf.MoveTowards(agent.speed, trueSpeed, 4.5f * 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);
var playerControllerB = MeetsStandardPlayerCollisionConditions(other, false, false);
if (playerControllerB != null && playerControllerB.IsOwner && playerControllerB == targetPlayer) {
var damage = ScarletNetworkManagerUtility.GetCriticalDamageToPlayer(playerControllerB, false);
playerControllerB.DamagePlayer(damage, true, true, CauseOfDeath.Mauling, 1, false, default(Vector3));
playerControllerB.JumpToFearLevel(1f, true);
RoundManager.PlayRandomClip(creatureVoice, ramAudioClips, false, 1f, 0);
CallDisappear();
}
}
[ClientRpc]
public void SyncKnightReplacementClientRpc(int index){
Plugin.logger.LogInfo($"Spawning ghost 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 ghost knight spawn at {index}, 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.LogInfo($"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.LogInfo("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);
}
}
}