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

742 lines
20 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using BehaviorDesigner.Runtime;
using DarkTonic.MasterAudio;
using DragonLi.Core;
using DragonLi.Frame;
using Mirror;
using UnityEngine;
namespace Valheim
{
public enum PetState
{
/// <summary>
/// 初始解锁
/// </summary>
Birth=99,
/// <summary>
/// 被困
/// </summary>
NotRescue = 0,
/// <summary>
/// 解救中
/// </summary>
Rescueing = 1,
/// <summary>
/// 野生
/// </summary>
Wild = 2,
/// <summary>
/// 已受训练
/// </summary>
Trained = 3,
/// <summary>
/// 濒死
/// </summary>
Terminal = 4,
/// <summary>
/// 待分配
/// </summary>
NeedAssigned = 5,
/// <summary>
/// 当前胜利
/// </summary>
CurWin=6,
/// <summary>
/// 通过一个区域胜利
/// </summary>
AreaWin=7,
}
public enum OrderType
{
/// <summary>
/// 无指令
/// </summary>
Null = 0,
/// <summary>
/// 攻击
/// </summary>
Attack = 1,
/// <summary>
/// 脱战
/// </summary>
Retreat = 2,
/// <summary>
/// 使用技能
/// </summary>
UserSkill=3,
}
public class Pet : Agent
{
#region
/// <summary>
/// 宠物唯一id
/// </summary>
public int id = 0;
/// <summary>
/// 宠物类型
/// </summary>
[NonSerialized]
public int petType;
/// <summary>
/// 宠物状态
/// </summary>
#if UNITY_EDITOR
[DisplayOnly]
#endif
[SyncVar]
public PetState state = PetState.Wild;
/// <summary>
/// 当前所受指令
/// </summary>
#if UNITY_EDITOR
[DisplayOnly]
#endif
[SyncVar]
public OrderType order = OrderType.Retreat;
/// <summary>
/// 等级
/// </summary>
#if UNITY_EDITOR
[DisplayOnly]
#endif
[SyncVar]
public int lvl;
/// <summary>
/// 速度
/// </summary>
[NonSerialized]
public float speed = 2;
/// <summary>
/// 攻击
/// </summary>
#if UNITY_EDITOR
[DisplayOnly]
#endif
[SyncVar]
public float atk = 5;
/// <summary>
/// 攻击范围
/// </summary>
public float atkArea = 1.5f;
/// <summary>
/// 导航半径
/// </summary>
#if UNITY_EDITOR
[DisplayOnly]
#endif
public float radius = 0.5f;
/// <summary>
/// 攻击速率
/// </summary>
#if UNITY_EDITOR
[DisplayOnly]
#endif
public float rate = 1.0f;
/// <summary>
/// 追击速度
/// </summary>
#if UNITY_EDITOR
[DisplayOnly]
#endif
public float seekSpeed = 1.0f;
/// <summary>
/// 经验值
/// </summary>
#if UNITY_EDITOR
[DisplayOnly]
#endif
[SyncVar]
public float exp = 0f;
/// <summary>
/// 所属队伍
/// </summary>
#if UNITY_EDITOR
[DisplayOnly]
#endif
[SyncVar]
public int teamId = -1;
/// <summary>
/// 所属笼子
/// </summary>
#if UNITY_EDITOR
[DisplayOnly]
#endif
public int cageId = -1;
/// <summary>
/// 编号
/// </summary>
public int index = -1;
/// <summary>
/// 模型缩放
/// </summary>
[SyncVar]
private float modelScale = 1.5f;
public string petName = "";
#endregion
[Header("捕捉范围盒")]
public Collider CatchCollider;
public BehaviorTree behaviorTree;
public LayerMask hitMask;
public Transform Petmodel;
public SkinnedMeshRenderer SkinnedMeshRenderer;
[NonSerialized]
public bool isSetOut = false;
[NonSerialized]
public float modelHeight;
private AudioSource _audioSource = null;
public AudioSource audioSource
{
get
{
if (_audioSource == null)
{
_audioSource = GetComponentInChildren<AudioSource>();
}
return _audioSource;
}
}
private Animator _animator = null;
public Animator animator
{
get
{
if (_animator == null)
{
_animator = GetComponentInChildren<Animator>();
}
return _animator;
}
}
public Transform uiPos;
public GameObject addHpEffect;
[SoundGroup] public string attackSound;
[SoundGroup] public string levelUpSound;
//[SoundGroup] public string speedSound;
[SoundGroup] public string idleSound;
[SoundGroup] public string runSound;
[SoundGroup] public string skill1Sound;
[SoundGroup] public string skill2Sound;
[SoundGroup] public string playSound;
[SoundGroup] public string walkSound;
#region
[Server]
public void OnSpawn(EnemyData data,int curLvl,PetState curState,int curCageId,int curIndex)
{
//id = data.EnemyId;
index = curIndex;
health = data.Hp;
OriginHealth = data.Hp;
atk = data.Atk;
petType = data.EnemyType;
lvl = curLvl;
cageId = curCageId;
campId = data.Camp;
NavAgent.acceleration = 3F;
//atkArea = transform.localScale.x*1.5f;
radius = NavAgent.radius;
modelHeight=uiPos.position.y;
transform.localScale=Vector3.one*modelScale;
petName = data.Name;
order = OrderType.Retreat;
Debug.Log("宠物状态->" + curState);
teamId = -1;
behaviorTree.enabled = true;
Debug.Log("宠物行为树打开");
GameManager.Ins.CreatePetUI(this);
addHpEffect.SetActive(false);
skillList.Clear();
foreach (var skillId in data.SkillIds)
{
var skill = GameManager.Ins.SkillDataList[skillId];
if (skill.SkillInterval <= 1)
{
skillHpInterval=skill.SkillInterval;
}
else
{
waitSkillTime = skill.SkillInterval;
}
skillList.Add(skillId);
}
curWaitSkillTime = waitSkillTime;
Change2State(curState);
RpcCoillder();
InitBree();
base.OnSpawn();
}
[ClientRpc]
public void RpcCoillder()
{
if (state != PetState.NotRescue)
transform.GetComponent<Collider>().isTrigger = false;
}
public void InitBree()
{
behaviorTree.RegisterEvent("Birth", Birth);
behaviorTree.RegisterEvent("StopPlay", StopPlay);
behaviorTree.RegisterEvent("UserSkill", UseSkill);
behaviorTree.RegisterEvent("SetBlood", SetIsBlood);
behaviorTree.RegisterEvent("EndBlood", EndBlood);
}
public override void Awake()
{
base.Awake();
}
public new void Update()
{
if (isServer)
{
OnUpdate();
if (state == PetState.Rescueing)
{
RigidbodyComponent.velocity = transform.forward * Time.deltaTime * 50;
}
if (NavAgent != null & NavAgent.enabled)
{
UpdateAniamtorValues(NavAgent.velocity);
}
else if (RigidbodyComponent)
{
UpdateAniamtorValues(RigidbodyComponent.velocity);
}
//缺少检测在攻击状态
if (!isUserSkill&& state==PetState.Trained)
{
curWaitSkillTime-=Time.deltaTime;
if (curWaitSkillTime <= 0)
{
isUserSkill = true;
}
}
if (!isUserHpSkill&&userHpSkillIndex<=0&& state==PetState.Trained)
{
if (skillHpInterval != 0 && health <= skillHpInterval)
isUserHpSkill = true;
}
if (isBlood)
{
if (!animator.GetBool("isDie"))
{
animator.SetBool("isDie",true);
animator.SetTrigger("die");
NavAgent.isStopped = true;
}
timer += Time.deltaTime;
if (timer >= interval)
{
AddBlood(1,OriginHealth*0.1f);
timer = 0;
}
}
addHpEffect.SetActive(isBlood);
}
}
#endregion
public override void Die(object info, Transform _sender)
{
base.Die(info, _sender);
}
public override void OnHeathChanged(float currentHealth)
{
base.OnHeathChanged(currentHealth);
}
/// <summary>
/// 出生
/// </summary>
public void Birth()
{
transform.GetComponent<Animator>().SetInteger("State",2);
MasterAudio.PlaySound3DAtTransform(playSound, transform);
// StartCoroutine(PlayBirthSound(() =>
// {
//
// }));
}
public IEnumerator PlayBirthSound(Action cb)
{
MasterAudio.PlaySound3DAtTransform(playSound, transform);
MasterAudioGroup audioGroup = MasterAudio.GrabGroup(playSound);
if (cb != null)
{
while (audioGroup.ActiveVoices>0)
{
yield return new WaitForSeconds(1f);
}
cb?.Invoke();
}
}
public void StopPlay()
{
transform.GetComponent<Animator>().SetInteger("State",0);
}
[Server]
public void Change2State(PetState state)
{
this.state = state;
RigidbodyComponent.isKinematic = true;
CatchCollider.gameObject.SetActive(false);
if (state == PetState.Rescueing)
{
RigidbodyComponent.isKinematic = false;
}
else if (state == PetState.Wild)
{
RigidbodyComponent.velocity = Vector3.zero;
RigidbodyComponent.isKinematic = true;
transform.position = new Vector3(transform.position.x, 0, transform.position.z);
CatchCollider.gameObject.SetActive(true);
}
}
/// <summary>
/// 捕捉宠物
/// </summary>
public void Catched(int curTeamId)
{
this.teamId = curTeamId;
state = PetState.Trained;
order = OrderType.Attack;
NavAgent.enabled = true;
CatchCollider.gameObject.SetActive(false);
}
/// <summary>
/// 攻击命令
/// </summary>
[Server]
public void AttackOrder(Enemy enemy)
{
//Debug.Log(string.Format("宠物{0}收到攻击命令,攻击怪物{1}", id, enemyId));
if (enemy != null)
{
order = OrderType.Attack;
behaviorTree.SetVariable("target", (SharedGameObject)enemy.gameObject);
}
}
/// <summary>
/// 撤退命令
/// </summary>
public void RetreatOrder()
{
//Debug.Log(string.Format("宠物{0}收到脱战命令", id));
order = OrderType.Retreat;
}
public void AttackOnce()
{
if (isServer)
{
GameObject target = (GameObject)behaviorTree.GetVariable("target").GetValue();
if (target == null) return;
// PlayAttackClip();
//PlayAudioVecRpc(attackSound, transform);
//创建攻击特效
Ray ray = new Ray(transform.position, transform.forward);
RaycastHit hit;
if (Physics.Raycast(ray, out hit, Mathf.Infinity, 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)
{
damagable.ApplyDamage(atk, false, transform);
//AddExp();
}
}
}
[ClientRpc]
public void PlayAudioVecRpc(string path, Transform target)
{
MasterAudio.PlaySound3DAtVector3(path, target.position);
}
public void AddBlood(int time, float repliesVolume)
{
if (Health < OriginHealth)
{
for (int i = 0; i < time; i++)
{
Health += repliesVolume;
Health = Mathf.Min(Health, OriginHealth);
}
}
}
[Server]
public void AddExp()
{
Upgrad();
PlayAudioVecRpc(levelUpSound, transform);
}
[Server]
public void Upgrad()
{
//exp -= lvUpExp;
lvl++;
EnemyData petData = GameManager.Ins.EnemyDataList[id];
OriginHealth = petData.Hp;
health = OriginHealth;
atk = petData.Atk;
// speed *= petData.Speed;
// rate *= petData.Rate;
animator.SetBool("isDie",false);
GameManager.Ins.CreateUpgradEffect(transform.position);
EndBlood();
//PlayLevelUpClip();
}
[ClientRpc]
public void PlayLevelUpClip()
{
AudioClip clip = Resources.Load<AudioClip>("Audios/pet_levelup");
if (audioSource != null && clip != null)
{
audioSource.loop = false;
audioSource.PlayOneShot(clip);
}
}
public IEnumerator AutoAddBlood(int interval = 1, int repliesVolume = 10)
{
while (Health < OriginHealth)
{
Debug.Log("Regeneration");
Health += repliesVolume;
Health = Mathf.Min(Health, OriginHealth);
yield return new WaitForSeconds(interval); // 每秒恢复10点生命值
}
StopCoroutine(AutoAddBlood());
}
[Server]
public void AutoAddPetBlood(int interval = 1, int repliesVolume = 10)
{
StartCoroutine(AutoAddBlood(interval, repliesVolume));
}
public void StopAutoAddPetBlood()
{
StopCoroutine(AutoAddBlood());
}
/// <summary>
/// 治愈魔法
/// </summary>
void OnParticleCollision(GameObject other)
{
if (other.gameObject.tag == "Enemy")
{
transform.GetComponent<IDamagable>().ApplyDamage(1.5F, null, null);
}
if (other.gameObject.tag == "Weapon")
{
AddBlood(1, 1F);
}
}
[Server]
public void SetOutLine(float value)
{
RpcSetOutLine(value);
}
[ClientRpc]
public void RpcSetOutLine(float value)
{
if (SkinnedMeshRenderer != null && SkinnedMeshRenderer.materials.Length > 1)
{
Material targetMaterial = SkinnedMeshRenderer.materials[1];
targetMaterial.SetFloat("_OutlineWidth", value);
}
}
// public void SetOutLine(float value)
// {
// if (SkinnedMeshRenderer != null && SkinnedMeshRenderer.materials.Length > 1)
// {
// Material targetMaterial = SkinnedMeshRenderer.materials[1];
// targetMaterial.SetFloat("_OutlineWidth", value);
// }
// }
public void SetOutLineColor()
{
Material targetMaterial = SkinnedMeshRenderer.materials[1];
targetMaterial.SetColor("_OutlineColor", new Color(0F, 0.2F, 0.2F));
}
public void PlayJump()
{
AnimatorComponent.SetBool("Jump", true);
}
private Coroutine currentSkillCoroutine;
public void UseSkill()
{
int skillId= (int)behaviorTree.GetVariable("skillId").GetValue();
var target= (GameObject)behaviorTree.GetVariable("target").GetValue();
SkillLogicBase skillLogic = GameManager.Ins.skillManager.GetSkillLogic(skillId);
if (skillLogic == null)
{
Debug.LogWarning($"技能ID[{skillId}] 不存在或未在 SkillManager 中注册");
return;
}
// 开启协程执行技能
if (currentSkillCoroutine != null)
{
// 如果上一个技能还在释放,可根据需求中断或直接返回
StopCoroutine(currentSkillCoroutine);
}
animator.SetInteger("SkillId", skillId);
animator.SetBool("userSkill", true);
currentSkillCoroutine = StartCoroutine(skillLogic.ExecuteSkill(gameObject, target));
}
public void EndSkill()
{
animator.SetBool("userSkill", false);
}
public void PlaySound(int soundType)
{
MasterAudio.StopAllSoundsOfTransform(transform);
string str = "";
switch (soundType)
{
case 0:
str = idleSound;
break;
case 1 :
str = playSound;
break;
case 2:
str = runSound;
break;
case 3:
str = attackSound;
break;
case 4:
str = walkSound;
break;
case 5:
str = skill1Sound;
break;
case 6:
str = skill2Sound;
break;
}
if(str!="")
MasterAudio.PlaySound3DAtTransform(str, transform);
}
private bool isBlood;
public float interval = 1F;
/// <summary>
/// 检测时间
/// </summary>
private float timer = 0;
private float waitAddHpTimer = 30;
public void OnTriggerEnter(Collider other)
{
// if (other.gameObject.CompareTag("Weapon"))
// {
// var weapon = other.transform.parent.GetComponent<IceStaff>();
// if (weapon != null&& weapon.hand==HandType.Left&& state==PetState.Terminal)
// {
// isBlood = true;
// addHpEffect.SetActive(true);
// }
// }
}
private void OnTriggerExit(Collider other)
{
// if (other.gameObject.CompareTag("Weapon"))
// {
// var weapon = other.transform.parent.GetComponent<IceStaff>();
// if (weapon != null&& weapon.hand==HandType.Left)
// {
// isBlood = false;
//
// }
// }
}
public void SetIsBlood()
{
isBlood = true;
}
public void EndBlood()
{
isBlood = false;
NavAgent.isStopped = false;
}
}
}