/** @format */ import {FILTER_ROLE_NODE} from '../ECS/systems/SysRoleState' import {ITouchProcessor} from '../ECS/systems/SysCocosView' import {FightWorld} from '../ECS/worlds/FightWorld' import {Data, Mgr} from '../GameControl' import {ccUtils} from '../utils/ccUtils' import {ComRole} from '../ECS/components/ComRole' import {EntityIndex} from '../ECS/lib/Const' import {cCollider} from '../collision/Collider' import {ComEnemy} from '../ECS/components/ComEnemy' import {cObject} from '../collision/Object' import {IStageConfig, StageConfig} from '../config/StageConfig' import {ConstValue} from '../data/ConstValue' import {IRoleConfig, RoleConfig} from '../config/RoleConfig' import { BUILDING, BUILDING_ATTR, CARD_CAST_AREA, CARD_END_POS, CARD_MORE_DIR, START_POS, ENTRY, GAME_ROLE_TIP, GAME_TYPE, GAME_WIN_TYPE, LANGUAGE_TYPE, MONSTER_TYPE, ROLE_TYPE, SKILL_DIR_TYPE, SKILL_TYPE, MOD, GAME_PREFAB_TYPE, GOODS, } from '../enums/Enum' import {UI} from '../enums/UI' import {FILTER_CAN_ATTACK_ENEMY, FILTER_CAN_ATTACK_FRIEND} from '../ECS/systems/SysFindEnemy' import {msgCmd} from '../proto/msg_cmd' import {adventureEndRsp, adventureLayer, adventureLayerRsp, speedUpDataRsp} from '../proto/game' import {label, list, node, observer, render} from '../mobx/observer' import {IStageInfoConfig, StageInfoConfig} from '../config/StageInfoConfig' import {SkillConfig} from '../config/SkillConfig' import {ICardSkillConfig} from '../config/CardSkillConfig' import {ComTransform} from '../ECS/components/ComTransform' import {ComMovable} from '../ECS/components/ComMovable' import {EntryObj} from '../ECS/core/GameInterface' import {SkillOptCell} from '../cell/SkillOptCell' import {MonsterConfig} from '../config/MonsterConfig' import {EliteInfoConfig, IEliteInfoConfig} from '../config/EliteInfoConfig' import {IGameResult, IRewardNty} from '../interface/UIInterface' import {ICard, IRole} from '../interface/GlobalInterface' import {GoodsConfig} from '../config/GoodsConfig' import {SOUND} from '../enums/Sound' import {i18nLabel} from '../uiutils/i18nLabel' import {idNum} from '../proto/typedef' import {AwardConfig} from '../config/AwardConfig' import {ZumaUI} from '../ui/ZumaUI' import {ZumabuffConfig} from '../config/ZumabuffConfig' import {BuffConfig} from '../config/BuffConfig' import List from '../uiutils/List' const {ccclass, property} = cc._decorator enum FightTip { wave = 1, //大波敌人 boss = 2, //boss } export enum RoundOutType { time = 1, //时间轴出怪 dieOut = 2, //相同时间数值的一组死亡后立刻出下一组 average = 3, //平均时间出怪 } interface IRoleCreate { id: number pos: number drop: idNum } enum ITrailType { stage = 1, //主线关卡触发 oneFailStage = 2, //指定关卡首次失败触发 } @ccclass @observer export default class FightCore extends cc.Component { get teamNowHP(): number { return this._teamNowHP } set teamNowHP(value: number) { if (value != this._teamNowHP && this.teamPb.progress < 1 && this.roundFight) { } this._teamNowHP = value ccUtils.setLabel(Math.toKMBNum(value), this.node, 'teamHpNode/num') } get teamNowShieldHP(): number { return this._teamNowShieldHP } set teamNowShieldHP(value: number) { if (value != this._teamNowShieldHP && this.teamShieldPb.progress < 1 && this.roundFight) { } this._teamNowShieldHP = value } @node('cost/mask') private costMaskNode: cc.Node @node('skillPop') public skillPop: cc.Node @node('fightTip') private fightTip: cc.Node @node('gameTypeTip/tipLb') private tipLb: cc.Node @node('gameTypeTip/time') private tipTime: cc.Node @node('gameTypeTip/time/lb') private tipTimeLb: cc.Node @node('gameTypeTip/killNum') private tipKillNum: cc.Node @node('gameTypeTip/killNum/lb') private tipKillNumLb: cc.Node @node('top/round') private roundNode: cc.Node @node('top/goods') private goods: cc.Node @node('goodsParent') private goodsParent: cc.Node @node('touchNode') private touchNode: cc.Node @node('speed') private speed: cc.Node @node('Layers/drop') private dropParent: cc.Node @node('bossHP') private bossHPNode: cc.Node @node('teamHpNode/HPS') private teamHPS: cc.Node @node('bag') bag: cc.Node @list('bag/scrollView') bagCardList: List @node('bgs/roll') bgRolls: cc.Node public dropPool: Map = new Map() public getDrop: Map = new Map() public roleTipPool: Map public gamePool: Map public roleEntityMap: Map public stageInfo: IStageInfoConfig | IEliteInfoConfig public teamEntity: EntityIndex[] = [] private _teamNowHP: number private _teamNowShieldHP: number public teamHP: number public teamPb: cc.ProgressBar public teamShieldPb: cc.ProgressBar public bossNum: number public eliteNum: number public monsterNum: number public world: FightWorld = null public gameOver: boolean public isStop: boolean = false public isTestScene: boolean = false public startTime: number private testClickNum: number = 0 private curStage: number private roundFight: boolean private curRoundIndex: number private roundTime: number private roundNextRoleTime: number private curRoundCreateIndex: number private curRoundCfg: IStageConfig private _touchHandler: ITouchProcessor[] = [] private skillArea: cc.Node private skillAreaCObject: cObject private curCardSkillCfg: ICardSkillConfig private curCard: ICard private curSkillOptCell: SkillOptCell private gameTime: number private roundOutType: RoundOutType = RoundOutType.time private roundOutTime: number = 0 private smallRoundArr: IRoleCreate[][] = [] private timeRolesTime: number[] private timeRoles: number[] private timeRolesPos: number[] private timeRolesDrop: idNum[] private createEnemyNum: number private extraEnemyCreateArr: boolean[] private rightAreaFriend: number[] = [] private allRoundNum: number = 0 private isScrollMap: Boolean = false private zumaUI: ZumaUI protected onLoad() { this.skillArea = cc.find('skillArea', this.node) this.skillAreaCObject = cc.find('skillAreaObject', this.node).getComponent(cObject) this.teamPb = cc.find('teamHpNode/HP', this.node).getComponent(cc.ProgressBar) this.teamShieldPb = cc.find('teamHpNode/HPshield', this.node).getComponent(cc.ProgressBar) this._touchHandler.length = 0 this.roleTipPool = new Map() for (let key in GAME_ROLE_TIP) { this.roleTipPool.set(key, new cc.NodePool()) } this.gamePool = new Map() for (let key in GAME_PREFAB_TYPE) { this.gamePool.set(key, new cc.NodePool()) } this.roleEntityMap = new Map() this.registerTouchEvent() } init(zumaUI: ZumaUI) { this.zumaUI = zumaUI this.world = FightWorld.getInstance() this.world.fightCore = this this.resetGameWorld() Mgr.audio.playBGM(SOUND.gameBgm) if (CC_DEV || (Mgr.platform.isH5() && window.location.href.includes('dev.black-art.cn/b22'))) { this.isTestScene = cc.director.getScene().name == 'test' //if (this.isTestScene) Mgr.ui.show(UI.GameDevUI, this) } //初始化战场大小 let skillTouch = cc.find('skillTouch', this.skillPop) //扩大200触摸范围,让边缘角色可以点击 skillTouch.width = Data.game.fightSizeMaxX - Data.game.fightSizeMinX + 200 skillTouch.height = Data.game.fightSizeMaxY - Data.game.fightSizeMinY skillTouch.y = (Data.game.fightSizeMaxY + Data.game.fightSizeMinY) / 2 skillTouch.x = (Data.game.fightSizeMaxX + Data.game.fightSizeMinX) / 2 } update(dt: number): void { this.scrollMap(dt) this.logicUpdate(dt) } protected logicUpdate(dt: number): void { if (this.isStop) return dt *= Data.game.gameSpeed this.gameTime += dt if (this.stageInfo?.type == GAME_WIN_TYPE.surviveTime && this.gameTime > 0 && !this.gameOver) { let dur = this.stageInfo.parameter - this.gameTime if (dur < 0) { this.zumaUI.endFightRound(true) dur = 0 } ccUtils.setLabel(Date.Format('mm:ss', dur), this.tipTimeLb) } if (this.world) this.world.update(dt) if (this.roundFight) { this.roundTime += dt //时间轴刷怪 if ( this.roundNextRoleTime >= 0 && this.roundTime >= this.roundNextRoleTime && this.curRoundCreateIndex >= 0 && (this.roundOutType == RoundOutType.time || this.roundOutType == RoundOutType.average) ) { let rolesTime = this.timeRolesTime let roles = this.timeRoles let rolesPos = this.timeRolesPos if (this.curRoundCreateIndex < roles.length) { let cfg = this.getRoleCfgByCfgID(roles[this.curRoundCreateIndex]) this.createEnemyNum += 1 this.world.createRoleEntity( cfg, true, rolesPos[this.curRoundCreateIndex], null, this.timeRolesDrop[this.curRoundCreateIndex], ) this.curRoundCreateIndex += 1 if (this.curRoundCreateIndex < rolesTime.length) { this.roundNextRoleTime = rolesTime[this.curRoundCreateIndex] } } else { let bigRoundTime = this.curRoundCfg.trigger if (bigRoundTime > 0) { this.roundFight = false this.scheduleOnce(this.readyNextRound, bigRoundTime) } let enemyNum = this.world.getFilter(FILTER_CAN_ATTACK_ENEMY).entities.size if (enemyNum <= 1 && !bigRoundTime) { this.roundFight = false this.readyNextRound() } } } } } protected onDisable() { this.unscheduleAllCallbacks() this.resetGameWorld() Mgr.event.removeAll(this) if (CC_DEV || ConstValue.DEBUG) { Mgr.ui.hide(UI.GameDevUI) } } onDestroy() {} //游戏逻辑======================================= resetGameWorld() { this._touchHandler.length = 0 this.world.init() this.world.clear(false) this.roleEntityMap.clear() cc.find('Layers', this.node).children.forEach(value => value.removeAllChildren()) cCollider.inst.clear() this.roundFight = false this.initSkillArea() this.unschedule(this.startNextSmallRound) this.unschedule(this.startNextRound) } getRoleCfgByCfgID(ID: number): IRoleConfig { let cfg: IRoleConfig if ([GAME_TYPE.normal, GAME_TYPE.none].includes(Data.game.gameType)) { cfg = (MonsterConfig[ID] as unknown) as IRoleConfig } return cfg } getRolePos(pos: number) { if (pos == 0) { return Math.randomRangeInt(1, 19) } else if (pos == 100) { return Math.randomRangeInt(101, 109) } else { return pos } } startStageGame() { this.startTime = 0 this.gameTime = 0 this.isStop = false this.createEnemyNum = 0 this.curStage = Data.game.stageID this.curRoundIndex = 0 this.allRoundNum = 0 for (let i = 0; i < 100; i++) { if (!this.getRoundCfg(false, i)) { this.allRoundNum = i break } } this.initGameSpeed() cc.find('testEndGame', this.node).active = ConstValue.DEBUG this.testClickNum = 0 ccUtils.setLabel('0', this.goods, 'lb') cc.find('stageLb', this.roundNode) .getComponent(i18nLabel) .setParamByIndex((this.curStage % 100) + '', 0) let cfg = this.getStageInfoCfg() this.stageInfo = cfg if (cfg) { this.tipKillNum.active = cfg.type == GAME_WIN_TYPE.killNum this.tipTime.active = cfg.type == GAME_WIN_TYPE.surviveTime let tipLbArr = [] tipLbArr[GAME_WIN_TYPE.boss] = LANGUAGE_TYPE.bossTip ccUtils.setLabel(tipLbArr[cfg.type] ?? '', this.tipLb) } if (Data.game.gameType == GAME_TYPE.none) { } else if (Data.game.gameType == GAME_TYPE.normal || Data.game.gameType == GAME_TYPE.elite) { let stageName = '' if (Data.game.gameType == GAME_TYPE.normal) { if ('des' in cfg) { stageName = `${Mgr.i18n.getLabel(cfg.name, [])}-${cfg.des}` } } else if (Data.game.gameType == GAME_TYPE.elite) { if ('name' in cfg) { stageName = `${Mgr.i18n.getLabel(cfg.name, [])}-${cfg.ID % 10}` } } Mgr.net.add(msgCmd.cmd_adventure_layer_rsp, this, this.onRoundStartRsp) } this.initRoles() let roundCfg = this.getRoundCfg() if (this.curStage > 0 && roundCfg) { this.startTime = Data.main.serverTime this.resetRound() this.changeKillNum() } else { Mgr.ui.tip('没有当前关卡' + this.curStage) } } readyNextRound() {} startNextRound() { if (this.isTestScene) { Data.game.stageRound += 1 this.curRoundIndex += 1 this.resetRound() } else { if (Data.game.gameType == GAME_TYPE.normal || Data.game.gameType == GAME_TYPE.elite) { let data: adventureLayer = { eliteNum: 0, bossNum: 0, cost: 0, layer: Data.game.stageRound, monsterNum: 0, } Mgr.net.send(msgCmd.cmd_adventure_layer, data) } } } resetRound() { this.fightTip.opacity = 0 this.bossHPNode.active = false this.bossNum = 0 this.eliteNum = 0 this.monsterNum = 0 this.gameOver = false this.skillPop.active = false this.rightAreaFriend.length = 0 this.curRoundCfg = this.getRoundCfg() this.showFightTip(this.curRoundCfg.tipType) this.roundTime = 0 this.roundFight = true this.curRoundCreateIndex = 0 this.roundOutType = this.curRoundCfg.summonType this.roundOutTime = this.curRoundCfg.time let cfgRolesTime = this.curRoundCfg.rolesTime let rolesTime = (Array.isArray(cfgRolesTime[0]) ? cfgRolesTime[0] : cfgRolesTime) as number[] this.timeRolesTime = rolesTime let cfgRoles = this.curRoundCfg.roles let roles = (Array.isArray(cfgRoles[0]) ? cfgRoles[0] : cfgRoles) as number[] this.timeRoles = roles let cfgRolesPos = this.curRoundCfg.rolesPos let rolesPos = (Array.isArray(cfgRolesPos[0]) ? cfgRolesPos[0] : cfgRolesPos) as number[] this.timeRolesPos = rolesPos let dropArr = [] if ('ball' in this.curRoundCfg) { let ballCfg = this.curRoundCfg.ball dropArr.push(...new Array(ballCfg[1]).fill(ballCfg[0])) } let setDrop = (drops: number[], allRoles: number[]) => { let tmpIdNums = [] let randomArr = [] let dropRolesPos = [] if (drops.length > 0) { //过滤掉boss,去头尾 for (let i = 1; i < allRoles.length - 1; i++) { if (this.getRoleCfgByCfgID(allRoles[i]).type != MONSTER_TYPE.boss) dropRolesPos.push(i) } //N组随机 let count = Math.ceil(dropRolesPos.length / drops.length) if (count <= 2) { randomArr = Math.randomRangeIntArr( 1, dropRolesPos.length - 2, Math.min(dropRolesPos.length - 2, drops.length), ) } else { //每N个数量随机一个下标 for (let i = 0; i < drops.length; i++) { if (i == 0) { randomArr.push(Math.randomRangeInt(1, Math.max(1, count - 1))) } else if (i == count - 1) { randomArr.push(Math.randomRangeInt(i * count, dropRolesPos.length - 2)) } else { randomArr.push(Math.randomRangeInt(i * count, (i + 1) * count - 1)) } } } } randomArr = randomArr.map(v => dropRolesPos[v]) for (let i = 0; i < allRoles.length; i++) { let id = 0 if (randomArr.includes(i)) { id = drops[randomArr.indexOf(i)] } tmpIdNums.push({id, num: 1}) } return tmpIdNums } if (this.roundOutType == RoundOutType.time) { if (roles.length != rolesPos.length) { console.error('角色配置和位置配置数量不一致') } this.timeRolesDrop = setDrop(dropArr, roles) } else if (this.roundOutType == RoundOutType.dieOut) { let allTime = {} let timeRolesDrop = setDrop(dropArr, roles) for (let i = 0; i < rolesTime.length; i++) { let tmpTime = rolesTime[i] if (!allTime[tmpTime]) { allTime[tmpTime] = [] } if (roles[i] && rolesPos[i]) { allTime[tmpTime].push({ id: roles[i], pos: rolesPos[i], drop: timeRolesDrop[i], }) } } this.smallRoundArr = Object.values(allTime) this.startNextSmallRound() } else if (this.roundOutType == RoundOutType.average) { let tmpObj: {id: number; pos: number; time: number}[] = [] for (let i = 0; i < this.curRoundCfg.roles.length; i++) { let tmpRoles = this.curRoundCfg.roles[i] if (Array.isArray(tmpRoles)) { //'301':{ ID: 301, roles: [[60031, 60032], [60031, 60031, 60031]], rolesPos: [[0, 0], [0, 0, 0]], rolesTime: [[10, 20], [30, 40, 50]], time: 10}, for (let k = 0; k < tmpRoles.length; k++) { let cfgPos = this.curRoundCfg.rolesPos[i][k] for (let j = 0; j < this.curRoundCfg.rolesTime[i][k]; j++) { let pos = this.getRolePos(cfgPos) tmpObj.push({ id: tmpRoles[k], pos, time: this.curRoundCfg.time * i + (this.curRoundCfg.time / this.curRoundCfg.rolesTime[i][k]) * j, }) } } } } tmpObj.sort((a, b) => a.time - b.time) this.timeRolesTime = [] this.timeRoles = [] this.timeRolesPos = [] this.timeRolesDrop = [] tmpObj.forEach(v => { this.timeRoles.push(v.id) this.timeRolesPos.push(v.pos) this.timeRolesTime.push(v.time) }) this.timeRolesDrop = setDrop(dropArr, this.timeRoles) } this.roundNextRoleTime = this.timeRolesTime[this.curRoundCreateIndex] if ('additional' in this.curRoundCfg) { this.extraEnemyCreateArr = new Array(this.curRoundCfg.additional.map(v => v).length).fill(false) } //初始化每轮开始的BUFF和技能 this.scheduleOnce(() => { this.zumaUI.activeBuff.forEach(v => { this.addTeamBuff(v) this.castTeamSkill(v) }) }, 1) this.bagCardList.numItems = this.zumaUI.activeBuff.length ccUtils.setProgress((this.curRoundIndex + 1) / this.allRoundNum, this.roundNode, 'roundPb') ccUtils.setLabel(`${this.curRoundIndex + 1}/${this.allRoundNum}`, this.roundNode, 'roundPb/lb') if (this.curRoundIndex > 0) { this.isScrollMap = true this.changeTeamRoleRun(true) this.scheduleOnce(() => { this.isScrollMap = false this.isStop = false this.changeTeamRoleRun(false) }, this.zumaUI.fightReadyTime) } else { this.isStop = false } } startNextSmallRound() { let bigRoundTime = this.curRoundCfg.trigger if (!bigRoundTime && this.curRoundCreateIndex >= this.smallRoundArr.length) { this.roundFight = false this.readyNextRound() return } for (let roleItem of this.smallRoundArr[this.curRoundCreateIndex]) { let cfg = this.getRoleCfgByCfgID(roleItem.id) this.createEnemyNum += 1 this.world.createRoleEntity(cfg, true, roleItem.pos, null, roleItem.drop) } //如果bigRoundTime>0 再最后一小波的时候直接开始下一大波 if (this.curRoundCreateIndex == this.smallRoundArr.length - 1 && bigRoundTime > 0) { this.roundFight = false this.scheduleOnce(this.readyNextRound, bigRoundTime) } this.curRoundCreateIndex += 1 } showFightTip(tip) { let tipStr = '' if (!tip) return switch (tip) { case FightTip.wave: tipStr = LANGUAGE_TYPE.waveFightTip break case FightTip.boss: tipStr = LANGUAGE_TYPE.bossFightTip break } this.fightTip.stopAllActions() this.fightTip.opacity = 255 ccUtils.setLabel(tipStr, this.fightTip, 'label') cc.tween(this.fightTip) .to(0.5, {opacity: 0}) .reverseTime() .repeat(3) .call(() => { this.fightTip.opacity = 0 }) .start() } initRoles() { this.teamEntity.length = 0 Data.user.teamRole.length = 0 Data.user.teamRole.push(Mgr.global.buildIRole({equip: [], lv: 1, sid: '', id: 12001})) Data.user.teamRole.push(Mgr.global.buildIRole({equip: [], lv: 1, sid: '', id: 10001})) Data.user.teamRole.push(Mgr.global.buildIRole({equip: [], lv: 1, sid: '', id: 11001})) let iRoles = Data.user.teamRole.filter(v => v) for (let i = 0; i < Data.game.friendNum; i++) { let iRole = iRoles[i] if (iRole) { //友军位置ID写死101-200 let entity = this.world.createRoleEntity(iRole.cfg.ID, false, 101 + i, iRole) console.warn('创建队友', entity) this.teamEntity.push(entity) } } this.teamHP = 0 this.changeTeamHp() } changeTeamRoleRun(isRun: boolean) { this.teamEntity.forEach(entity => { this.world.changeRoleRun(entity, isRun) }) } addTeamBuff(buffID) { let cfg = ZumabuffConfig[buffID] cfg.buff.forEach(buffID => { let buffCfg = BuffConfig[buffID] if (buffCfg) { for (let i = 0; i < this.teamEntity.length; i++) { let entity = this.teamEntity[i] let comRole = this.world.getComponent(entity, ComRole) if (comRole && cfg.profession.includes(comRole.roleCfg.profession)) { this.world.addBuff(entity, this.world.createBuff(buffID, null), null) } } } }) } castTeamSkill(buffID) { let cfg = ZumabuffConfig[buffID] cfg.skill.forEach(skillID => { let skillCfg = SkillConfig[skillID] if (skillCfg) { this.teamEntity.forEach(entity => { let comRole = this.world.getComponent(entity, ComRole) if (comRole) { this.world.createSkill( {buffs: [], entryMap: new Map(), ...comRole}, skillCfg, entity, null, skillCfg.isHurt ? Data.game.gFriendHurtSkill : Data.game.gFriendGainSkill, ) } }) } }) } initSkillItem(node, index) { let buffID = this.zumaUI.activeBuff[index] this.zumaUI.initZumaBuffItem(buffID, cc.find('buff', node).children[0]) let skillOptCell = node.getComponent(SkillOptCell) skillOptCell.init(buffID, this) } initSkillArea() { this.skillAreaCObject.init(Data.game.gSkillArea, -100) cCollider.inst.insert(this.skillAreaCObject.body) this.skillArea.active = false } resetSkillArea() { this.skillArea.active = false } showSkillPop(iCard: ICard) { this.skillPop.active = true let cfg = iCard.cfg ccUtils.setLabel(cfg.name, this.skillPop, 'tip/skillname') ccUtils.setRichLabel(cfg.tip, this.skillPop, 'tip/scrollView/view/content/rt') if (cfg.attrNum.length > 0) { let i18nLb = cc.find('tip/scrollView/view/content/rt', this.skillPop).getComponent(i18nLabel) i18nLb.init( cfg.tip, cfg.attrNum.map(v => v.toString()), ) } this.skillArea.width = cfg.width this.skillArea.height = cfg.height let radiusAni = cc.find('radiusAni', this.skillArea) radiusAni.width = cfg.width radiusAni.height = cfg.height let rangeAni = cc.find('rectAni', this.skillArea) rangeAni.width = cfg.width rangeAni.height = cfg.height rangeAni.x = -cfg.width / 2 this.skillAreaCObject.updateBoxShape(cfg.width, cfg.height) this.unschedule(this.resetSkillArea) this.curCardSkillCfg = cfg this.curCard = iCard } skillOptShow(skillOptCell: SkillOptCell) { let iCard = skillOptCell.zumabuffCfg if (!this.skillPop.active) { this.curSkillOptCell = skillOptCell } } castSkillByCard() { let friend = [] let enemy = [] let world = this.world let targetEntities: number[] = [] let cardSkillCfg = this.curCard.cfg let iCard = this.curCard let skillCfg = SkillConfig[cardSkillCfg.skills[0]] //先找出所有目标 if (cardSkillCfg.castType == CARD_CAST_AREA.fullScreen) { let filter = skillCfg.isHurt ? this.world.getFilter(FILTER_CAN_ATTACK_ENEMY) : this.world.getFilter(FILTER_CAN_ATTACK_FRIEND) filter.walk((entity: number) => { targetEntities.push(entity) return false }) } else if (cardSkillCfg.castType == CARD_CAST_AREA.radius || cardSkillCfg.castType == CARD_CAST_AREA.rect) { //范围技能 //this.skillAreaCObject.trigger = true cCollider.inst.update(0) this.skillAreaCObject.containsBody.forEach((body, key) => { let comRole = world.getComponent(key, ComRole) if (!world.isDie(comRole) && !world.isBase(comRole)) { if (world.getComponent(key, ComEnemy)) { enemy.push(key) } else { friend.push(key) } } }) this.skillAreaCObject.setPosition(cc.v3(0, -200, 0)) //this.skillAreaCObject.trigger = false targetEntities = skillCfg.isHurt ? enemy : friend } //技能子弹不能打到建筑 if (cardSkillCfg.bullet > 0) { targetEntities = targetEntities.filter(v => { let comRole = world.getComponent(v, ComRole) return comRole.roleCfg.type != ROLE_TYPE.base }) } targetEntities.outOrder() let startPos: cc.Vec2 = cc.Vec2.ZERO let startCfgType = cardSkillCfg.posType[0] let endPos: cc.Vec2 = cc.Vec2.ZERO let endCfgType = cardSkillCfg.posType[1] let endPosArr: cc.Vec2[] = [] let areaWidth = this.skillArea.width let areaHeight = this.skillArea.height let leftAreaX = this.skillArea.x - areaWidth / 2 let rightAreaX = this.skillArea.x + areaWidth / 2 let belowAreaY = this.skillArea.y - areaHeight / 2 let topAreaY = this.skillArea.y + areaHeight / 2 let castDirType = cardSkillCfg.moreSkill[0] let castTimes = cardSkillCfg.moreSkill[1] let castInterval = cardSkillCfg.moreSkill[2] ? cardSkillCfg.moreSkill[2] : 0 if (endCfgType == CARD_END_POS.rowCol) { let stepX = areaWidth / (cardSkillCfg.posType[3] + 1) let stepY = areaHeight / (cardSkillCfg.posType[2] + 1) for (let i = 1; i <= cardSkillCfg.posType[2]; i++) { for (let j = 1; j <= cardSkillCfg.posType[3]; j++) { let x = leftAreaX + j * stepX let y = topAreaY - i * stepY endPosArr.push(cc.v2(x, y)) } } if (endPosArr.length != cardSkillCfg.moreSkill.length) { console.error('卡牌技能多行多列数量与moreSkill配置不一致') } } if (targetEntities.length > 0 && targetEntities.length < castTimes && castDirType == CARD_MORE_DIR.one2One) { //重复数组补齐 let repeat = Math.ceil(castTimes / targetEntities.length) for (let j = 0; j < repeat; j++) { targetEntities = targetEntities.concat(targetEntities) } } for (let i = 0; i < castTimes; i++) { this.scheduleOnce(() => { if (!this.node.activeInHierarchy) return let comTransform = world.getComponent(targetEntities[i], ComTransform) let randomX = Math.randomRangeFloat(leftAreaX, rightAreaX) let randomY = Math.randomRangeFloat(belowAreaY, topAreaY) if (startCfgType == START_POS.straightFall || startCfgType == START_POS.obliqueFall) { startPos.y = ConstValue.CANVAS_HEIGHT / 2 if (endCfgType == CARD_END_POS.random || (endCfgType == CARD_END_POS.role && !comTransform)) { startPos.x = randomX endPos.x = randomX endPos.y = randomY } else if (endCfgType == CARD_END_POS.rowCol) { startPos.x = randomX endPos = endPosArr[i] } else if (endCfgType == CARD_END_POS.role && comTransform) { startPos.x = comTransform.x if (startCfgType == START_POS.obliqueFall) startPos.x -= 200 endPos.x = comTransform.x endPos.y = comTransform.y } } else if (startCfgType == START_POS.leftScreen) { startPos.x = -ConstValue.CANVAS_WIDTH / 2 startPos.y = this.skillArea.y if (endCfgType == CARD_END_POS.rightScreen) { endPos.x = ConstValue.CANVAS_WIDTH endPos.y = startPos.y } } else if (startCfgType == START_POS.areaCenter) { startPos.x = this.skillArea.x startPos.y = this.skillArea.y } else if (startCfgType == START_POS.fightCenter) { startPos.x = (Data.game.fightSizeMaxX + Data.game.fightSizeMinX) / 2 startPos.y = (Data.game.fightSizeMaxY + Data.game.fightSizeMinY) / 2 } //子弹技能 if (cardSkillCfg.bullet > 0 && castDirType == CARD_MORE_DIR.one2One) { this.world.createCardBulletEntity(iCard, startPos, endPos, targetEntities[i]) } else if (!cardSkillCfg.bullet) { if (castDirType == CARD_MORE_DIR.noOne) { let skillEntity = world.createSkill( {buffs: [], entryMap: new Map(), ...iCard}, skillCfg, 0, cc.v3(startPos), skillCfg.isHurt ? Data.game.gFriendHurtSkill : Data.game.gFriendGainSkill, ) if (cardSkillCfg.moveSpeed > 0 && skillCfg.type == SKILL_TYPE.attackBack) { let comMovable = world.addComponent(skillEntity, ComMovable) comMovable.pointIdx = 0 comMovable.points.length = 0 comMovable.speed = cardSkillCfg.moveSpeed comMovable.points.push(endPos) } } else if (castDirType == CARD_MORE_DIR.one2All) { if (skillCfg.dirType == SKILL_DIR_TYPE.minHpRole) { targetEntities.sort((a, b) => { let comA = world.getComponent(a, ComRole) let comB = world.getComponent(b, ComRole) return comA?.nowHP - comB?.nowHP }) } for (let i = 0; i < Math.min(skillCfg.maxRole, targetEntities.length); i++) { world.createSkill( {buffs: [], entryMap: new Map(), ...iCard}, skillCfg, targetEntities[i], null, skillCfg.isHurt ? Data.game.gFriendHurtSkill : Data.game.gFriendGainSkill, ) } } } }, castInterval * i) } this.curSkillOptCell?.startCD() } getGameInfo(): {roleNum: number; enemyNum: number; castCost: number} { let roleNum = 0 let enemyNum = 0 this.world?.getFilter(FILTER_ROLE_NODE)?.walk((entity: number) => { if (this.world.getComponent(entity, ComEnemy)) { enemyNum++ } else { roleNum++ } return false }) return {roleNum, enemyNum, castCost: 0} } createDevEntity(ID: number, isEnemy?: boolean, posCfgID?: number, iRole?: IRole) { let roleCfg = RoleConfig[ID] if (!roleCfg) { roleCfg = (MonsterConfig[ID] as unknown) as IRoleConfig } this.world.createRoleEntity(roleCfg, isEnemy, posCfgID, iRole) } changeStageChange(stage: number) { this.curStage = stage Data.game.stageRound = stage * 100 + 1 Data.game.gameType = GAME_TYPE.normal this.resetGameWorld() this.startStageGame() } changeTeamHp() { let nowHp = 0 let nowShieldHp = 0 for (let i = 0; i < this.teamEntity.length; i++) { let entity = this.teamEntity[i] let comRole = this.world.getComponent(entity, ComRole) nowHp += comRole ? comRole.nowHP : 0 nowShieldHp += comRole ? comRole.shieldHP : 0 } this.teamNowHP = nowHp this.teamNowShieldHP = nowShieldHp if (!this.teamHP) this.teamHP = nowHp let allHp = this.teamHP if (this.teamNowHP + this.teamNowShieldHP > allHp) allHp = this.teamNowHP + this.teamNowShieldHP this.teamPb.progress = this.teamNowHP / allHp this.teamShieldPb.progress = (this.teamNowHP + this.teamNowShieldHP) / allHp let hpCellNum = Math.ceil(allHp / 100) //修改为固定10格血 if (hpCellNum > 10) hpCellNum = 10 if (hpCellNum != this.teamHPS.children.length) { ccUtils.instantChildren(cc.find('hp', this.teamHPS), hpCellNum) } if (this.roundFight && this.teamNowHP <= 0 && this.teamNowShieldHP <= 0) { this.roundFight = false this.zumaUI.endFightRound(false) } } changeKillNum() { if (!this.stageInfo) return let enemyNum = this.world.getFilter(FILTER_CAN_ATTACK_ENEMY).entities.size if (this.stageInfo.type == GAME_WIN_TYPE.killNum || this.stageInfo.type == GAME_WIN_TYPE.boss) { let allNum = this.monsterNum + this.eliteNum + this.bossNum ccUtils.setLabel(`${Math.floor((allNum / this.timeRoles.length) * 100)}%`, this.tipKillNumLb) if (allNum >= this.timeRoles.length) { this.zumaUI.endFightRound(true) } } else if (this.stageInfo.type == GAME_WIN_TYPE.surviveTime) { //通关类型3 (存活指定时间 或 击杀配置中所有怪物,即判定胜利 if (enemyNum <= 1 && !this.roundFight) { this.zumaUI.endFightRound(true) } } if (this.roundFight && this.roundOutType == RoundOutType.dieOut) { //1代表0血的敌人 if (enemyNum <= 1) { this.scheduleOnce(this.startNextSmallRound, this.roundOutTime) } } } dropOutGoods(pos: cc.Vec3, idNum) { if (!idNum || !idNum.id) return let origin = cc.find('icon', this.goods) if (!this.dropPool.get(idNum.id)) this.dropPool.set(idNum.id, new cc.NodePool()) let node: cc.Node = this.dropPool.get(idNum.id).get() if (!node) node = cc.instantiate(origin) node.stopAllActions() node.parent = this.goodsParent let endPos = ccUtils.convertNode2NodePosAR(this.goods, this.goodsParent) node.setPosition(pos) cc.tween(node) .then(cc.tween().to(1, {x: endPos.x, y: endPos.y}, {easing: 'quartOut'})) .call(() => { if (cc.isValid(node)) { if (idNum.id == GOODS.ball) { this.zumaUI.curBalls += idNum.num } else { let num = this.getDrop.get(idNum.id) num += idNum.num ccUtils.setLabel(num.toString(), this.goods, 'lb') this.getDrop.set(idNum.id, num) } this.dropPool.get(idNum.id).put(node) } }) .start() } setBallNum(num: number) { ccUtils.setLabel(num.toString(), this.goods, 'lb') } handleDie(entity: EntityIndex) { let comRole = this.world.getComponent(entity, ComRole) if (!comRole) return let roleCfg = comRole.roleCfg let entities = this.roleEntityMap.get(roleCfg.ID) if (entities) { entities.indexOf(entity) >= 0 && entities.splice(entities.indexOf(entity), 1) let transCfgID = -1 comRole.skills.forEach(skill => { if (skill.type == SKILL_TYPE.transRole) { transCfgID = skill.createRole[0] } }) let transArr = this.roleEntityMap.get(transCfgID * 100 + (comRole.roleCfg.ID % 100)) || [] let resetID = -1 //唯一卡死亡后重新召唤(排除变身后还在场上) if (roleCfg.type == ROLE_TYPE.only && (transCfgID == -1 || (transCfgID > 0 && !transArr.length))) { resetID = roleCfg.ID } //唯一卡变身后死亡重启召唤 let transOnly = comRole.transID && RoleConfig[comRole.transID].type == ROLE_TYPE.only if (transOnly && !this.roleEntityMap.get(comRole.transID)?.length) { resetID = comRole.transID } } if (comRole.drop && comRole.drop.num > 0) { let comTransform = this.world.getComponent(entity, ComTransform) this.dropOutGoods(cc.v3(comTransform.x, comTransform.y, 0), comRole.drop) } //移除右半场已经死亡的友军 if (comRole.group == Data.game.gFriend) { let inRightIndex = this.rightAreaFriend.indexOf(entity) if (inRightIndex >= 0) this.rightAreaFriend.splice(inRightIndex, 1) } } initGameSpeed() { this.speed.active = true if (!Mgr.global.checkModOpen(MOD.speedGame)) return Mgr.net.add(msgCmd.cmd_speed_up_data_rsp, this, this.speedUpDataRsp) Mgr.net.send(msgCmd.cmd_speed_up_data) } showClickAni(pos: cc.Vec3) { let node = cc.find('clickAni', this.node) node.position = pos node.stopAllActions() node.scale = 0.5 node.opacity = 255 let t = cc.tween //node放大渐隐 t(node) .then(t().parallel(t().to(0.5, {scale: 1.8}), t().delay(0.2).to(0.3, {opacity: 0}))) .start() } showBossHP(comRole: ComRole) { this.bossHPNode.active = true let maxBars = 6 let healthPerBar = Math.floor(comRole.HP / maxBars) // 每管血量 let currentBars = comRole.nowHP > healthPerBar * (maxBars - 1) ? maxBars : Math.ceil(comRole.nowHP / healthPerBar) let lastBars = comRole.lastHP > healthPerBar * (maxBars - 1) ? maxBars : Math.ceil(comRole.lastHP / healthPerBar) // 更新标签 ccUtils.setLabel(`x${currentBars}`, this.bossHPNode, 'label') let getImageNum = index => { if (index > 0) { return index % 5 ? index % 5 : 5 } return 0 } if (currentBars != lastBars || comRole.nowHP == comRole.HP) { if (currentBars > 1) { this.zumaUI.loadTexImg(`GameUI/boss_blood_${getImageNum(currentBars - 1)}`, this.bossHPNode, 'bg') } else { ccUtils.setSpriteFrame(null, this.bossHPNode, 'bg') } this.zumaUI.loadTexImg(`GameUI/boss_blood_${getImageNum(currentBars)}`, this.bossHPNode, 'pb/bar') } ccUtils.setProgress( currentBars == maxBars ? (comRole.nowHP - healthPerBar * (maxBars - 1)) / (healthPerBar + (comRole.HP % healthPerBar)) : (comRole.nowHP % healthPerBar) / healthPerBar, this.bossHPNode, 'pb', ) } hideBossHP() { this.bossHPNode.active = false } //我方右半区友军大于一定数量增加刷怪 addRightAreaFriendNum(entity) { if (!this.curRoundCfg) return //处理下容错,刚好这一帧死亡,下一次推入新的友军可以过滤掉 this.rightAreaFriend = this.rightAreaFriend.filter(v => !this.world.isDie(v)) this.rightAreaFriend.push(entity) if ('extraRoles' in this.curRoundCfg) { for (let i = 0; i < this.extraEnemyCreateArr.length; i++) { if (!this.extraEnemyCreateArr[i] && this.rightAreaFriend.length >= this.curRoundCfg.additional[i]) { for (let j = 0; j < this.curRoundCfg.addNum[i]; j++) { this.world.createRoleEntity( this.getRoleCfgByCfgID(this.curRoundCfg.extraRoles[i]), true, this.getRolePos(this.curRoundCfg.position[i]), null, null, true, ) } this.extraEnemyCreateArr[i] = true } } } } getStageInfoCfg() { let cfg: IStageInfoConfig if (Data.game.gameType == GAME_TYPE.normal || Data.game.gameType == GAME_TYPE.none) { cfg = StageInfoConfig[this.curStage] } return cfg } getRoundCfg(isNext: boolean = false, roundIndex: number = 0) { let cfg: IStageConfig let baseRound = Data.game.stageRound if (isNext) baseRound += 1 if (roundIndex) baseRound += roundIndex if (Data.game.gameType == GAME_TYPE.normal || Data.game.gameType == GAME_TYPE.none) { cfg = StageConfig[baseRound] } return cfg } scrollMap(dt: number) { if (!this.isScrollMap) return let children = this.bgRolls.children children.forEach(node => { node.x -= dt * 300 }) children.sort((a, b) => a.x - b.x) if (children[0].x <= -children[0].width) { children[0].x = children[1].x + children[0].width } } changeBagShow(isShow: boolean) { this.bag.stopAllActions() let targetY = -830 this.bag.y = targetY + (isShow ? this.bag.height : 0) cc.tween(this.bag) .to(this.zumaUI.fightReadyTime, {y: targetY + (isShow ? 0 : this.bag.height)}) .start() } //---------------------点击事件-------------------------------- onCardSkillSureClick(e) { let pos = ccUtils.convertWorldPosToNode(this.node, ccUtils.convertTouchPosToWorld(e)) let isHurt = SkillConfig[this.curCardSkillCfg.skills[0]].isHurt if (this.curCardSkillCfg.castType == CARD_CAST_AREA.fullScreen) { // let filterE = this.world.getFilter(FILTER_CAN_ATTACK_ENEMY) // let filter = this.world.getFilter(FILTER_CAN_ATTACK_FRIEND) // let isHurt = SkillConfig[this.curCardSkillCfg.skills[0]].isHurt // if ((filterE.entities.size == 0 && isHurt) || (filter.entities.size == 0 && !isHurt)) { // Mgr.ui.tip('没有目标可以施放卡牌技能') // return // } this.castSkillByCard() } else if ( this.curCardSkillCfg.castType == CARD_CAST_AREA.radius || this.curCardSkillCfg.castType == CARD_CAST_AREA.rect ) { let curNode = this.skillArea this.zumaUI.loadTexImg( `GameUI/skill_range_${this.curCardSkillCfg.castType}${isHurt ? '' : '_friend'}`, curNode, ) let radiusAni = cc.find('radiusAni', curNode) radiusAni.active = this.curCardSkillCfg.castType == CARD_CAST_AREA.radius if (radiusAni.active) { this.zumaUI.loadTexImg( `GameUI/skill_range_${this.curCardSkillCfg.castType}_ani${isHurt ? '' : '_friend'}`, radiusAni, ) radiusAni.stopAllActions() cc.tween(radiusAni).to(0, {scale: 0.2}).to(0.5, {scale: 1}).union().repeatForever().start() } let rangeAni = cc.find('rectAni', curNode) rangeAni.active = this.curCardSkillCfg.castType == CARD_CAST_AREA.rect if (rangeAni.active) { rangeAni.stopAllActions() cc.tween(rangeAni).to(0, {scaleX: 0}).to(0.5, {scaleX: 1}).union().repeatForever().start() } if (this.curCardSkillCfg.castType == CARD_CAST_AREA.rect) { if (pos.x - curNode.width / 2 < Data.game.fightSizeMinX) pos.x = Data.game.fightSizeMinX + curNode.width / 2 if (pos.x + curNode.width / 2 > Data.game.fightSizeMaxX) pos.x = Data.game.fightSizeMaxX - curNode.width / 2 if (pos.y - curNode.height / 2 < Data.game.fightSizeMinY) pos.y = Data.game.fightSizeMinY + curNode.height / 2 if (pos.y + curNode.height / 2 > Data.game.fightSizeMaxY) pos.y = Data.game.fightSizeMaxY - curNode.height / 2 } curNode.setPosition(pos) this.skillAreaCObject.setPosition(cc.v3(pos)) //cCollider.inst.update(0) // if (this.skillAreaCObject.containsBody.size <= 0) { // Mgr.ui.tip('没有目标可以施放卡牌技能') // return // } this.scheduleOnce(this.castSkillByCard, 1) this.scheduleOnce(this.resetSkillArea, 1) } this.skillArea.active = true this.skillPop.active = false } onSkillPopCloseClick() { this.skillPop.active = false cc.find('popChoose', this.curSkillOptCell.node).active = false } onClickGameSpeed() { if (Data.game.gameSpeed == 1) { let isDouble = Data.game.speedUpTime > 0 if (isDouble || true) { Data.game.gameSpeed = 2 } else { this.isStop = true Mgr.ui.show(UI.GameSpeedUI) } } else { Data.game.gameSpeed = 1 } } onClickDrop(e) { let node = e.currentTarget let idNum = node['idNum'] let pool = this.dropPool.get(idNum.id) if (!pool) { pool = new cc.NodePool() this.dropPool.set(idNum.id, pool) } pool.put(node) let awardCfg = AwardConfig[GoodsConfig[idNum.id]?.award] if (awardCfg) { let addNum = Math.randomRangeInt(awardCfg.oddsMin[0], awardCfg.oddsMax[0]) } } onTestEndGameClick() { this.testClickNum += 1 if (this.testClickNum == 5) { this.zumaUI.endGame(true) } } //网络事件======================================= onRoundStartRsp(data: adventureLayerRsp) { Data.game.stageRound += 1 this.curRoundIndex += 1 this.resetRound() } speedUpDataRsp(rsp: speedUpDataRsp) { Data.game.speedUpTime = rsp.time this.speed.active = true let isDouble = rsp.time > 0 let tip = cc.find('tip', this.speed) tip.active = !isDouble if (tip.active) { tip.y = -105 tip.stopAllActions() cc.tween(tip) .by(1, {position: cc.v3(0, 10)}, {easing: 'sineOut'}) // 缓慢上移10像素 .by(1, {position: cc.v3(0, -10)}, {easing: 'sineIn'}) // 缓慢下落10像素 .union() .repeat(3) .call(() => { tip.active = false }) .start() } } //触发事件======================================= @render addTime() { let time = Data.main.serverTime if (this.speed.active) { if (Data.game.speedUpTime > 0) { Data.game.speedUpTime -= 1 if (Data.game.speedUpTime <= 0) Data.game.speedUpTime = 0 ccUtils.setLabel(Date.Format('hh:mm:ss', Data.game.speedUpTime, true), this.speed, 'time') } else { ccUtils.setLabel('', this.speed, 'time') Data.game.gameSpeed = 1 } } } @render showSpeed() { let speed = Data.game.gameSpeed let tip = cc.find('tip', this.speed) tip.active = speed > 1 //cc.find('tip', this.speed).active = speed > 1 // if (speedCircle.active) { // speedCircle.runAction(cc.rotateBy(1, 360).repeatForever()) // } } public setGameTex(url: string, node?: cc.Node, childUrl?: string) { if (childUrl) { node = cc.find(childUrl, node) } let sprite = node.getComponent(cc.Sprite) if (sprite) { sprite.spriteFrame = Data.game.gameAssetMap.get(`texture/${url}`) as cc.SpriteFrame } } private registerTouchEvent() { this.touchNode.on(cc.Node.EventType.TOUCH_START, this._onTouchStart, this) this.touchNode.on(cc.Node.EventType.TOUCH_MOVE, this._onTouchMove, this) this.touchNode.on(cc.Node.EventType.TOUCH_END, this._onTouchEnd, this) this.touchNode.on(cc.Node.EventType.TOUCH_CANCEL, this._onTouchCancel, this) } private _onTouchStart(e: cc.Event.EventTouch) { for (let i = 0; i < this._touchHandler.length; i++) { this._touchHandler[i].onTouchStart(ccUtils.convertTouchPosToWorld(e), this.world) } } private _onTouchMove(e: cc.Event.EventTouch) { for (let i = 0; i < this._touchHandler.length; i++) { this._touchHandler[i].onTouchMove(ccUtils.convertTouchPosToWorld(e), this.world) } } private _onTouchEnd(e: cc.Event.EventTouch) { for (let i = 0; i < this._touchHandler.length; i++) { this._touchHandler[i].onTouchEnd(ccUtils.convertTouchPosToWorld(e), this.world) } } private _onTouchCancel(e: cc.Event.EventTouch) { for (let i = 0; i < this._touchHandler.length; i++) { this._touchHandler[i].onTouchCancel(ccUtils.convertTouchPosToWorld(e), this.world) } } public registerTouchHandler(handler: ITouchProcessor) { this._touchHandler.push(handler) } public unRegisterTouchHandler(handler: ITouchProcessor) { for (let i = this._touchHandler.length - 1; i >= 0; i--) { if (this._touchHandler[i] == handler) { this._touchHandler.splice(i, 1) } } } }