742 lines
20 KiB
C#
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;
|
|
}
|
|
}
|
|
} |