ZombieBase.ts 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922
  1. import PlatUtils from "../../common-plugin/Scripts/PlatUtils";
  2. import { cocosz } from "../Framework/CocosZ";
  3. import Constant, { ZindexLayer } from "../Framework/Constant";
  4. import GameDate from "../Game/gameDate";
  5. import { gameMgr } from "../Game/gameMgr";
  6. import Person from "../Game/person";
  7. import { upgradeMgr } from "../Game/UpgradeMgr";
  8. import Bullet from "./bullet";
  9. const { ccclass, property } = cc._decorator;
  10. enum AtkType {
  11. front = 0,// 前方
  12. area,// 区域
  13. charge,// 冲锋
  14. range,// 远程
  15. }
  16. @ccclass
  17. export default class ZombieBase extends Person {
  18. @property({ tooltip: "僵尸id" })
  19. zombieId: number = 0;
  20. @property({ type: cc.Prefab, tooltip: "子弹预制体", visible() { return [5, 6, 20].includes(this.zombieId) } })
  21. bullet_prefab: cc.Prefab = null;
  22. @property({ type: sp.Skeleton, tooltip: "警告圈", visible() { return [5, 6, 20].includes(this.zombieId) } })
  23. sp_hongzhaquan: sp.Skeleton = null;
  24. @property({ type: cc.AudioClip, tooltip: "攻击音效1" })
  25. audio_attack1: cc.AudioClip = null;
  26. @property({ type: cc.AudioClip, tooltip: "攻击音效2" })
  27. audio_attack2: cc.AudioClip = null;
  28. @property({ type: cc.AudioClip, tooltip: "受伤音效" })
  29. audio_hart: cc.AudioClip = null;
  30. @property({ type: cc.AudioClip, tooltip: "死亡音效" })
  31. audio_die: cc.AudioClip = null;
  32. isBoss: boolean = false;
  33. protected _aniLayer: cc.Node = null;
  34. protected _spAni: sp.Skeleton = null;
  35. protected onLoad(): void {
  36. this.id = 94;
  37. // 初始化配置表属性
  38. if (GameDate.ZombieMess[this.zombieId]) {
  39. this.totleHp = GameDate.ZombieMess[this.zombieId].hp;
  40. this.atkNum = GameDate.ZombieMess[this.zombieId].atk;
  41. this.atkRange = GameDate.ZombieMess[this.zombieId].atkRange;
  42. this.MoveSpeed = GameDate.ZombieMess[this.zombieId].speed;
  43. if (PlatUtils.IsOPPO) { this.MoveSpeed /= 2; }
  44. }
  45. // 大小缩放
  46. if (this.isBoss) {
  47. this.node.scale = 1;
  48. } else {
  49. this.node.scale = 0.8;
  50. }
  51. // 刚体
  52. this.rig = this.node.getComponent(cc.RigidBody);
  53. if (this.rig) { this.rig.linearDamping = 0.2; }
  54. // spine动画
  55. this._aniLayer = this.node.getChildByName("aniLayer");
  56. if (this._aniLayer) {
  57. this._spAni = this._aniLayer.getChildByName("ani").getComponent(sp.Skeleton);
  58. }
  59. // 监听动画
  60. if (this._spAni) {
  61. this._spAni.setStartListener(() => { this.startListenerCall() });
  62. this._spAni.setCompleteListener(() => { this.endListenerCall() });
  63. }
  64. }
  65. protected onDestroy() {
  66. // 取消监听
  67. cc.game.targetOff(this);
  68. }
  69. protected start(): void { }
  70. initNode() {
  71. gameMgr && gameMgr.setMapTs.checkNode(this.node, true);
  72. // 消息监听
  73. cc.game.on(Constant.E_GAME_LOGIC, this._onGameMessageHandler, this);
  74. gameMgr.zombieCurNum++;
  75. this.node.stopAllActions();
  76. this.node.zIndex = ZindexLayer.zindex_zombie + this.zombieId;
  77. this.node.opacity = 255;
  78. this.atkDir = cc.Vec2.ZERO;
  79. this.curHp = this.totleHp;
  80. this.isDeath = false;
  81. this.isAtk = false;
  82. this._canAtk = true;
  83. this.canMoveDir = true;
  84. this.canMove = true;
  85. // 播放出场动画
  86. if (this._spAni && this._spAni.isValid) {
  87. this._spAni.node.scaleX = Math.abs(this._spAni.node.scaleX);
  88. this._spAni.node.opacity = 255;
  89. this._spAni.node.color = cc.Color.WHITE;
  90. if (this.isBoss) {
  91. this._spAni.setAnimation(0, "spawn", false);
  92. this.scheduleOnce(() => {
  93. // 碰撞体
  94. let boxCollider = this.node.getComponent(cc.BoxCollider);
  95. if (boxCollider) boxCollider.enabled = true;
  96. }, 2)
  97. } else {
  98. this._spAni.setAnimation(0, "idle", true);
  99. // 碰撞体
  100. let boxCollider = this.node.getComponent(cc.BoxCollider);
  101. if (boxCollider) boxCollider.enabled = true;
  102. }
  103. }
  104. // 轰炸圈
  105. if (this.sp_hongzhaquan) {
  106. this.sp_hongzhaquan.node.setParent(this.node.parent);
  107. this.sp_hongzhaquan.node.zIndex = ZindexLayer.zinedx_floorTip;
  108. this.sp_hongzhaquan.setCompleteListener(() => {
  109. if (this.sp_hongzhaquan && this.sp_hongzhaquan.isValid) {
  110. this.sp_hongzhaquan.node.active = false;
  111. }
  112. })
  113. }
  114. // boss
  115. if (this.isBoss) {
  116. cc.game.emit(Constant.E_GAME_LOGIC, { type: Constant.E_Commonzombie_Destory });
  117. gameMgr && gameMgr.showBossHp(1);
  118. if (this.node.getChildByName("boos")) this.node.getChildByName("boos").active = true;
  119. }
  120. }
  121. putNodePool() {
  122. // 取消监听
  123. cc.game.targetOff(this);
  124. // boss
  125. if (this.isBoss) {
  126. // boss光影
  127. if (this.node.getChildByName("boos")) this.node.getChildByName("boos").active = false;
  128. // boss边界
  129. if (gameMgr) {
  130. gameMgr && gameMgr.showBossHp(0);
  131. if (gameMgr.boss_border && cc.isValid(gameMgr.boss_border)) {
  132. gameMgr.boss_border.destroy();
  133. gameMgr.boss_border = null;
  134. }
  135. }
  136. this.isBoss = false;
  137. }
  138. // 轰炸圈
  139. if (this.sp_hongzhaquan && this.sp_hongzhaquan.isValid) {
  140. this.sp_hongzhaquan.node.setParent(this.node);
  141. this.sp_hongzhaquan.node.active = false;
  142. }
  143. gameMgr.zombieCurNum--;
  144. gameMgr && gameMgr.isValid && gameMgr.nodePut(this.node.name, this.node);
  145. }
  146. startListenerCall() {
  147. if (this._spAni.animation.includes("spawn")) {
  148. this._spAni.timeScale = 0.4;
  149. this.scheduleOnce(() => { this._spAni.timeScale = 1; }, 1)
  150. this.canMoveDir = false;
  151. // 出场方向
  152. let fromPos = this.node.getPosition();
  153. let toPos = gameMgr.playerTs.node.getPosition();
  154. let div = toPos.subSelf(fromPos);
  155. this._spAni.node.scaleX = (div.x > 0 ? 1 : -1) * Math.abs(this._spAni.node.scaleX);
  156. }
  157. else if (this._spAni.animation.includes("attack")) {
  158. this.isAtk = true;
  159. this.canMoveDir = false;
  160. if ([8, 9, 18].includes(this.zombieId) && this._spAni.animation == "attack2") {
  161. this.speedRate *= 2.5;
  162. this.udpateRBody(this.moveDir);
  163. this.scheduleOnce(() => {
  164. this.speedRate /= 2.5;
  165. this.moveDir = cc.Vec2.ZERO;
  166. this.udpateRBody(this.moveDir);
  167. }, 0.2)
  168. } else if ([10, 11, 12, 13].includes(this.zombieId) && this._spAni.animation == "attack2") {
  169. this.speedRate *= 3;
  170. this.udpateRBody(this.moveDir);
  171. this.scheduleOnce(() => {
  172. this.speedRate /= 3;
  173. this.moveDir = cc.Vec2.ZERO;
  174. this.udpateRBody(this.moveDir);
  175. }, 0.2)
  176. } else if ([14, 19].includes(this.zombieId) && this._spAni.animation.includes("attack")) {
  177. this.speedRate *= 3;
  178. this.udpateRBody(this.moveDir);
  179. this.scheduleOnce(() => {
  180. this.speedRate /= 3;
  181. this.moveDir = cc.Vec2.ZERO;
  182. this.udpateRBody(this.moveDir);
  183. }, 0.2)
  184. } else if (this.zombieId == 15 && this._spAni.animation == "attack") {
  185. this.speedRate *= 3;
  186. this.udpateRBody(this.moveDir);
  187. this.scheduleOnce(() => {
  188. this.speedRate /= 3;
  189. this.moveDir = cc.Vec2.ZERO;
  190. this.udpateRBody(this.moveDir);
  191. }, 0.4)
  192. } else if (this.zombieId == 16 && this._spAni.animation == "attack") {
  193. this.speedRate *= 2;
  194. this.udpateRBody(this.moveDir);
  195. this.scheduleOnce(() => {
  196. this.speedRate /= 2;
  197. this.moveDir = cc.Vec2.ZERO;
  198. this.udpateRBody(this.moveDir);
  199. }, 0.3)
  200. } else if (this.zombieId == 17 && this._spAni.animation.includes("attack")) {
  201. this.speedRate *= 2;
  202. this.udpateRBody(this.moveDir);
  203. this.scheduleOnce(() => {
  204. this.speedRate /= 2;
  205. this.moveDir = cc.Vec2.ZERO;
  206. this.udpateRBody(this.moveDir);
  207. }, 0.3)
  208. } else {
  209. this.moveDir = cc.Vec2.ZERO;
  210. this.udpateRBody(this.moveDir);
  211. }
  212. }
  213. else if (this._spAni.animation.includes("jump_up")) {
  214. this.canMoveDir = false;
  215. }
  216. }
  217. endListenerCall() {
  218. if (this._spAni.animation.includes("spawn")) {
  219. this._spAni.setAnimation(0, "idle", true);
  220. this.canMoveDir = true;
  221. this.updateMove();
  222. this.updatePerson();
  223. this.udpateRBody(this.moveDir);
  224. } else if (this._spAni.animation.includes("attack")) {
  225. this._spAni.setAnimation(0, "idle", true);
  226. this.isAtk = false;
  227. this.canMoveDir = true;
  228. this.updateMove();
  229. this.updatePerson();
  230. this.udpateRBody(this.moveDir);
  231. this.atkDir = cc.Vec2.ZERO;
  232. } else if (this._spAni.animation.includes("jump_down")) {
  233. this._spAni.setAnimation(0, "idle", true);
  234. this.canMoveDir = true;
  235. this.moveDir = cc.Vec2.ZERO;
  236. this.udpateRBody(this.moveDir);
  237. }
  238. }
  239. /** 消息 */
  240. private _onGameMessageHandler(event: any) {
  241. switch (event.type) {
  242. // 清除所有僵尸
  243. case Constant.E_Allzombie_Destory: {
  244. this.hart(9999999, null, null, true, true, cc.Color.WHITE, true);
  245. break;
  246. }
  247. // boss出现清除普通僵尸
  248. case Constant.E_Commonzombie_Destory: {
  249. if (!this.isBoss) {
  250. cc.tween(this.node).to(0.3, { opacity: 1 }, { easing: "fade" }).call(() => { this.putNodePool(); }).start();
  251. }
  252. break;
  253. }
  254. }
  255. }
  256. private _divToPlayer: cc.Vec2 = cc.Vec2.ZERO;
  257. protected _time: number = -1;
  258. lateUpdate(dt: number): void {
  259. if (this.isDeath || cocosz.isPause || !gameMgr.isGameStart || gameMgr.isWin || gameMgr.isFail) {
  260. this.udpateRBody(cc.Vec2.ZERO);
  261. return;
  262. }
  263. this._time++;
  264. if (this._time % 15 == 0) {
  265. if (this._time % 30 == 0) {
  266. this.updateDiv();
  267. this.updateMove();
  268. }
  269. this.updateAtk();
  270. this.updateAni();
  271. this.updatePerson();
  272. this.udpateRBody(this.moveDir);
  273. }
  274. }
  275. updateDiv() {
  276. let fromPos = this.node.getPosition();
  277. let toPos = gameMgr.playerTs.node.getPosition();
  278. this._divToPlayer = toPos.subSelf(fromPos);
  279. // 超出屏幕距离删除
  280. if (!this.isBoss && this._divToPlayer.mag() > cc.winSize.height / 2 / gameMgr.mainCamera.zoomRatio) {
  281. this.putNodePool();
  282. }
  283. }
  284. /** 更新移动 */
  285. updateMove() {
  286. if (this.rig.type == cc.RigidBodyType.Dynamic) {
  287. if (this.canMove && this.canMoveDir) {
  288. // 有目标
  289. if (gameMgr.playerTs) {
  290. // 玩家存活
  291. if (!gameMgr.playerTs.isDeath) {
  292. // 跳跃
  293. if (this.zombieId == 3 && Math.random() < 0.2) {
  294. if (!this._spAni.animation.includes("jump")) {
  295. this._spAni.setAnimation(0, "jump_up", false);
  296. this._spAni.addAnimation(0, "jump_down", false);
  297. this.moveDir = this._divToPlayer.normalize();
  298. }
  299. }
  300. // 朝着玩家移动
  301. else if (this._divToPlayer.mag() >= this.atkRange * 0.8) {
  302. this.moveDir = cc.v2(this._divToPlayer.normalize()).rotateSelf(Math.PI / 2 * (0.5 - Math.random()));
  303. }
  304. // 距离玩家很近,停止移动
  305. else {
  306. this.moveDir = cc.Vec2.ZERO;
  307. }
  308. }
  309. // 玩家死亡
  310. else {
  311. // 距离很近则远离玩家
  312. if (this._divToPlayer.mag() < (this.isBoss ? 800 : 1500)) {
  313. this.moveDir = this._divToPlayer.normalize().negSelf();
  314. }
  315. // 随机移动
  316. else {
  317. this.moveDir = cc.v2(this._divToPlayer.normalize()).rotateSelf(2 * Math.PI * Math.random());
  318. }
  319. }
  320. }
  321. }
  322. }
  323. }
  324. /** 更新攻击 */
  325. updateAtk() {
  326. // 能否攻击攻击
  327. if (!this.canAtk()) {
  328. // ...
  329. }
  330. // 新的攻击
  331. else if (gameMgr.playerTs && !gameMgr.playerTs.isDeath) {
  332. // 普通距离攻击
  333. if (this._divToPlayer.mag() < this.atkRange) {
  334. this.atkDir = this._divToPlayer.normalize();
  335. this.atk();
  336. }
  337. // 远距离攻击
  338. else {
  339. switch (this.zombieId) {
  340. case 8:
  341. case 9:
  342. case 10:
  343. case 11:
  344. case 12:
  345. case 13:
  346. case 18: {
  347. if (this._divToPlayer.mag() < 600 && Math.random() < 0.1 * (this.isBoss ? 2 : 1)) {
  348. this.atkDir = this._divToPlayer.normalize();
  349. this.moveDir = this._divToPlayer.normalize()
  350. this.atk("attack2");
  351. }
  352. break;
  353. }
  354. case 14:
  355. case 16:
  356. case 19: {
  357. if (this._divToPlayer.mag() < 800 && Math.random() < 0.1 * (this.isBoss ? 2 : 1)) {
  358. this.atkDir = this._divToPlayer.normalize();
  359. this.moveDir = this._divToPlayer.normalize()
  360. this.atk("attack");
  361. }
  362. break;
  363. }
  364. case 15: {
  365. if (this._divToPlayer.mag() < 800 && Math.random() < 0.1 * (this.isBoss ? 2 : 1)) {
  366. this.atkDir = this._divToPlayer.normalize();
  367. this.moveDir = this._divToPlayer.normalize()
  368. this.atk("attack");
  369. }
  370. break;
  371. }
  372. case 16: {
  373. if (this._divToPlayer.mag() < 600 && Math.random() < 0.1 * (this.isBoss ? 2 : 1)) {
  374. this.atkDir = this._divToPlayer.normalize();
  375. this.moveDir = this._divToPlayer.normalize()
  376. this.atk("attack");
  377. }
  378. break;
  379. }
  380. case 17: {
  381. if (this._divToPlayer.mag() < 600 && Math.random() < 0.15 * (this.isBoss ? 2 : 1)) {
  382. this.atkDir = this._divToPlayer.normalize();
  383. this.moveDir = this._divToPlayer.normalize()
  384. this.atk();
  385. }
  386. break;
  387. }
  388. case 20: {
  389. if (this._divToPlayer.mag() < 600) {
  390. if (Math.random() < 0.15 * (this.isBoss ? 2 : 1)) {
  391. this.atkDir = this._divToPlayer.normalize();
  392. this.moveDir = this._divToPlayer.normalize()
  393. this.atk("attack");
  394. }
  395. } else if (this.sp_hongzhaquan.isValid && !this.sp_hongzhaquan.node.active && this._divToPlayer.mag() > 800) {
  396. this.atk("bullet_chui");
  397. }
  398. break;
  399. }
  400. }
  401. }
  402. }
  403. else {
  404. this.atkDir = cc.Vec2.ZERO;
  405. }
  406. }
  407. /** 更新动画 */
  408. updateAni() {
  409. if (this.isDeath == false) {
  410. // 暂停
  411. if (this._spAni.timeScale == 0) {
  412. // ...a
  413. }
  414. // 出场
  415. else if (this._spAni.animation.includes("spawn")) { }
  416. // 攻击
  417. else if (this._spAni.animation.includes("attack")) { }
  418. // 跳跃
  419. else if (this._spAni.animation.includes("jump")) { }
  420. // 其它
  421. else {
  422. // 移动
  423. if (this.moveDir && this.moveDir.mag()) {
  424. if (this.zombieId == 17) {
  425. !this._spAni.animation.includes("wing") && this._spAni.setAnimation(0, "wing", true);
  426. } else {
  427. !this._spAni.animation.includes("run") && this._spAni.setAnimation(0, "run", true);
  428. }
  429. }
  430. // 待机
  431. else {
  432. !this._spAni.animation.includes("idle") && this._spAni.setAnimation(0, "idle", true);
  433. }
  434. }
  435. }
  436. // 死亡
  437. else if (this._spAni && this._spAni.skeletonData && this._spAni.skeletonData.skeletonJson.animations["die"]) {
  438. !this._spAni.animation.includes("die") && this._spAni.setAnimation(0, "die", false);
  439. }
  440. }
  441. updatePerson() {
  442. let dir = null;
  443. if (this.atkDir && this.atkDir.mag()) {
  444. dir = this.atkDir;
  445. } else if (this.moveDir && this.moveDir.mag()) {
  446. dir = this.moveDir;
  447. }
  448. // 动画方向
  449. if (dir) {
  450. this._spAni.node.scaleX = Math.abs(this._spAni.node.scaleX) * (dir.x > 0 ? 1 : -1);
  451. }
  452. }
  453. /** 刚体移动 */
  454. udpateRBody(dir: cc.Vec2, isMust: boolean = false) {
  455. if (this.rig.type == cc.RigidBodyType.Dynamic) {
  456. if (this.canMove || isMust) {
  457. if (dir && !dir.equals(cc.Vec2.ZERO)) {
  458. this.rig.linearVelocity = dir.mul(Math.floor(this.MoveSpeed * this.speedRate * (1 - 0.2 * Math.random())));
  459. } else {
  460. this.rig.linearVelocity = cc.Vec2.ZERO;
  461. }
  462. }
  463. }
  464. }
  465. /** 攻击 */
  466. atk(aniName: string = "") {
  467. let atkType: AtkType = AtkType.front;
  468. let atkRange: number = this.atkRange;
  469. let atkTime: number = 0;
  470. let bulletTime: number = 2;
  471. let clip: cc.AudioClip = this.audio_attack1;
  472. switch (this.zombieId) {
  473. case 0:
  474. case 1: {
  475. if (Math.random() < 0.5) {
  476. aniName = "attack";
  477. } else {
  478. aniName = "attack2";
  479. clip = this.audio_attack2;
  480. }
  481. atkTime = 0.3;
  482. break;
  483. }
  484. case 2:
  485. case 3: {
  486. aniName = "attack1";
  487. atkTime = 0.3;
  488. break;
  489. }
  490. case 4: {
  491. aniName = "attack";
  492. atkTime = 0.3;
  493. break;
  494. }
  495. case 5: {
  496. aniName = "attack";
  497. atkTime = 4;
  498. atkType = AtkType.range;
  499. bulletTime = 2;
  500. this._canAtk = false;
  501. break;
  502. }
  503. case 6: {
  504. aniName = "attack";
  505. atkTime = 4;
  506. atkType = AtkType.range;
  507. bulletTime = 2;
  508. this._canAtk = false;
  509. break;
  510. }
  511. case 7: {
  512. break;
  513. }
  514. case 8:
  515. case 18: {
  516. if (aniName == "attack2") {
  517. aniName = "attack2";
  518. atkRange = 300;
  519. atkTime = 0.3;
  520. atkType = AtkType.area;
  521. clip = this.audio_attack2;
  522. }
  523. else {
  524. aniName = "attack";
  525. atkRange = 350;
  526. atkTime = 0.3;
  527. }
  528. break;
  529. }
  530. case 9: {
  531. if (aniName == "attack2") {
  532. aniName = "attack2";
  533. atkRange = 450;
  534. atkTime = 0.3;
  535. clip = this.audio_attack2;
  536. }
  537. else {
  538. aniName = "attack";
  539. atkRange = 350;
  540. atkTime = 0.3;
  541. }
  542. break;
  543. }
  544. case 10: {
  545. if (aniName == "attack2") {
  546. aniName = "attack2";
  547. atkRange = 200;
  548. atkTime = 0.3;
  549. clip = this.audio_attack2;
  550. }
  551. else {
  552. aniName = "attack";
  553. atkRange = 200;
  554. atkTime = 0.3;
  555. }
  556. break;
  557. }
  558. case 11:
  559. case 12:
  560. case 13: {
  561. if (aniName == "attack2") {
  562. aniName = "attack2";
  563. atkRange = 250;
  564. atkTime = 0.3;
  565. clip = this.audio_attack2;
  566. }
  567. else {
  568. aniName = "attack";
  569. atkRange = 250;
  570. atkTime = 0.3;
  571. }
  572. break;
  573. }
  574. case 14:
  575. case 19: {
  576. if (Math.random() < 0.6) {//冲盾
  577. aniName = "attack";
  578. atkRange = 350;
  579. atkTime = 0.3;
  580. atkType = AtkType.charge;
  581. clip = this.audio_attack1;
  582. }
  583. else {
  584. aniName = "attack2";//挥棍
  585. atkRange = 350;
  586. atkTime = 0.3;
  587. clip = this.audio_attack2;
  588. }
  589. break;
  590. }
  591. case 15: {
  592. if (aniName == "attack") {//冲锋
  593. aniName = "attack";
  594. atkRange = 250;
  595. atkTime = 0.3;
  596. atkType = AtkType.charge;
  597. }
  598. else {
  599. aniName = "attack2";//喷火
  600. atkRange = 400;
  601. atkTime = 0.3;
  602. }
  603. break;
  604. }
  605. case 16: {
  606. if (aniName == "attack" || Math.random() < 0.4) {
  607. aniName = "attack";
  608. atkRange = 450;
  609. atkTime = 0.3;
  610. atkType = AtkType.area;
  611. }
  612. else {
  613. aniName = "attack2";
  614. atkRange = 300;
  615. atkTime = 0.3;
  616. }
  617. break;
  618. }
  619. case 17: {
  620. if (Math.random() < 0.5) {
  621. aniName = "attack";
  622. atkRange = 200;
  623. atkTime = 0.3;
  624. }
  625. else {
  626. aniName = "attack2";
  627. atkRange = 200;
  628. atkTime = 0.3;
  629. atkType = AtkType.charge;
  630. }
  631. break;
  632. }
  633. case 20: {
  634. if (aniName == "bullet_chui") {
  635. atkType = AtkType.range;
  636. aniName = "attack2";
  637. bulletTime = 1.5;
  638. }
  639. else if (aniName == "attack") {
  640. aniName = "attack";
  641. atkRange = 500;
  642. atkTime = 0.3;
  643. }
  644. else {
  645. aniName = "attack2";
  646. atkRange = 300;
  647. atkTime = 0.3;
  648. }
  649. break;
  650. }
  651. }
  652. // 执行动画
  653. if (aniName) this._spAni.setAnimation(0, aniName, false);
  654. if (clip && clip.isValid) gameMgr.playClip(clip, this.node, 0.5);
  655. // 攻击伤害
  656. let call = () => {
  657. if (gameMgr.playerTs && !gameMgr.playerTs.isDeath) {
  658. let fromPos = this.node.getPosition();
  659. let toPos = gameMgr.playerTs.node.getPosition();
  660. let div = toPos.subSelf(fromPos);
  661. // 判断是否在攻击范围和攻击方向
  662. if (div.mag() < atkRange) {
  663. if (atkType == AtkType.front) {
  664. if (div.mag() < 200 || (div.x * this._spAni.node.scale > 0 && Math.abs(div.y / div.x) < 1.4)) {
  665. gameMgr.playerTs.hart(1, this.node, div.normalizeSelf(), true);
  666. }
  667. } else if (atkType === AtkType.charge) {
  668. gameMgr.playerTs.hart(1, this.node, div.normalizeSelf(), true);
  669. this.unschedule(call);
  670. } else {
  671. gameMgr.playerTs.hart(1, this.node, div.normalizeSelf(), true);
  672. }
  673. }
  674. };
  675. }
  676. if (atkType == AtkType.front || atkType == AtkType.area) {
  677. this.scheduleOnce(call, atkTime);
  678. } else if (atkType == AtkType.charge) {
  679. this.schedule(call, 0, 15);
  680. } else if (atkType == AtkType.range) {
  681. let pos_from = this.node.getPosition();
  682. let pos_to = gameMgr.playerTs.node.getPosition();
  683. let p2 = cc.v2((pos_from.x + pos_to.x) / 2, pos_from.y + 1500);
  684. // 生成子弹
  685. if (this.bullet_prefab) {
  686. let bullet = cc.instantiate(this.bullet_prefab);
  687. // 子弹脚本
  688. let ts = bullet.getComponent(Bullet);
  689. ts.id = this.id;
  690. ts.atker = this.node;
  691. ts.atk = this.atkNum;
  692. ts.isAngle = true;
  693. // 子弹属性
  694. bullet.setPosition(pos_from);
  695. bullet.zIndex = ZindexLayer.zindex_bullet_sky;
  696. bullet.parent = this.node.parent;
  697. // 子弹移动
  698. cc.tween(bullet)
  699. .call(() => {
  700. // 轰炸预警
  701. if (this.sp_hongzhaquan && this.sp_hongzhaquan.isValid) {
  702. this.sp_hongzhaquan.node.active = true;
  703. this.sp_hongzhaquan.node.setPosition(pos_to);
  704. this.sp_hongzhaquan.setAnimation(0, "animation", false);
  705. }
  706. })
  707. .bezierTo(bulletTime, pos_from, p2, pos_to)
  708. .call(() => {
  709. // 生成爆炸子弹
  710. if (ts.boomEffect) {
  711. let boom = cc.instantiate(ts.boomEffect)
  712. boom.parent = ts.node.parent;
  713. boom.setPosition(ts.node.getPosition());
  714. let curBullet = boom.getComponent(Bullet);
  715. curBullet.atk = ts.atk;
  716. curBullet.atker = ts.atker;
  717. curBullet.id = ts.id;
  718. gameMgr.playEffect("explo", boom);
  719. if (ts.hitEffect) {
  720. let pos = bullet.getPosition();
  721. let node = cc.instantiate(ts.hitEffect);
  722. node.parent = bullet.parent;
  723. node.setPosition(pos);
  724. node.zIndex = ZindexLayer.zindex_effect_hit;
  725. }
  726. }
  727. bullet.destroy();
  728. })
  729. .start();
  730. this.scheduleOnce(() => { this._canAtk = true; }, atkTime);
  731. }
  732. }
  733. }
  734. /** 受伤 */
  735. hart(atkNum: number, from: cc.Node, dir?: cc.Vec2, isAudio: boolean = true, isEmit: boolean = true, labelColor?: cc.Color, isMust: boolean = false): void {
  736. if (this.isDeath) return;
  737. if (isMust == false && cocosz.isPause) return;
  738. // 防止dir过大
  739. if (dir && dir.mag() > 3) dir.normalizeSelf().mulSelf(3);
  740. // 减伤
  741. atkNum = (1 - this.damageReduction) * atkNum;
  742. // 数字
  743. gameMgr.showRoleTip(this.node, Math.min(this.HP, atkNum).toFixed(0), labelColor);
  744. // 设置血量
  745. this.HP -= atkNum;
  746. if (this.isBoss) {
  747. gameMgr && gameMgr.showBossHp(this.HP / this.totleHp);
  748. }
  749. // 受伤效果
  750. if (!this.isAttackedEffect && this._spAni && this._spAni.node && this._spAni.node.isValid) {
  751. this.isAttackedEffect = true;
  752. // 受伤音效
  753. if (isAudio) {
  754. // 受伤音效
  755. if (this.audio_hart) {
  756. gameMgr.playClip(this.audio_hart, this.node, 0.5);
  757. }
  758. }
  759. // 缩放
  760. cc.tween(this._aniLayer)
  761. .to(0.1, { scale: 0.7 }, { easing: "sineIn" })
  762. .to(0.1, { scale: 1 }, { easing: "sineOut" })
  763. .call(() => { this.isAttackedEffect = false; })
  764. .start();
  765. // 变色
  766. if (this._canColor) {
  767. cc.tween(this._spAni.node)
  768. .to(0.1, { color: new cc.Color(0, 0, 0, 255) })
  769. .to(0.1, { color: cc.Color.WHITE })
  770. .start();
  771. }
  772. // 后退
  773. if (this.rig.type == cc.RigidBodyType.Dynamic && dir && this.canMove && this.canMoveDir) {
  774. // 控制
  775. this.canMove = false;
  776. this.scheduleOnce(() => { this.canMove = true; }, 0.1);
  777. // 后退
  778. let div = dir.mulSelf(400 * dir.mag()).addSelf(this.rig.linearVelocity);
  779. let maxDiv: number = this.isBoss ? 100 : 300;
  780. if (div.mag() > maxDiv) {
  781. div.normalizeSelf().mulSelf(maxDiv);
  782. }
  783. this.rig.linearVelocity = div;
  784. }
  785. }
  786. if (this.HP <= 0) {
  787. this.death();
  788. // 死亡事件
  789. cc.game.emit(Constant.E_GAME_LOGIC, { type: Constant.E_Zombie_Death, node: this.node, from: from })
  790. } else {
  791. // 受伤事件
  792. if (from && isEmit) {
  793. cc.game.emit(Constant.E_GAME_LOGIC, { type: Constant.E_Zombie_Hart, node: this.node })
  794. }
  795. }
  796. }
  797. /** 死亡 */
  798. death() {
  799. this.isAtk = false;
  800. this.isDeath = true;
  801. this._spAni.timeScale = 1;
  802. // 碰撞体
  803. this.node.getComponents(cc.Collider).forEach(v => v.enabled = false);
  804. // 隐藏销毁
  805. cc.tween(this._spAni.node).to(1, { opacity: 0 }).start();
  806. // 死亡音效
  807. if (this.audio_die) gameMgr.playClip(this.audio_die, null, 0.2);
  808. // 死亡效果
  809. this.updateAni();
  810. if (this.zombieId < 8) {
  811. let pre = cocosz.resMgr.getRes("effect_death", cc.Prefab);
  812. if (pre) {
  813. let effect_death: cc.Node = cc.instantiate(pre);
  814. effect_death.zIndex = ZindexLayer.zinedx_floorSkill;
  815. effect_death.setPosition(this.node.position);
  816. effect_death.setParent(this.node.parent);
  817. }
  818. }
  819. // 死亡掉落道具
  820. this.creatItem();
  821. this.scheduleOnce(() => { this.putNodePool(); }, 2);
  822. }
  823. creatItem() {
  824. if (gameMgr.isWin || gameMgr.isFail) return;
  825. if (upgradeMgr && upgradeMgr.isValid) {
  826. let count = (this.zombieId < 8 ? 1 : 10);
  827. for (let i = 0; i < count; i++) {
  828. let pos = this.node.getPosition();
  829. if (i > 0) {
  830. pos.addSelf(cc.v2(20 * i, 0).rotateSelf(2 * Math.PI * Math.random()));
  831. }
  832. upgradeMgr.createJingyan(pos);
  833. }
  834. }
  835. }
  836. private _canColor: boolean = true;
  837. frozenStart() {
  838. this._canColor = false;
  839. // 停止受伤变色
  840. this._spAni.node.stopAllActions();
  841. // 启动变色
  842. this._spAni.node.color = cc.Color.BLUE;
  843. if (this._spAni) {
  844. this._spAni.timeScale = 0;
  845. }
  846. // 移动暂停
  847. this.isAtk = false;
  848. this.canMoveDir = false;
  849. this.moveDir = cc.Vec2.ZERO;
  850. this.udpateRBody(this.moveDir, true);
  851. }
  852. frozenEnd() {
  853. if (!this.isDeath) {
  854. this._canColor = true;
  855. // 恢复颜色
  856. this._spAni.node.color = cc.Color.WHITE;
  857. if (this._spAni) {
  858. this._spAni.timeScale = 1;
  859. this._spAni.setAnimation(0, "idle", true);
  860. }
  861. // 移动恢复
  862. this.canMoveDir = true;
  863. this.isAtk = false;
  864. this.atkDir = cc.Vec2.ZERO;
  865. this.updateMove();
  866. this.updatePerson();
  867. this.udpateRBody(this.moveDir);
  868. }
  869. }
  870. fire_start() {
  871. this._canColor = false;
  872. // 停止受伤变色
  873. this._spAni.node.stopAllActions();
  874. // 启动变色
  875. this._spAni.node.color = cc.Color.RED;
  876. }
  877. fire_end() {
  878. this._canColor = true;
  879. // 恢复颜色
  880. this._spAni.node.color = cc.Color.WHITE;
  881. }
  882. private _canAtk: boolean = true;
  883. canAtk() {
  884. if (!this._canAtk || this.isAtk || this._spAni.timeScale == 0 || this._spAni.animation.includes("spawn") || this._spAni.animation.includes("jump")) {
  885. return false;
  886. }
  887. return true;
  888. }
  889. }