import { MemComBtlTeam } from './../domain/battleField/ComBattleTeamField'; import { ItemModel } from './../db/Item'; import { IT_TYPE } from './../consts'; import { COM_BTL_QUALITY } from './../consts/constModules/itemConst'; import { FriendRelationModel } from './../db/FriendRelation'; import { RoleModel, RoleType } from './../db/Role'; import { EquipPrintDropType, EquipPrintDropModel } from './../db/EquipPrintDrop'; import { FriendPointModel } from './../db/FriendPoint'; import { STATUS } from './../consts/statusCode'; import { COM_TEAM_STATUS, FRIEND_DROP_TYPE, COM_BTL_CONST, FRIEND_DROP_MAX } from './../consts'; import { RoleStatus, ComBattleTeamModel, ComBattleTeamType } from './../db/ComBattleTeam'; import { getRandEelm, getRandValue, resResult, ratioReward, getRandValueByMinMax, getRandEelmWithWeight, getRobotInfo } from "../pubUtils/util"; import { getRandRobot, transBossHpArr } from "./battleService"; import { difference, omit } from 'underscore'; import { Channel, ChannelService } from 'pinus'; import { TREASURE, EXTERIOR } from '../pubUtils/dicParam'; import { decreaseItems } from './rewardService'; import { getFriendLvAdd } from './friendService'; import { getRoleIds } from '../pubUtils/friendUtil'; import { getTeamSearchByQuality, rmRoleFromQueue } from './redisService'; import { addUserToChannel } from './roleService'; import { ChannelUser } from '../domain/ChannelUser'; import { checkActivityTask, checkTaskInComBattleEnd } from './taskService'; import { getRewardByBlueprtId, gameData, comBtlRangeByLv, getBossHpByBlueprtId } from '../pubUtils/data'; import { getFriendPointObject } from '../pubUtils/itemUtils'; import { DicWar } from '../pubUtils/dictionary/DicWar'; import { getZeroPointD } from '../pubUtils/timeUtil'; /** * 在给定的品质列表中随机返回一定数量的藏宝图Id * @param qualityArr 品质数组,在所有给定品质的藏宝图中筛选1 * @param cnt 返回藏宝图数量 */ export function getRandBlueprtId(qualityArr: number[], cnt = 1) { if (!qualityArr || !qualityArr.length) return null; let blueprtIdArr: number[] = []; for (let q of qualityArr) { blueprtIdArr = blueprtIdArr.concat(gameData.blueprt.get(q)); } if (blueprtIdArr.length === 0) return null; const res = getRandEelm(blueprtIdArr, cnt); return res; } export function getRandComBtlRobots(topLineupCe: number, ceLimit: number, lv: number, cnt: number) { let robotHeroes = getRandRobot(cnt); // 随机几个阵容 let robotInfos = []; // 随机几个机器人信息 for (let i = 0; i < cnt; i++) { robotInfos.push(getRobotInfo()); } // 创建并添加机器人 let robotStArr = [], robotIdArr = []; if (robotHeroes && robotInfos && robotHeroes.length && robotInfos.length && robotInfos.length === robotHeroes.length) { robotHeroes.forEach((robot, idx) => { let robotCe = 0; if (ceLimit) { robotCe = getRandValueByMinMax(ceLimit * COM_BTL_CONST.ROBOT_CE_LIMIT_MIN, ceLimit * COM_BTL_CONST.ROBOT_CE_LIMIT_MAX, 0); } else { robotCe = getRandValue(topLineupCe || 0, COM_BTL_CONST.ROBOT_CE_RATIO, 0); } const robotLv = getRandValue(lv, COM_BTL_CONST.ROBOT_CE_RATIO, 0); // const imgHid = robot[Math.floor(Math.random() * robot.length)]; const { robotRoleId, robotRoleName } = robotInfos[idx]; let robotStatus = new RoleStatus(robotRoleId, robotRoleName, false, false, EXTERIOR.EXTERIOR_FACE, EXTERIOR.EXTERIOR_FACECASE, EXTERIOR.EXTERIOR_APPEARANCE, robotCe, robotLv, robot, true); robotStArr.push(robotStatus); robotIdArr.push(robotRoleId); }); } return { robotStArr, robotIdArr } } export function checkComBattleResult(teamStatus) { if (teamStatus.bossCurHp === 0) { return COM_TEAM_STATUS.WIN; } else { let allPlayerKilled = true; let robotRestHurt = 0; // 看看是否还有活人 teamStatus.roleStatus.forEach(st => { // 设置了阵容,且阵容人数和阵亡人数一样,说明玩家战败 if (!st.isRobot && st.heroes && ((st.heroes.length > 0 && st.killed.length < st.heroes.length) || st.heroes.length === 0)) { allPlayerKilled = false } }); // 没有活人的话看看还有没有机器人没打完伤害 if (allPlayerKilled && teamStatus.curRnd < COM_BTL_CONST.ROBOT_RND_LMT) { teamStatus.roleStatus.forEach(st => { if (st.isRobot) { const deltaRnd = COM_BTL_CONST.ROBOT_RND_LMT - teamStatus.curRnd; let hurtHp = getRandValue(teamStatus.bossHp / COM_BTL_CONST.ROBOT_RND_LMT * COM_BTL_CONST.ROBOT_HURT_RATIO, COM_BTL_CONST.ROBOT_HURT_CH_RATIO, 0) * deltaRnd; // 1 个机器人对 boss 造成的总伤害 robotRestHurt += hurtHp; } }) } if (allPlayerKilled) { // 没有活人且机器人剩余伤害打不死 boss,战败 if (teamStatus.bossCurHp > robotRestHurt) { return COM_TEAM_STATUS.LOOSE; } else { return COM_TEAM_STATUS.WIN; } } } return COM_TEAM_STATUS.FIGHTING; } /** * ! deprecated * @description 计算寻宝结算 * @export * @param {string} roleId * @param {string} battleCode * @returns */ export async function checkComBattleDrop(roleId: string, battleCode: string) { let team = await ComBattleTeamModel.getTeamByRoleAndBattleCode(roleId, battleCode); if (team.status !== COM_TEAM_STATUS.WIN) return { status: -1, resResult: resResult(STATUS.COM_BATTLE_REWARD_ERR) }; let roleSt = null; team.roleStatus.forEach(st => { if (st.roleId === roleId) { roleSt = st; } }); if (!roleSt || roleSt.gotReward) return { status: -1, resResult: resResult(STATUS.COM_BATTLE_REWARD_ERR) }; let fixReward = getRewardByBlueprtId(team.blueprtId); if (!roleSt.isCap) { if (roleSt.isFrd) { fixReward = []; } else { fixReward = ratioReward(fixReward, COM_BTL_CONST.ASSIST_REWARD_RATIO); } } await ComBattleTeamModel.updateRewardSt(team.teamCode, roleId, true); return { status: 0, fixReward }; } export function clearComBtlTimer(teamCode: string, timerMap: Map) { let timer = timerMap.get(teamCode); if (timer) { clearTimeout(timer); } } export function setComBtlTimer(teamCode: string, timer: NodeJS.Timer, timerMap: Map) { let preTimer = timerMap.get(teamCode); if (preTimer) { clearTimeout(preTimer); } timerMap.set(teamCode, timer); } export async function getRealReward(blueprtId: number, roleSt: RoleStatus) { let fixReward = getRewardByBlueprtId(blueprtId); if (!roleSt.isCap) { if (roleSt.isFrd) { let frdPointRec = await FriendPointModel.getFrdPointRecToday(roleSt.roleId, FRIEND_DROP_TYPE.COM_BATTLE); if (!frdPointRec || frdPointRec.cnt <= FRIEND_DROP_MAX.COM_BTL - COM_BTL_CONST.FRDCNT_DROP) { fixReward = [getFriendPointObject(COM_BTL_CONST.FRDCNT_DROP)]; } else if (frdPointRec.cnt < FRIEND_DROP_MAX.COM_BTL) { fixReward = [getFriendPointObject(COM_BTL_CONST.FRDCNT_DROP - frdPointRec.cnt)]; } else { fixReward = []; } } else { fixReward = ratioReward(fixReward, COM_BTL_CONST.ASSIST_REWARD_RATIO); } } return fixReward; } export async function getAllAssistCnt(roleId: string) { let cntMap = await getAssistTimesByQuality(roleId); let cnt = []; for (let i = 0; i < COM_BTL_QUALITY.length; ++i) { cnt[i] = cntMap.get(i + 1) || 0; } return cnt; } export async function getAssistTimesByQuality(roleId: string, qualityArr?: number[]) { let teams = await ComBattleTeamModel.getAssistTeamsByTime(roleId, qualityArr, getZeroPointD(), true); let cntMap = new Map(); teams.forEach(team => { if (team && team.quality && team.roleStatus) { for (let st of team.roleStatus) { if (st.roleId !== roleId || st.isFrd) { continue; } let cnt = cntMap.get(team.quality) || 0; cntMap.set(team.quality, cnt + 1) } } }); return cntMap; } export async function getFrd(roleId: string, quality: number) { let isFrd = false; let assistTimes = await getAssistTimesByQuality(roleId, [quality]); let assistTime = assistTimes.get(quality); let { assistanceTime } = gameData.xunbao.get(quality); if (assistTime >= assistanceTime) isFrd = true; return isFrd; } /** * @description 更新队伍状态 * @export * @param {number} preStatus 更新前状态,作为筛选条件 * @param {number} newStatus 要设置的新状态 */ export async function updateTeamStatus(preStatus: number, newStatus: number) { if (preStatus === newStatus) return; await ComBattleTeamModel.updateStatusByStatus(preStatus, newStatus); } /** * @description 计算机器人每次对 boss 造成的伤害 * @param {number} bossHp boss 总血量 */ function robotEachHurt(bossHp: number, bossCnt: number) { const robotTotalHurt = bossHp * COM_BTL_CONST.ROBOT_HURT_RATIO; const robotAverageHurt = robotTotalHurt / COM_BTL_CONST.ROBOT_ACT_LMT / bossCnt; return getRandValue(robotAverageHurt, COM_BTL_CONST.ROBOT_HURT_CH_RATIO, 0) } /** * @description 更新机器人阵亡情况 * @param {number} bossHp * @param {RoleStatus} roleSt */ function updateRobotKilled(bossHp: number, roleSt: RoleStatus) { const robotTotalHurt = bossHp * COM_BTL_CONST.ROBOT_HURT_RATIO; // 让阵亡人数和打出伤害的进度同步,比如有 5 个武将,每打出目标总伤害的 1 / 5 应该增加一个阵亡武将 const dmgProgress = Math.floor(roleSt.totalDmg / (robotTotalHurt / roleSt.heroes.length)); if (dmgProgress > roleSt.killed.length && dmgProgress <= roleSt.heroes.length) { const newKilledCnt = dmgProgress - roleSt.killed.length; const aliveHeroes = difference(roleSt.heroes, roleSt.killed); const newKilledHeroes = getRandEelm(aliveHeroes, newKilledCnt); roleSt.killed = roleSt.killed.concat(newKilledHeroes); } } export async function handleComBtlProgress(teamStatus: MemComBtlTeam, robotHurtTimer: Map, teamMap: Map, channel: Channel) { const { teamCode, roleIds, capId, quality } = teamStatus; // 判断战斗是否结束 let battleSt = checkComBattleResult(teamStatus); teamStatus.status = battleSt; if (battleSt === COM_TEAM_STATUS.WIN || battleSt === COM_TEAM_STATUS.LOOSE) { let result = battleSt === COM_TEAM_STATUS.WIN; if (result) { teamStatus.bossHpArr.forEach(bs => { bs.curHp = 0; }); for (let st of teamStatus.roleStatus) { st.fixReward = await getRealReward(teamStatus.blueprtId, st); }; } let team = await ComBattleTeamModel.syncTeamData({ teamCode, status: battleSt, roleStatus: teamStatus.roleStatus, bossHpArr: teamStatus.bossHpArr }); if (!team) return resResult(STATUS.COM_BATTLE_RESULT_ERR); // 战斗胜利队长扣减藏宝图 if (result && teamStatus.capId != 'robot') { const { sid } = channel.getMember(teamStatus.capId); let res = await decreaseItems(teamStatus.capId, sid, [{ id: teamStatus.blueprtId, count: 1 }]); if (res === true) return resResult(STATUS.COM_BATTLE_BLUEPRT_NOT_ENOUGH); } clearRobotHurtTimer(teamStatus, robotHurtTimer); channel.pushMessage('onTeamComplete', resResult(STATUS.SUCCESS, { teamCode, result })); // 任务 await checkTaskInComBattleEnd(teamStatus.roleStatus, capId, quality); teamMap.delete(teamCode); } } /** * @description 更新机器人对 boss 的伤害 * @export * @param {*} teamStatus 要更新的队伍信息 * @param {RoleStatus} roleSt 要更新的玩家信息 */ export function updateRobotHurt(teamStatus: MemComBtlTeam, roleSt: RoleStatus, channel: Channel) { // 机器人的伤害为:boss 血量的一定比例,平均到一定回合数内,再平均到每个敌军,上下浮动一定比例 let eachHurtHp = robotEachHurt(teamStatus.bossHp, teamStatus.bossHpArr.length); let robotTotalHurt = 0; let actBossHurts = []; for (let boss of teamStatus.bossHpArr) { if (boss.curHp === 0) continue; if (boss.curHp >= eachHurtHp) { actBossHurts.push({ dataId: boss.dataId, hurtHp: eachHurtHp }); robotTotalHurt += eachHurtHp; boss.curHp -= eachHurtHp; } else if (boss.curHp > 0) { // 丢弃溢出的伤害 actBossHurts.push({ dataId: boss.dataId, hurtHp: boss.curHp }); robotTotalHurt += boss.curHp; boss.curHp = 0; } break; } teamStatus.bossCurHp -= robotTotalHurt; roleSt.totalDmg += robotTotalHurt; updateRobotKilled(teamStatus.bossHp, roleSt); channel.pushMessage('onTeammateAct', resResult(STATUS.SUCCESS, { teamCode: teamStatus.teamCode, bossCurHp: teamStatus.bossCurHp, bossHpArr: teamStatus.bossHpArr, roleStatus: teamStatus.roleStatus, actRoleId: roleSt.roleId, actBossHurts })); } /** * @description 按一定时间间隔刷新机器人伤害 * @export * @param {*} teamStatus * @param {RoleStatus} roleSt * @param {number} interval * @param {Channel} channel * @param {Map} robotHurtTimer */ export function updateRobotHurtByTime(teamStatus: MemComBtlTeam, roleSt: RoleStatus, interval: number, channel: Channel, robotHurtTimer: Map, teamMap: Map) { const timerKey = `${teamStatus.teamCode}_${roleSt.roleId}`; const robotTimer = setInterval(() => { const robotTotalHurt = teamStatus.bossHp * COM_BTL_CONST.ROBOT_HURT_RATIO; if (roleSt.totalDmg < robotTotalHurt && teamStatus.bossCurHp > 0 && teamMap.has(teamStatus.teamCode)) { updateRobotHurt(teamStatus, roleSt, channel); handleComBtlProgress(teamStatus, robotHurtTimer, teamMap, channel); } else { clearInterval(robotTimer); } }, interval * 1000); if (!robotHurtTimer.has(timerKey)) { robotHurtTimer.set(timerKey, robotTimer); } } /** * @description 清理机器人伤害的 timer * @export * @param {*} teamStatus 寻宝队伍状态 * @param {Map} robotHurtTimer */ export function clearRobotHurtTimer(teamStatus, robotHurtTimer: Map) { teamStatus.roleStatus.forEach(st => { const timerKey = `${teamStatus.teamCode}_${st.roleId}`; if (st.isRobot === true && robotHurtTimer.has(timerKey)) { clearInterval(robotHurtTimer.get(timerKey)); } }); } /** * @description 检查寻宝等级是否合法 * @export * @param {number} lv 玩家等级 * @param {number} lvRange 藏宝图等级范围 * @returns */ export function comBtlLvInvalid(lv: number, lvRange: number) { const lvs = gameData.comBtlLvRange.get(lvRange); if (!lvs) return true; const minLv = lvs[0]; return lv < minLv; } export async function dismissTeam(teamStatus: MemComBtlTeam, teamMap: Map, roleId: string, teamDisTimer: Map, channel) { const { teamCode } = teamStatus; if (!teamStatus || !teamStatus.roleIds || teamStatus.roleIds.indexOf(roleId) === -1) return resResult(STATUS.COM_BATTLE_TEAM_INVALID); if (teamStatus.status !== COM_TEAM_STATUS.DEFAULT) return resResult(STATUS.COM_BATTLE_DISSMISS_ERR); if (roleId !== teamStatus.capId) return resResult(STATUS.COM_BATTLE_CAP_ONLY); let team = await ComBattleTeamModel.removeTeam(teamCode); if (!team) return resResult(STATUS.COM_BATTLE_DISSMISS_ERR); let rmSt = teamMap.delete(teamCode); if (!rmSt) return resResult(STATUS.COM_BATTLE_DISSMISS_ERR); channel.pushMessage('onTeamDismiss', resResult(STATUS.SUCCESS, { teamCode })); channel.destroy(); clearComBtlTimer(teamCode, teamDisTimer); // 队伍解散停止解散计时 return resResult(STATUS.SUCCESS); } export function setDismissTimer(teamStatus: MemComBtlTeam, teamMap: Map, roleId: string, teamDisTimer: Map, channel) { if (teamIsFullToStart(teamStatus)) { clearComBtlTimer(teamStatus.teamCode, teamDisTimer); let timer = setTimeout(async () => { await dismissTeam(teamStatus, teamMap, roleId, teamDisTimer, channel); }, COM_BTL_CONST.CAP_START_TIME); teamDisTimer.set(teamStatus.teamCode, timer); } } function initEquipPrintDropData(roleId: string, roleName: string) { let result: EquipPrintDropType = new EquipPrintDropModel().toJSON(); result.roleId = roleId; result.roleName = roleName; result.capTarget = getRandValueByMinMax(1, TREASURE.CAPTAIN_DROP, 0); result.teammateTarget = getRandValueByMinMax(1, TREASURE.TEAMMATE_DROP, 0); return result; } function incEquipPrintDropData(roleSt: RoleStatus, dropRec: EquipPrintDropType) { const { isCap } = roleSt; let dropResult = false; if (isCap === true) { dropRec.capAll += 1; dropRec.capCur += 1; if (dropRec.capCur > TREASURE.CAPTAIN_DROP) { dropRec.capCur = 1; dropRec.capTarget = getRandValueByMinMax(1, TREASURE.CAPTAIN_DROP, 0); } if (dropRec.capCur === dropRec.capTarget) { dropResult = true; dropRec.capDropAll += 1; } } else { dropRec.teammateAll += 1; dropRec.teammateCur += 1; if (dropRec.teammateCur > TREASURE.TEAMMATE_DROP) { dropRec.teammateCur = 1; dropRec.teammateTarget = getRandValueByMinMax(1, TREASURE.TEAMMATE_DROP, 0); } if (dropRec.teammateCur === dropRec.teammateTarget) { dropResult = true; dropRec.teammateDropAll += 1; } } return dropResult; } export async function incEquipPrintDrop(roleSt: RoleStatus) { const { roleId, roleName } = roleSt; let dropRec = await EquipPrintDropModel.getByRoleId(roleId); if (!dropRec) { dropRec = await EquipPrintDropModel.createDoc(initEquipPrintDropData(roleId, roleName)); } const dropResult = incEquipPrintDropData(roleSt, dropRec); dropRec = await EquipPrintDropModel.updateDoc(roleId, omit(dropRec, ['_id', 'createdAt', 'updatedAt'])); return { dropResult, dropRec }; } export function randEquipPrintId(warInfo: DicWar) { if (!warInfo || !warInfo.jackpotReward) { return null; } const result = getRandEelmWithWeight(warInfo.jackpotReward); if (!result || !result.dic || !result.dic.id) { return null; } return result.dic.id; } /** * 按照好友关系,新增加成 * * @param {RoleStatus[]} roleStatus 队伍内的所有队友 */ export async function getComBattleFriendAdd(roleStatus: RoleStatus[]) { let hasAdd: string[] = []; //roleIds for (let myRoleStatus of roleStatus) { for (let rs of roleStatus) { if (myRoleStatus.roleId == rs.roleId) continue; if (myRoleStatus.isRobot || rs.isRobot) continue; let { roleIds } = getRoleIds([myRoleStatus.roleId, rs.roleId]); if (hasAdd.includes(roleIds)) continue; let add = await getFriendLvAdd(myRoleStatus.roleId, rs.roleId); rs.addFrdRatio(add); myRoleStatus.addFrdRatio(add); hasAdd.push(roleIds); } } } /** * @description 检查玩家是否符合加入队伍的条件 * @param {Partial} roleInfo 要加入玩家的信息 * @param {string} roleId 要加入玩家的 id * @param {string[]} roleIds 队伍中当前玩家列表 * @param {number} ceLimit * @returns */ async function teammateValid(roleInfo: Partial, roleId: string, roleIds: string[], ceLimit: number) { if (!roleInfo || roleIds.indexOf(roleId) !== -1) return false; const isBlack = await teammateInBlackList(roleId, roleIds); if (isBlack) return false; let { topLineupCe } = roleInfo; if (topLineupCe < ceLimit) return false; return true; } /** * @description 将用户信息转换成寻宝组队所需的 RoleStatus * @export * @param {string} roleId 要加入玩家的信息 * @param {string[]} roleIds 队伍中当前玩家列表 * @param {number} ceLimit * @param {number} quality * @returns */ export async function getValidTeammateRoleSt(roleId: string, roleIds: string[], ceLimit: number, quality: number) { let roleInfo = await RoleModel.findByRoleId(roleId, null, true); let { roleName, head = EXTERIOR.EXTERIOR_FACE, frame = EXTERIOR.EXTERIOR_FACECASE, spine = EXTERIOR.EXTERIOR_APPEARANCE, topLineupCe, lv } = roleInfo; const valid = await teammateValid(roleInfo, roleId, roleIds, ceLimit); if (!valid) return null; let isFrd = await getFrd(roleId, quality); const result = new RoleStatus(roleId, roleName, false, isFrd, head, frame, spine, topLineupCe, lv); return result; } /** * @description 检查队伍中是否有人互为黑名单 * @export * @param {string} roleId 要加入队伍的玩家 id * @param {string[]} roleIds 队伍中已有的玩家 id * @returns */ export async function teammateInBlackList(roleId: string, roleIds: string[]) { for (let teammateRoleId of roleIds) { const isBlack = await FriendRelationModel.isInBlackList(roleId, teammateRoleId); if (isBlack === true) return true; } return false; } export function blueprtIdValid(id: number) { const goodData = gameData.goods.get(id); return goodData && goodData.itid === IT_TYPE.BLUEPRT && COM_BTL_QUALITY.indexOf(goodData.quality) !== -1; } /** * @description 创建 ComBattleTeam 的数据结构 * @export * @param {string} teamCode * @param {boolean} pub * @param {number} blueprtId * @param {number} status * @param {string} capId * @param {number} ceLimit * @param {number} bossHp * @param {number} quality * @param {BossHp[]} bossHpArr * @returns */ export function createComTeamData(teamCode: string, pub: boolean, blueprtId: number, capId: string, ceLimit: number) { const { lvLimited, quality } = gameData.goods.get(blueprtId); const lvRange = comBtlRangeByLv(lvLimited); const { bossHpSum, bossHpArr } = getBossHpByBlueprtId(blueprtId); const curRnd = 0; const roleCnt = 1; const timeout = false; const bossCurHp = bossHpSum; const bossHp = bossHpSum; const status = COM_TEAM_STATUS.DEFAULT; return { teamCode, pub, blueprtId, status, capId, ceLimit, bossHp, bossCurHp, quality, bossHpArr: transBossHpArr(bossHpArr), curRnd, roleCnt, timeout, lvRange }; } /** * @description 检查是否有足够的藏宝图,未结束的战斗也占用一张藏宝图 * @export * @param {string} roleId * @param {number} blueprtId * @returns */ export async function hasEnoughBlueprt(roleId: string, blueprtId: number) { let blueprt = await ItemModel.findbyRoleAndGidAndCount(roleId, blueprtId, 1); if (!blueprt) return false; // 检查是否有已创建未结束的寻宝,预先占用一张藏宝图 let teams = await ComBattleTeamModel.getBlueprtInUse(roleId, COM_TEAM_STATUS.FIGHTING, blueprtId); if (teams && blueprt.count <= teams.length) return false; return true; } /** * @description 将玩家加入到队伍数据结构中,需将玩家信息做转化 * @export * @param {MemComBtlTeam} comTeam 队伍数据结构 * @param {RoleType} roleInfo 原始玩家信息 * @param {boolean} isCap * @param {boolean} isFrd */ export function addRoleToTeam(comTeam: MemComBtlTeam, roleInfo: RoleType, isCap: boolean, isFrd: boolean) { const { roleId, roleName, head = EXTERIOR.EXTERIOR_FACE, frame = EXTERIOR.EXTERIOR_FACECASE, spine = EXTERIOR.EXTERIOR_APPEARANCE, lv } = roleInfo; let { topLineupCe = 1000 } = roleInfo; const roleSt = new RoleStatus(roleId, roleName, isCap, isFrd, head, frame, spine, topLineupCe, lv); addRoleStToTeam(comTeam, roleSt); } /** * @description 将玩家加入到队伍数据结构中 * @export * @param {MemComBtlTeam} comTeam 队伍数据结构 * @param {RoleStatus} roleSt 要加入的玩家数据 */ export function addRoleStToTeam(comTeam: MemComBtlTeam, roleSt: RoleStatus) { const { roleStatus = [], roleIds = [] } = comTeam; comTeam.roleStatus = [roleSt, ...roleStatus]; comTeam.roleIds = [roleSt.roleId, ...roleIds]; } /** * @description 将符合要求的用户匹配到队伍中 * @export * @param {MemComBtlTeam} comTeam 队伍数据 * @param {string} sid 当前用户 sid * @returns */ export async function addValidSearchingRoles(comTeam: MemComBtlTeam, channelService: ChannelService) { const { quality, lvLimited } = gameData.goods.get(comTeam.blueprtId); let teammates = await getTeamSearchByQuality(quality, comBtlRangeByLv(lvLimited)); if (teammates && teammates.length) { for (let teammate of teammates) { const { roleId: teammateRoleId, sid } = teammate; const st = await getValidTeammateRoleSt(teammateRoleId, comTeam.roleIds, comTeam.ceLimit, quality); if (!st) continue; await rmRoleFromQueue(teammateRoleId, sid, COM_BTL_QUALITY, null); // 匹配成功后删除redis中该用户的匹配记录 addRoleStToTeam(comTeam, st); const channel = channelService.getChannel(comTeam.teamCode, false); addUserToChannel(channel, new ChannelUser(teammateRoleId, sid)); } channelService.pushMessageByUids('onTeamJoin', resResult(STATUS.SUCCESS, { teamInfo: comTeam }), teammates.map(t => { return { uid: t.roleId, sid: t.sid } })); } } /** * @description 判断队伍是否处于可加入状态 * @export * @param {MemComBtlTeam} comTeam * @param {string} [roleId=''] 要加入的玩家 id * @returns */ export function validToJoin(comTeam: MemComBtlTeam, roleId = '') { return comTeam && comTeam.roleIds && comTeam.status === COM_TEAM_STATUS.DEFAULT && comTeam.roleIds.length < 3 && comTeam.roleIds.indexOf(roleId) === -1; } /** * @description 判断队伍是否处于可满员状态 * @export * @param {MemComBtlTeam} comTeam * @returns */ export function teamIsFullToStart(comTeam: MemComBtlTeam) { return comTeam && comTeam.roleIds && comTeam.status === COM_TEAM_STATUS.DEFAULT && comTeam.roleIds.length === 3; } /** * @description 添加指定数量的机器人到队伍 * @export * @param {MemComBtlTeam} comTeam * @param {number} roleCe 真实玩家战力,用来做机器人战力基准 * @param {number} roleLv 真实玩家等级,用来做机器人等级基准 * @param {number} [count=1] */ export async function addRobotsToTeam(comTeam: MemComBtlTeam, roleId: string, roleCe: number, roleLv: number, teamMap: Map, teamDisTimer: Map, channel: Channel, count = 1) { const { teamCode } = comTeam; const { robotStArr, robotIdArr } = getRandComBtlRobots(roleCe, comTeam.ceLimit, roleLv, count); for (let st of robotStArr) { addRoleStToTeam(comTeam, st); await ComBattleTeamModel.addRole(teamCode, st); } channel.pushMessage('onTeamJoin', resResult(STATUS.SUCCESS, { teamInfo: comTeam })); setDismissTimer(comTeam, teamMap, roleId, teamDisTimer, channel); } /** * @description 在不同的时间加入机器人 * @export * @param {MemComBtlTeam} comTeam * @param {RoleType} roleInfo * @param {Map} teamMap * @param {Map} teamDisTimer * @param {Channel} channel */ export async function addRobotsLater(comTeam: MemComBtlTeam, roleInfo: RoleType, teamMap: Map, teamDisTimer: Map, channel: Channel) { const teamStatus = comTeam; const { teamCode } = teamStatus; const { roleId, lv } = roleInfo; let { topLineupCe = 1000 } = roleInfo; if (validToJoin(teamStatus)) { const robotCnt = 3 - teamStatus.roleIds.length; for (let robotIdx = 0; robotIdx < robotCnt; robotIdx++) { const joinTime = getRandValueByMinMax(COM_BTL_CONST.MIN_CAP_TIME, COM_BTL_CONST.MAX_CAP_TIME, 0); setTimeout(async () => { const curTeamStatus = teamMap.get(teamCode); if (validToJoin(curTeamStatus)) { await addRobotsToTeam(curTeamStatus, roleId, topLineupCe, lv, teamMap, teamDisTimer, channel, 1); } }, joinTime); } } } /** * @description 从数组里找出第一个不在黑名单的队伍 * @export * @param {ComBattleTeamType[]} teams 队伍数组 * @param {string} roleId 要加入的玩家 id * @returns */ export async function oneTeamNotInBlack(teams: ComBattleTeamType[], roleId: string) { for (let team of teams) { let { roleIds } = team; const inBlackList = await teammateInBlackList(roleId, roleIds); if (!inBlackList) { return team; } } return null; }