Files
ZYZ/game-server/app/services/comBattleService.ts
2021-08-17 19:35:44 +08:00

736 lines
30 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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, pinus } from 'pinus';
import { TREASURE, EXTERIOR } from '../pubUtils/dicParam';
import { addItems, decreaseItems, handleCost } 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, getBossHpByBlueprtId } from '../pubUtils/data';
import { getFriendPointObject } from '../pubUtils/itemUtils';
import { DicWar } from '../pubUtils/dictionary/DicWar';
import { getZeroPointD } from '../pubUtils/timeUtil';
import { dispatch } from '../util/dispatcher';
/**
* 在给定的品质列表中随机返回一定数量的藏宝图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: { robotRoleName: string, robotRoleId: string }[] = []; // 随机几个机器人信息
for (let i = 0; i < cnt; i++) {
robotInfos.push(getRobotInfo());
}
// 创建并添加机器人
let robotStArr: RoleStatus[] = [], robotIdArr: string[] = [];
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);
}
let robotLv = getRandValue(lv, COM_BTL_CONST.ROBOT_CE_RATIO, 0);
if(robotLv > gameData.maxPlayerLv.max) robotLv = gameData.maxPlayerLv.max;
// 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.battleStatus != 2 && 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, teammateReward } = getRewardByBlueprtId(team.blueprtId);
if (!roleSt.isCap) {
if (roleSt.isFrd) {
fixReward = [];
} else {
fixReward = teammateReward;
}
}
await ComBattleTeamModel.updateRewardSt(team.teamCode, roleId, true);
return { status: 0, fixReward };
}
export function clearComBtlTimer(teamCode: string, timerMap: Map<string, NodeJS.Timer>) {
let timer = timerMap.get(teamCode);
if (timer) {
clearTimeout(timer);
}
}
export function setComBtlTimer(teamCode: string, timer: NodeJS.Timer, timerMap: Map<string, NodeJS.Timer>) {
let preTimer = timerMap.get(teamCode);
if (preTimer) {
clearTimeout(preTimer);
}
timerMap.set(teamCode, timer);
}
export async function getRealReward(blueprtId: number, roleSt: RoleStatus) {
let { fixReward, teammateReward } = getRewardByBlueprtId(blueprtId);
if (!roleSt.isCap) {
if (roleSt.isFrd) {
fixReward = []
} else {
fixReward = teammateReward;
}
}
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<number, number>();
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 = roleSt.heroes.filter(hero => {
return roleSt.killed.indexOf(hero.id) == -1;
}).map(cur => cur.id);
const newKilledHeroes = getRandEelm(aliveHeroes, newKilledCnt);
roleSt.killed = roleSt.killed.concat(newKilledHeroes);
}
}
export async function handleComBtlProgress(teamStatus: MemComBtlTeam, robotHurtTimer: Map<string, NodeJS.Timer>, teamMap: Map<string, MemComBtlTeam>, 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);
}
// // 为了背包显示,队长藏宝图在创建队伍的时候就扣掉了,如果输了,返还藏宝图
// if (!result && teamStatus.capId != 'robot') {
// const { sid } = channel.getMember(teamStatus.capId);
// await addItems(teamStatus.capId, teamStatus.capId, sid, [{ id: teamStatus.blueprtId, count: 1 }]);
// }
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<string, NodeJS.Timer>} robotHurtTimer
*/
export function updateRobotHurtByTime(teamStatus: MemComBtlTeam, roleSt: RoleStatus, interval: number, channel: Channel, robotHurtTimer: Map<string, NodeJS.Timer>, teamMap: Map<string, MemComBtlTeam>) {
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<string, NodeJS.Timer>} robotHurtTimer
*/
export function clearRobotHurtTimer(teamStatus, robotHurtTimer: Map<string, NodeJS.Timer>) {
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 range = gameData.comBtlLvRange.get(lvRange);
if (!range) return true;
let { min, max } = range;
return lv < min || lv > max;
}
export async function dismissTeam(teamStatus: MemComBtlTeam, teamMap: Map<string, MemComBtlTeam>, roleId: string, teamDisTimer: Map<string, NodeJS.Timer>, 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<string, MemComBtlTeam>, roleId: string, teamDisTimer: Map<string, NodeJS.Timer>, 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<RoleType>} roleInfo 要加入玩家的信息
* @param {string} roleId 要加入玩家的 id
* @param {string[]} roleIds 队伍中当前玩家列表
* @param {number} ceLimit
* @returns
*/
async function teammateValid(roleInfo: Partial<RoleType>, 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 { equipLvl, quality } = gameData.goods.get(blueprtId);
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: equipLvl
};
}
/**
* @description 检查是否有足够的藏宝图,未结束的战斗也占用一张藏宝图
* @export
* @param {string} roleId
* @param {number} blueprtId
* @returns
*/
export async function hasEnoughBlueprt(roleId: string, sid: string, blueprtId: number) {
let blueprt = await ItemModel.findbyRoleAndGidAndCount(roleId, blueprtId, 1);
if (!blueprt || blueprt.count <= 0) return false;
// 检查是否有已创建未结束的寻宝,预先占用一张藏宝图
// // 背包中占用的藏宝图不显示,所以这里就扣掉,结束再加回去
// let result = await handleCost(roleId, sid, [{ id: blueprtId, count: 1 }]);
// return result;
let teams = await ComBattleTeamModel.getBlueprtInUse(roleId, COM_TEAM_STATUS.FIGHTING, blueprtId);
console.log('hasEnoughBlueprt', blueprt.count, teams.length)
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, equipLvl } = gameData.goods.get(comTeam.blueprtId);
let teammates = await getTeamSearchByQuality(quality, equipLvl);
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<string, MemComBtlTeam>, teamDisTimer: Map<string, NodeJS.Timer>, channel: Channel, count = 1) {
const { teamCode, roleStatus } = comTeam;
let hasCap = roleStatus.findIndex(cur => cur.isCap) != -1;
const { robotStArr, robotIdArr } = getRandComBtlRobots(roleCe, comTeam.ceLimit, roleLv, count);
for (let st of robotStArr) {
if(!hasCap) {
st.isCap = true;
hasCap = true;
}
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<string, MemComBtlTeam>} teamMap
* @param {Map<string, NodeJS.Timer>} teamDisTimer
* @param {Channel} channel
*/
export async function addRobotsLater(comTeam: MemComBtlTeam, roleInfo: RoleType, teamMap: Map<string, MemComBtlTeam>, teamDisTimer: Map<string, NodeJS.Timer>, 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;
}
export async function setComBtlOnUserLeave(roleId: string, teamCode: string) {
let res = dispatch(teamCode, pinus.app.getServersByType('battle'), 'battle');
await pinus.app.rpc.battle.comBattleRemote.leaveFromBattle.toServer(res.id, roleId, teamCode);
}