import { ComBattleInvitation, MemComBtlTeam } from '../../../domain/battleField/ComBattleTeamField'; import { difference, pick } from 'underscore'; /* * @Author: 梁桐川 * @Date: 2020-11-30 15:05:48 * @Last Modified by: 梁桐川 * @Last Modified time: 2021-08-27 17:47:56 */ import { IT_TYPE, CURRENCY_BY_TYPE, CURRENCY_TYPE, COM_TEAM_STATUS, COM_BTL_CONST, CONSUME_TYPE, MSG_SOURCE, ROLE_SELECT, TASK_TYPE, KING_EXP_RATIO_TYPE, ITEM_CHANGE_REASON, getChannelType, CHANNEL_PREFIX, DEBUG_MAGIC_WORD, PUSH_ROUTE, POPULATE_TYPE } from '../../../consts'; import Role, { RoleModel } from '../../../db/Role'; import { STATUS } from '../../../consts/statusCode'; import { Application, BackendSession } from 'pinus'; import { resResult, getRandSingleEelm, cal } from '../../../pubUtils/util'; import { RoleStatus, ComBattleTeamModel, ComBattleTeamType, BossHp, ComRoleStatusHero } from '../../../db/ComBattleTeam'; import { ItemModel, ItemType } from '../../../db/Item'; import { addItems, handleCost } from '../../../services/role/rewardService'; import { checkRoleInQueue, getServerName, redisClient, rmCreatedTeamFromRedis, rmRoleFromQueue, setTeamSearchReq } from '../../../services/redisService'; import { getRandBlueprtId, getFrd, updateRobotHurtByTime, comBtlLvInvalid, clearRobotHurtTimer, setDismissTimer, dismissTeam, handleComBtlProgress, getComBattleFriendAdd, teammateInBlackList, blueprtIdValid, hasEnoughBlueprt, addRoleToTeam, addRoleStToTeam, addValidSearchingRoles, validToJoin, addRobotsToTeam, addRobotsLater, teamIsFullToStart, oneTeamNotInBlack, getAllAssistCnt, checkHasMyTeam, checkTeamStatusAndSend, getComBtlLvByPlayerLv, addToSearchingTeams, getCapFrd, getCapExtraCnt, startTeam } from '../../../services/battle/comBattleService'; import { setAp } from '../../../services/actionPointService'; import { roleLevelup } from '../../../services/normalBattleService'; import { getSimpleRoleInfo } from '../../../services/roleService'; import { pushComBtlTeamMsg, pushFriendTeamInviteMsg, pushNormalItemMsg, pushTeamInviteMsg } from '../../../services/chatService'; import { EXTERIOR, INFO_WINDOW } from '../../../pubUtils/dicParam'; import { getZeroPointD, getTimeFunD, getSeconds, nowSeconds } from '../../../pubUtils/timeUtil'; import { FriendParams } from '../../../domain/roleField/friend'; import { checkTask, checkTaskInComBattleStart } from '../../../services/task/taskService'; import { gameData, getWarByBlueprtId } from '../../../pubUtils/data'; import { HeroModel } from '../../../db/Hero'; import { addUserToTeamChannel, delTeamChannel, removeFromTeamChannel, sendMessageToTeam, sendMessageToUsersWithSuc, sendMessageToUserWithSuc } from '../../../services/pushService'; import { getFriendRelationType, getRecommendType } from '../../../services/friendService'; import { FriendRelationModel } from '../../../db/FriendRelation'; import { isHeroHidden } from '../../../services/dataService'; import { addComTeam, clearComBtlTimer,deleteComBattle,getComTeamByCode } from '../../../services/memoryCache/comBattleData'; import { clearComBattleRoute } from '../../../pubUtils/dispatcher'; export default function(app: Application) { return new ComBattleHandler(app); } export class ComBattleHandler { constructor(private app: Application) { } /** * @description 队长创建队伍 * @param {{blueprtId: number, pub: boolean, ceLimit: number}} msg 藏宝图Id,是否公开,战力限制 * @param {BackendSession} session * @returns * @memberof ComBattleHandler */ async createTeam(msg: {blueprtId: number, pub: boolean, ceLimit: number}, session: BackendSession) { let roleId = session.get('roleId'); let sid = session.get('sid'); let teamCode = session.get('teamCode'); const { blueprtId, pub, ceLimit } = msg; // console.log('createTeam msg: ', msg); // 检查藏宝图Id是否合法 if (!blueprtIdValid(blueprtId)) return resResult(STATUS.COM_BATTLE_BLUEPRT_INVALID); const enoughBlueprt = await hasEnoughBlueprt(roleId, sid, blueprtId); if (!enoughBlueprt) return resResult(STATUS.COM_BATTLE_BLUEPRT_NOT_ENOUGH); const roleInfo = await RoleModel.findByRoleId(roleId, null, true); const { lv, topLineupCe } = roleInfo; if (lv < COM_BTL_CONST.ENABLE_LV) return resResult(STATUS.COM_BATTLE_LV_NOT_ENOUGH); if(ceLimit && ceLimit > topLineupCe * (1 + COM_BTL_CONST.ROBOT_CE_RATIO)) { return resResult(STATUS.COM_BATTLE_CREATE_CE_LIMIT) } // 创建队伍数据结构 let comTeam = new MemComBtlTeam(teamCode, pub, blueprtId, roleId, ceLimit, sid); let isFrd = await getCapFrd(roleId); addRoleToTeam(comTeam, roleInfo, sid, true, isFrd); addUserToTeamChannel(teamCode, true, roleId, sid); // 将正在匹配的符合要求的玩家加入队伍,并推送入队消息 if(pub == true) await addValidSearchingRoles(comTeam); // 队伍数据持久化 const team = await ComBattleTeamModel.createTeam(comTeam); if (!team) { delTeamChannel(teamCode); return resResult(STATUS.COM_BATTLE_CREATE_ERR); } addComTeam(teamCode, comTeam) // 倒计时一定时间给队长匹配机器人 if (pub === true) { await addRobotsLater(comTeam, roleInfo); } setDismissTimer(comTeam, roleId); return resResult(STATUS.SUCCESS, { teamCode, roleStatus: comTeam.roleStatus }); } /** * @description 匹配队伍 * @param {{ lv: number }} msg 要匹配藏宝图品阶 * @param {BackendSession} session * @returns * @memberof ComBattleHandler */ async searchTeam(msg: { lv: number[], magicWord: string }, session: BackendSession) { let roleId = session.get('roleId'); let sid = session.get('sid'); const { lv: lvs = [], magicWord } = msg; const roleInfo = await RoleModel.findByRoleId(roleId, null, true); const { lv: playerLv } = roleInfo; let { topLineupCe = 1000 } = roleInfo; if(await checkHasMyTeam(roleId) && magicWord != DEBUG_MAGIC_WORD) { return resResult(STATUS.COM_BATTLE_IS_RUNNING); } if (comBtlLvInvalid(playerLv, lvs)) { return resResult(STATUS.COM_BATTLE_ASSIST_LV_NOT_ENOUGH); } const teams = await ComBattleTeamModel.getOtherTeamByLvAndSt(roleId, lvs, COM_TEAM_STATUS.DEFAULT, topLineupCe); const team: ComBattleTeamType = await oneTeamNotInBlack(teams, roleId); if (team && team.roleIds.length < 3 && team.status === COM_TEAM_STATUS.DEFAULT && team.roleIds.indexOf(roleId) === -1 && team.blacklist && team.blacklist.indexOf(roleId) === -1) { return resResult(STATUS.SUCCESS, {teamCode: team.teamCode}); } let teamCode = session.get('teamCode'); let hasAdd = await addToSearchingTeams(roleId, sid, lvs); if(hasAdd) return(STATUS.SUCCESS); await setTeamSearchReq(roleId, sid, lvs); let thiz = this; // 倒计时匹配两个机器人 setTimeout(async () => { let inQueue = await checkRoleInQueue(roleId, sid, lvs); if (!inQueue) return; await rmRoleFromQueue(roleId, sid, lvs); // 创建队伍 let blueprtId = getRandBlueprtId(lvs).pop(); let comTeam = new MemComBtlTeam(teamCode, false, blueprtId, 'robot', 0, '') let isFrd = await getFrd(roleId); // 将玩家加入队伍 addRoleToTeam(comTeam, roleInfo, sid, false, isFrd); addUserToTeamChannel(teamCode, true, roleId, sid); // 创建并添加机器人 await addRobotsToTeam(comTeam, roleId, topLineupCe, playerLv, 2); await ComBattleTeamModel.createTeam(comTeam); addComTeam(teamCode, comTeam); // 机器人队伍 3 秒直接开战 setTimeout(async () => { thiz.startBattle({teamCode}, session); }, 3000); }, COM_BTL_CONST.ASSIST_TIME); return resResult(STATUS.SUCCESS); } /** * @description 取消匹配 * @param {{}} msg * @param {BackendSession} session * @memberof ComBattleHandler */ async cancelSearch(msg: {}, session: BackendSession) { let roleId = session.get('roleId'); let sid = session.get('sid'); await rmRoleFromQueue(roleId, sid); let teamCode = session.get('teamCode'); if(teamCode) clearComBattleRoute(redisClient(), teamCode); return resResult(STATUS.SUCCESS); } /** * @description 当匹配 searchTeam 匹配到其它队伍时,需要调用此接口加入 * @param {{teamCode: string, isFrd: boolean}} msg 要加入的队伍编号,是否情谊助战 * @param {BackendSession} session * @returns * @memberof ComBattleHandler */ async joinTeam(msg: {teamCode: string, isFrd: boolean}, session: BackendSession) { let roleId = session.get('roleId'); let sid = session.get('sid'); let { teamCode, isFrd } = msg; let teamStatus = getComTeamByCode(teamCode); if (!teamStatus) return resResult(STATUS.COM_BATTLE_TEAM_INVALID); if(teamStatus.status !== COM_TEAM_STATUS.DEFAULT) return resResult(STATUS.COM_BATTLE_TEAM_NOT_DEFAULT); if (teamIsFullToStart(teamStatus)) return resResult(STATUS.COM_BATTLE_MEMBER_LIMIT); if (teamStatus.roleIds.indexOf(roleId) !== -1) return resResult(STATUS.COM_BATTLE_DUP_ENTER); if (teamStatus.blacklist.indexOf(roleId) != -1) return resResult(STATUS.COM_BATTLE_BE_KICKED); let role = await Role.findByRoleId(roleId, null, true); if (role.lv < COM_BTL_CONST.ENABLE_LV) return resResult(STATUS.COM_BATTLE_LV_NOT_ENOUGH); if (comBtlLvInvalid(role.lv, [teamStatus.lv])) return resResult(STATUS.COM_BATTLE_ASSIST_LV_NOT_ENOUGH); if (role.topLineupCe < teamStatus.ceLimit) return resResult(STATUS.COM_BATTLE_CE_LIMIT); if (!isFrd) { isFrd = await getFrd(roleId); } // 加入队伍 if (!validToJoin(teamStatus, roleId)) return resResult(STATUS.COM_BATTLE_TEAM_INVALID); let roleStatus = new RoleStatus(role, sid, false, isFrd); const team = await ComBattleTeamModel.addRole(teamCode, roleStatus); if (!team) { return resResult(STATUS.COM_BATTLE_JOIN_ERR); } addRoleStToTeam(teamStatus, roleStatus); // 推送队伍信息给所有玩家 addUserToTeamChannel(teamCode, false, roleId, sid); sendMessageToTeam(teamCode, PUSH_ROUTE.TEAM_JOIN, { teamInfo: teamStatus }); setDismissTimer(teamStatus, roleId); return resResult(STATUS.SUCCESS, { teamInfo: teamStatus}); } /** * @description 设置是否情谊助战 * @param {{teamCode: string, isFrd: boolean}} msg 队伍编号;情谊助战要设置的值,true 为情谊助战 * @param {BackendSession} session * @returns * @memberof ComBattleHandler */ async setFrdAssistance(msg: {teamCode: string, isFrd: boolean}, session: BackendSession) { let roleId = session.get('roleId'); let { isFrd, teamCode } = msg; const isFrdPre = isFrd; let teamStatus = getComTeamByCode(teamCode); if (!teamStatus || teamStatus.status !== COM_TEAM_STATUS.DEFAULT) return resResult(STATUS.COM_BATTLE_TEAM_INVALID); if (teamStatus.capId === roleId) return resResult(STATUS.COM_BATTLE_SET_FRD_ERR); if (!isFrd) { isFrd = await getFrd(roleId); } if (isFrd !== isFrdPre) { return resResult(STATUS.COM_BATTLE_ASSIST_NOT_ENOUGH); } teamStatus.roleStatus.forEach(async st => { if(st.roleId == roleId) { st.isFrd = isFrd; await ComBattleTeamModel.updateRoleStFrd(teamCode, roleId, isFrd); } }); return resResult(STATUS.SUCCESS, { teamCode, isFrd }); } // /** // * ! 获取队伍列表-deprecated // * @param msg // * @param session // */ // async getTeams(msg: { blueprtIds: number[] }, session: BackendSession) { // let roleId = session.get('roleId'); // let { lv } = await Role.findByRoleId(roleId); // if (lv < COM_BTL_CONST.ENABLE_LV) return resResult(STATUS.COM_BATTLE_LV_NOT_ENOUGH); // const teams = await ComBattleTeamModel.getTeamByBlueprt(msg.blueprtIds, COM_TEAM_STATUS.DEFAULT, true); // if (!teams) return resResult(STATUS.COM_BATTLE_NO_VALID_TEAM); // return resResult(STATUS.SUCCESS, { teamInfos: teams}); // } // /** // * ! deprecate // * @description 队伍准备 // * @param {{teamCode: string, heroes: number[]}} msg // * @param {BackendSession} session // * @returns // * @memberof ComBattleHandler // */ // async teammateReady(msg: {teamCode: string, heroes: number[]}, session: BackendSession) { // let roleId = session.get('roleId'); // let { teamCode, heroes: hids } = msg; // let teamStatus = this.teamMap.get(teamCode); // if (!teamStatus || !teamStatus.roleIds || teamStatus.roleIds.indexOf(roleId) === -1) return resResult(STATUS.COM_BATTLE_TEAM_INVALID); // const heroDBs = await HeroModel.findByHidRange(hids, roleId); // const heroes = heroDBs.map(hero => { // return new ComRoleStatusHero(hero); // }); // let team = await ComBattleTeamModel.updateHeroes(teamCode, roleId, heroes); // if (!team) return resResult(STATUS.COM_BATTLE_UPDATE_HEROES_ERR); // teamStatus.roleStatus.forEach(st => { // if (st && st.roleId === roleId) { // st.heroes = heroes; // } // }); // sendMessageToTeam(teamCode, PUSH_ROUTE.TEAMMATE_READY, {teamCode, roleId, heroes}); // return resResult(STATUS.SUCCESS); // } /** * @description 出兵时记录玩家阵容 * @param {{teamCode: string, heroes: number[]}} msg 队伍编号;阵容 * @param {BackendSession} session * @returns * @memberof ComBattleHandler */ async setupHeroes(msg: {teamCode: string, heroes: number[], battleCode: string}, session: BackendSession) { let roleId = session.get('roleId'); let { teamCode, heroes: hids, battleCode = 'default' } = msg; if (!hids || hids.length === 0) return resResult(STATUS.COM_BATTLE_HEROES_ERR); if(isHeroHidden(...hids)) return resResult(STATUS.HERO_IS_HIDDEN); let teamStatus = getComTeamByCode(teamCode); if (!teamStatus || !teamStatus.roleIds || teamStatus.roleIds.indexOf(roleId) === -1) return resResult(STATUS.COM_BATTLE_TEAM_INVALID); const heroDBs = await HeroModel.findByHidRange(hids, roleId); const heroes = heroDBs.map(hero => { return new ComRoleStatusHero(hero); }); let team = await ComBattleTeamModel.setupBattleInfo(teamCode, roleId, heroes, battleCode); if (!team) return resResult(STATUS.COM_BATTLE_UPDATE_HEROES_ERR); teamStatus.roleStatus.forEach(st => { if (st && st.roleId === roleId) { st.heroes = heroes; st.battleCode = battleCode; } }); sendMessageToTeam(teamCode, PUSH_ROUTE.TEAMMATE_READY, {teamCode, roleId, heroes}); return resResult(STATUS.SUCCESS); } /** * @description 从队伍中移除玩家 * @param {{teamCode: string, roleIdToRm: string}} msg 队伍编号;要移除的玩家 Id * @param {BackendSession} session * @returns * @memberof ComBattleHandler */ async rmTeammate(msg: {teamCode: string, roleIdToRm: string}, session: BackendSession) { let roleId = session.get('roleId'); let { teamCode, roleIdToRm } = msg; let teamStatus = getComTeamByCode(teamCode); if (!teamStatus || !teamStatus.roleIds || teamStatus.roleIds.indexOf(roleId) === -1) return resResult(STATUS.COM_BATTLE_TEAM_INVALID); if (roleId === teamStatus.capId && roleId === roleIdToRm) return resResult(STATUS.COM_BATTLE_RM_SELF); // 队长移除自己视为解散,提示确认 if (roleId !== teamStatus.capId && roleId !== roleIdToRm) return resResult(STATUS.COM_BATTLE_CAN_NOT_RM); // if (roleId !== teamStatus.capId) return resResult(STATUS.COM_BATTLE_CAP_ONLY); // 只有队长可以移除玩家 // 移除操作 let team = await ComBattleTeamModel.removeRole(teamCode, roleIdToRm); if (!team) return resResult(STATUS.COM_BATTLE_RM_TEAMMATE_ERR); teamStatus.roleStatus.some((elem, idx) => { if (elem.roleId === roleIdToRm) { teamStatus.roleStatus.splice(idx, 1); } }); teamStatus.roleIds = teamStatus.roleStatus.map(cur => cur.roleId); if(roleId === teamStatus.capId) { // 队长移除队友,并把他加入黑名单 teamStatus.blacklist.push(roleIdToRm); } // 先推送离队消息,再将玩家从房间中移除 sendMessageToTeam(teamCode, PUSH_ROUTE.LEAVE_TEAM, {teamCode, roleId: roleIdToRm}); removeFromTeamChannel(teamCode, roleIdToRm); clearComBtlTimer(teamCode); // 移除队员停止解散计时 let thiz = this; setTimeout(async () => { let curTeamStatus = getComTeamByCode(teamCode); if (validToJoin(curTeamStatus)) { let roleSt: RoleStatus; for (let st of curTeamStatus.roleStatus) { if (st.isCap) { roleSt = st; } } if (!roleSt) return; let { roleId, topLineupCe, lv } = roleSt; await addRobotsToTeam(curTeamStatus, roleId, topLineupCe, lv, 3 - curTeamStatus.roleIds.length); } }, COM_BTL_CONST.ASSIST_TIME); return resResult(STATUS.SUCCESS); } /** * @description 队长解散队伍 * @param {{teamCode: string}} msg 要解散的队伍编号 * @param {BackendSession} session * @returns * @memberof ComBattleHandler */ async dismiss(msg: {teamCode: string}, session: BackendSession) { let roleId = session.get('roleId'); let { teamCode } = msg; let teamStatus = getComTeamByCode(teamCode); if(!teamStatus) return resResult(STATUS.SUCCESS); let result = await dismissTeam(teamStatus, roleId); return result; } /** * @description 开始战斗,队长操作或机器人队伍操作 * @param {{teamCode: string}} msg 队伍编号 * @param {BackendSession} session * @returns * @memberof ComBattleHandler */ async startBattle(msg: {teamCode: string}, session: BackendSession) { let roleId = session.get('roleId'); let roleName = session.get('roleName'); let sid = session.get('sid'); let { teamCode } = msg; let teamStatus = getComTeamByCode(teamCode); if (!teamStatus || !teamStatus.roleIds || teamStatus.roleIds.indexOf(roleId) === -1) return resResult(STATUS.COM_BATTLE_TEAM_INVALID); if (teamStatus.capId !== roleId && teamStatus.capId !== 'robot') return resResult(STATUS.COM_BATTLE_CAP_ONLY); if (teamStatus.status !== COM_TEAM_STATUS.DEFAULT) return resResult(STATUS.COM_BATTLE_ALREADY_START); startTeam(teamStatus); let team = await ComBattleTeamModel.updateStatusByCode(teamCode, teamStatus.status, teamStatus.startTime, teamStatus.endTime, teamStatus.hasTimeExtraReward); if (!team) return resResult(STATUS.COM_BATTLE_START_ERR); rmCreatedTeamFromRedis(teamCode, teamStatus.lv); clearComBtlTimer(teamCode); // 战斗开始停止解散计时 await getComBattleFriendAdd(teamStatus.roleStatus); sendMessageToTeam(teamCode, PUSH_ROUTE.COMBATTLE_START, {teamCode, roleStatus: teamStatus.roleStatus}); // 每场倒计时结算 let thiz = this; setTimeout(async () => { let teamStatus = getComTeamByCode(teamCode); if (teamStatus && teamStatus.status === COM_TEAM_STATUS.FIGHTING) { clearRobotHurtTimer(teamStatus); let team = await ComBattleTeamModel.syncTeamData({teamCode, status: COM_TEAM_STATUS.LOOSE, roleStatus: teamStatus.roleStatus, bossHpArr: teamStatus.bossHpArr}, true); if (!team) return resResult(STATUS.COM_BATTLE_RESULT_ERR); teamStatus.timeout = true; sendMessageToTeam(teamCode, PUSH_ROUTE.TEAM_COMPLETE, {teamCode, result: false, timeout: true}); deleteComBattle(teamCode); clearComBattleRoute(redisClient(), teamCode) } }, COM_BTL_CONST.BTL_TIME_LMT); teamStatus.roleStatus.forEach((st, idx) => { if (st.isRobot === true) { updateRobotHurtByTime(teamStatus, st, COM_BTL_CONST.ROBOT_BASE_TIME_INTERVAL + idx); } }); await checkTaskInComBattleStart(teamStatus.roleStatus, teamStatus.capId, teamStatus.blueprtId); return resResult(STATUS.SUCCESS); } /** * @description 战场中的状态同步,战斗结果判定 * @param {{teamCode: string, bossHurts: Array<{dataId: number, hurtHp: number}>, killed: number[], curRnd: number}} msg 队伍编号;boss 承伤;武将阵亡;当前回合 * @param {BackendSession} session * @returns * @memberof ComBattleHandler */ async action(msg: {teamCode: string, bossHurts: Array<{hid: number, dataId: number, hurtHp: number}>, killed: number[], curRnd: number, timegap: number, roundTime: number }, session: BackendSession) { let roleId = session.get('roleId'); let sid = session.get('sid'); let { teamCode, killed, bossHurts, curRnd, timegap = 0, roundTime = 0 } = msg; let teamStatus = getComTeamByCode(teamCode); if (!teamStatus || !teamStatus.roleIds || teamStatus.roleIds.indexOf(roleId) === -1) { checkTeamStatusAndSend(teamCode, roleId, sid); return resResult(STATUS.SUCCESS); } if (teamStatus.status !== COM_TEAM_STATUS.FIGHTING) { checkTeamStatusAndSend(teamCode, roleId, sid); return resResult(STATUS.SUCCESS); } for (let st of teamStatus.roleStatus) { if (st.roleId === roleId && (!st.heroes || st.heroes.length === 0)) { return resResult(STATUS.SUCCESS); } } // 重置总血量,计算真实伤害 const roleSt = teamStatus.findMemberByRoleId(roleId); if(!roleSt) return resResult(STATUS.SUCCESS); if(!roleSt.startActionTime || roleSt.startActionTime == 0) { // 第一次调用设置初始 roleSt.startActionTime = nowSeconds(); } if ((roundTime > 0 && roundTime < 200) || (timegap - (nowSeconds() - roleSt.startActionTime) > 5)) { // 前后端误差超过5秒拦截 return resResult(STATUS.SUCCESS, { isError: true, timegap: nowSeconds() - roleSt.startActionTime }); } let actBossHurts: {dataId: number, hurtHp: number}[] = []; let totalHurtHp = 0; teamStatus.bossHpArr.forEach(boss => { for (let bh of bossHurts) { if (boss.dataId === bh.dataId && bh.hurtHp < 0) { let deltaHp = Math.abs(bh.hurtHp); if (boss.curHp >= deltaHp) { actBossHurts.push({dataId: boss.dataId, hurtHp: deltaHp}); totalHurtHp = cal.add(totalHurtHp, deltaHp); boss.curHp = cal.sub(boss.curHp, deltaHp); } else if (boss.curHp > 0) { actBossHurts.push({dataId: boss.dataId, hurtHp: boss.curHp}); totalHurtHp = cal.add(totalHurtHp, boss.curHp); boss.curHp = 0; } if(bh.hid) { roleSt.addHeroDamage(bh.hid, deltaHp); } } } }); // 更新玩家武将阵亡情况,计算玩家总伤害 roleSt.totalDmg = cal.add(roleSt.totalDmg, totalHurtHp); if (killed && killed.length) { roleSt.killed = Array.from(new Set([...roleSt.killed, ...killed])); } teamStatus.curRnd = curRnd; teamStatus.bossCurHp = cal.sub(teamStatus.bossCurHp, totalHurtHp); if (teamStatus.bossCurHp < 0) teamStatus.bossCurHp = 0; // 推送本次行动情况 const roleStatus = teamStatus.roleStatus.map(st => {return {roleId: st.roleId, totalDmg: st.totalDmg, killed: st.killed}}); sendMessageToTeam(teamCode, PUSH_ROUTE.TEAMMATE_ACT, { teamCode, bossCurHp: teamStatus.bossCurHp, bossHpArr: teamStatus.bossHpArr, roleStatus, actRoleId: roleId, actBossHurts }); const result = await handleComBtlProgress(teamStatus); if (result && result.code !== 0) return result; return resResult(STATUS.SUCCESS, { isError: false, timegap: nowSeconds() - roleSt.startActionTime }); } // /** // * ! 客户端给服务器的战斗结束通知-deprecated // * @param msg // * @param session // */ // async battleEnd(msg: {teamCode: string, isSuccess: boolean}, session: BackendSession) { // let roleId = session.get('roleId'); // let { teamCode, isSuccess } = msg; // let teamStatus = this.teamMap.get(teamCode); // if (!teamStatus || !teamStatus.roleIds || teamStatus.roleIds.indexOf(roleId) === -1) return resResult(STATUS.COM_BATTLE_TEAM_INVALID); // if (isSuccess && teamStatus.bossCurHp <= 0) { // let team = await ComBattleTeamModel.updateResult(teamCode, roleId, isSuccess); // if (!team) return resResult(STATUS.COM_BATTLE_RESULT_ERR); // sendMessageToTeam(teamCode, PUSH_ROUTE.TEAM_COMPLETE, {teamCode, result: isSuccess}); // } else if (!isSuccess) { // let team = await ComBattleTeamModel.updateResult(teamCode, roleId, isSuccess); // if (!team) return resResult(STATUS.COM_BATTLE_RESULT_ERR); // if (team.status === COM_TEAM_STATUS.LOOSE) { // sendMessageToTeam(teamCode, PUSH_ROUTE.TEAM_COMPLETE, {teamCode, result: isSuccess}); // } else { // sendMessageToTeam(teamCode, PUSH_ROUTE.TEAMMATE_ACT, {teamCode, bossCurHp: teamStatus.bossCurHp, roleStatus: {roleId, battleStatus: 2}}); // } // } // return resResult(STATUS.SUCCESS, { bossCurHp: teamStatus.bossCurHp }); // } /** * @description 寻宝结算 * @param {{teamCode: string}} msg 寻宝队伍编号 * @param {BackendSession} session * @returns 掉落物品和玩家升级信息 * @memberof ComBattleHandler */ async comBattleEnd(msg: {teamCode: string}, session: BackendSession) { let roleId = session.get('roleId'); let roleName = session.get('roleName'); let sid = session.get('sid'); let ip = session.get('ip'); let serverId = session.get('serverId'); let { teamCode } = msg; let team = await ComBattleTeamModel.getTeamByCode(teamCode); clearRobotHurtTimer(team); if (!team || team.status !== COM_TEAM_STATUS.WIN) return resResult(STATUS.COM_BATTLE_REWARD_ERR); let { roleStatus, blueprtId, status, bossHpArr } = team; let roleSt = roleStatus.find(st => st && st.roleId === roleId); if(!roleSt) return resResult(STATUS.COM_BATTLE_REWARD_ERR); await ComBattleTeamModel.updateRewardSt(teamCode, roleId, true); const goods = await addItems(roleId, roleName, sid, roleSt.rewards, ITEM_CHANGE_REASON.COM_BATTLE_END); return resResult(STATUS.SUCCESS, { battleGoods: goods, teamInfo: {status, teamCode, roleStatus, bossHpArr} }); } async retreat(msg: {teamCode: string}, session: BackendSession) { let roleId = session.get('roleId'); let sid = session.get('sid'); let { teamCode } = msg; let teamStatus = getComTeamByCode(teamCode); if (!teamStatus || !teamStatus.roleIds || teamStatus.roleIds.indexOf(roleId) === -1) { checkTeamStatusAndSend(teamCode, roleId, sid); return resResult(STATUS.SUCCESS); } if(teamStatus.roleStatus.length > 1) { return resResult(STATUS.COM_BATTLE_RETREAT_ERR) } clearRobotHurtTimer(teamStatus); let team = await ComBattleTeamModel.syncTeamData({ teamCode, status: COM_TEAM_STATUS.LOOSE, roleStatus: teamStatus.roleStatus, bossHpArr: teamStatus.bossHpArr, endTime: Date.now() }, false, true); if (!team) return resResult(STATUS.COM_BATTLE_RESULT_ERR); sendMessageToTeam(teamCode, PUSH_ROUTE.TEAM_COMPLETE, { teamCode, result: false }); deleteComBattle(teamCode); clearComBattleRoute(redisClient(), teamCode); return resResult(STATUS.SUCCESS, { teamInfo: pick(team, ['status', 'teamCode', 'roleStatus', 'bossHpArr']) }); } async getComBtlStatus(msg: {teamCode: string}, session: BackendSession) { let { teamCode } = msg; let roleStatus: RoleStatus[], status: number, bossHpArr: BossHp[], timeout: boolean, endTime = 0; let memTeam = getComTeamByCode(teamCode); if (memTeam) { ({ roleStatus, status, bossHpArr, timeout, endTime } = memTeam); } else { let dbTeam = await ComBattleTeamModel.getTeamByCode(teamCode); if (!dbTeam) return resResult(STATUS.COM_BATTLE_TEAM_INVALID); ({ roleStatus, status, bossHpArr, timeout, endTime } = dbTeam); clearComBattleRoute(redisClient(), teamCode); } return resResult(STATUS.SUCCESS, { teamInfo: {status, teamCode, roleStatus, bossHpArr, timeout, endTime} }); } /** * @description 获取自己一段时间内的寻宝记录 * @param {{}} msg * @param {BackendSession} session * @returns * @memberof ComBattleHandler */ async getTeamRec(msg: {}, session: BackendSession) { let roleId = session.get('roleId'); let timef = getTimeFunD(); let teams = await ComBattleTeamModel.getTeamByRoleAndTime(roleId, null, timef.getBeforeDayWithHour(2)); if (!teams) return resResult(STATUS.COM_BATTLE_NO_RECENT_REC); return resResult(STATUS.SUCCESS, {teamInfos: teams}); } /** * @description 获取藏宝图数量 * @param {{ids: number[]}} msg 藏宝图Id数组,可选参数 * @param {BackendSession} session * @returns * @memberof ComBattleHandler */ async getBlueprtCount(msg: {ids: number[]}, session: BackendSession) { let roleId = session.get('roleId'); let { ids } = msg; let blueprts: ItemType[]; if (ids && ids.length) { blueprts = await ItemModel.findbyRoleAndIds(roleId, ids); } else { blueprts = await ItemModel.findByRoleAndType(roleId, CONSUME_TYPE.BLUEPRT); } return resResult(STATUS.SUCCESS, { blueprts }); } /** * @description 获取今日已助战次数 * @param {{}} msg * @param {BackendSession} session * @returns * @memberof ComBattleHandler */ async getAssistCnt(msg: {}, session: BackendSession) { let roleId = session.get('roleId'); let cnt = await getAllAssistCnt(roleId); return resResult(STATUS.SUCCESS, {cnt}); } /** * @description 获取寻宝可用次数,包含藏宝图数量和助战次数,分别用于创建和加入队伍 * @param {{}} msg 无参数 * @param {BackendSession} session * @returns * @memberof ComBattleHandler */ async getComBtlCnt(msg: {}, session: BackendSession) { let roleId = session.get('roleId'); let cnt = await getAllAssistCnt(roleId); const blueprts = await ItemModel.findByRoleAndType(roleId, CONSUME_TYPE.BLUEPRT); let capExtraCnt = await getCapExtraCnt(roleId); return resResult(STATUS.SUCCESS, { blueprts, assistCnt: cnt, capExtraCnt }); } async sendTeamMsg(msg: { teamCode: string, type: number, content: string, targetRoleId: string, targetMsgCode: string }, session: BackendSession) { let roleId = session.get('roleId'); let sid = session.get('sid'); let roleName = session.get('roleName'); let serverId = session.get('serverId'); const { teamCode, type, content, targetRoleId, targetMsgCode } = msg; const result = await pushComBtlTeamMsg(teamCode, roleId, roleName, type, MSG_SOURCE.TEAM_ROLE, content, targetRoleId, targetMsgCode); if (!result) { return resResult(STATUS.WRONG_PARMS); } // 任务 await checkTask(serverId, roleId, sid, TASK_TYPE.CHAT, { chatType: getChannelType(CHANNEL_PREFIX.TEAM) }); return resResult(STATUS.SUCCESS); } /** * @description 一键邀请,自动在世界频道发送消息 * @param {{ teamCode: string }} msg * @param {BackendSession} session * @memberof ComBattleHandler */ async autoInvite(msg: { teamCode: string }, session: BackendSession) { let roleId = session.get('roleId'); let roleName = session.get('roleName'); let guildCode = session.get('guildCode'); if (!guildCode) return resResult(STATUS.COM_BATTLE_INVITE_GUILD_ERR); const { teamCode } = msg; let teamStatus = getComTeamByCode(teamCode); if(!teamStatus) return resResult(STATUS.WRONG_PARMS); await pushTeamInviteMsg(roleId, roleName, guildCode, teamCode, teamStatus.blueprtId, teamStatus.lv, teamStatus.ceLimit); await ComBattleTeamModel.invite(teamCode, guildCode); return resResult(STATUS.SUCCESS); } /** * @description 邀请队友 * @param {{ teamCode: string, targetRoleId: string }} msg * @param {BackendSession} session * @memberof ComBattleHandler */ async inviteTeammate(msg: { teamCode: string, targetRoleId: string }, session: BackendSession) { let roleId = session.get('roleId'); let roleName = session.get('roleName'); let guildCode = session.get('guildCode'); const { teamCode, targetRoleId } = msg; let teamStatus = getComTeamByCode(teamCode); if(!teamStatus) return resResult(STATUS.WRONG_PARMS); let role = await RoleModel.findByRoleId(targetRoleId, 'lv'); if(comBtlLvInvalid(role.lv, [teamStatus.lv])) { return resResult(STATUS.COM_BATTLE_INVITE_LV_NOT_ENOUGH); } if (role.topLineupCe < teamStatus.ceLimit) { return resResult(STATUS.COM_BATTLE_CE_LIMIT); } await ComBattleTeamModel.invite(teamCode, targetRoleId); let relation = await getFriendRelationType(roleId, targetRoleId); const msgData = await pushFriendTeamInviteMsg(roleId, roleName, teamCode, teamStatus.blueprtId, targetRoleId, relation); if (!msgData) return resResult(STATUS.WRONG_PARMS); const roleInfo = await getSimpleRoleInfo(targetRoleId); let index = teamStatus.blacklist.indexOf(targetRoleId); if(index != -1) { teamStatus.blacklist.splice(index, 1); } return resResult(STATUS.SUCCESS, {...msgData, roleInfo}); } /** * @description 最近组队过的队友列表 * @param {} msg * @param {BackendSession} session */ async getRecentTeammates(msg: { }, session: BackendSession) { let roleId = session.get('roleId'); const teams = await ComBattleTeamModel.getTeamByRoleAndTime(roleId, null, getZeroPointD()); let roleIdList = new Array(); for(let { roleIds } of teams) { for(let r of roleIds) { if(r != roleId) roleIdList.push(r); } } const roles = await RoleModel.findByRoleIds(roleIdList, ROLE_SELECT.SHOW_SIMPLE, true); let result = new Array(); for(let role of roles) { if(role.quitTime == role.loginTime) { let r = new FriendParams(role); result.push(r); } } return resResult(STATUS.SUCCESS, { list: result }); } async getTeamInvitation(msg: { }, session: BackendSession) { let roleId = session.get('roleId'); let guildCode = session.get('guildCode'); let role = await RoleModel.findByRoleId(roleId, 'lv topLineupCe'); const assistCnt = await getAllAssistCnt(roleId); let { minLv, maxLv } = getComBtlLvByPlayerLv(role.lv); let refreshTime = nowSeconds() - INFO_WINDOW.TEAM_INFORMATION_TIME; const invitations = await ComBattleTeamModel.findInvitations(roleId, guildCode, minLv, maxLv, role.topLineupCe, refreshTime, INFO_WINDOW.TEAM_VIEW); let roleIds = invitations.map(cur => cur.capId); let roles = await RoleModel.findByRoleIds(roleIds); let myFriendRelation = await FriendRelationModel.findFriendByRole(roleId, POPULATE_TYPE.NOT); const teams: ComBattleInvitation[] = []; for(let invitation of invitations) { let leader = roles.find(role => role.roleId == invitation.capId); if(leader) { let leaderParam = new FriendParams(leader); let type = getRecommendType(myFriendRelation, roleId, leader.roleId); leaderParam.setType(type); let serverName = await getServerName(leader.serverId); leaderParam.setServerName(leader.serverId, serverName); teams.push(new ComBattleInvitation(invitation, leaderParam)); } } return resResult(STATUS.SUCCESS, { teams, assistCnt }); } // ! 测试接口 /** * 修改玩家的等级-方便测试组队 * 寻宝创建队伍 - 会先调用这个接口 * @param {{ targetLv: number }} msg * @param {BackendSession} session * @return {*} * @memberof ComBattleHandler */ async debugSetRoleLv(msg: { magicWord: string, targetLv: number }, session: BackendSession) { const roleId = session.get('roleId'); const { targetLv, magicWord } = msg; if (magicWord !== DEBUG_MAGIC_WORD) { return resResult(STATUS.TOKEN_ERR); } let role = await RoleModel.updateRoleInfo(roleId, { lv: targetLv }); return resResult(STATUS.SUCCESS, { lv: role.lv, roleId }); } }