FightWorld.ts 79 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846
  1. /** @format */
  2. import {ECSWorld} from '../lib/ECSWorld'
  3. import {IRoleConfig, RoleConfig} from '../../config/RoleConfig'
  4. import {Data, Mgr} from '../../GameControl'
  5. import {cCollider} from '../../collision/Collider'
  6. import {ComRole} from '../components/ComRole'
  7. import {ComAttackable} from '../components/ComAttackable'
  8. import {ComType, EntityIndex} from '../lib/Const'
  9. import {ComCocosNode} from '../components/ComCocosNode'
  10. import {
  11. EventGraphicsDraw,
  12. EventHurtOrAdd,
  13. EventRoleTip,
  14. EventRun,
  15. EventSlowDown,
  16. EventStand,
  17. EventType,
  18. } from '../core/NodeEvent'
  19. import {ComTransform} from '../components/ComTransform'
  20. import {ComMovable} from '../components/ComMovable'
  21. import {ComNodeConfig} from '../components/ComNodeConfig'
  22. import {IStageRolePosConfig, StageRolePosConfig} from '../../config/StageRolePosConfig'
  23. import {ComEnemy} from '../components/ComEnemy'
  24. import {ComFriend} from '../components/ComFriend'
  25. import {ISkillConfig, SkillConfig} from '../../config/SkillConfig'
  26. import {ComFindEnemy} from '../components/ComFindEnemy'
  27. import {BulletConfig} from '../../config/BulletConfig'
  28. import {ComBullet} from '../components/ComBullet'
  29. import {ComBehaviorTree} from '../components/ComBehaviorTree'
  30. import {BT} from '../../behaviorTree/BehaviorTree'
  31. import {Log} from '../../utils/LogUtils'
  32. import {
  33. ANI_TYPE,
  34. ATTR_NAME,
  35. BUFF_ANI_TYPE,
  36. BUFF_EFFECT_TYPE,
  37. BUFF_TYPE,
  38. BULLET_EFFECT_TYPE,
  39. BULLET_FLY_TYPE,
  40. BULLET_TYPE,
  41. ENTRY,
  42. GAME_LAYER,
  43. GAME_PREFAB_TYPE,
  44. GAME_ROLE_TIP,
  45. GAME_SCALE_TYPE,
  46. GAME_TYPE,
  47. GAME_WIN_TYPE,
  48. HIT_ANI_TYPE,
  49. LANGUAGE_TYPE,
  50. MONSTER_TYPE,
  51. NEGATIVE_TYPE,
  52. ROLE_TYPE,
  53. SKILL_DIR_TYPE,
  54. SKILL_EFFECT_TYPE,
  55. SKILL_TYPE,
  56. START_POS,
  57. } from '../../enums/Enum'
  58. import {BuffConfig} from '../../config/BuffConfig'
  59. import {ccUtils} from '../../utils/ccUtils'
  60. import {ComRoleSpine} from '../components/ComRoleSpine'
  61. import {EntryObj, IAttackEffect, IBuff, IFightData} from '../core/GameInterface'
  62. import {ComSkill} from '../components/ComSkill'
  63. import {ComDizzy} from '../components/ComDizzy'
  64. import {ComSkillAbel} from '../components/ComSkillAbel'
  65. import {FrameAniConfig} from '../../config/FrameAniConfig'
  66. import {ComFrameAni} from '../components/ComFrameAni'
  67. import {EntryConfig} from '../../config/EntryConfig'
  68. import {TalentConfig} from '../../config/TalentConfig'
  69. import {IBaseAttr, ICard, IRole} from '../../interface/GlobalInterface'
  70. import {ComEnter} from '../components/ComEnter'
  71. import {ArmorConfig} from '../../config/ArmorConfig'
  72. import {ConstValue} from '../../data/ConstValue'
  73. import {SysHasSkill} from '../systems/SysHasSkill'
  74. import {SysFindEnemy} from '../systems/SysFindEnemy'
  75. import {SysBullet} from '../systems/SysBullet'
  76. import {SysBehaviorTree} from '../systems/SysBehaviorTree'
  77. import {SysBuff} from '../systems/SysBuff'
  78. import {SysSkillAbel} from '../systems/SysSkillAbel'
  79. import {SysSkill} from '../systems/SysSkill'
  80. import {SysMovable} from '../systems/SysMovable'
  81. import {SysAttack} from '../systems/SysAttack'
  82. import {SysDizzy} from '../systems/SysDizzy'
  83. import {SysDie} from '../systems/SysDie'
  84. import {SysLifeTime} from '../systems/SysLifeTime'
  85. import {SysFrameAni} from '../systems/SysFrameAni'
  86. import {SysRoleState} from '../systems/SysRoleState'
  87. import {SysCocosView} from '../systems/SysCocosView'
  88. import {SysEnter} from '../systems/SysEnter'
  89. import GameUI from '../../zuma/FightCore'
  90. import {CastleSkillConfig} from '../../config/CastleSkillConfig'
  91. import {idNum} from '../../proto/typedef'
  92. import {SysCastleSkill} from '../systems/SysCastleSkill'
  93. import {ShapeType} from '../../collision/Shape'
  94. import {ComHasSkill} from '../components/ComHasSkill'
  95. import FrameAnimation from '../../uiutils/FrameAnimation'
  96. import {EventProcess} from '../core/EventProcess'
  97. import FightCore from '../../zuma/FightCore'
  98. import {ZumabuffConfig} from '../../config/ZumabuffConfig'
  99. import {RoleSpineProcess} from '../core/RoleSpineProcess'
  100. export class FightWorld extends ECSWorld {
  101. //创建单例
  102. static instance: FightWorld
  103. static getInstance() {
  104. if (!FightWorld.instance) {
  105. FightWorld.instance = new FightWorld()
  106. FightWorld.instance.createWorldCocosView()
  107. }
  108. return FightWorld.instance
  109. }
  110. fightCore: FightCore
  111. rolePosMap: Map<number, IStageRolePosConfig[]>
  112. init() {
  113. this.rolePosMap = new Map<number, IStageRolePosConfig[]>()
  114. this.rolePosMap.set(Data.game.gFriend, [])
  115. this.rolePosMap.set(Data.game.gEnemy, [])
  116. for (let key in StageRolePosConfig) {
  117. if (+key <= 100) {
  118. this.rolePosMap.get(Data.game.gEnemy).push(StageRolePosConfig[key])
  119. } else if (+key > 100 && +key <= 200) {
  120. this.rolePosMap.get(Data.game.gFriend).push(StageRolePosConfig[key])
  121. }
  122. }
  123. this.initEntry()
  124. this.fightCore.registerTouchHandler(this.getSystem(SysCocosView))
  125. }
  126. createWorldCocosView() {
  127. //添加系统顺序不能错,保证行为树的判断能正常执行
  128. this.createEntity() // 创建0号实体
  129. this.addSystem(new SysHasSkill()) // 是否有技能施放
  130. this.addSystem(new SysFindEnemy()) // 寻敌锁定
  131. this.addSystem(new SysBullet()) // 子弹系统(在移动系统之前判断)
  132. this.addSystem(new SysBehaviorTree()) // 行为树
  133. this.addSystem(new SysBuff()) // buff光环系统
  134. this.addSystem(new SysSkillAbel()) // 技能吟唱
  135. this.addSystem(new SysSkill()) // 技能持续生效
  136. this.addSystem(new SysMovable()) // 移动
  137. this.addSystem(new SysAttack()) // 攻击动作
  138. this.addSystem(new SysDizzy()) // 晕眩
  139. this.addSystem(new SysDie()) // 死亡
  140. this.addSystem(new SysLifeTime()) // 生命时间倒计时
  141. this.addSystem(new SysFrameAni()) // 特效
  142. this.addSystem(new SysRoleState()) // role state
  143. this.addSystem(new SysCocosView()) // cocos view
  144. this.addSystem(new SysEnter()) // 角色入场
  145. }
  146. //-------------------------------角色-----------------------------------------
  147. public createRoleEntity(
  148. IDOrCfg: number | IRoleConfig,
  149. isEnemy?: boolean,
  150. posCfgID?: number,
  151. iRole?: IRole,
  152. dropOutGoods?: idNum,
  153. isExtraEnemy?: boolean,
  154. ): EntityIndex {
  155. let roleCfg: IRoleConfig
  156. if (typeof IDOrCfg == 'number') {
  157. roleCfg = RoleConfig[IDOrCfg]
  158. } else {
  159. roleCfg = IDOrCfg
  160. }
  161. if (!roleCfg) {
  162. console.error('没有这个角色配置----------->', IDOrCfg)
  163. }
  164. let entity = this.createEntity()
  165. // 添加nodeconfig
  166. let comMap = this.addComponent(entity, ComNodeConfig)
  167. comMap.id = 1
  168. comMap.layer = GAME_LAYER[GAME_LAYER.role]
  169. comMap.prefabName = GAME_PREFAB_TYPE.role
  170. let entitySpine = this.createEntity()
  171. // 添加spineNode
  172. let comSpineMap = this.addComponent(entitySpine, ComNodeConfig)
  173. comSpineMap.id = 1
  174. comSpineMap.layer = GAME_LAYER[GAME_LAYER.roleSpine]
  175. comSpineMap.prefabName = GAME_PREFAB_TYPE.roleSpine
  176. //移除cocosNode
  177. this.removeComponent(entity, ComCocosNode)
  178. // 添加transform
  179. let comTrans = this.addComponent(entity, ComTransform)
  180. comTrans.x = isEnemy ? Data.game.fightSizeMaxX : Data.game.fightSizeMinX
  181. if (posCfgID > 0 && roleCfg.type != ROLE_TYPE.base) {
  182. comTrans.x = StageRolePosConfig[posCfgID].posX
  183. }
  184. let posArr = this.rolePosMap.get(isEnemy ? Data.game.gEnemy : Data.game.gFriend)
  185. //id为1的是基地
  186. comTrans.y =
  187. posCfgID > 0
  188. ? StageRolePosConfig[posCfgID].posY
  189. : roleCfg.type == ROLE_TYPE.base
  190. ? StageRolePosConfig[1].posY
  191. : posArr[Math.randomRangeInt(0, posArr.length - 1)].posY
  192. if (isEnemy) {
  193. this.addComponent(entity, ComEnemy)
  194. } else {
  195. this.addComponent(entity, ComFriend)
  196. }
  197. // 添加spineNode transform
  198. let comSpineTrans = this.addComponent(entitySpine, ComTransform)
  199. comSpineTrans.x = comTrans.x
  200. comSpineTrans.y = comTrans.y
  201. let comRoleSpine = this.addComponent(entitySpine, ComRoleSpine)
  202. comRoleSpine.roleCfg = roleCfg
  203. comRoleSpine.group = isEnemy ? Data.game.gEnemy : Data.game.gFriend
  204. let comRole = this.addComponent(entity, ComRole)
  205. comRole.roleCfg = roleCfg
  206. comRole.drop = dropOutGoods
  207. let spineData: sp.spine.SkeletonData = Data.main.roleSpineMap.get(roleCfg.url)?.getRuntimeData()
  208. comRole.width = spineData ? (spineData.width * roleCfg.gameScale[GAME_SCALE_TYPE.spine]) / 100 : 60
  209. comRole.height = spineData ? (spineData.height * roleCfg.gameScale[GAME_SCALE_TYPE.spine]) / 100 : 100
  210. comRole.spineEntity = entitySpine
  211. comTrans.halfHeight = comRole.height / 2
  212. let baseAttr: IBaseAttr = iRole ? iRole : roleCfg
  213. Mgr.global.changeBaseAttr(comRole, baseAttr)
  214. comRole.attackRange = roleCfg.attackRange
  215. comRole.attackCD = roleCfg.attackCD == Data.game.cdMax ? Infinity : roleCfg.attackCD
  216. comRole.killNum = 0
  217. comRole.attackNum = 0
  218. comRole.group = isEnemy ? Data.game.gEnemy : Data.game.gFriend
  219. comRole.skills.length = 0
  220. comRole.skillCDs.length = 0
  221. comRole.enterCDs.length = 0
  222. comRole.skillCountDowns.length = 0
  223. comRole.babys.length = 0
  224. comRole.parent = 0
  225. comRole.parentRoleID = 0
  226. comRole.HPDirty = true
  227. comRole.transDirty = false
  228. comRole.transID = 0
  229. comRole.buffs.length = 0
  230. comRole.entryMap.clear()
  231. comRole.equips = null
  232. //所有技能
  233. let allSkill = []
  234. let allSkillCDs = []
  235. let allEnterCDs = []
  236. //人物技能
  237. roleCfg.skills.forEach((skill, index) => {
  238. if (skill) {
  239. allSkill.push(skill)
  240. allSkillCDs.push(roleCfg.skillCDs[index])
  241. allEnterCDs.push(roleCfg.enterCDs[index])
  242. }
  243. })
  244. //天赋词条
  245. for (let id of Data.user.talents) {
  246. this.putEntry2Map(comRole, this.convertEntry2Obj(TalentConfig[id].entryID))
  247. }
  248. //装备词条
  249. if (iRole) {
  250. comRole.equips = iRole.equips
  251. for (let iEquip of iRole.equips) {
  252. if (iEquip) {
  253. if (iEquip.cfg.entry && iEquip.cfg.entry < ENTRY.textShow) {
  254. for (let i = 1; i <= iEquip.cfg.quality; i++) {
  255. let ID = Math.floor(iEquip.cfg.ID / 100) * 100 + i
  256. if (ArmorConfig[ID.toString()].entry)
  257. this.putEntry2Map(comRole, this.convertEntry2Obj(ArmorConfig[ID.toString()].entry))
  258. }
  259. }
  260. //装备技能
  261. iEquip.cfg.skills?.forEach((skill, index) => {
  262. if (skill) {
  263. allSkill.push(skill)
  264. allSkillCDs.push(iEquip.cfg.skillCDs[index])
  265. allEnterCDs.push(iEquip.cfg.enterCDs[index])
  266. }
  267. })
  268. }
  269. }
  270. }
  271. //遗迹词条
  272. if (Data.game.gameType == GAME_TYPE.relic) {
  273. //基础属性
  274. if (Data.main.relicAdd[roleCfg.profession]) {
  275. let keys = Object.keys(ATTR_NAME)
  276. for (let attrKey of keys) {
  277. if (Data.main.relicAdd[roleCfg.profession][attrKey]) {
  278. comRole[attrKey] *= 1 + Data.main.relicAdd[roleCfg.profession][attrKey] / Data.game.rateNum
  279. }
  280. }
  281. }
  282. for (let entry of Data.game.relicEntryArr) {
  283. this.putEntry2Map(comRole, this.convertEntry2Obj(entry))
  284. }
  285. }
  286. if (comRole.entryMap.get(ENTRY.startShieldHP)?.num) {
  287. comRole.shieldHP = comRole.entryMap.get(ENTRY.startShieldHP).num
  288. }
  289. allSkill.forEach((skill, index) => {
  290. if (skill && SkillConfig[skill]) {
  291. comRole.skills.push(SkillConfig[skill])
  292. comRole.skillCDs.push(allSkillCDs[index])
  293. comRole.enterCDs.push(allEnterCDs[index])
  294. let cd = allSkillCDs[index]
  295. if (cd == Data.game.cdMax) cd = Infinity
  296. if (cd > 0 && cd != Infinity && comRole.entryMap.get(ENTRY.skillCD)?.num) {
  297. cd *= 1 - comRole.entryMap.get(ENTRY.skillCD).num / Data.game.rateNum
  298. if (cd < 0) cd = 0
  299. }
  300. //入场CD
  301. let enterCD = allEnterCDs[index]
  302. comRole.skillCountDowns.push(enterCD > 0 && cd != Infinity ? enterCD : cd)
  303. } else {
  304. console.error('没有这个技能配置----------->', allSkill[index])
  305. }
  306. })
  307. let attackTime = Infinity
  308. let castTime = new Array(allSkill.length).fill(0) //spine没有技能动作
  309. let enterTime = 0
  310. let dieTime = 0
  311. if (spineData) {
  312. for (let i = 1; i <= roleCfg.skills.length; i++) {
  313. if (spineData.findAnimation(`skill${i}`)) {
  314. castTime[i - 1] = spineData.findAnimation(`skill${i}`).duration
  315. }
  316. }
  317. if (spineData.findAnimation('skill') && castTime.length > 0) {
  318. castTime[0] = spineData.findAnimation('skill').duration
  319. }
  320. if (spineData.findAnimation('enter')) {
  321. enterTime = spineData.findAnimation('enter').duration
  322. }
  323. if (spineData.findAnimation('attack')) {
  324. attackTime = spineData.findAnimation('attack').duration
  325. }
  326. if (spineData.findAnimation('die')) {
  327. dieTime = spineData.findAnimation('die').duration
  328. }
  329. }
  330. comRole.attackTime = attackTime
  331. comRole.castTime = castTime
  332. comRole.enterTime = enterTime
  333. comRole.dieTime = dieTime
  334. //血量等词条装备处理完后处理
  335. comRole.nowHP = comRole.HP
  336. comRole.lastHP = comRole.HP
  337. comRole.shieldHP = 0
  338. comRole.lastShieldHP = 0
  339. comRole.isExtraEnemy = isExtraEnemy
  340. //处理gameUI显示
  341. if (this.fightCore) {
  342. let entities = this.fightCore.roleEntityMap.get(roleCfg.ID)
  343. if (!entities) {
  344. entities = []
  345. this.fightCore.roleEntityMap.set(roleCfg.ID, entities)
  346. }
  347. entities.push(entity)
  348. if (roleCfg.type == MONSTER_TYPE.boss) {
  349. if (this.fightCore.stageInfo.type == GAME_WIN_TYPE.boss) {
  350. }
  351. this.fightCore.showBossHP(comRole)
  352. }
  353. }
  354. return entity
  355. }
  356. setRoleHP(entity, HP) {
  357. if (!HP) return
  358. let comRole = this.getComponent(entity, ComRole)
  359. if (!comRole) return
  360. comRole.HP = HP
  361. comRole.lastHP = HP
  362. comRole.nowHP = HP
  363. }
  364. public initRoleBehavior(comBehavior: ComBehaviorTree, roleConfig: IRoleConfig) {
  365. let findEnemyNode = new BT.FindEnemyNode()
  366. let meleeMonitorNode = new BT.MeleeMonitorNode()
  367. let meleeXMonitorNode = new BT.MeleeXMonitorNode()
  368. let attackMonitorNode = new BT.AttackMonitorNode()
  369. let attackNode = new BT.AttackNode()
  370. let meleeWalkToTargetNode = new BT.MeleeWalkToTargetNode(roleConfig.moveSpeed * 1.5)
  371. let stopNode = new BT.StopNode()
  372. let hasAttackEnemy = new BT.HasAttackEnemyNode()
  373. let hasBeAttackNode = new BT.HasBeAttackNode()
  374. let willAttackNode = new BT.WillAttackNode()
  375. let willBeAttackNode = new BT.WillBeAttackNode()
  376. let willAttackNotWillAttackNode = new BT.WillAttackNotWillAttackNode()
  377. let stopAttackNode = new BT.StopAttackNode()
  378. let beDizzyNode = new BT.BeDizzyNode()
  379. let notBeDizzyNode = new BT.InverterNode(new BT.BeDizzyNode())
  380. let dizzyNode = new BT.DizzyNode()
  381. let skillNode = new BT.SkillNode()
  382. let hasSkillNode = new BT.HasSkillNode()
  383. let walkToCloseXTargetNode = new BT.WalkToCloseTargetNode(roleConfig.moveSpeed, true)
  384. let walkToCloseTargetNode = new BT.WalkToCloseTargetNode(roleConfig.moveSpeed, false)
  385. let notFindEnemyNode = new BT.InverterNode(findEnemyNode)
  386. let notMeleeMonitorNode = new BT.InverterNode(meleeMonitorNode)
  387. let notMeleeXMonitorNode = new BT.InverterNode(meleeXMonitorNode)
  388. let notAttackMonitorNode = new BT.InverterNode(attackMonitorNode)
  389. let notHasAttackEnemy = new BT.InverterNode(hasAttackEnemy)
  390. let notWillBeAttackNode = new BT.InverterNode(willBeAttackNode)
  391. let notWillAttackNode = new BT.InverterNode(willAttackNode)
  392. let notHasBeAttackNode = new BT.InverterNode(hasBeAttackNode)
  393. let notHasSkillNode = new BT.InverterNode(hasSkillNode)
  394. let notCastSkillNode = new BT.InverterNode(new BT.CastSkillNode())
  395. let stopSkillNode = new BT.StopSkillNode()
  396. let shootRoot = new BT.RootSelectorNode(
  397. [
  398. // 晕眩
  399. new BT.SequenceNode([beDizzyNode, stopNode, stopSkillNode, stopAttackNode, dizzyNode]),
  400. // 放技能
  401. new BT.SequenceNode([notBeDizzyNode, hasSkillNode, stopNode, stopAttackNode, skillNode]),
  402. // 远程攻击范围有敌人,攻击最近的敌人
  403. new BT.SequenceNode([notBeDizzyNode, attackMonitorNode, stopNode, attackNode]),
  404. // 远程攻击范围没有敌人,场上有敌军, 往最近敌方X轴方向移动
  405. new BT.SequenceNode([
  406. notBeDizzyNode,
  407. notHasSkillNode,
  408. findEnemyNode,
  409. notAttackMonitorNode,
  410. walkToCloseTargetNode,
  411. ]),
  412. // 没有敌人停止移动
  413. new BT.SequenceNode([notFindEnemyNode, stopNode]),
  414. ],
  415. false,
  416. )
  417. let meleeRoot = new BT.RootSelectorNode([
  418. // 晕眩
  419. new BT.SequenceNode([beDizzyNode, stopNode, stopSkillNode, stopAttackNode, dizzyNode]),
  420. // 放技能
  421. new BT.SequenceNode([notBeDizzyNode, hasSkillNode, stopNode, stopAttackNode, skillNode]),
  422. // X轴Y轴近战探测范围没有敌人,场上有敌军, 往最近敌方X轴方向移动
  423. new BT.SequenceNode([
  424. notBeDizzyNode,
  425. notHasSkillNode,
  426. findEnemyNode,
  427. notHasAttackEnemy,
  428. notWillBeAttackNode,
  429. notWillAttackNode,
  430. notMeleeXMonitorNode,
  431. notMeleeMonitorNode,
  432. walkToCloseXTargetNode,
  433. ]),
  434. // X轴近战探测范围有敌人,往最近敌方X轴方向移动,直到进入攻击范围
  435. new BT.SequenceNode([
  436. notBeDizzyNode,
  437. notHasSkillNode,
  438. findEnemyNode,
  439. notHasAttackEnemy,
  440. notWillBeAttackNode,
  441. notWillAttackNode,
  442. meleeXMonitorNode,
  443. walkToCloseXTargetNode,
  444. ]),
  445. // X轴近战攻击范围没有敌人,没有被近战即将攻击, 往最近Y轴的敌方方向移动
  446. new BT.SequenceNode([
  447. notBeDizzyNode,
  448. notHasSkillNode,
  449. findEnemyNode,
  450. notHasAttackEnemy,
  451. notWillBeAttackNode,
  452. notWillAttackNode,
  453. notMeleeXMonitorNode,
  454. meleeMonitorNode,
  455. walkToCloseTargetNode,
  456. ]),
  457. // 自己即将被攻击,停止
  458. new BT.SequenceNode([notBeDizzyNode, notHasSkillNode, notHasAttackEnemy, willBeAttackNode, stopNode]),
  459. // 自己即将攻击敌人,跑到攻击点
  460. new BT.SequenceNode([
  461. notBeDizzyNode,
  462. notHasAttackEnemy,
  463. // notHasBeAttackNode,
  464. willAttackNode,
  465. willAttackNotWillAttackNode,
  466. meleeWalkToTargetNode,
  467. ]),
  468. // 到达了攻击地点,继续攻击
  469. new BT.SequenceNode([notBeDizzyNode, hasAttackEnemy, stopNode, attackNode]),
  470. // 停止移动
  471. new BT.SequenceNode([notFindEnemyNode, stopNode]),
  472. ])
  473. comBehavior.root = roleConfig.attackRange > Data.game.meleeRange ? shootRoot : meleeRoot
  474. if (roleConfig.attackRange == 0) comBehavior.root = null
  475. }
  476. public changeMoveSpeed(entity: EntityIndex, speed: number) {
  477. let comMovable = this.getComponent(entity, ComMovable)
  478. let comCocosNode = this.getComponent(entity, ComCocosNode)
  479. let comRole = this.getComponent(entity, ComRole)
  480. if (!comMovable || !comCocosNode) return
  481. let isChange = comMovable.speed != speed
  482. comMovable.speed = speed
  483. //同步spine战立和移动
  484. let comSpineNode = this.getComponent(comRole?.spineEntity, ComCocosNode)
  485. if (comMovable.speed > 0) {
  486. isChange && comCocosNode.events.push(new EventRun()) && comSpineNode?.events.push(new EventRun())
  487. } else {
  488. comMovable.points.length = 0
  489. comMovable.pointIdx = 0
  490. isChange && comCocosNode.events.push(new EventStand()) && comSpineNode?.events.push(new EventStand())
  491. }
  492. }
  493. public changeRoleRun(entity: EntityIndex, isRun: boolean) {
  494. let comRole = this.getComponent(entity, ComRole)
  495. if (!comRole) return
  496. let comSpineNode = this.getComponent(comRole.spineEntity, ComCocosNode)
  497. comSpineNode?.node.getComponent(RoleSpineProcess).processEvent(isRun ? new EventRun() : new EventStand())
  498. }
  499. isDie(idOrCom: EntityIndex | ComRole) {
  500. let comRole: ComRole
  501. if (typeof idOrCom == 'number') {
  502. if (!idOrCom) return true
  503. comRole = this.getComponent(idOrCom, ComRole)
  504. } else {
  505. comRole = idOrCom
  506. }
  507. return !comRole || comRole.nowHP + comRole.shieldHP <= 0
  508. }
  509. isEnter(id: EntityIndex) {
  510. let comEnter = this.getComponent(id, ComEnter)
  511. return comEnter != null
  512. }
  513. isBase(idOrCom: EntityIndex | ComRole) {
  514. return this.isRoleType(idOrCom, ROLE_TYPE.base)
  515. }
  516. isBoss(idOrCom: EntityIndex | ComRole) {
  517. return this.isRoleType(idOrCom, MONSTER_TYPE.boss)
  518. }
  519. isRoleType(idOrCom: EntityIndex | ComRole, type: ROLE_TYPE | MONSTER_TYPE) {
  520. let comRole: ComRole
  521. if (typeof idOrCom == 'number') {
  522. if (!idOrCom) return false
  523. comRole = this.getComponent(idOrCom, ComRole)
  524. } else {
  525. comRole = idOrCom
  526. }
  527. return !comRole || comRole.roleCfg.type === type
  528. }
  529. //-------------------------战斗扣血加血-----------------------------------------------
  530. //普攻扣血
  531. changeHpByHurt(role: EntityIndex, hurtHP: number, isCrit: boolean, HitAniID: number) {
  532. if (!hurtHP) return
  533. let changeHPRole = this.getComponent(role, ComRole)
  534. if (changeHPRole.invincible) return
  535. let changeHPComCocosNode = this.getComponent(role, ComCocosNode)
  536. changeHPRole.lastHP = changeHPRole.nowHP
  537. changeHPRole.lastShieldHP = changeHPRole.shieldHP
  538. changeHPComCocosNode.events.push(
  539. new EventRoleTip(
  540. isCrit ? GAME_ROLE_TIP.critTip : GAME_ROLE_TIP.damageTip,
  541. -hurtHP,
  542. `-${Math.toKMBNum(hurtHP)}`,
  543. ),
  544. )
  545. let comSpineNode = this.getComponent(changeHPRole.spineEntity, ComCocosNode)
  546. comSpineNode?.events.push(new EventHurtOrAdd(-hurtHP, isCrit))
  547. if (changeHPRole.shieldHP > 0) {
  548. changeHPRole.shieldHP -= hurtHP
  549. if (changeHPRole.shieldHP < 0) {
  550. changeHPRole.nowHP += changeHPRole.shieldHP
  551. changeHPRole.shieldHP = 0
  552. }
  553. } else {
  554. changeHPRole.nowHP -= hurtHP
  555. }
  556. changeHPRole.HPDirty = true
  557. if (!this.isBase(changeHPRole) && changeHPRole.roleCfg.type != ROLE_TYPE.totem && HitAniID > 0) {
  558. this.createAni(HitAniID, role)
  559. }
  560. }
  561. getDefenseRate(defenseObj: IFightData, attackObj?: IFightData) {
  562. let ignoreDefenseE = attackObj?.entryMap.get(ENTRY.ignoreDefense)?.num || 0
  563. return (
  564. 1 -
  565. (defenseObj.defense * (1 - ignoreDefenseE / Data.game.rateNum)) /
  566. (defenseObj.defense + Data.game.defenseConst)
  567. )
  568. }
  569. getSpellDefenseRate(fightData: IFightData, attackObj?: IFightData) {
  570. let ignoreDefenseE = attackObj?.entryMap.get(ENTRY.ignoreDefense)?.num || 0
  571. return (
  572. 1 -
  573. (fightData.spellDefense * (1 - ignoreDefenseE / Data.game.rateNum)) /
  574. (fightData.spellDefense + Data.game.defenseConst)
  575. )
  576. }
  577. //攻击扣血值
  578. getHurtByAttackRole(attackObj: IFightData, role: EntityIndex, attackEffect: IAttackEffect): number {
  579. let hurtHP = 0
  580. let changeHPRole = this.getComponent(role, ComRole)
  581. let changeHPComCocosNode = this.getComponent(role, ComCocosNode)
  582. if (this.isDie(changeHPRole) || !changeHPComCocosNode || !changeHPComCocosNode.loaded) return hurtHP
  583. let critAddAttack = 0
  584. let rateNum = Data.game.rateNum
  585. let attack = attackObj.attack
  586. let spellAttack = attackObj.spellAttack
  587. let realAttack = attackObj.realAttack
  588. // 所有的比例加伤
  589. let hurtAddRate = 0
  590. let realHurtAdd: number = 0
  591. attackObj.buffs.forEach(buff => {
  592. let buffConfig = buff.buffCfg
  593. if (buffConfig.buffType == BUFF_TYPE.backAddHurt && attackEffect.isBack) {
  594. //后背加伤(万分比)
  595. hurtAddRate += buffConfig.attrRate[0]
  596. } else if (buffConfig.buffType == BUFF_TYPE.realByHp) {
  597. //血量比例加真实攻击(万分比)
  598. realHurtAdd += attackObj.nowHP * (buffConfig.attrRate[0] / rateNum)
  599. } else if (
  600. buffConfig.buffType == BUFF_TYPE.attackNumAddHurt &&
  601. attackObj.attackNum % buffConfig.attrNum[0] == 0
  602. ) {
  603. //N弹加伤(万分比)
  604. hurtAddRate += buffConfig.attrRate[0]
  605. }
  606. })
  607. //普攻对boss加伤
  608. if (attackObj.entryMap.get(ENTRY.boosHurt)?.num && changeHPRole.roleCfg.type == MONSTER_TYPE.boss) {
  609. hurtAddRate += attackObj.entryMap.get(ENTRY.boosHurt).num
  610. }
  611. changeHPRole.buffs.forEach(buff => {
  612. let buffConfig = buff.buffCfg
  613. if (buffConfig.buffType == BUFF_TYPE.shieldReduceHurt) {
  614. //顶盾减伤(万分比)
  615. hurtAddRate += buffConfig.attrRate[0]
  616. }
  617. })
  618. //暴击
  619. if (attackEffect.isCrit) {
  620. attack *= attackObj.critNum / rateNum
  621. spellAttack *= attackObj.critNum / rateNum
  622. realAttack *= attackObj.critNum / rateNum
  623. }
  624. //溅射
  625. if (attackEffect.sputter) {
  626. attack *= attackEffect.sputter / rateNum
  627. spellAttack *= attackEffect.sputter / rateNum
  628. realAttack *= attackEffect.sputter / rateNum
  629. }
  630. if (attack || spellAttack || realAttack) {
  631. //物理伤害 物理攻击*(1-物理防御/(物理防御+防御常数))
  632. let hurt = attack * this.getDefenseRate(changeHPRole, attackObj)
  633. //魔法伤害 魔法攻击*(1-魔法防御/(魔法防御+防御常数))
  634. let spellHurt = spellAttack * this.getSpellDefenseRate(changeHPRole, attackObj)
  635. //真实伤害 真实攻击
  636. let realHurt = realAttack
  637. realHurt += realHurtAdd
  638. let endHurtRate = 1 + hurtAddRate / rateNum
  639. if (endHurtRate < 0) endHurtRate = 0
  640. hurtHP = (hurt + spellHurt + realHurt) * (1 + Data.game.curHurtRate / rateNum) * endHurtRate
  641. }
  642. //普攻减伤
  643. if (changeHPRole.entryMap.get(ENTRY.hurtReduce)?.num > 0) {
  644. hurtHP -= changeHPRole.entryMap.get(ENTRY.hurtReduce).num
  645. }
  646. if (hurtHP < 0) hurtHP = 0
  647. return hurtHP
  648. }
  649. //技能加减血
  650. changeHpBySkill(skill: ComSkill, role: EntityIndex) {
  651. let changeHPRole = this.getComponent(role, ComRole)
  652. let changeHPComCocosNode = this.getComponent(role, ComCocosNode)
  653. if (
  654. this.isDie(changeHPRole) ||
  655. (this.isBase(changeHPRole) && !skill.skillCfg.effectType.includes(SKILL_EFFECT_TYPE.hurtBase)) ||
  656. changeHPRole.invincible ||
  657. !changeHPComCocosNode ||
  658. !changeHPComCocosNode.loaded
  659. )
  660. return
  661. let fightData = skill.fightData
  662. if (
  663. (skill.skillCfg.type == SKILL_TYPE.normal || skill.skillCfg.type == SKILL_TYPE.attackBack) &&
  664. skill.skillCfg.hurtRate
  665. ) {
  666. let rateNum = Data.game.rateNum
  667. let hurtHP = 0
  668. let attack = fightData.attack
  669. let spellAttack = fightData.spellAttack
  670. let realAttack = fightData.realAttack
  671. let hurtAddRate = skill.skillCfg.hurtRate
  672. //技能加伤
  673. if (fightData.entryMap.get(ENTRY.skillHurtRate)?.num) {
  674. hurtAddRate += fightData.entryMap.get(ENTRY.skillHurtRate).num
  675. }
  676. //技能对boss加伤
  677. if (fightData.entryMap.get(ENTRY.boosHurt)?.num && changeHPRole.roleCfg.type == MONSTER_TYPE.boss) {
  678. hurtAddRate += fightData.entryMap.get(ENTRY.boosHurt).num
  679. }
  680. if (attack || spellAttack || realAttack) {
  681. //物理伤害 物理攻击*(1-物理防御/(物理防御+防御常数))
  682. let hurt = attack * this.getDefenseRate(changeHPRole)
  683. //魔法伤害 魔法攻击*(1-魔法防御/(魔法防御+防御常数))
  684. let spellHurt = spellAttack * this.getSpellDefenseRate(changeHPRole)
  685. //真实伤害 真实攻击
  686. let realHurt = realAttack
  687. hurtHP = (hurt + spellHurt + realHurt) * (1 + Data.game.curHurtRate / rateNum) * (hurtAddRate / rateNum)
  688. }
  689. //技能减伤
  690. if (changeHPRole.entryMap.get(ENTRY.hurtReduce)?.num > 0) {
  691. hurtHP -= changeHPRole.entryMap.get(ENTRY.hurtReduce).num
  692. }
  693. if (hurtHP < 0) hurtHP = 0
  694. //受击特效统一处理,这里是0
  695. this.changeHpByHurt(role, hurtHP, false, 0)
  696. }
  697. let addNum = 0
  698. if (skill.skillCfg.type == SKILL_TYPE.addHP) {
  699. addNum = (skill.skillCfg.hurtRate / Data.game.rateNum) * fightData.spellAttack
  700. let moreAddNum = 0
  701. if (addNum + changeHPRole.nowHP > changeHPRole.HP) {
  702. moreAddNum = addNum + changeHPRole.nowHP - changeHPRole.HP
  703. addNum = changeHPRole.HP - changeHPRole.nowHP
  704. }
  705. for (let i = 0; i < skill.skillCfg.effectType.length; i++) {
  706. if (skill.skillCfg.effectType[i] == SKILL_EFFECT_TYPE.addExtraHP) {
  707. if (changeHPRole.nowHP / changeHPRole.HP < skill.skillCfg.effectParm[i][0] / Data.game.rateNum) {
  708. addNum += skill.skillCfg.effectParm[i][1]
  709. }
  710. } else if (skill.skillCfg.effectType[i] == SKILL_EFFECT_TYPE.addRateHP) {
  711. addNum += (skill.skillCfg.effectParm[i][0] / Data.game.rateNum) * changeHPRole.HP
  712. }
  713. }
  714. //增加治疗量
  715. if (fightData.entryMap.get(ENTRY.addHealing)?.num) {
  716. addNum *= 1 + fightData.entryMap.get(ENTRY.addHealing).num / Data.game.rateNum
  717. }
  718. //受治疗增加量
  719. if (changeHPRole.entryMap.get(ENTRY.healingMore)?.num) {
  720. addNum *= 1 + changeHPRole.entryMap.get(ENTRY.healingMore).num / Data.game.rateNum
  721. }
  722. //多的治疗量转为白血盾
  723. if (changeHPRole.entryMap.get(ENTRY.healing2Shield)?.num && moreAddNum) {
  724. changeHPRole.shieldHP +=
  725. (changeHPRole.entryMap.get(ENTRY.healing2Shield).num / Data.game.rateNum) * moreAddNum
  726. }
  727. this.addHP(addNum, role)
  728. }
  729. if (
  730. skill.skillCfg.type == SKILL_TYPE.addShieldHPByHP ||
  731. skill.skillCfg.type == SKILL_TYPE.addShieldHPBySpAttack
  732. ) {
  733. addNum =
  734. (skill.skillCfg.hurtRate / Data.game.rateNum) *
  735. (skill.skillCfg.type == SKILL_TYPE.addShieldHPByHP ? fightData.HP : fightData.spellAttack)
  736. if (changeHPRole.shieldHP < addNum) changeHPRole.shieldHP = addNum
  737. if (skill.skillCfg.type == SKILL_TYPE.addShieldHPBySpAttack) {
  738. for (let i = 0; i < skill.skillCfg.effectType.length; i++) {
  739. if (skill.skillCfg.effectType[i] == SKILL_EFFECT_TYPE.addRateShieldHP) {
  740. addNum += (skill.skillCfg.effectParm[i][0] / Data.game.rateNum) * changeHPRole.HP
  741. }
  742. }
  743. }
  744. }
  745. changeHPRole.HPDirty = true
  746. }
  747. changeHpByRebound(role: EntityIndex, enemy: EntityIndex) {
  748. let comRoleEnemy: ComRole = this.getComponent(enemy, ComRole)
  749. let comRole: ComRole = this.getComponent(enemy, ComRole)
  750. if (this.isDie(comRoleEnemy)) return
  751. comRoleEnemy.buffs.forEach(buff => {
  752. let buffType = buff.buffCfg.buffType
  753. if (buffType == BUFF_TYPE.reboundAttack || buffType == BUFF_TYPE.reboundReal) {
  754. //真实伤害
  755. let defenseRate = 1
  756. if (buffType == BUFF_TYPE.reboundAttack) {
  757. //物理伤害
  758. defenseRate = this.getDefenseRate(comRole, comRoleEnemy)
  759. }
  760. let hurt = (comRoleEnemy.attack * defenseRate * buff.buffCfg.attrRate[0]) / Data.game.rateNum
  761. this.changeHpByHurt(role, hurt, false, 0)
  762. }
  763. })
  764. if (comRoleEnemy.entryMap.get(ENTRY.hurtRebound)?.num) {
  765. let defenseRate = this.getDefenseRate(comRole, comRoleEnemy)
  766. let hurt =
  767. (comRoleEnemy.attack * defenseRate * comRoleEnemy.entryMap.get(ENTRY.hurtRebound)?.num) /
  768. Data.game.rateNum
  769. this.changeHpByHurt(role, hurt, false, 0)
  770. }
  771. }
  772. moveToMeleeAttackPos(
  773. selfEntity: EntityIndex,
  774. comTrans: ComTransform,
  775. targetComTrans: ComTransform,
  776. targetComMovable: ComMovable,
  777. isBack?: boolean,
  778. ) {
  779. let targetPos = cc.v2(targetComTrans.x, targetComTrans.y)
  780. // 近战前往目标敌人没被锁定的某一个近战范围攻击点
  781. let isLeft = comTrans.x - targetComTrans.x < 0
  782. if (isBack) isLeft = !isLeft
  783. if (targetComTrans.x >= Data.game.fightSizeMaxX) {
  784. isLeft = true
  785. }
  786. if (targetComTrans.x <= Data.game.fightSizeMinX) {
  787. isLeft = false
  788. }
  789. let isDown = comTrans.y - targetComTrans.y < 0
  790. let attackArrayPos = isLeft ? Data.game.attackLeftRadiusPoints : Data.game.attackRightRadiusPoints
  791. let curAttackArrayPos = isLeft
  792. ? targetComMovable.leftMeleeEnemiesPosDirty
  793. : targetComMovable.rightMeleeEnemiesPosDirty
  794. let posIndex = curAttackArrayPos.indexOf(-1)
  795. let secondPosIndex = curAttackArrayPos.indexOf(-1, posIndex + 1)
  796. let targetIndex
  797. //因为leftMeleeEnemiesPosDirty数组是上下位置交替插入的,所以找出离自己近的点
  798. if (posIndex >= 0 && secondPosIndex > 0) {
  799. if (isDown) {
  800. targetIndex = attackArrayPos[posIndex].y < 0 ? posIndex : secondPosIndex
  801. } else {
  802. targetIndex = attackArrayPos[posIndex].y >= 0 ? posIndex : secondPosIndex
  803. }
  804. } else if (posIndex >= 0 && secondPosIndex <= 0) {
  805. targetIndex = posIndex
  806. } else {
  807. targetIndex = Math.randomRangeInt(0, attackArrayPos.length - 1)
  808. }
  809. curAttackArrayPos[targetIndex] = selfEntity
  810. targetPos.addSelf(attackArrayPos[targetIndex])
  811. if (targetPos.y <= Data.game.fightSizeMinY) targetPos.y = Data.game.fightSizeMinY
  812. if (targetPos.y >= Data.game.fightSizeMaxY) targetPos.y = Data.game.fightSizeMaxY
  813. return targetPos
  814. }
  815. //加血吸血
  816. addHP(addHPNum: number, role: EntityIndex) {
  817. let comRole = this.getComponent(role, ComRole)
  818. let comCocosNode = this.getComponent(role, ComCocosNode)
  819. if (!this.isDie(role)) {
  820. if (addHPNum >= 1) {
  821. comCocosNode.events.push(new EventRoleTip(GAME_ROLE_TIP.hpTip, addHPNum, `+${Math.toKMBNum(addHPNum)}`))
  822. }
  823. let target = comRole.nowHP + addHPNum
  824. if (target > comRole.HP) {
  825. addHPNum = comRole.HP - comRole.nowHP
  826. }
  827. comRole.nowHP += addHPNum
  828. }
  829. }
  830. //-------------------------------子弹-----------------------------------------
  831. public createBulletEntity(role: EntityIndex, isCrit: boolean): EntityIndex[] {
  832. let comRole = this.getComponent(role, ComRole)
  833. let comTransform = this.getComponent(role, ComTransform)
  834. let comFindEnemy = this.getComponent(role, ComFindEnemy)
  835. let comAttackable = this.getComponent(role, ComAttackable)
  836. let entities = []
  837. if (this.isDie(comRole)) {
  838. //Log.warn('创建子弹失败')
  839. return entities
  840. }
  841. let bulletID = comRole.roleCfg.bullet
  842. if (!bulletID) {
  843. return entities
  844. }
  845. let bulletCfg = BulletConfig[bulletID]
  846. let enemies = [comAttackable.curAttack]
  847. let endPosArr: cc.Vec2[] = []
  848. //跟踪敌人改成固定角度
  849. if (bulletCfg.splitNum > 1) {
  850. // let otherEnemies = comFindEnemy.attackCObject.containsBody.keysArr()
  851. // otherEnemies = otherEnemies.filter(
  852. // v =>
  853. // Math.sign(comTransform.dir.x) != Math.sign(this.getComponent(v, ComTransform)?.dir.x) &&
  854. // v != comAttackable.curAttack,
  855. // )
  856. //
  857. // otherEnemies.sort((a, b) => {
  858. // let at = this.getComponent(a, ComTransform)
  859. // let bt = this.getComponent(b, ComTransform)
  860. // return at.x ** 2 + at.y ** 2 - (bt.x ** 2 + bt.y ** 2)
  861. // })
  862. // enemies = enemies.concat(otherEnemies)
  863. let enemyComTransform = this.getComponent(enemies[0], ComTransform)
  864. let startPos = cc.v2(comTransform.x, comTransform.y + comTransform.halfHeight)
  865. let hitPos = cc.v2(enemyComTransform.x, enemyComTransform.y + enemyComTransform.halfHeight)
  866. for (let i = 1; i < bulletCfg.splitNum; i++) {
  867. endPosArr.push(this.rotatePoint(startPos, hitPos, 10 * i * (i % 2 == 0 ? -1 : 1)))
  868. }
  869. }
  870. for (let i = 0; i < bulletCfg.splitNum; i++) {
  871. //if (i < enemies.length) {
  872. let enemyComTransform = this.getComponent(enemies[i], ComTransform)
  873. if (!enemyComTransform) {
  874. enemyComTransform = new ComTransform()
  875. enemyComTransform.x = endPosArr[i - 1].x
  876. enemyComTransform.y = endPosArr[i - 1].y
  877. }
  878. let entity = this.createEntity()
  879. // 添加nodeconfig
  880. let comMap = this.addComponent(entity, ComNodeConfig)
  881. comMap.id = 1
  882. comMap.layer = GAME_LAYER[GAME_LAYER.bullet]
  883. comMap.prefabName = GAME_PREFAB_TYPE.bullet
  884. //移除cocosNode
  885. this.removeComponent(entity, ComCocosNode)
  886. // 添加transform
  887. let comTrans = this.addComponent(entity, ComTransform)
  888. let bulletPosScaleX = comTransform.dir.x >= 0 ? 1 : -1
  889. let spineScale = comRole.roleCfg.gameScale[GAME_SCALE_TYPE.spine] / 100
  890. comTrans.x = comTransform.x + comRole.roleCfg.bulletPos[0] * bulletPosScaleX * spineScale
  891. comTrans.y = comTransform.y + comRole.roleCfg.bulletPos[1] * spineScale
  892. let comMovable = this.addComponent(entity, ComMovable)
  893. comMovable.speed = 0
  894. let comBullet = this.addComponent(entity, ComBullet)
  895. comBullet.group = comRole.group == Data.game.gFriend ? Data.game.gFriendBullet : Data.game.gEnemyBullet
  896. comBullet.fightData = {...comRole, roleEntity: role}
  897. comBullet.fightData.buffs = ccUtils.deepCopy(comRole.buffs)
  898. comBullet.bulletCfg = bulletCfg
  899. comBullet.ejectionEnemies.length = 0
  900. comBullet.ejectionEnemy = enemies[i] ? enemies[i] : 0
  901. comBullet.hitEnemies.length = 0
  902. comBullet.isCrit = isCrit
  903. this.initBulletDir(comTrans, enemyComTransform, comMovable, comBullet)
  904. entities.push(entity)
  905. // }
  906. }
  907. return entities
  908. }
  909. public createRoleSkillBulletEntity(
  910. bulletID: number,
  911. createEntity: EntityIndex,
  912. enemy: EntityIndex,
  913. randomPos: boolean = false,
  914. pos?: cc.Vec2,
  915. ): EntityIndex[] {
  916. let bulletCfg = BulletConfig[bulletID]
  917. let roleComTransform = this.getComponent(createEntity, ComTransform)
  918. let enemyComTransform = this.getComponent(enemy, ComTransform)
  919. if (pos) {
  920. enemyComTransform = new ComTransform()
  921. enemyComTransform.x = pos.x
  922. enemyComTransform.y = pos.y
  923. }
  924. if (!enemyComTransform || !bulletCfg) {
  925. return
  926. }
  927. let endPosArr: cc.Vec2[] = []
  928. //跟踪敌人改成固定角度
  929. if (bulletCfg.splitNum > 1) {
  930. let startPos = cc.v2(roleComTransform.x, roleComTransform.y + roleComTransform.halfHeight)
  931. let hitPos = cc.v2(enemyComTransform.x, enemyComTransform.y + enemyComTransform.halfHeight)
  932. for (let i = 1; i < bulletCfg.splitNum; i++) {
  933. endPosArr.push(this.rotatePoint(startPos, hitPos, 10 * i * (i % 2 == 0 ? -1 : 1)))
  934. }
  935. }
  936. let entityArr = []
  937. for (let i = 0; i < bulletCfg.splitNum; i++) {
  938. let entity = this.createEntity()
  939. // 添加nodeconfig
  940. let comMap = this.addComponent(entity, ComNodeConfig)
  941. comMap.id = 1
  942. comMap.layer = GAME_LAYER[GAME_LAYER.bullet]
  943. comMap.prefabName = GAME_PREFAB_TYPE.bullet
  944. //移除cocosNode
  945. this.removeComponent(entity, ComCocosNode)
  946. let comRole = this.getComponent(createEntity, ComRole)
  947. // 添加transform
  948. let comTrans = this.addComponent(entity, ComTransform)
  949. let bulletPosScaleX = roleComTransform.dir.x >= 0 ? 1 : -1
  950. let spineScale = comRole.roleCfg.gameScale[GAME_SCALE_TYPE.spine] / 100
  951. comTrans.x = roleComTransform.x + comRole.roleCfg.bulletPos[0] * bulletPosScaleX * spineScale
  952. comTrans.y =
  953. roleComTransform.y +
  954. (comRole.roleCfg.bulletPos[1] ? comRole.roleCfg.bulletPos[1] * spineScale : roleComTransform.halfHeight)
  955. let comMovable = this.addComponent(entity, ComMovable)
  956. comMovable.speed = 0
  957. let comBullet = this.addComponent(entity, ComBullet)
  958. comBullet.group = comRole.group == Data.game.gFriend ? Data.game.gFriendBullet : Data.game.gEnemyBullet
  959. comBullet.fightData = {...comRole, roleEntity: createEntity}
  960. comBullet.bulletCfg = bulletCfg
  961. comBullet.ejectionEnemies.length = 0
  962. comBullet.ejectionEnemy = enemy
  963. comBullet.hitEnemies.length = 0
  964. comBullet.isCrit = false
  965. //特效子弹位置随机
  966. if (randomPos && SkillConfig[bulletCfg.skill]) {
  967. let randomWidth = SkillConfig[bulletCfg.skill].attackRangeX
  968. let randomHeight = SkillConfig[bulletCfg.skill].attackRangeY
  969. let randomX = enemyComTransform.x + Math.randomRangeFloat(-randomWidth / 2, randomWidth / 2)
  970. let randomY = enemyComTransform.y + Math.randomRangeFloat(-randomHeight / 2, randomHeight / 2)
  971. enemyComTransform = new ComTransform()
  972. enemyComTransform.x = randomX
  973. enemyComTransform.y = randomY
  974. }
  975. if (bulletCfg.born == START_POS.straightFall || bulletCfg.born == START_POS.obliqueFall) {
  976. comTrans.x = enemyComTransform.x - (bulletCfg.born == START_POS.obliqueFall ? 200 : 0)
  977. comTrans.y = ConstValue.CANVAS_HEIGHT / 2
  978. }
  979. if (i > 0) {
  980. enemyComTransform = new ComTransform()
  981. enemyComTransform.x = endPosArr[i - 1].x
  982. enemyComTransform.y = endPosArr[i - 1].y
  983. }
  984. this.initBulletDir(comTrans, enemyComTransform, comMovable, comBullet)
  985. entityArr.push(entity)
  986. }
  987. return entityArr
  988. }
  989. public createCardBulletEntity(
  990. iCard: ICard,
  991. startPos: cc.Vec2,
  992. endPos: cc.Vec2,
  993. enemy: EntityIndex = 0,
  994. ): EntityIndex {
  995. let cardSkillCfg = iCard.cfg
  996. let bulletCfg = BulletConfig[cardSkillCfg.bullet]
  997. let entity = this.createEntity()
  998. // 添加nodeconfig
  999. let comMap = this.addComponent(entity, ComNodeConfig)
  1000. comMap.id = 1
  1001. comMap.layer = GAME_LAYER[GAME_LAYER.bullet]
  1002. comMap.prefabName = GAME_PREFAB_TYPE.bullet
  1003. //移除cocosNode
  1004. this.removeComponent(entity, ComCocosNode)
  1005. // 添加transform
  1006. let comTrans = this.addComponent(entity, ComTransform)
  1007. comTrans.x = startPos.x
  1008. comTrans.y = startPos.y
  1009. let comMovable = this.addComponent(entity, ComMovable)
  1010. comMovable.speed = 0
  1011. let comBullet = this.addComponent(entity, ComBullet)
  1012. comBullet.group = Data.game.gFriendBullet
  1013. comBullet.fightData = {buffs: [], entryMap: new Map<ENTRY, EntryObj>(), ...iCard}
  1014. comBullet.bulletCfg = bulletCfg
  1015. comBullet.ejectionEnemies.length = 0
  1016. comBullet.ejectionEnemy = enemy
  1017. comBullet.hitEnemies.length = 0
  1018. comBullet.isCrit = false
  1019. let enemyComTransform = new ComTransform()
  1020. enemyComTransform.x = endPos.x
  1021. enemyComTransform.y = endPos.y
  1022. this.initBulletDir(comTrans, enemyComTransform, comMovable, comBullet)
  1023. return entity
  1024. }
  1025. public createBaseBulletEntity(posOrTrans: ComTransform | cc.Vec2) {
  1026. let sysCastleSkill = this.getSystem(SysCastleSkill)
  1027. if (sysCastleSkill.rechargeTime > 0) {
  1028. Mgr.ui.tip(LANGUAGE_TYPE.inRecharge)
  1029. return
  1030. }
  1031. if (!Data.user.useCastleSkillID) return
  1032. sysCastleSkill.reduceLeftNum()
  1033. let entity = this.createEntity()
  1034. // 添加nodeconfig
  1035. let comMap = this.addComponent(entity, ComNodeConfig)
  1036. comMap.id = 1
  1037. comMap.layer = GAME_LAYER[GAME_LAYER.bullet]
  1038. comMap.prefabName = GAME_PREFAB_TYPE.bullet
  1039. //移除cocosNode
  1040. this.removeComponent(entity, ComCocosNode)
  1041. // 添加transform
  1042. let comTrans = this.addComponent(entity, ComTransform)
  1043. comTrans.x = -10000
  1044. comTrans.y = 10000
  1045. let comMovable = this.addComponent(entity, ComMovable)
  1046. comMovable.speed = 0
  1047. let comBullet = this.addComponent(entity, ComBullet)
  1048. comBullet.group = Data.game.gFriendBullet
  1049. let baseBulletCfg = CastleSkillConfig[Data.user.useCastleSkillID]
  1050. comBullet.fightData = {buffs: [], entryMap: new Map<ENTRY, EntryObj>(), ...baseBulletCfg}
  1051. comBullet.bulletCfg = BulletConfig[baseBulletCfg.bullet]
  1052. comBullet.ejectionEnemies.length = 0
  1053. comBullet.ejectionEnemy = 0
  1054. comBullet.hitEnemies.length = 0
  1055. comBullet.ejectionAnis.length = 0
  1056. comBullet.isCrit = false
  1057. let enemyComTransform
  1058. if (posOrTrans instanceof ComTransform) {
  1059. enemyComTransform = posOrTrans
  1060. } else {
  1061. let startPos = cc.v2(comTrans.x, comTrans.y)
  1062. let direction = posOrTrans.sub(startPos).normalize()
  1063. let endPos = startPos.add(direction.mul(baseBulletCfg.attackRange))
  1064. enemyComTransform = new ComTransform()
  1065. enemyComTransform.x = endPos.x
  1066. enemyComTransform.y = endPos.y
  1067. }
  1068. this.initBulletDir(comTrans, enemyComTransform, comMovable, comBullet)
  1069. return entity
  1070. }
  1071. public initBulletDir(
  1072. comTrans: ComTransform,
  1073. enemyComTransform: ComTransform,
  1074. comMovable: ComMovable,
  1075. comBullet: ComBullet,
  1076. comCocosNode?: ComCocosNode,
  1077. ) {
  1078. let startPos = cc.v2(comTrans.x, comTrans.y)
  1079. let hitPos = cc.v2(enemyComTransform.x, enemyComTransform.y + enemyComTransform.halfHeight)
  1080. // 计算子弹的飞行方向向量
  1081. let direction = hitPos.sub(startPos).normalize()
  1082. let bulletCfg = comBullet.bulletCfg
  1083. if (
  1084. !bulletCfg.isEjection &&
  1085. bulletCfg.type == BULLET_TYPE.normal &&
  1086. !bulletCfg.effectType.includes(BULLET_EFFECT_TYPE.attackRange) &&
  1087. !bulletCfg.effectType.includes(BULLET_EFFECT_TYPE.boomerangRange)
  1088. ) {
  1089. //如果是直线子弹,将子弹终点设置为边界外的某个点,子弹到达边界点,移除子弹
  1090. hitPos = this.findIntersection(startPos, direction)
  1091. //当子弹从区域外进入,直接让子弹结束
  1092. if (!hitPos) hitPos = startPos
  1093. }
  1094. let hitPosArr = [hitPos]
  1095. if (bulletCfg.flyType == BULLET_FLY_TYPE.parabolic) {
  1096. hitPosArr = this.getParabolaPoints(startPos, hitPos, 50)
  1097. if (hitPosArr.length >= 2) direction = hitPosArr[1].sub(hitPosArr[0]).normalize()
  1098. } else if (bulletCfg.flyType == BULLET_FLY_TYPE.bezier) {
  1099. hitPosArr.length = 0
  1100. hitPosArr.push(...this.getEquidistantPoints(startPos, hitPos, 10, comBullet.group))
  1101. hitPosArr.push(hitPos)
  1102. } else if (bulletCfg.flyType == BULLET_FLY_TYPE.boomerang) {
  1103. hitPosArr.length = 0
  1104. let parms = bulletCfg.effectParm[bulletCfg.effectType.indexOf(BULLET_EFFECT_TYPE.boomerangRange)]
  1105. let halfPoints = this.getEquidistantPoints(startPos, hitPos, 10, comBullet.group, parms[1])
  1106. let leftPoints = this.getEquidistantPoints(hitPos, startPos, 10, comBullet.group, parms[1])
  1107. hitPosArr.push(...halfPoints)
  1108. hitPosArr.push(hitPos)
  1109. hitPosArr.push(...leftPoints)
  1110. hitPosArr.push(startPos)
  1111. } else if (bulletCfg.flyType == BULLET_FLY_TYPE.horizon) {
  1112. hitPosArr.length = 0
  1113. hitPosArr.push(cc.v2(comTrans.x + (direction.x >= 0 ? 1000 : -1000), comTrans.y))
  1114. }
  1115. comTrans.dir.x = direction.x
  1116. comTrans.dir.y = direction.y
  1117. comMovable.pointIdx = 0
  1118. comMovable.points.length = 0
  1119. comMovable.points.push(...hitPosArr)
  1120. if (comCocosNode) {
  1121. //计算发射角度
  1122. let angle = Math.atan2(comTrans.dir.y, comTrans.dir.x)
  1123. comCocosNode.node.angle = cc.misc.radiansToDegrees(angle)
  1124. }
  1125. }
  1126. getParabolaPoints(a: cc.Vec2, b: cc.Vec2, interval: number): cc.Vec2[] {
  1127. let xA = a.x,
  1128. yA = a.y
  1129. let xB = b.x,
  1130. yB = b.y
  1131. let yMax = Data.game.fightSizeMaxY - Math.randomRangeInt(0, Data.game.fightSizeMaxY - Math.max(yA, yB)) // 最高点的高度
  1132. let xC = (xA + xB) / 2 // 假设C是A和B的中点(水平方向)
  1133. let xD = xA + (xC - xA) / 2 // 控制点D的x坐标是A和C的x坐标的平均值
  1134. let yD = yA + (yMax - yA) / 2 // 控制点D的y坐标是A和最高点之间的某个值(这里取中点)
  1135. let points = []
  1136. let step = 1 / (interval - 1)
  1137. for (let t = 0; t <= 1; t += step) {
  1138. let x =
  1139. Math.pow(1 - t, 3) * xA +
  1140. 3 * Math.pow(1 - t, 2) * t * xD +
  1141. 3 * (1 - t) * Math.pow(t, 2) * xC +
  1142. Math.pow(t, 3) * xB
  1143. let y =
  1144. Math.pow(1 - t, 3) * yA +
  1145. 3 * Math.pow(1 - t, 2) * t * yD +
  1146. 3 * (1 - t) * Math.pow(t, 2) * yMax +
  1147. Math.pow(t, 3) * yB
  1148. points.push(cc.v2(x, y))
  1149. }
  1150. return points
  1151. }
  1152. boundaries = [
  1153. {dir: 'right', eq: Data.game.fightAreaMaxX, var: 'x'}, // 右边界
  1154. {dir: 'left', eq: Data.game.fightAreaMinX, var: 'x'}, // 左边界
  1155. {dir: 'top', eq: Data.game.fightAreaMaxY, var: 'y'}, // 上边界
  1156. {dir: 'bottom', eq: Data.game.fightAreaMinY, var: 'y'}, // 下边界
  1157. ]
  1158. findIntersection(a, v) {
  1159. // 将矩形边界转换为线性方程组
  1160. // 例如,对于矩形的上边界(y-centerY = height/2),转换为 y = centerY + height/2
  1161. // 并以此类推其他三个边界
  1162. // 定义四个边界线性方程
  1163. let intersection = null
  1164. //起点Y位置加上了人物身高,这里做个限制,让子弹起始点在屏幕内
  1165. this.boundaries.forEach(boundary => {
  1166. let t
  1167. if (boundary.var === 'x') {
  1168. // 解 x = a.x + t*v.x
  1169. t = (boundary.eq - a.x) / v.x
  1170. } else {
  1171. // 解 y = a.y + t*v.y
  1172. t = (boundary.eq - a.y) / v.y
  1173. }
  1174. // 检查t是否在[0, +∞)范围内,确保交点在射线方向上且不过原点
  1175. if (t >= 0) {
  1176. let intersectPoint = cc.v2(a.x + t * v.x, a.y + t * v.y)
  1177. // 判断交点是否在矩形内或边上
  1178. if (
  1179. (boundary.dir === 'right' || boundary.dir === 'left') &&
  1180. intersectPoint.y >= Data.game.fightAreaMinY &&
  1181. intersectPoint.y <= Data.game.fightAreaMaxY
  1182. ) {
  1183. if (!intersection || intersection.sub(a).mag() < intersectPoint.sub(a).mag())
  1184. intersection = intersectPoint
  1185. } else if (
  1186. (boundary.dir === 'top' || boundary.dir === 'bottom') &&
  1187. intersectPoint.x >= Data.game.fightAreaMinX &&
  1188. intersectPoint.x <= Data.game.fightAreaMaxX
  1189. ) {
  1190. if (!intersection || intersection.sub(a).mag() < intersectPoint.sub(a).mag())
  1191. intersection = intersectPoint
  1192. }
  1193. }
  1194. })
  1195. return intersection
  1196. }
  1197. public removeBulletEntity(entity: EntityIndex) {
  1198. let comCocosNode = this.getComponent(entity, ComCocosNode)
  1199. let comBullet = this.getComponent(entity, ComBullet)
  1200. if (!comBullet) {
  1201. return
  1202. //console.log('子弹已经移除')
  1203. }
  1204. //1秒后移除弹射特效
  1205. comBullet.ejectionAnis.forEach(aniEntity => {
  1206. let aniEntityCom = this.getComponent(aniEntity, ComFrameAni)
  1207. if (aniEntityCom && aniEntityCom.countDown > 1) aniEntityCom.countDown = 1
  1208. })
  1209. if (comBullet.cObject) {
  1210. cCollider.inst.remove(comBullet.cObject.body)
  1211. comCocosNode.node.removeComponent(comBullet.cObject)
  1212. comBullet.cObject = null
  1213. }
  1214. this.removeNodeEntity(entity)
  1215. }
  1216. rotatePoint(a: cc.Vec2, b: cc.Vec2, angleDegrees: number): cc.Vec2 {
  1217. // 将角度转换为弧度
  1218. const angleRadians = (angleDegrees * Math.PI) / 180
  1219. // 计算cos和sin值
  1220. const cosAngle = Math.cos(angleRadians)
  1221. const sinAngle = Math.sin(angleRadians)
  1222. // 计算新的坐标
  1223. const x_new = (b.x - a.x) * cosAngle - (b.y - a.y) * sinAngle + a.x
  1224. const y_new = (b.x - a.x) * sinAngle + (b.y - a.y) * cosAngle + a.y
  1225. return cc.v2(x_new, y_new)
  1226. }
  1227. bezierPoint(t, a, c, d, b) {
  1228. let x =
  1229. Math.pow(1 - t, 3) * a.x +
  1230. 3 * Math.pow(1 - t, 2) * t * c.x +
  1231. 3 * (1 - t) * Math.pow(t, 2) * d.x +
  1232. Math.pow(t, 3) * b.x
  1233. let y =
  1234. Math.pow(1 - t, 3) * a.y +
  1235. 3 * Math.pow(1 - t, 2) * t * c.y +
  1236. 3 * (1 - t) * Math.pow(t, 2) * d.y +
  1237. Math.pow(t, 3) * b.y
  1238. return cc.v2(x, y)
  1239. }
  1240. getBezierLength(a, c, d, b, numPoints) {
  1241. let length = 0
  1242. let previousPoint = a
  1243. for (let i = 1; i <= numPoints; i++) {
  1244. let t = i / numPoints
  1245. let currentPoint = this.bezierPoint(t, a, c, d, b)
  1246. length += previousPoint.sub(currentPoint).mag()
  1247. previousPoint = currentPoint
  1248. }
  1249. return length
  1250. }
  1251. getEquidistantPoints(a, b, numPoints, group, height?) {
  1252. let {c, d} = this.calculatePointCD(a.x, a.y, b.x, b.y, group, height)
  1253. let points = []
  1254. let totalLength = this.getBezierLength(a, c, d, b, numPoints)
  1255. let segmentLength = totalLength / (numPoints - 1)
  1256. let previousPoint = a
  1257. points.push(previousPoint)
  1258. for (let i = 1; i < numPoints; i++) {
  1259. let targetLength = segmentLength * i
  1260. let low = (i - 1) / numPoints
  1261. let high = i / numPoints
  1262. while (high - low > 1e-5) {
  1263. let mid = (low + high) / 2
  1264. let point = this.bezierPoint(mid, a, c, d, b)
  1265. let length = previousPoint.sub(point).mag()
  1266. if (length < targetLength) {
  1267. low = mid
  1268. } else {
  1269. high = mid
  1270. }
  1271. }
  1272. let point = this.bezierPoint((low + high) / 2, a, c, d, b)
  1273. points.push(point)
  1274. previousPoint = point
  1275. }
  1276. return points
  1277. }
  1278. calculatePointCD(x1, y1, x2, y2, group, height?) {
  1279. let a = cc.v2(x1, y1)
  1280. let b = cc.v2(x2, y2)
  1281. let dir = b.sub(a).normalize()
  1282. let length = a.sub(b).mag()
  1283. let k = Math.max(100000 / length, 50)
  1284. k = Math.min(k, 100)
  1285. if (height) k = height
  1286. // 垂直方向向量
  1287. let vDir = dir.rotate(90 * (Math.PI / 180)).mul(group == Data.game.gFriendBullet ? 1 : -1)
  1288. let c = a.add(vDir.mul(k)).add(dir.mul(length / 3))
  1289. let d = a.add(vDir.mul(k)).add(dir.mul((length * 2) / 3))
  1290. return {c, d}
  1291. }
  1292. //-------------------------------技能-----------------------------------------
  1293. public createSkill(
  1294. fightData: IFightData,
  1295. skillCfg: ISkillConfig,
  1296. role: EntityIndex,
  1297. pos?: cc.Vec3,
  1298. group?: number,
  1299. ) {
  1300. let entity = this.createEntity()
  1301. // 添加nodeconfig
  1302. let comMap = this.addComponent(entity, ComNodeConfig)
  1303. comMap.id = 1
  1304. comMap.layer = GAME_LAYER[skillCfg.layer ? skillCfg.layer : GAME_LAYER.skill]
  1305. comMap.prefabName = GAME_PREFAB_TYPE.skill
  1306. //移除cocosNode
  1307. this.removeComponent(entity, ComCocosNode)
  1308. // 添加transform
  1309. let comTrans = this.addComponent(entity, ComTransform)
  1310. //提升自己数值技能
  1311. let comSkill = this.addComponent(entity, ComSkill)
  1312. comSkill.skillCfg = skillCfg
  1313. comSkill.fightData = fightData
  1314. comSkill.countDown = skillCfg.duration
  1315. comSkill.role = role
  1316. comSkill.group = group
  1317. comSkill.hurtFrameCompleted = false
  1318. comSkill.dirty = false
  1319. if (role > 0) {
  1320. let comRoleTrans = this.getComponent(role, ComTransform)
  1321. let comRole = this.getComponent(role, ComRole)
  1322. comTrans.x = comRoleTrans.x
  1323. comTrans.y = comRoleTrans.y + comRoleTrans.halfHeight * (skillCfg.offsetPos[0] - 1)
  1324. comSkill.group =
  1325. comRole.group == Data.game.gFriend
  1326. ? skillCfg.isHurt
  1327. ? skillCfg.dirType == SKILL_DIR_TYPE.selfNormal
  1328. ? Data.game.gFriendHurtSkill
  1329. : Data.game.gEnemyHurtSkill
  1330. : Data.game.gFriendGainSkill
  1331. : skillCfg.isHurt
  1332. ? skillCfg.dirType == SKILL_DIR_TYPE.selfNormal
  1333. ? Data.game.gEnemyHurtSkill
  1334. : Data.game.gFriendHurtSkill
  1335. : Data.game.gEnemyGainSkill
  1336. } else {
  1337. if (!pos || !group) Log.error('技能创建失败')
  1338. comTrans.x = pos.x
  1339. comTrans.y = pos.y
  1340. }
  1341. comTrans.x += skillCfg.offsetPos[1]
  1342. comTrans.y += skillCfg.offsetPos[2]
  1343. if (skillCfg.type == SKILL_TYPE.attackBack && skillCfg.effectType.indexOf(SKILL_EFFECT_TYPE.move) >= 0) {
  1344. let comMovable = this.addComponent(entity, ComMovable)
  1345. comMovable.pointIdx = 0
  1346. comMovable.points.length = 0
  1347. comMovable.speed = skillCfg.effectParm[skillCfg.effectType.indexOf(SKILL_EFFECT_TYPE.move)][0]
  1348. comMovable.points.push(
  1349. cc.v2(
  1350. comSkill.group == Data.game.gFriendHurtSkill || comSkill.group == Data.game.gFriendGainSkill
  1351. ? Data.game.fightAreaMaxX
  1352. : Data.game.fightAreaMinX,
  1353. comTrans.y,
  1354. ),
  1355. )
  1356. }
  1357. return entity
  1358. }
  1359. public createSkillByFightData(fightData: IFightData, skillCfg: ISkillConfig, roles: EntityIndex[]) {
  1360. let isSuccess = false
  1361. roles.forEach((role: EntityIndex) => {
  1362. let comRole = this.getComponent(role, ComRole)
  1363. if (this.isDie(comRole)) return
  1364. this.createSkill(fightData, skillCfg, role)
  1365. isSuccess = true
  1366. })
  1367. return isSuccess
  1368. }
  1369. public createCardBulletSkill(comBullet: ComBullet, role: EntityIndex, pos?: cc.Vec3) {
  1370. //卡牌弹射子弹每次命中目标放技能
  1371. let skillCfg = SkillConfig[comBullet.bulletCfg.skill]
  1372. for (let i = 0; i < skillCfg.effectType.length; i++) {
  1373. let type = skillCfg.effectType[i]
  1374. if (type == SKILL_EFFECT_TYPE.reduce) {
  1375. comBullet.fightData.attack *= 1 - skillCfg.effectParm[i][0] / Data.game.rateNum
  1376. comBullet.fightData.spellAttack *= 1 - skillCfg.effectParm[i][0] / Data.game.rateNum
  1377. comBullet.fightData.realAttack *= 1 - skillCfg.effectParm[i][0] / Data.game.rateNum
  1378. }
  1379. }
  1380. if (role) {
  1381. this.createSkillByFightData(comBullet.fightData, skillCfg, [role])
  1382. } else {
  1383. let skillEntity = this.createSkill(
  1384. comBullet.fightData,
  1385. skillCfg,
  1386. 0,
  1387. pos,
  1388. comBullet.group == Data.game.gEnemyBullet
  1389. ? skillCfg.isHurt
  1390. ? Data.game.gEnemyHurtSkill
  1391. : Data.game.gEnemyGainSkill
  1392. : skillCfg.isHurt
  1393. ? Data.game.gFriendHurtSkill
  1394. : Data.game.gFriendGainSkill,
  1395. )
  1396. let tmp = this.getComponent(skillEntity, ComSkill)
  1397. }
  1398. //地板受击
  1399. if (skillCfg.hitID[HIT_ANI_TYPE.floor]) {
  1400. this.createAni(skillCfg.hitID[HIT_ANI_TYPE.floor], role, role ? null : cc.v2(pos))
  1401. }
  1402. }
  1403. public isOneRoleSkill(skillCfg: ISkillConfig): boolean {
  1404. return (
  1405. skillCfg.dirType == SKILL_DIR_TYPE.selfRole ||
  1406. skillCfg.dirType == SKILL_DIR_TYPE.anyRole ||
  1407. skillCfg.dirType == SKILL_DIR_TYPE.minHpRole ||
  1408. skillCfg.dirType == SKILL_DIR_TYPE.baby
  1409. )
  1410. }
  1411. removeSkillEntity(entity) {
  1412. let comSkill = this.getComponent(entity, ComSkill)
  1413. let comCocosNode = this.getComponent(entity, ComCocosNode)
  1414. if (!comSkill || !comCocosNode) return
  1415. cCollider.inst.remove(comSkill.cObject.body)
  1416. comCocosNode.node.removeComponent(comSkill.cObject)
  1417. comSkill.cObject = null
  1418. this.removeNodeEntity(entity)
  1419. }
  1420. //-------------------------------BUFF-----------------------------------------
  1421. public createBuff(buffID, fightData: IFightData) {
  1422. if (!BuffConfig[buffID]) return
  1423. let buff = {buffCfg: BuffConfig[buffID]} as IBuff
  1424. buff.countDown = buff.buffCfg.duration == Data.game.cdMax ? Infinity : buff.buffCfg.duration
  1425. buff.countUp = 0
  1426. buff.hurtFrameCompleted = false
  1427. buff.hurtTimes = 0
  1428. buff.immuneTimes = 0
  1429. buff.curAttrChangeNum = []
  1430. buff.createBaseAttr = Mgr.global.buildIBaseAttr(fightData, !fightData)
  1431. buff.aniEntityArr = []
  1432. return buff
  1433. }
  1434. public addBuff(skillEntity: EntityIndex, buff: IBuff, fightData: IFightData) {
  1435. if (!buff) return
  1436. let comRoleSkill = this.getComponent(skillEntity, ComRole)
  1437. let comRoleTrans = this.getComponent(skillEntity, ComTransform)
  1438. let comCocosNodeSkill = this.getComponent(skillEntity, ComCocosNode)
  1439. let comHasSkillSkill = this.getComponent(skillEntity, ComHasSkill)
  1440. let comSpineNode = this.getComponent(comRoleSkill.spineEntity, ComCocosNode)
  1441. if (!comCocosNodeSkill || !comCocosNodeSkill.loaded || this.isDie(comRoleSkill) || this.isBase(comRoleSkill)) {
  1442. return
  1443. }
  1444. //图腾对象不施加BUFF
  1445. if (comRoleSkill.roleCfg.type == ROLE_TYPE.totem) return
  1446. let sameIDBuffs = comRoleSkill.buffs.filter(buffTmp => buffTmp.buffCfg.ID == buff.buffCfg.ID)
  1447. let buffCfg = buff.buffCfg
  1448. let sameArrIDBuffs = comRoleSkill.buffs.filter(
  1449. buffTmp => buffCfg.arrID > 0 && buffTmp.buffCfg.arrID == buffCfg.arrID,
  1450. )
  1451. let addBuffFuc = (isReplace: boolean = false) => {
  1452. let hasImmune = false
  1453. comRoleSkill.buffs.forEach(buffTmp => {
  1454. let buffCfg = buffTmp.buffCfg
  1455. if (buffCfg.buffType == BUFF_TYPE.negativeImmune) {
  1456. let negativeArr = buffCfg.effectParm[buffCfg.effectType.indexOf(BUFF_EFFECT_TYPE.immuneNegative)]
  1457. if (negativeArr) {
  1458. hasImmune ||=
  1459. negativeArr.includes(NEGATIVE_TYPE.delay) && buff.buffCfg.buffType == BUFF_TYPE.dizzy
  1460. hasImmune ||=
  1461. negativeArr.includes(NEGATIVE_TYPE.slow) &&
  1462. buff.buffCfg.attrRate[buff.buffCfg.attr.indexOf(ATTR_NAME.moveSpeed)] < 0
  1463. }
  1464. }
  1465. })
  1466. if (hasImmune) return
  1467. //光环buff不施放,在处理hasSkill处理
  1468. comRoleSkill.buffs.push(buff)
  1469. //comCocosNodeSkill.events.push(new EventBuffChange(comRoleSkill.buffs, [buff], []))
  1470. for (let aniID of buff.buffCfg.buffUrl) {
  1471. if (!aniID || isReplace) continue
  1472. if (aniID == BUFF_ANI_TYPE.slow) {
  1473. comSpineNode?.events.push(new EventSlowDown(true))
  1474. }
  1475. buff.aniEntityArr.push(this.createAni(aniID, skillEntity))
  1476. }
  1477. buffCfg.buffIcon.forEach(value => {
  1478. comCocosNodeSkill.events.push(new EventRoleTip(GAME_ROLE_TIP.buffTip, buffCfg.hurtEffect, value))
  1479. })
  1480. if (buff.buffCfg.buffType == BUFF_TYPE.dizzy) {
  1481. let comDizzy = this.getComponent(skillEntity, ComDizzy)
  1482. if (comDizzy && !this.isDie(comRoleSkill)) {
  1483. comDizzy.countDown = buff.buffCfg.attrNum[0]
  1484. for (let i = 0; i < buff.buffCfg.effectType.length; i++) {
  1485. let type = buff.buffCfg.effectType[i]
  1486. if (type == BUFF_EFFECT_TYPE.boss && comRoleSkill.roleCfg.type == MONSTER_TYPE.boss) {
  1487. comDizzy.countDown *= 1 - buff.buffCfg.effectParm[i][0] / Data.game.rateNum
  1488. }
  1489. }
  1490. if (comRoleSkill.entryMap.get(ENTRY.delayReduce)?.num) {
  1491. comDizzy.countDown *= 1 - comRoleSkill.entryMap.get(ENTRY.delayReduce).num / Data.game.rateNum
  1492. }
  1493. if (fightData && fightData.entryMap.get(ENTRY.delayAdd)?.num) {
  1494. comDizzy.countDown *= 1 + fightData.entryMap.get(ENTRY.delayAdd).num / Data.game.rateNum
  1495. }
  1496. //将顶盾的buff去掉
  1497. let findIndex = comRoleSkill.buffs.findIndex(
  1498. value => value.buffCfg.buffType == BUFF_TYPE.shieldReduceHurt,
  1499. )
  1500. if (findIndex != -1) comRoleSkill.buffs.splice(findIndex, 1)
  1501. let comDizzySkillAbel = this.getComponent(skillEntity, ComSkillAbel)
  1502. comDizzySkillAbel.isInShield = false
  1503. //将顶盾的罩子,友军免疫远程攻击buff去掉
  1504. this.removeNodeEntity(comDizzySkillAbel.ani)
  1505. let skillIndex = comRoleSkill.skills.findIndex(v => v.type == SKILL_TYPE.holo)
  1506. if (skillIndex >= 0) {
  1507. let allEntity = Array.from(comHasSkillSkill.skillCObjects[skillIndex].containsBody.keys())
  1508. allEntity = allEntity.filter(entityTmp => !this.isDie(entityTmp))
  1509. allEntity.forEach(v => {
  1510. let tmpComRole = this.getComponent(v, ComRole)
  1511. let findIndex = tmpComRole.buffs.findIndex(
  1512. value => value.buffCfg.buffType == BUFF_TYPE.rangedImmune,
  1513. )
  1514. if (findIndex != -1) {
  1515. console.warn('去掉免疫buff')
  1516. tmpComRole.buffs.splice(findIndex, 1)
  1517. }
  1518. })
  1519. }
  1520. //将技能弹射子弹去除
  1521. if (comDizzySkillAbel.bullets) {
  1522. comDizzySkillAbel.bullets.forEach(v => {
  1523. this.removeBulletEntity(v)
  1524. })
  1525. comDizzySkillAbel.bullets.length = 0
  1526. }
  1527. }
  1528. }
  1529. }
  1530. let replaceBuffFuc = (replaceBuff: IBuff) => {
  1531. //addType==0直接替换,==1替换效果最好的
  1532. if (
  1533. buffCfg.addType == 0 ||
  1534. (buffCfg.addType == 1 && buffCfg.attrRate[0] >= replaceBuff.buffCfg.attrRate[0])
  1535. ) {
  1536. let removeBuffs = comRoleSkill.buffs.splice(comRoleSkill.buffs.indexOf(replaceBuff), 1)
  1537. this.removeBuffAttr(skillEntity, removeBuffs[0])
  1538. addBuffFuc(true)
  1539. //特效继承,时间重置
  1540. buff.aniEntityArr.push(...removeBuffs[0].aniEntityArr)
  1541. for (let aniEntity of buff.aniEntityArr) {
  1542. let comFrameAni = this.getComponent(aniEntity, ComFrameAni)
  1543. if (comFrameAni) comFrameAni.countDown = comFrameAni.aniConfig.duration
  1544. }
  1545. }
  1546. }
  1547. if (buffCfg.arrID > 0) {
  1548. //有buff组,根据叠加和替换规则确定是否叠加和替换
  1549. if (buffCfg.isAdd && (!buffCfg.addNum || (buffCfg.addNum > 0 && sameArrIDBuffs.length < buffCfg.addNum))) {
  1550. //叠加
  1551. addBuffFuc()
  1552. } else if (!buffCfg.isAdd) {
  1553. //不叠加
  1554. if (sameArrIDBuffs.length == 0) {
  1555. addBuffFuc()
  1556. } else {
  1557. replaceBuffFuc(sameArrIDBuffs[0])
  1558. }
  1559. }
  1560. } else {
  1561. //不叠加
  1562. if (sameIDBuffs.length == 0) {
  1563. addBuffFuc()
  1564. } else {
  1565. replaceBuffFuc(sameIDBuffs[0])
  1566. }
  1567. }
  1568. }
  1569. public removeBuffAttr(role: EntityIndex, buff: IBuff) {
  1570. let comRole = this.getComponent(role, ComRole)
  1571. if (this.isDie(comRole)) return
  1572. let buffConfig = buff.buffCfg
  1573. for (let j = 0; j < buffConfig.attr.length; j++) {
  1574. if (buff.curAttrChangeNum[j]) {
  1575. comRole[buffConfig.attr[j]] -= buff.curAttrChangeNum[j]
  1576. if (buffConfig.attr[j] == ATTR_NAME.attackRange) {
  1577. let comFindEnemy = this.getComponent(role, ComFindEnemy)
  1578. if (comFindEnemy.attackCObject.radius != comRole.attackRange) {
  1579. comFindEnemy.attackCObject.updateSphereShape(comRole.attackRange)
  1580. }
  1581. }
  1582. }
  1583. }
  1584. }
  1585. //-------------------------------特效-----------------------------------------
  1586. public createAni(aniID: number, role: EntityIndex, pos?: cc.Vec2) {
  1587. let frameAniConfig = FrameAniConfig[aniID]
  1588. if (!frameAniConfig) {
  1589. console.error('frameAniID不存在', aniID)
  1590. return
  1591. }
  1592. if (frameAniConfig.type == ANI_TYPE.shader) return
  1593. let entity = this.createEntity()
  1594. // 添加nodeconfig
  1595. let comMap = this.addComponent(entity, ComNodeConfig)
  1596. comMap.id = 1
  1597. comMap.layer = GAME_LAYER[frameAniConfig.layer ? frameAniConfig.layer : GAME_LAYER.skill]
  1598. comMap.prefabName = GAME_PREFAB_TYPE.frameAni
  1599. //移除cocosNode
  1600. this.removeComponent(entity, ComCocosNode)
  1601. // 添加transform
  1602. let comTrans = this.addComponent(entity, ComTransform)
  1603. let comFrameAni = this.addComponent(entity, ComFrameAni)
  1604. comFrameAni.countDown = frameAniConfig.duration
  1605. comFrameAni.dirty = false
  1606. comFrameAni.aniConfig = frameAniConfig
  1607. comFrameAni.role = role
  1608. comFrameAni.lineStartPos = null
  1609. comFrameAni.lineStartEntity = 0
  1610. comFrameAni.lineEndEntity = 0
  1611. let comRoleTrans = this.getComponent(role, ComTransform)
  1612. if (pos) {
  1613. comTrans.x = pos.x
  1614. comTrans.y = pos.y
  1615. } else if (comRoleTrans) {
  1616. comTrans.x = comRoleTrans.x
  1617. comTrans.y =
  1618. comRoleTrans.y +
  1619. (frameAniConfig.offsetPos[0] ? comRoleTrans.halfHeight * (frameAniConfig.offsetPos[0] - 1) : 0)
  1620. }
  1621. comTrans.x += frameAniConfig.offsetPos[1]
  1622. comTrans.y += frameAniConfig.offsetPos[2]
  1623. return entity
  1624. }
  1625. //--------------------------------词条-------------------------------------------------
  1626. isGameEntry(type: ENTRY) {
  1627. return type >= ENTRY.ignoreDefense && type <= 100
  1628. }
  1629. initEntry() {
  1630. if (Data.game.gameType != GAME_TYPE.relic) {
  1631. for (let id of Data.user.talents) {
  1632. let talentCfg = TalentConfig[id]
  1633. let type = EntryConfig[talentCfg.entryID].type
  1634. switch (type) {
  1635. case ENTRY.startCostAdd:
  1636. break
  1637. }
  1638. }
  1639. }
  1640. }
  1641. convertEntry2Obj(entryID: number): EntryObj {
  1642. let entryCfg = EntryConfig[entryID]
  1643. let num = 0
  1644. if (entryCfg.parmArr[0] && entryCfg.parmArr.length == 1) num = entryCfg.parmArr[0]
  1645. if (entryCfg.rateArr[0] && entryCfg.rateArr.length == 1) num = entryCfg.rateArr[0]
  1646. let numArrTmp = []
  1647. if (entryCfg.parmArr.length > 1) numArrTmp.push(entryCfg.parmArr)
  1648. if (entryCfg.rateArr.length > 1) numArrTmp.push(entryCfg.rateArr)
  1649. return {num, numArr: numArrTmp, type: entryCfg.type, profession: entryCfg.profession}
  1650. }
  1651. putEntry2Map(comRole: ComRole, entryObj: EntryObj) {
  1652. let map = comRole.entryMap
  1653. if (!this.isGameEntry(entryObj.type) || !entryObj.profession.includes(comRole.roleCfg.profession)) return
  1654. let obj = map.get(entryObj.type)
  1655. if (obj) {
  1656. //叠加规则
  1657. if (entryObj.num > 0) {
  1658. //默认数值相加
  1659. obj.num += entryObj.num
  1660. } else if (entryObj.numArr.length > 0) {
  1661. //数组默认覆盖最好的效果
  1662. let lastNum = obj.numArr[0][obj.numArr[0].length - 1]
  1663. let newLastNum = entryObj.numArr[0][entryObj.numArr[0].length - 1]
  1664. if (newLastNum > lastNum) {
  1665. obj.numArr[0][obj.numArr[0].length - 1] = newLastNum
  1666. }
  1667. }
  1668. } else {
  1669. map.set(entryObj.type, entryObj)
  1670. }
  1671. }
  1672. //--------------------------------改变无敌状态-------------------------------------------------
  1673. changeInvincible(role: EntityIndex, invincible: boolean) {
  1674. let comRole = this.getComponent(role, ComRole)
  1675. if (this.isDie(comRole)) return
  1676. comRole.invincible = invincible
  1677. }
  1678. //--------------------------------绘制碰撞体-------------------------------------------------
  1679. drawDebug(entity: EntityIndex, cObject) {
  1680. let comCocosNode = this.getComponent(entity, ComCocosNode)
  1681. let comTrans = this.getComponent(entity, ComTransform)
  1682. if (!comCocosNode || !comTrans || !cObject || !CC_DEV || !cc.debug.isDisplayStats()) return
  1683. //绘制碰撞体
  1684. let localPoints
  1685. let offset = cc.v2(comTrans.x, comTrans.y).add(cc.v2(cObject.center))
  1686. if (cObject.type == ShapeType.Box) {
  1687. localPoints = []
  1688. localPoints.push(cc.v2(offset.x - cObject.size.x / 2, offset.y + cObject.size.y / 2)) // 左上角
  1689. localPoints.push(cc.v2(offset.x + cObject.size.x / 2, offset.y + cObject.size.y / 2)) // 右上角
  1690. localPoints.push(cc.v2(offset.x + cObject.size.x / 2, offset.y - cObject.size.y / 2)) // 右下角
  1691. localPoints.push(cc.v2(offset.x - cObject.size.x / 2, offset.y - cObject.size.y / 2)) // 左下角
  1692. }
  1693. if (cObject.type == ShapeType.Sphere) {
  1694. localPoints = [cc.v2(comTrans.x, comTrans.y).add(cc.v2(cObject.center))]
  1695. }
  1696. if (localPoints) {
  1697. let color
  1698. let comRole = this.getComponent(entity, ComRole)
  1699. if (comRole) {
  1700. color = comRole.group == Data.game.gFriend ? cc.Color.GREEN : cc.Color.RED
  1701. }
  1702. let comSkill = this.getComponent(entity, ComSkill)
  1703. if (comSkill) {
  1704. color =
  1705. comSkill.group == Data.game.gFriendGainSkill || comSkill.group == Data.game.gFriendHurtSkill
  1706. ? cc.Color.GREEN
  1707. : cc.Color.RED
  1708. }
  1709. comCocosNode.events.push(
  1710. new EventGraphicsDraw(localPoints, cObject.type == ShapeType.Box ? 0 : cObject.radius, color),
  1711. )
  1712. }
  1713. }
  1714. //--------------------------------移除战场上的实体-------------------------------------------------
  1715. removeNodeEntity(entity: EntityIndex) {
  1716. this.putNode(entity)
  1717. this.removeAllComponents(entity)
  1718. this.removeEntity(entity)
  1719. }
  1720. putNode(entity: EntityIndex) {
  1721. let comCocosNode = this.getComponent(entity, ComCocosNode)
  1722. if (comCocosNode) {
  1723. let node = comCocosNode.node
  1724. if (node) {
  1725. let eventProcess = node.getComponent(EventProcess)
  1726. eventProcess.onDetach()
  1727. node.removeFromParent()
  1728. if (this.fightCore) {
  1729. let pool = this.fightCore.gamePool.get(node.name)
  1730. if (pool) {
  1731. this.resetGameNode(node)
  1732. pool.put(node)
  1733. }
  1734. }
  1735. }
  1736. }
  1737. }
  1738. resetGameNode(node) {
  1739. ccUtils.stopAni(node)
  1740. node.stopAllActions()
  1741. node.opacity = 255
  1742. node.angle = 0
  1743. node.scale = 1
  1744. node.position = cc.v2(-10000, -10000)
  1745. let sprite = node.getComponent(cc.Sprite)
  1746. if (sprite) sprite.spriteFrame = null
  1747. let spine = node.getComponent(sp.Skeleton)
  1748. if (spine) spine.skeletonData = null
  1749. let frameAnimation = node.getComponent(FrameAnimation)
  1750. if (frameAnimation) {
  1751. frameAnimation.stop()
  1752. //frameAnimation.spriteAtlas = null
  1753. }
  1754. if (node.name != GAME_PREFAB_TYPE.role) node.children.forEach(v => this.resetGameNode(v))
  1755. }
  1756. }