Files
valheim/Assets/_Valheim/Scripts/Enemys/Enemy.cs
2025-07-04 14:16:14 +08:00

517 lines
14 KiB
C#

using System;
using BehaviorDesigner.Runtime;
using DarkTonic.MasterAudio;
using DragonLi.Core;
using DragonLi.Frame;
using Mirror;
using UnityEngine;
namespace Valheim
{
public enum EnemyState
{
/// <summary>
/// 地上
/// </summary>
GroundIdle = 0,
/// <summary>
/// 地上警戒
/// </summary>
GroundAlert = 2,
/// <summary>
/// 地下警戒
/// </summary>
UnderGroundAlert = 1,
/// <summary>
/// 幽灵
/// </summary>
Ghost = 3,
///// <summary>
/// 受击
/// </summary>
TakeDamag = 4,
/// <summary>
/// 返回老巢状态
/// </summary>
ReturnToNest=5,
}
public class Enemy : Agent
{
#region
/// <summary>
/// 怪物唯一id
/// </summary>
public int id = 0;
/// <summary>
/// 所属区域
/// </summary>
#if UNITY_EDITOR
[DisplayOnly]
#endif
public int areaId = -1;
[SyncVar]
public float modelScale = 1.5f;
/// <summary>
/// 怪物状态
/// </summary>
#if UNITY_EDITOR
[DisplayOnly]
#endif
public EnemyState state = EnemyState.GroundAlert;
/// <summary>
/// 等级
/// </summary>
#if UNITY_EDITOR
[DisplayOnly]
#endif
[SyncVar]
public int lvl = 0;
/// <summary>
/// 速度
/// </summary>
[NonSerialized]
public float speed = 3;
/// <summary>
/// 攻击
/// </summary>
#if UNITY_EDITOR
[DisplayOnly]
#endif
public float atk = 5;
/// <summary>
/// 攻击速率
/// </summary>
[NonSerialized]
public float rate = 1;
/// <summary>
/// 攻击范围
/// </summary>
// #if UNITY_EDITOR
// [DisplayOnly]
// #endif
public float atkArea = 0.4f;
/// <summary>
/// 占地半径
/// </summary>
[NonSerialized]
public float radius = 0.6f;
/// <summary>
/// 警戒范围
/// </summary>
[NonSerialized]
public float alertArea = 3.0f;
public EnemyType type;
#endregion
public BehaviorTree behaviorTree;
public LayerMask hitMask;
public GameObject model;
public Material[] skin;
public float modelHeight;
public string enemyName;
public Transform uiPos;
public Transform startPos;
/// <summary>
/// 皮肤
/// </summary>
public SkinnedMeshRenderer skinnedMeshRenderer;
/// <summary>
/// 装饰品
/// </summary>
public GameObject[] Ornamental;
/// <summary>
/// 武器
/// </summary>
public GameObject[] Weapon;
public GameObject ui;
private AudioSource _audioSource = null;
public AudioSource audioSource
{
get
{
if (_audioSource == null)
{
_audioSource = GetComponentInChildren<AudioSource>();
}
return _audioSource;
}
}
[SoundGroup] public string attackSound;
[SoundGroup] public string idleSound;
[SoundGroup] public string runSound;
[SoundGroup] public string dieSound;
public override void Awake()
{
base.Awake();
}
public override void InitData()
{
base.InitData();
}
public void InitBree()
{
behaviorTree.RegisterEvent("UserSkill", UseSkill);
}
[Server]
public virtual void OnSpawn(EnemyData data, int areaId, int lvl, EnemyState state)
{
base.OnSpawn();
//this.type = stat;
//id = curId;
this.areaId = areaId;
this.lvl = lvl;
this.state = state;
atk = data.Atk;
health = data.Hp;
OriginHealth = data.Hp;
//radius = transform.localScale.x/2f;
//NavAgent.radius = radius;
campId = data.Camp;
modelHeight = uiPos.position.y;
atkArea=transform.localScale.x;
enemyName = data.Name;
startPos=transform;
RpcSyncModel(modelScale);
RpcChangeSkin();
RpcShowOrnamental();
// if (state == EnemyState.UnderGroundAlert)
// {
// AnimatorComponent.SetInteger("State", (int)EnemyState.UnderGroundAlert); RpcHideModel();
// }
behaviorTree.enabled = true;
NavAgent.enabled = true;
behaviorTree.SetVariable("InitPos", (SharedVector3)transform.position);
behaviorTree.SetVariable("InitRotate", (SharedVector3)transform.eulerAngles);
behaviorTree.RegisterEvent("TrueDie", TrueDie);
behaviorTree.RegisterEvent("FoundEnemy", FoundEnemy);
GameManager.Ins.CreateEnemyUI(this);
skillList.Clear();
foreach (var skillId in data.SkillIds)
{
GameManager.Ins.SkillDataList.TryGetValue(skillId, out var skill);
if(skill==null)
continue;
if (skill.SkillInterval <= 1)
{
skillHpInterval=skill.SkillInterval;
}
else
{
waitSkillTime = skill.SkillInterval;
}
skillList.Add(skillId);
}
curWaitSkillTime = waitSkillTime;
InitBree();
}
[ClientRpc]
public void RpcSyncModel(float scale)
{
if (model != null)
{
model.transform.localScale = new Vector3(scale, scale, scale);
}
}
[ClientRpc]
public void RpcChangeSkin()
{
// int randomIndex = UnityEngine.Random.Range(0, skin.Length);
//
// if (skinnedMeshRenderer != null)
// {
// skinnedMeshRenderer.material = skin[randomIndex];
// }
}
/// <summary>
/// 随机展示一个装饰物品
/// </summary>
[ClientRpc]
public void RpcShowOrnamental()
{
if (Ornamental.Length > 0)
{
Ornamental[UnityEngine.Random.Range(0, Ornamental.Length)].SetActive(true);
}
if (Weapon.Length > 0)
{
Weapon[UnityEngine.Random.Range(0, Weapon.Length)].SetActive(true);
}
}
[ClientRpc]
public void RpcHideModel()
{
if (model != null)
{
model.SetActive(false);
}
}
public override void Update()
{
if (isServer && IsAlive)
{
if (NavAgent != null & NavAgent.enabled)
{
UpdateAniamtorValues(NavAgent.velocity);
}
else if (RigidbodyComponent)
{
UpdateAniamtorValues(RigidbodyComponent.velocity);
}
OnUpdate();
if (!isUserSkill)
{
curWaitSkillTime-=Time.deltaTime;
if (curWaitSkillTime <= 0)
{
isUserSkill = true;
}
}
if (!isUserHpSkill&&userHpSkillIndex<=0)
{
if (skillHpInterval != 0 && health <= skillHpInterval)
isUserHpSkill = true;
}
}
}
public override void Die(object info, Transform _sender)
{
GameManager.Ins.EnemyList.Remove(this);
NavAgent.enabled = false;
GameManager.Ins.CreateSpawnBallPoint(new Vector3(transform.position.x, 0.1F, transform.position.z));
GameManager.Ins.CheckAreaSettle(areaId);
}
/// <summary>
/// 攻击
/// </summary>
public virtual void AttackOnce()
{
if (isServer)
{
GameObject target = (GameObject)behaviorTree.GetVariable("Target").GetValue();
if (target == null) return;
Ray ray = new Ray(transform.position, transform.forward);
if (Physics.Raycast(ray, out RaycastHit hit, 3F, hitMask))
{
Vector3 vector3 = new Vector3(hit.point.x, 0.2F, hit.point.z);
GameManager.Ins.CreateHitEffect(vector3);
}
IDamagable damagable = target.GetComponent<IDamagable>();
if (damagable != null)
{
Debug.Log("宠物受伤:"+atk);
damagable.ApplyDamage(atk, false, transform);
}
}
}
public virtual void FoundEnemy()
{
RpcShowModel();
}
[ClientRpc]
public void RpcShowModel()
{
if (model != null)
{
model.SetActive(true);
}
}
public void TrueDie()
{
NetworkServer.Destroy(transform.gameObject);
NetworkServer.Destroy(ui.gameObject);
}
[ClientRpc]
public void PlayAudioRpc(string path)
{
if (audioSource != null)
{
AudioClip clip = Resources.Load<AudioClip>(path);
if (clip != null)
{
audioSource.PlayOneShot(clip);
}
}
}
[ClientRpc]
public void PlayAudioVecRpc(string path, Transform target)
{
MasterAudio.PlaySound3DAtVector3(path, target.position);
}
[ClientRpc]
public void PlayAudioFollowTrans(string path, Transform target)
{
MasterAudio.PlaySound3DFollowTransform(path, target);
}
// todo
[Server]
public void Iced(float atk)
{
IDamagable damagable = transform.GetComponent<IDamagable>();
if (damagable != null)
{
damagable.ApplyDamage(atk, false, null);
}
}
public void SetRadiu(float value)
{
radius = value;
}
[SoundGroup] public string hitSound;
/// <summary>
/// 被玩家击中
/// </summary>
[Server]
public void KnockTakeDamage(float atk, Vector3 hitPoint)
{
//造成伤害
IDamagable damagable = transform.GetComponent<IDamagable>();
if (damagable != null)
{
damagable.ApplyDamage(atk, false, null);
}
//PlayAudioVecRpc(hitSound, transform);
//受击特效
Vector3 vector3 = new Vector3(hitPoint.x, hitPoint.y, hitPoint.z);
GameManager.Ins.CreateHitEffect(vector3);
//播放受击动画
}
public int LastState;
/// <summary>
/// 受击
/// </summary>
[Server]
public void PlayTakeDamageAnim()
{
RpcTakeDamage();
}
public void RpcTakeDamage()
{
AnimatorStateInfo stateInfo = AnimatorComponent.GetCurrentAnimatorStateInfo(0);
if (!stateInfo.IsName("TakeDamage"))
{
if (AnimatorComponent.GetInteger("State") != 5)
{
LastState = AnimatorComponent.GetInteger("State");
}
state = EnemyState.TakeDamag;
behaviorTree.SetVariable("isHit", (SharedBool)true);
}
}
private Coroutine currentSkillCoroutine;
public void UseSkill()
{
int skillId= (int)behaviorTree.GetVariable("skillId").GetValue();
var target= (GameObject)behaviorTree.GetVariable("Target").GetValue();
if(target==null)
return;
SkillLogicBase skillLogic = GameManager.Ins.skillManager.GetSkillLogic(skillId);
if (skillLogic == null)
{
Debug.LogWarning($"技能ID[{skillId}] 不存在或未在 SkillManager 中注册");
return;
}
// 开启协程执行技能
if (currentSkillCoroutine != null)
{
// 如果上一个技能还在释放,可根据需求中断或直接返回
StopCoroutine(currentSkillCoroutine);
}
AnimatorComponent.SetInteger("SkillId", skillId);
AnimatorComponent.SetBool("userSkill", true);
currentSkillCoroutine = StartCoroutine(skillLogic.ExecuteSkill(gameObject, target));
}
public void PlaySound(int soundType)
{
MasterAudio.StopAllSoundsOfTransform(transform);
string str = "";
switch (soundType)
{
case 0:
str = idleSound;
break;
case 1:
str = runSound;
break;
case 2:
str = attackSound;
break;
case 3:
str = dieSound;
break;
}
if(str!="")
MasterAudio.PlaySound3DAtVector3(str, transform.position);
}
}
}