387 lines
11 KiB
C#
387 lines
11 KiB
C#
using System;
|
||
using System.Collections;
|
||
using System.Collections.Generic;
|
||
using DG.Tweening;
|
||
using DragonLi.Core;
|
||
using UnityEngine;
|
||
using Random = UnityEngine.Random;
|
||
|
||
public class DeathKnightBoss : Enemy
|
||
{
|
||
[Header("Base")]
|
||
public GameObject clipQuadObj;
|
||
public Animator bossManAnim;
|
||
public Animator houseAnim;
|
||
|
||
[Header("预制体")]
|
||
public GameObject swordEx;
|
||
public GameObject swordPre;
|
||
|
||
[Header("Sword Skill")]
|
||
public DeathSwordProjectile swordProjectilePrefab;
|
||
public Transform swordFirePoint;
|
||
|
||
[HideInInspector]
|
||
public bool IsSwordSkillFinished;
|
||
|
||
|
||
[Header("Phantom Skill")]
|
||
public GameObject phantomPrefab;
|
||
private float phantomSpacing = 3f;
|
||
private float phantomChargeDistance = 30f;
|
||
private float phantomChargeSpeed = 4f;
|
||
|
||
[HideInInspector]
|
||
public bool isPhantomSkillFinished;
|
||
|
||
|
||
public EnemyBoneHit skill1Bone;
|
||
|
||
|
||
public GameObject[] bossCrystals;
|
||
|
||
private void Start()
|
||
{
|
||
swordEx.SetActive(false);
|
||
swordPre.SetActive(false);
|
||
bloodSlider.gameObject.SetActive(false);
|
||
skill1Bone.SetDamage(Data.Atk_1P);
|
||
|
||
foreach (var item in bossCrystals)
|
||
{
|
||
item.SetActive(false);
|
||
}
|
||
transform.LookAt(GameManager.Ins.player.transform.position.ReflectVectorXOZ());
|
||
}
|
||
|
||
public override void Update()
|
||
{
|
||
base.Update();
|
||
if (enemyState != EnemyState.Show && enemyState != EnemyState.Dead)
|
||
{
|
||
skill1Timer += Time.deltaTime;
|
||
skill2Timer += Time.deltaTime;
|
||
}
|
||
}
|
||
|
||
#region Show
|
||
|
||
public override void Show()
|
||
{
|
||
base.Show();
|
||
GameInit.Ins.self.SetAiPath(false);
|
||
clipQuadObj.transform.DOLocalMoveY(10, 5).OnComplete(() =>
|
||
{
|
||
Roar();
|
||
StartCoroutine(RotateToPlayer());
|
||
isShowEnd = true;
|
||
});
|
||
}
|
||
|
||
public override void EndShow()
|
||
{
|
||
base.EndShow();
|
||
swordPre.SetActive(true);
|
||
bloodSlider.gameObject.SetActive(true);
|
||
foreach (var item in bossCrystals)
|
||
{
|
||
item.SetActive(true);
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
public void ResetSkill1() => skill1Timer = 0;
|
||
public void ResetSkill2() => skill2Timer = 0;
|
||
public bool IsUserSkill3()
|
||
{
|
||
float hpPercent =health / maxHealth;
|
||
int index = Mathf.FloorToInt(hpPercent / 0.2f);
|
||
|
||
if (index < spikeTriggerIndex)
|
||
{
|
||
spikeTriggerIndex = index;
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
public bool HasAnySkillReady()
|
||
{
|
||
return skill1Timer >= skill1Cooldown ||
|
||
skill2Timer >= skill2Cooldown ||
|
||
IsUserSkill3();
|
||
}
|
||
|
||
public void Roar()
|
||
{
|
||
bossManAnim.SetInteger("state", 0);
|
||
houseAnim.SetInteger("state", 0);
|
||
GameManager.Ins.PlaySound3D("1.2", transform);
|
||
}
|
||
|
||
public void Retreat()
|
||
{
|
||
houseAnim.SetInteger("state", 4);
|
||
}
|
||
|
||
public void Run()
|
||
{
|
||
bossManAnim.SetInteger("state",2);
|
||
houseAnim.SetInteger("state",2);
|
||
}
|
||
|
||
public void Walk()
|
||
{
|
||
houseAnim.SetInteger("state",3);
|
||
}
|
||
|
||
public void Idle()
|
||
{
|
||
bossManAnim.SetInteger("state",1);
|
||
houseAnim.SetInteger("state",1);
|
||
GameManager.Ins.PlaySound3D("1.45", transform);
|
||
swordEx.SetActive(false);
|
||
}
|
||
|
||
public void Slash()
|
||
{
|
||
bossManAnim.SetInteger("state",3);
|
||
houseAnim.SetInteger("state",1);
|
||
GameManager.Ins.PlaySound3D("1.57",transform,true);
|
||
|
||
}
|
||
|
||
public void ThrowSword()
|
||
{
|
||
bossManAnim.SetInteger("state",4);
|
||
houseAnim.SetInteger("state",1);
|
||
GameManager.Ins.PlaySound3D("1.59",transform,true);
|
||
}
|
||
|
||
public override void Hit()
|
||
{
|
||
if(enemyState!= EnemyState.Idle)
|
||
return;
|
||
base.Hit();
|
||
int hitIndex = Random.Range(0, 100);
|
||
string hitStr=hitIndex>=50 ?"isHit1":"isHit2";
|
||
bossManAnim.SetTrigger(hitStr);
|
||
}
|
||
|
||
public float rotateSpeed = 90f;
|
||
public IEnumerator RotateToPlayer()
|
||
{
|
||
Transform player = GameManager.Ins.player.transform;
|
||
Vector3 dir = player.position - transform.position;
|
||
dir.y = 0;
|
||
if (dir.sqrMagnitude < 0.01f)
|
||
yield break;
|
||
Walk();
|
||
Quaternion targetRot = Quaternion.LookRotation(dir);
|
||
|
||
while (Quaternion.Angle(transform.rotation, targetRot) > 1f)
|
||
{
|
||
if (isDead)
|
||
{
|
||
StopAllCoroutines();
|
||
}
|
||
transform.rotation = Quaternion.RotateTowards(
|
||
transform.rotation,
|
||
targetRot,
|
||
rotateSpeed * Time.deltaTime
|
||
);
|
||
yield return null;
|
||
}
|
||
Idle();
|
||
}
|
||
|
||
public float retreatDis = 15;
|
||
public float retreatSpeed = 4;
|
||
public IEnumerator BossRetreat()
|
||
{
|
||
Retreat();
|
||
Transform player = GameManager.Ins.player.transform;
|
||
float curDis=Vector3.Distance(player.position.ReflectVectorXOZ(), transform.position.ReflectVectorXOZ());
|
||
Vector3 startPos = transform.position;
|
||
Vector3 forward = (player.position - startPos).normalized;
|
||
forward.y = 0;
|
||
while (curDis < retreatDis)
|
||
{
|
||
float step = retreatSpeed * Time.deltaTime;
|
||
transform.position -= forward * step;
|
||
curDis=Vector3.Distance(player.position.ReflectVectorXOZ(), transform.position.ReflectVectorXOZ());
|
||
yield return null;
|
||
}
|
||
Idle();
|
||
}
|
||
|
||
public void LaunchSword()
|
||
{
|
||
IsSwordSkillFinished = false;
|
||
|
||
Vector3 targetPos = GameManager.Ins.player.transform.position.ReflectVectorXOZ();
|
||
swordPre.SetActive(false); // Boss 手上剑隐藏
|
||
swordEx.SetActive(false);
|
||
DeathSwordProjectile sword = Instantiate(
|
||
swordProjectilePrefab,
|
||
swordFirePoint.position,
|
||
swordFirePoint.rotation
|
||
);
|
||
|
||
sword.Init(
|
||
swordFirePoint,
|
||
targetPos,
|
||
Data.Atk_2,
|
||
() => IsSwordSkillFinished = true
|
||
);
|
||
|
||
//sword.SetOriginSword(sword); // 绑定自身
|
||
//swordEx.SetActive(false);
|
||
//sword.SetActive(false); // Boss 手上剑隐藏
|
||
}
|
||
|
||
|
||
public IEnumerator ExecutePhantomCharge()
|
||
{
|
||
Transform player = GameManager.Ins.player.transform;
|
||
|
||
Vector3 startPos = transform.position;
|
||
Vector3 forward = (player.position - startPos).normalized;
|
||
forward.y = 0;
|
||
|
||
Vector3 right = Vector3.Cross(Vector3.up, forward);
|
||
|
||
// ===== 1️⃣ 随机一个分身空位(0~5)=====
|
||
int emptySlot = Random.Range(0, 6);
|
||
|
||
List<GameObject> allChargers = new List<GameObject>();
|
||
|
||
// ===== 2️⃣ 生成左边 3 个 =====
|
||
for (int i = 0; i < 3; i++)
|
||
{
|
||
int slotIndex = i; // 0,1,2
|
||
|
||
if (slotIndex == emptySlot)
|
||
continue;
|
||
|
||
Vector3 offset = -right * (i + 1) * phantomSpacing;
|
||
SpawnPhantom(startPos + offset, forward, allChargers);
|
||
}
|
||
|
||
// ===== 3️⃣ 生成右边 3 个 =====
|
||
for (int i = 0; i < 3; i++)
|
||
{
|
||
int slotIndex = i + 3; // 3,4,5
|
||
|
||
if (slotIndex == emptySlot)
|
||
continue;
|
||
|
||
Vector3 offset = right * (i + 1) * phantomSpacing;
|
||
SpawnPhantom(startPos + offset, forward, allChargers);
|
||
}
|
||
|
||
// ===== 4️⃣ 加入本体(永远存在)=====
|
||
allChargers.Add(gameObject);
|
||
GameManager.Ins.PlaySound3D("1.54",transform,true);
|
||
yield return new WaitForSeconds(3f);
|
||
Slash();
|
||
houseAnim.SetInteger("state", 2);
|
||
|
||
GameManager.Ins.PlaySound3D("1.55",transform,true);
|
||
// ===== 5️⃣ 直线冲锋 =====
|
||
float moved = 0f;
|
||
|
||
while (moved < phantomChargeDistance)
|
||
{
|
||
float step = phantomChargeSpeed * Time.deltaTime;
|
||
moved += step;
|
||
foreach (var obj in allChargers)
|
||
{
|
||
if (obj == null) continue;
|
||
if(obj.GetComponent<DeathKnightPhantomHit>()!=null)
|
||
obj.GetComponent<DeathKnightPhantomHit>().Run();
|
||
obj.transform.position += forward * step;
|
||
}
|
||
yield return null;
|
||
}
|
||
yield return new WaitForSeconds(1f);
|
||
foreach (var obj in allChargers)
|
||
{
|
||
if (obj == null) continue;
|
||
if(obj.GetComponent<DeathKnightPhantomHit>()!=null)
|
||
obj.GetComponent<DeathKnightPhantomHit>().Idle();
|
||
Idle();
|
||
}
|
||
yield return new WaitForSeconds(2f);
|
||
// ===== 6️⃣ 销毁分身 =====
|
||
|
||
GameManager.Ins.PlaySound3D("1.64",transform,true);
|
||
foreach (var obj in allChargers)
|
||
{
|
||
if (obj != gameObject)
|
||
Destroy(obj);
|
||
}
|
||
}
|
||
|
||
void SpawnPhantom(Vector3 pos, Vector3 forward, List<GameObject> list)
|
||
{
|
||
GameObject phantom = Instantiate(
|
||
phantomPrefab,
|
||
pos,
|
||
Quaternion.LookRotation(forward)
|
||
);
|
||
var phantomStr=phantom.GetComponent<DeathKnightPhantomHit>();
|
||
phantomStr.SetDamage(Data.Atk_3);
|
||
list.Add(phantom);
|
||
}
|
||
|
||
public override void ChangeHp(float value, object info, Transform _sender)
|
||
{
|
||
if(enemyState== EnemyState.Show)
|
||
return;
|
||
base.ChangeHp(value, info, _sender);
|
||
}
|
||
|
||
public override void Dead()
|
||
{
|
||
if (isDead)
|
||
{
|
||
return;
|
||
}
|
||
GameManager.Ins.curLevel++;
|
||
isDead = true;
|
||
health = 0;
|
||
if (bloodSlider != null)
|
||
bloodSlider.gameObject.SetActive(false);
|
||
if(ColliderComponent!=null)
|
||
ColliderComponent.enabled = false;
|
||
if(behaviourTree != null)
|
||
behaviourTree.enabled = false;
|
||
swordPre.SetActive(false);
|
||
GameManager.Ins.PlaySound3D("1.53",transform,true);
|
||
foreach (var item in bossCrystals)
|
||
{
|
||
if(item==null)
|
||
continue;
|
||
item.SetActive(false);
|
||
}
|
||
StartCoroutine(DeathDissolve());
|
||
}
|
||
|
||
IEnumerator DeathDissolve()
|
||
{
|
||
clipQuadObj.SetActive(true);
|
||
bloodSlider.gameObject.SetActive(false);
|
||
clipQuadObj.transform.localPosition = new Vector3(0, 5f, 0);
|
||
houseAnim.SetBool("isDie",true);
|
||
bossManAnim.SetBool("isDie",true);
|
||
yield return new WaitForSeconds(3f);
|
||
clipQuadObj.transform.DOLocalMoveY(-5f, 5);
|
||
yield return new WaitForSeconds(8f);
|
||
Destroy(gameObject);
|
||
GameManager.Ins.WinEndGame();
|
||
}
|
||
}
|