WaterSprite.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using Sirenix.OdinInspector;
  5. public class WaterSprite : Boss
  6. {
  7. public enum AttackMethods
  8. {
  9. Move, //移动,浮空+点水,动画控制高度,代码控制位移x;点水处生化
  10. Umbrella, //黑伞回旋镖
  11. Lotus, //召唤莲花
  12. Hair, //辫子攻击
  13. Rush, //水面冲刺
  14. Shoot, //苦无射击
  15. }
  16. public enum BossStage
  17. {
  18. waterSprite, //水诡形态
  19. polliwog, //蝌蚪形态
  20. }
  21. [System.Serializable]
  22. public struct AttackAssignment
  23. {
  24. public AttackMethods attack;
  25. public int weight; //权重
  26. }
  27. [System.Serializable]
  28. public struct BossAttackType
  29. {
  30. public AttackCategories category;
  31. public AttackAssignment[] attacks;
  32. }
  33. [Space(30)]
  34. [Header("水诡白家娘娘")]
  35. [Header("攻击配置")]
  36. public BossAttackType[] attackConfigurations;
  37. public AttackMethods curAttackType;
  38. private WaterSpriteAttackController attack;
  39. public GameObject smokeFx;
  40. public int attackNum;
  41. public int attackCount = 0;
  42. public float comboInterval;
  43. [Header("瞬身")]
  44. public float bodyFlickerTime;
  45. [HideInInspector]public float bodyFlickerTimer;
  46. public bool isDisappear = false;
  47. [Header("移动")]
  48. private WaterSpriteJumpMove jumpMoveCS;
  49. [Header("变化形态")]
  50. [LabelText("召唤蝌蚪的数量")]
  51. public int polliNum; //召唤蝌蚪的数量
  52. [LabelText("召唤蝌蚪的脚本")]
  53. public PolliwogShot pws;
  54. private BossStage curBossStage;
  55. [Range(1,100)]
  56. [LabelText("血量到达多少百分比后切换蝌蚪形态")]
  57. public int[] changeStateHPPer; //每次转化形态的血量百分比预设
  58. private int nextAimHP; //下一个血量预设值
  59. private int changeHPID; //下一个是预设数组里的第几个
  60. [LabelText("过多久从蝌蚪形态切回水诡形态")]
  61. public float polliTime;
  62. private float pastPoliTime; //已经过去的蝌蚪时间
  63. [Header("变化阶段")]
  64. [LabelText("愤怒状态攻击间隔")] public float angryAttackInterval;
  65. [LabelText("愤怒状态持续时间")] public float angryDuration;
  66. private float angryTimer;
  67. [FoldoutGroup("状态提示")] public bool isAngry = false;
  68. [FoldoutGroup("状态提示")] public bool isTakingUmbre = false;
  69. [FoldoutGroup("状态提示")] public bool isHoldingUmbre = true;
  70. [FoldoutGroup("状态提示")] public bool isReadyChangStage = false;
  71. [FoldoutGroup("状态提示")] public bool isAppearing = false;
  72. private Coroutine shootCoroutine;
  73. private float hairAttackTimer;
  74. public GameObject smokeShell;
  75. public GameObject summonStart;
  76. public override void Init()
  77. {
  78. base.Init();
  79. attack = GetComponent<WaterSpriteAttackController>();
  80. jumpMoveCS = GetComponent<WaterSpriteJumpMove>();
  81. jumpMoveCS.ws = this;
  82. nextAimHP = (int)(changeStateHPPer[changeHPID] / 100f * totalHp);
  83. changeHPID++;
  84. curInterval = statOfPhase[bossPhase].maxInterval;
  85. StartCoroutine(Appear());
  86. GameObject[] injuryNums = new GameObject[100];
  87. for (int i = 0; i < 100; i++)
  88. {
  89. injuryNums[i] = PoolManager.Instantiate(injuryNumTextSummon);
  90. }
  91. foreach(GameObject injuryNum in injuryNums)
  92. {
  93. injuryNum.SetActive(false);
  94. }
  95. }
  96. public override void Update()
  97. {
  98. base.Update();
  99. OnStage();
  100. if (bossPhase < statOfPhase.Length - 1)
  101. {
  102. if ((float)hp / (float)totalHp < statOfPhase[bossPhase + 1].hpRadioThreshold)
  103. {
  104. ChangePhase();
  105. }
  106. }
  107. if (isAngry)
  108. {
  109. if (angryTimer >= 0) angryTimer -= Time.deltaTime;
  110. else
  111. {
  112. Debug.Log("退出愤怒状态");
  113. isAngry = false;
  114. StartCoroutine(Appear());
  115. ChangeCirculate(3);
  116. }
  117. }
  118. if(!isHoldingUmbre) attack.OnBlackUmbrella();
  119. }
  120. //步步生花
  121. public override void ToMove()
  122. {
  123. jumpMoveCS.ToJump();
  124. }
  125. public override void OnMove()
  126. {
  127. jumpMoveCS.OnJumpMove();
  128. }
  129. public override void BeHit(AttackController.AttackMethod attackMethod, Character attackFrom, int damage = -1)
  130. {
  131. base.BeHit(attackMethod, attackFrom, damage);
  132. if (hp <= nextAimHP)
  133. {
  134. isReadyChangStage = true;
  135. }
  136. }
  137. public override void RandomAttackType(AttackCategories cate)
  138. {
  139. AttackAssignment[] attacks = attackConfigurations[(int)cate].attacks;
  140. int[] powers = new int[attacks.Length];
  141. for (int i = 0; i < attacks.Length; i++)
  142. {
  143. if (!isHoldingUmbre && attacks[i].attack == AttackMethods.Umbrella)
  144. {
  145. continue;
  146. }
  147. if(curAttackType == AttackMethods.Hair && attacks[i].attack == AttackMethods.Hair && !isAngry) continue;
  148. powers[i] = attacks[i].weight;
  149. }
  150. int a = RandomWithWeight(powers);
  151. curAttackType = attacks[a].attack;
  152. attackCount = 1;
  153. switch (curAttackType)
  154. {
  155. case AttackMethods.Hair:
  156. if (bossPhase == 2) attackCount = Random.Range(1, 3);
  157. break;
  158. case AttackMethods.Shoot:
  159. if (bossPhase == 2) attackCount = Random.Range(1, 3);
  160. if (bossPhase == 2) attackCount = Random.Range(2, 4);
  161. break;
  162. }
  163. }
  164. public override void ChangePhase()
  165. {
  166. base.ChangePhase();
  167. switch (bossPhase)
  168. {
  169. case 1:
  170. ChangeCirculate(2);
  171. break;
  172. case 2:
  173. ChangeCirculate(1);
  174. bodyTrans.gameObject.SetActive(false);
  175. isAngry = true;
  176. angryTimer = angryDuration;
  177. RandomAttackState();
  178. ClearAllSkills();
  179. isTakingUmbre = false;
  180. break;
  181. }
  182. }
  183. public override void ClearAllSkills()
  184. {
  185. if (attack.blackUmbrella)
  186. {
  187. if(attack.blackUmbrella.gameObject.activeSelf)
  188. attack.blackUmbrella.BeCleared();
  189. isHoldingUmbre = true;
  190. }
  191. if(shootCoroutine != null) StopCoroutine(shootCoroutine);
  192. }
  193. public override void ChangeBossState(BossState bs)
  194. {
  195. base.ChangeBossState(bs);
  196. if(bs == BossState.rise && oldState != BossState.weak && !isHoldingUmbre) ani.Play("idle_no3", 0, 0);
  197. }
  198. #region 状态相关
  199. public override void ChangeState(CharacterState newState)
  200. {
  201. if (state == newState || newState == CharacterState.FramePause || isAppearing)
  202. {
  203. return;
  204. }
  205. Debug.Log("从" + state + "切换到" + newState);
  206. switch (state)
  207. {
  208. case CharacterState.Run:
  209. rb.velocity = Vector3.zero;
  210. break;
  211. case CharacterState.Attack:
  212. spineEvent.isAttackOn = false;
  213. break;
  214. default:
  215. break;
  216. }
  217. state = newState;
  218. switch (newState)
  219. {
  220. case CharacterState.Idle:
  221. if (bossState != BossState.weak)
  222. {
  223. if (isHoldingUmbre)
  224. {
  225. //Debug.Log(222);
  226. int randomAni = RandomWithWeight(weight);
  227. ani.Play(idleAniNames[randomAni], 0, 0);
  228. }
  229. else
  230. {
  231. ani.Play("idle_no3", 0, 0);
  232. }
  233. }
  234. break;
  235. case CharacterState.Run:
  236. ToMove();
  237. break;
  238. case CharacterState.Die:
  239. ani.Play("die", 0, 0);
  240. rb.constraints &= ~RigidbodyConstraints.FreezePositionY;
  241. ChangeBossState(BossState.invincible);
  242. StartCoroutine(DieEnd(13.067f));
  243. ClearAllSkills();
  244. isDie = true;
  245. break;
  246. case CharacterState.Attack:
  247. if (!isToBossRoom)
  248. {
  249. //hitResistance = 300;
  250. Attack();
  251. }
  252. break;
  253. default:
  254. break;
  255. }
  256. spineEvent.isAttackOn = false;
  257. spineEvent.isAttackOff = false;
  258. }
  259. public override void OnState()
  260. {
  261. if (curBossStage == BossStage.polliwog) return;
  262. switch (state)
  263. {
  264. case CharacterState.Idle:
  265. if (isReadyChangStage)
  266. {
  267. ChangeBossState(BossState.invincible);
  268. ani.Play("fall");
  269. if (spineEvent.isAttackOn) ChangeStage(BossStage.polliwog);
  270. return;
  271. }
  272. else if (isTakingUmbre)
  273. {
  274. return;
  275. }
  276. if (isToBossRoom)
  277. {
  278. OnChangeBG();
  279. }
  280. else if(attackCount > 0)
  281. {
  282. curInterval -= Time.deltaTime;
  283. if (curInterval <= 0)
  284. {
  285. //Debug.Log("触发连招");
  286. ChangeState(CharacterState.Attack);
  287. }
  288. }
  289. else if (bossState != BossState.weak)
  290. {
  291. //Debug.Log(curInterval);
  292. curInterval -= Time.deltaTime;
  293. if (curInterval <= 0)
  294. {
  295. RandomAttackState();
  296. }
  297. }
  298. break;
  299. case CharacterState.Run:
  300. OnMove();
  301. if (isReadyChangStage) ChangeState(CharacterState.Idle);
  302. break;
  303. case CharacterState.Attack:
  304. OnAttack();
  305. break;
  306. case CharacterState.HitStun:
  307. hitFeedbackSystem.HitStunUpdate();
  308. break;
  309. case CharacterState.SpecialStatus_Float:
  310. attributeStatus.SpecialStateEffect(SpecialState.FloatState);
  311. break;
  312. case CharacterState.SpecialStatus_BlowUp:
  313. attributeStatus.SpecialStateEffect(SpecialState.BlownUp);
  314. break;
  315. case CharacterState.SpecialStatus_ShotDown:
  316. attributeStatus.SpecialStateEffect(SpecialState.ShotDown);
  317. break;
  318. case CharacterState.SpecialStatus_Weak:
  319. attributeStatus.SpecialStateEffect(SpecialState.Weak);
  320. break;
  321. default:
  322. break;
  323. }
  324. if (isInBossRoom)
  325. {
  326. changeBackTime += Time.deltaTime;
  327. if (changeBackTime >= toOrigNeedTime)
  328. {
  329. ChangeBG(false);
  330. changeBackTime = 0;
  331. }
  332. }
  333. OnBossState();
  334. if (stage < hpRadio.Length)
  335. {
  336. if ((float)hp / (float)totalHp < hpRadio[stage])
  337. {
  338. stage++;
  339. DropSouls();
  340. }
  341. }
  342. }
  343. #endregion
  344. IEnumerator DieEnd(float time)
  345. {
  346. yield return new WaitForSeconds(time);
  347. gameObject.SetActive(false);
  348. }
  349. IEnumerator Appear()
  350. {
  351. ChangeBossState(BossState.invincible);
  352. PoolManager.Instantiate(smokeShell, transform.position);
  353. bodyTrans.gameObject.SetActive(false);
  354. isAppearing = true;
  355. yield return new WaitForSeconds(1.5f);
  356. bodyTrans.gameObject.SetActive(true);
  357. ani.Play("smoke");
  358. yield return new WaitForSeconds(1.7f);
  359. spineEvent.isAttackOn = false;
  360. ChangeBossState(BossState.normal);
  361. ani.Play("idle");
  362. isAppearing = false;
  363. }
  364. #region 形态相关
  365. public void ChangeStage(BossStage bs)
  366. {
  367. if (bs == curBossStage)
  368. {
  369. return;
  370. }
  371. isReadyChangStage = false;
  372. curBossStage = bs;
  373. switch (bs)
  374. {
  375. case BossStage.waterSprite:
  376. curInterval = Random.Range(statOfPhase[bossPhase].minInterval, statOfPhase[bossPhase].maxInterval);
  377. if (changeHPID < changeStateHPPer.Length - 1)
  378. {
  379. nextAimHP = (int)(changeStateHPPer[changeHPID] / 100f * totalHp);
  380. changeHPID++;
  381. }
  382. else
  383. {
  384. nextAimHP = -1000;
  385. }
  386. StartCoroutine(Appear());
  387. //所有蝌蚪消失
  388. pws.AllPoliDie();
  389. break;
  390. case BossStage.polliwog:
  391. PoolManager.Instantiate(summonStart, transform.position);
  392. pastPoliTime = 0;
  393. bodyTrans.gameObject.SetActive(false);
  394. //在原地召唤一堆蝌蚪
  395. pws.Shoot(polliNum, bodyTrans.position);
  396. ChangeState(CharacterState.Idle);
  397. break;
  398. default:
  399. break;
  400. }
  401. ClearAllSkills();
  402. isTakingUmbre = false;
  403. attackCount = 0;
  404. }
  405. private void OnStage()
  406. {
  407. switch (curBossStage)
  408. {
  409. case BossStage.polliwog:
  410. pastPoliTime += Time.deltaTime;
  411. if (pastPoliTime >= polliTime)
  412. {
  413. ChangeStage(BossStage.waterSprite);
  414. pastPoliTime = 0;
  415. }
  416. break;
  417. }
  418. }
  419. #endregion
  420. #region 攻击相关
  421. public override void Attack()
  422. {
  423. attackCount--;
  424. switch (curAttackType)
  425. {
  426. case AttackMethods.Move:
  427. jumpMoveCS.CountJumpTime();
  428. ChangeState(CharacterState.Run);
  429. break;
  430. case AttackMethods.Umbrella:
  431. isHoldingUmbre = false;
  432. ani.Play("shoot");
  433. attack.SkillBlackUmbrella();
  434. break;
  435. case AttackMethods.Hair:
  436. if (isHoldingUmbre) ani.Play("roar");
  437. else ani.Play("roar_no3");
  438. int attackTimes = 0;
  439. switch (bossPhase)
  440. {
  441. case 0:
  442. attackTimes = 6;
  443. break;
  444. case 1:
  445. attackTimes = 6;
  446. break;
  447. case 2:
  448. attackTimes = 10;
  449. break;
  450. }
  451. if (isAngry) attackTimes = 10;
  452. attack.SkillHairSprint(attackTimes);
  453. ChangeBossState(BossState.invincible);
  454. hairAttackTimer = Time.time;
  455. break;
  456. case AttackMethods.Shoot:
  457. if (isHoldingUmbre) ani.Play("smoke", 0, 0);
  458. else ani.Play("smoke_no3", 0, 0);
  459. bodyFlickerTimer = Time.time + 2;
  460. break;
  461. default:
  462. break;
  463. }
  464. }
  465. public override void OnAttack()
  466. {
  467. switch (curAttackType)
  468. {
  469. case AttackMethods.Umbrella:
  470. if (spineEvent.isAttackOn)
  471. {
  472. spineEvent.isAttackOn = false;
  473. attack.BlackUmbrellaRelease();
  474. EndCurAttackState(true);
  475. }
  476. break;
  477. case AttackMethods.Hair:
  478. if (Time.time - hairAttackTimer > 1.67f)
  479. {
  480. EndCurAttackState(true);
  481. }
  482. break;
  483. case AttackMethods.Shoot:
  484. if (Time.time - bodyFlickerTimer > bodyFlickerTime)
  485. {
  486. if (isDisappear)
  487. {
  488. if(pc == null) pc = PlayersInput.instance[0];
  489. int faceDir = pc.bodyTrans.transform.localScale.x > 0 ? 1 : -1;
  490. transform.position = pc.transform.position + Vector3.right * faceDir * 5;
  491. BodyFlickerAppear();
  492. if (isHoldingUmbre) ani.Play("attack", 0, 0);
  493. else ani.Play("attack_no3", 0, 0);
  494. }
  495. if (spineEvent.isAttackOn)
  496. {
  497. spineEvent.isAttackOn = false;
  498. int randomInt = Random.Range(0, 2);
  499. int num = 0;
  500. switch (randomInt)
  501. {
  502. case 0:
  503. switch (bossPhase)
  504. {
  505. case 0:
  506. num = 11;
  507. break;
  508. case 1:
  509. num = 15;
  510. break;
  511. case 2:
  512. num = 19;
  513. break;
  514. }
  515. attack.KunaiAttack((pc.transform.position - pws.transform.position).normalized, num,8);
  516. break;
  517. case 1:
  518. shootCoroutine = StartCoroutine(TestAttack(bossPhase));
  519. break;
  520. }
  521. EndCurAttackState(true);
  522. }
  523. }
  524. else
  525. {
  526. if (spineEvent.isAttackOn)
  527. {
  528. spineEvent.isAttackOn = false;
  529. BodyFlickerDisappear();
  530. }
  531. }
  532. break;
  533. default:
  534. break;
  535. }
  536. IEnumerator TestAttack(int bossPhase)
  537. {
  538. float shortInterval = 0.08f;
  539. for(int i = 0;i < 3; i++)
  540. {
  541. for (int j = 0; j < bossPhase+2; j++)
  542. {
  543. attack.KunaiAttack((pc.transform.position - pws.transform.position).normalized, 1);
  544. yield return new WaitForSeconds(shortInterval);
  545. }
  546. yield return new WaitForSeconds(0.2f);
  547. }
  548. yield return new WaitForSeconds(0.2f);
  549. for (int i = 0; i < 7 + bossPhase * 2; i++)
  550. {
  551. attack.KunaiAttack((pc.transform.position - pws.transform.position).normalized, 1);
  552. yield return new WaitForSeconds(shortInterval);
  553. }
  554. yield return new WaitForSeconds(0.2f);
  555. }
  556. }
  557. #endregion
  558. #region 瞬身相关
  559. public void BodyFlickerDisappear()
  560. {
  561. if (isDisappear || curBossStage == BossStage.polliwog) return;
  562. //Debug.Log("瞬身开始");
  563. bodyTrans.gameObject.SetActive(false);
  564. PoolManager.Instantiate(smokeFx, transform.position);
  565. ChangeBossState(BossState.invincible);
  566. bodyFlickerTimer = Time.time;
  567. isDisappear = true;
  568. }
  569. public void BodyFlickerAppear()
  570. {
  571. if (!isDisappear || curBossStage == BossStage.polliwog) return;
  572. //Debug.Log("瞬身结束");
  573. bodyTrans.gameObject.SetActive(true);
  574. PoolManager.Instantiate(smokeFx, transform.position);
  575. ChangeBossState(BossState.normal);
  576. isDisappear = false;
  577. }
  578. public override void EndCurAttackState(bool hasIntervalTime)
  579. {
  580. //和下一个动作有没有间隔
  581. if (hasIntervalTime)
  582. {
  583. if (attackCount <= 0)
  584. {
  585. if (bossPhase < statOfPhase.Length) curInterval = Random.Range(statOfPhase[bossPhase].minInterval, statOfPhase[bossPhase].maxInterval);
  586. else Debug.LogError("没有配置该阶段属性");
  587. }
  588. else curInterval = comboInterval;
  589. }
  590. ChangeState(CharacterState.Idle);
  591. ChangeBossState(BossState.rise);
  592. }
  593. #endregion
  594. }