Sundries.ts 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. import { _decorator, Collider, Enum, Node, RigidBody, Animation, Vec3 } from 'cc';
  2. import { BaseExp } from '../core/base/BaseExp';
  3. import { PoolManager } from '../core/manager/PoolManager';
  4. import { userIns } from '../data/UserData';
  5. import { autoBind } from '../extend/AutoBind';
  6. import { Logger } from '../extend/Logger';
  7. import { ResUtil } from '../utils/ResUtil';
  8. import { Game } from './Game';
  9. import { Enemy, EPartType } from './Enemy';
  10. import { audioMgr } from '../core/manager/AudioManager';
  11. import { Constants } from '../data/Constants';
  12. import MsgHints from '../utils/MsgHints';
  13. const { ccclass, property } = _decorator;
  14. //沙袋堆动画
  15. const sandbge_take = "take"
  16. /**
  17. * 战场障碍物类型
  18. */
  19. export enum ObstacleType {
  20. /** 沙袋堆 */
  21. SANDBAG_PILE = "沙袋堆",
  22. /** 油桶 */
  23. OIL_BARREL = "油桶",
  24. /** 军车 */
  25. MILITARY_TRUCK = "军车",
  26. /** 小沙袋 */
  27. SMALL_SANDBAG = "小沙袋"
  28. }
  29. /**
  30. * 杂物建筑物类
  31. */
  32. @ccclass('Sundries')
  33. export class Sundries extends BaseExp {
  34. @property({ type: Enum(ObstacleType), tooltip: "沙袋堆:SANDBAG_PILE 油桶:OIL_BARREL 军车: MILITARY_TRUCK 小沙袋:SMALL_SANDBAG", displayName: "障碍物类型"})
  35. public obstacleType: ObstacleType = ObstacleType.SANDBAG_PILE;
  36. //添加爆炸队列和标记
  37. private static explosionQueue: {
  38. target: Enemy | Sundries,
  39. damage: number,
  40. source: any }[] = [];
  41. //标记是否正在处理爆炸队列
  42. private static isProcessingQueue = false;
  43. //杂物信息数据
  44. public data: any = null;
  45. //杂物是否被打报废
  46. private isDead: boolean = false;
  47. //杂物当前的总血量
  48. private totalHp: number = 0;
  49. start() {
  50. //给碰撞体绑定数据
  51. this.setupColliders(this.node);
  52. //根据障碍物类型设置总血量
  53. let sundrie_id: number = -1;
  54. switch (this.obstacleType) {
  55. case ObstacleType.SANDBAG_PILE:
  56. sundrie_id = 11001;
  57. break;
  58. case ObstacleType.OIL_BARREL:
  59. sundrie_id = 11002;
  60. break;
  61. case ObstacleType.MILITARY_TRUCK:
  62. sundrie_id = 11003;
  63. break;
  64. case ObstacleType.SMALL_SANDBAG:
  65. sundrie_id = 11004;
  66. break;
  67. }
  68. if(sundrie_id !== -1){
  69. const data: any = userIns.sundriesTable.find(e=>e.id == sundrie_id);
  70. this.data = data;
  71. this.totalHp = data.hp;
  72. }else{
  73. Logger.log('未找到对应的杂物信息');
  74. }
  75. }
  76. /**
  77. * 杂物被打掉血
  78. * @param hp 血
  79. * @param pData 玩家数据
  80. */
  81. public subHP(hp: number, pData:any){
  82. if(!this.node
  83. ||Game.I.isPause
  84. ||this.isDead
  85. ||hp == null
  86. ||this.totalHp <= 0){
  87. return;
  88. }
  89. this.totalHp -= hp;
  90. //杂物打打死报废
  91. if(this.totalHp <= 0
  92. && !this.isDead){
  93. this.isDead = true;
  94. this.scrap();
  95. }
  96. }
  97. /**
  98. * 杂物被打报废
  99. */
  100. public scrap(){
  101. switch (this.obstacleType) {
  102. case ObstacleType.SANDBAG_PILE:{//沙袋堆
  103. let skeletal:Animation = this.node.getComponent(Animation);
  104. if(!skeletal)return;
  105. //沙袋掀开的动画
  106. skeletal.play(sandbge_take);
  107. skeletal.scheduleOnce(()=>{
  108. skeletal.pause();
  109. },0.9);
  110. }
  111. break;
  112. case ObstacleType.OIL_BARREL:{//油桶
  113. //油漆桶音效
  114. audioMgr.playOneShot(Constants.audios.Oildrum_explosion);
  115. //爆炸后生成爆炸特效 粒子路径、自动回收时间、外部设置回调
  116. ResUtil.playParticle(
  117. `effects/Prefabs/OilBoom`,
  118. 4,
  119. (particle) => {
  120. particle.parent = this.node.parent;
  121. const targetPos: Vec3 = this.node.worldPosition.clone();
  122. particle.worldPosition = targetPos;
  123. particle.active = true;
  124. this.recycle();
  125. //爆炸后生成爆炸伤害
  126. this.explosionDamage(targetPos);
  127. }
  128. );
  129. }
  130. break;
  131. case ObstacleType.MILITARY_TRUCK:{//军车
  132. //加载报废的军车
  133. ResUtil.loadRes(`enemy/sundries/scrap_car`, this.node)
  134. .then((military: Node | null) => {
  135. if(!military)return;
  136. military.active = true;
  137. military.parent = this.node.parent;
  138. const targetPos: Vec3 = this.node.worldPosition.clone();
  139. military.worldPosition = targetPos;
  140. military.eulerAngles = this.node.eulerAngles.clone();
  141. military.scale = this.node.scale.clone();
  142. //回收原有的军车
  143. this.recycle();
  144. //播放爆炸过后的浓烟
  145. ResUtil.playParticle(
  146. `effects/Prefabs/HeavySmoke`,
  147. 3,
  148. (particle) => {
  149. particle.parent = Game.I.map.node;
  150. particle.worldPosition = targetPos;
  151. particle.active = true;
  152. //爆炸后生成爆炸伤害
  153. this.explosionDamage(targetPos);
  154. }
  155. );
  156. });
  157. }
  158. break;
  159. case ObstacleType.SMALL_SANDBAG:{//小沙袋
  160. //获取刚体组件
  161. const rigidBody = this.node.getComponent(RigidBody);
  162. if (!rigidBody) return;
  163. //计算抛射方向
  164. const startPos = this.node.worldPosition.clone();
  165. const playerPos = Game.I.player.node.worldPosition.clone();
  166. const direction = startPos.subtract(playerPos).normalize();
  167. //随机抛射参数 水平力1500-2500N
  168. const horizontalForce = 1500 + Math.random() * 1000;
  169. //垂直力800-1500N
  170. const verticalForce = 800 + Math.random() * 700;
  171. //旋转扭矩50-150
  172. const torqueForce = 50 + Math.random() * 100;
  173. //施加作用力
  174. rigidBody.applyForce(new Vec3(
  175. direction.x * horizontalForce,
  176. verticalForce,
  177. direction.z * horizontalForce
  178. ));
  179. //施加旋转扭矩
  180. rigidBody.applyTorque(new Vec3(0, torqueForce, 0));
  181. }
  182. break;
  183. }
  184. }
  185. /**
  186. * 爆炸物产生爆炸伤害
  187. * @param targetPos 爆炸物的位置
  188. */
  189. public explosionDamage(targetPos: Vec3){
  190. //爆炸半径
  191. const eRadius: number = this.data.explosion_radius / 10;
  192. //查找不是自身的所有杂物 然后去看看是否有连续爆炸
  193. const allSundries: Sundries[] = Game.I.map.sundries.children
  194. .filter(node => node.active && node.parent && node != this.node)
  195. .map(node => node.getComponent(Sundries))
  196. .filter(comp =>
  197. comp && !comp.isDead
  198. ) as Sundries[];
  199. const allEnemys = [...Game.I.buildEnemys.allEnemys.filter(e=>!e.isDead),...allSundries];
  200. if(allEnemys.length <= 0)return;
  201. //爆炸伤害
  202. const damage: number = this.data.explosion_injury;
  203. allEnemys.forEach((e: any) => {
  204. if(e.node){
  205. const ePos: Vec3 = e.node.worldPosition;
  206. const distance = ePos.clone().subtract(targetPos).length();
  207. if(distance <= eRadius) {
  208. if(e instanceof Enemy){//敌人直接扣血
  209. e.subHP(damage, this.data);
  210. }else{//爆炸物 如果队列未在处理则开始处理 将爆炸目标加入队列而不是立即触发
  211. Sundries.explosionQueue.push({
  212. target: e,
  213. damage: damage,
  214. source: this.data
  215. });
  216. if(!Sundries.isProcessingQueue) {
  217. this.processExplosionQueue();
  218. }
  219. }
  220. }
  221. }
  222. })
  223. }
  224. /**
  225. * 处理爆炸队列(新增方法)
  226. */
  227. private processExplosionQueue() {
  228. if (Sundries.explosionQueue.length == 0) {
  229. Sundries.isProcessingQueue = false;
  230. return;
  231. }
  232. Sundries.isProcessingQueue = true;
  233. const explosion = Sundries.explosionQueue.shift()!;
  234. //触发当前爆炸
  235. explosion.target.subHP(explosion.damage, explosion.source);
  236. //添加0.2秒延迟后处理下一个爆炸
  237. this.scheduleOnce(()=>{
  238. this.processExplosionQueue();
  239. },0.2)
  240. }
  241. /**
  242. * 递归设置所有子节点的Collider参数
  243. * @param node 起始节点
  244. */
  245. private setupColliders(node: Node) {
  246. const collider = node.getComponent(Collider);
  247. if(collider){
  248. collider["args"] = [EPartType.sundries, this];
  249. }
  250. //递归处理所有子节点
  251. node.children.forEach(child => {
  252. this.setupColliders(child);
  253. });
  254. }
  255. /**
  256. * 杂物回收
  257. */
  258. public recycle(){
  259. if(this.node.parent){
  260. this.isDead = true;
  261. PoolManager.putNode(this.node);
  262. }
  263. }
  264. }