Files
ZYZ/game-server/app/servers/battle/handler/comBattleHandler.ts
T

842 lines
38 KiB
TypeScript

import { difference } from 'underscore';
/*
* @Author: 梁桐川
* @Date: 2020-11-30 15:05:48
* @Last Modified by: 梁桐川
* @Last Modified time: 2020-12-03 21:36:00
*/
import { IT_TYPE, CURRENCY_BY_TYPE, CURRENCY_TYPE, COM_TEAM_STATUS, COM_BTL_CONST, CONSUME_TYPE, COM_BTL_QUALITY, QUALITY_TYPE } from './../../../consts';
import { getGoodById, getBossHpByBlueprtId, getBlueprtComposeByQuality, getBluePrtByQuality, getWarById, getWarIdByBlueprtId, comBtlRangeInfo, comBtlRangeByLv } from '../../../pubUtils/gamedata';
import { ComBattleTeamModel, BossHp } from '../../../db/ComBattleTeam';
import Role, { RoleModel } from '../../../db/Role';
import { STATUS } from '../../../consts/statusCode';
import { Application, BackendSession } from 'pinus';
import { resResult, getRandomByLen, calculateNum, getRandValue, ratioReward, reduceCe, getRandValueByMinMax } from '../../../pubUtils/util';
import { RoleStatus } from '../../../db/ComBattleTeam';
import { ItemModel } from '../../../db/Item';
import { handleFixedReward, addItems, handleCost, decreaseItems } from '../../../services/rewardService';
import { checkRoleInQueue, getTeamSearchByQuality, rmRoleFromQueue, setTeamSearchReq } from '../../../services/redisService';
import { transBossHpArr } from '../../../services/battleService';
import { getRandBlueprtId, getRandComBtlRobots, checkComBattleResult, clearComBtlTimer, getRealReward, getAssistTimesByQuality, getFrd, updateRobotHurtByTime, comBtlLvInvalid, clearRobotHurtTimer, setDismissTimer, dismissTeam, incEquipPrintDrop, randEquipPrintId, handleComBtlProgress } from '../../../services/comBattleService';
import { setAp } from '../../../services/actionPointService';
import { roleLevelup } from '../../../services/normalBattleService';
export default function(app: Application) {
return new ComBattleHandler(app);
}
class ComTeam {
// 队伍唯一编号
teamCode: string;
// 玩家列表
roleIds: Array<string>
// 队伍是否开放加入
pub: boolean;
// 对应藏宝图 Id
blueprtId: number;
// 战斗状态 0:未开始,1:已开始,2:胜利,3:失败
status: number;
// 玩家状态
roleStatus: Array<RoleStatus>;
// 队长 roleId
capId: string;
// 战力限制
ceLimit: number;
// boss 总血量
bossHp: number;
// boss 当前血量
bossCurHp: number;
// 单个 boss 血量状态
bossHpArr: Array<BossHp>;
// 品质
quality: number;
// 当前回合
curRnd: number;
// 队伍中人数
roleCnt: number;
// 藏宝图等级所处范围,用来匹配
lvRange: number;
// 队伍是否开放加入
timeout: boolean;
constructor(teamCode: string, pub: boolean, blueprtId: number, status: number, capId: string, ceLimit: number, bossHp: number, quality: number, bossHpArr:Array<BossHp> ) {
this.teamCode = teamCode;
this.pub = pub;
this.blueprtId = blueprtId;
this.status = status;
this.capId = capId;
this.ceLimit = ceLimit;
this.bossHp = bossHp;
this.bossCurHp = bossHp;
this.quality = quality;
this.bossHpArr = bossHpArr;
this.curRnd = 0;
this.roleCnt = 1;
this.timeout = false;
let { lvLimited } = getGoodById(blueprtId);
this.lvRange = comBtlRangeByLv(lvLimited);
}
}
export class ComBattleHandler {
constructor(private app: Application) {
}
private teamMap: Map<string, ComTeam> = new Map();
private teamDisTimer: Map<string, NodeJS.Timer> = new Map();
private robotHurtTimer: Map<string, NodeJS.Timer> = new Map();
/**
* @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 roleName = session.get('roleName');
let sid = session.get('sid');
let teamCode = session.get('teamCode');
const { blueprtId, pub, ceLimit } = msg;
console.log('createTeam msg: ', msg);
// 检查藏宝图Id是否合法
let goodData = getGoodById(blueprtId);
if (!goodData || goodData.itid !== IT_TYPE.BLUEPRT || COM_BTL_QUALITY.indexOf(goodData.quality) === -1) return resResult(STATUS.COM_BATTLE_BLUEPRT_INVALID);
// 检查藏宝图是否足够
let { lv, headHid = 19, topFiveCe = 100, sHid = 19 } = await RoleModel.findByRoleId(roleId);
topFiveCe = reduceCe(topFiveCe);
if (lv < COM_BTL_CONST.ENABLE_LV) return resResult(STATUS.COM_BATTLE_LV_NOT_ENOUGH);
let blueprt = await ItemModel.findbyRoleAndGidAndCount(roleId, blueprtId, 1);
if (!blueprt) return resResult(STATUS.COM_BATTLE_BLUEPRT_NOT_FOUND);
// 检查是否有已创建未结束的寻宝,预先占用一张藏宝图
let teams = await ComBattleTeamModel.getTeamByCapAndStatus(roleId, COM_TEAM_STATUS.FIGHTING);
if (teams && blueprt.count <= teams.length) return resResult(STATUS.COM_BATTLE_BLUEPRT_NOT_ENOUGH);
let roleStatus = [];
let roleIds = [];
roleIds.push(roleId);
let capStatus = new RoleStatus(roleId, roleName, true, false, headHid, sHid, topFiveCe, lv);
roleStatus.push(capStatus);
// 检查是否有正在匹配的符合要求的玩家
let teammates = await getTeamSearchByQuality(goodData.quality, comBtlRangeByLv(goodData.lvLimited));
if (teammates) {
for (let teammate of teammates) {
let roleInfo = await RoleModel.findByRoleId(teammate.roleId);
if (!roleInfo || roleIds.indexOf(teammate.roleId) !== -1) continue;
let {roleId, roleName, headHid = 19, sHid = 19, topFiveCe, lv} = roleInfo;
topFiveCe = reduceCe(topFiveCe);
if (topFiveCe < ceLimit) continue;
let isFrd = await getFrd(roleId, goodData.quality);
const st = new RoleStatus(roleId, roleName, false, isFrd, headHid, sHid, topFiveCe, lv);
roleStatus.push(st);
roleIds.push(roleInfo.roleId);
}
}
let { bossHpSum, bossHpArr } = getBossHpByBlueprtId(blueprtId);
// 创建队伍数据结构
let comTeam = new ComTeam(teamCode, pub, blueprtId, COM_TEAM_STATUS.DEFAULT, roleId, ceLimit, bossHpSum || 10000, goodData.quality, transBossHpArr(bossHpArr));
comTeam.roleStatus = roleStatus;
comTeam.roleIds = roleIds;
this.teamMap.set(teamCode, comTeam);
// TODO: 处理助战回滚
const team = await ComBattleTeamModel.createTeam(comTeam);
if (!team) return resResult(STATUS.COM_BATTLE_CREATE_ERR);
let channelService = this.app.get('channelService');
let channel = channelService.getChannel(teamCode, true);
let users = channel.getMembers();
if (users.indexOf(roleId) === -1) {
channel.add(roleId, sid);
}
let uids = [];
uids.push({roleId, sid});
if (teammates) {
for (let teammate of teammates) {
let { roleId, sid } = teammate;
uids.push({uid: roleId, sid});
let users = channel.getMembers();
if (users.indexOf(roleId) === -1) {
channel.add(roleId, sid);
}
}
channelService.pushMessageByUids('onTeamJoin', resResult(STATUS.SUCCESS, {teamInfo: comTeam}), uids);
}
let thiz = this;
// 倒计时一定时间给队长匹配机器人
let teamStatus = thiz.teamMap.get(teamCode);
if (pub === true && teamStatus && teamStatus.roleIds && teamStatus.status === COM_TEAM_STATUS.DEFAULT && teamStatus.roleIds.length < 3) {
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 () => {
if (teamStatus.status === COM_TEAM_STATUS.DEFAULT && teamStatus.roleIds.length < 3) {
let { robotStArr, robotIdArr } = getRandComBtlRobots(topFiveCe, teamStatus.ceLimit, lv, 1);
teamStatus.roleIds = teamStatus.roleIds.concat(robotIdArr);
teamStatus.roleStatus = teamStatus.roleStatus.concat(robotStArr);
for (let st of robotStArr) {
await ComBattleTeamModel.addRole(teamCode, st);
}
channel.pushMessage('onTeamJoin', resResult(STATUS.SUCCESS, {teamInfo: teamStatus}));
setDismissTimer(teamStatus, thiz.teamMap, roleId, thiz.teamDisTimer, thiz.app);
}
}, joinTime);
}
}
setDismissTimer(teamStatus, thiz.teamMap, roleId, thiz.teamDisTimer, thiz.app);
return resResult(STATUS.SUCCESS, { teamCode, roleStatus });
}
/**
* @description 匹配队伍
* @param {{qualityArr: [number]}} msg 要匹配的品质数组列表
* @param {BackendSession} session
* @returns
* @memberof ComBattleHandler
*/
async searchTeam(msg: {qualityArr: [number], lvRange: number}, session: BackendSession) {
let roleId = session.get('roleId');
let roleName = session.get('roleName');
let sid = session.get('sid');
const { qualityArr, lvRange = 1 } = msg;
let { lv, headHid = 19, topFiveCe = 1000, sHid = 19 } = await RoleModel.findByRoleId(roleId);
topFiveCe = reduceCe(topFiveCe);
if (comBtlLvInvalid(lv, lvRange)) {
return resResult(STATUS.COM_BATTLE_ASSIST_LV_NOT_ENOUGH);
}
if (difference(qualityArr, COM_BTL_QUALITY).length !== 0) {
return resResult(STATUS.COM_BLUEPRT_QUALITY_ERROR);
}
const team = await ComBattleTeamModel.getOtherTeamByQualityAndSt(roleId, qualityArr, COM_TEAM_STATUS.DEFAULT, lvRange, topFiveCe);
// TODO: 有匹配的队伍还需要重新 joinTeam 加入,有失败可能,体验不好,需优化
if (team && team.roleIds.length < 3 && team.status === COM_TEAM_STATUS.DEFAULT && team.roleIds.indexOf(roleId) === -1) {
return resResult(STATUS.SUCCESS, {teamCode: team.teamCode});
}
let teamCode = session.get('teamCode');
await setTeamSearchReq(roleId, sid, qualityArr, lvRange);
let thiz = this;
// 倒计时匹配两个机器人
setTimeout(async () => {
let inQueue = await checkRoleInQueue(roleId, sid, qualityArr, lvRange);
if (!inQueue) return;
await rmRoleFromQueue(roleId, sid, qualityArr, lvRange);
let roleStatus = [];
let roleIds = [];
// 创建队伍
let blueprtId = getRandBlueprtId(qualityArr).pop();
let { quality } = getGoodById(blueprtId);
let { bossHpSum, bossHpArr } = getBossHpByBlueprtId(blueprtId);
let comTeam = new ComTeam(teamCode, false, blueprtId, COM_TEAM_STATUS.DEFAULT, 'robot', 0, bossHpSum || 10000, quality, transBossHpArr(bossHpArr));
let isFrd = await getFrd(roleId, quality);
// 将玩家加入队伍
let roleInfo = new RoleStatus(roleId, roleName, false, isFrd, headHid, sHid, topFiveCe, lv);
roleStatus.push(roleInfo);
roleIds.push(roleId);
// 创建并添加机器人
let { robotStArr, robotIdArr } = getRandComBtlRobots(topFiveCe, comTeam.ceLimit, lv, 2);
roleStatus = roleStatus.concat(robotStArr);
roleIds = roleIds.concat(robotIdArr);
comTeam.roleStatus = roleStatus;
comTeam.roleIds = roleIds;
let channelService = thiz.app.get('channelService');
const team = await ComBattleTeamModel.createTeam(comTeam);
// if (!team) channelService.pushMessageByUids('onTeamJoin', {teamInfo: null}, [{uid: roleId, sid}]);
thiz.teamMap.set(teamCode, comTeam);
// 推送组队消息
channelService.pushMessageByUids('onTeamJoin', resResult(STATUS.SUCCESS, {teamInfo: comTeam}), [{uid: roleId, sid}]);
let channel = channelService.getChannel(teamCode, true);
let users = channel.getMembers();
if (users.indexOf(roleId) === -1) {
channel.add(roleId, sid);
}
// 机器人队伍 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, COM_BTL_QUALITY, null);
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 roleName = session.get('roleName');
let sid = session.get('sid');
let { teamCode, isFrd } = msg;
let teamStatus = this.teamMap.get(teamCode);
if (!teamStatus || teamStatus.status !== COM_TEAM_STATUS.DEFAULT) return resResult(STATUS.COM_BATTLE_TEAM_INVALID);
if (teamStatus.roleIds.length === 3) return resResult(STATUS.COM_BATTLE_MEMBER_LIMIT);
if (teamStatus.roleIds.indexOf(roleId) !== -1) return resResult(STATUS.COM_BATTLE_DUP_ENTER);
let { lv = 1, headHid = 19, topFiveCe = 0, sHid = 19 } = await Role.findByRoleId(roleId);
topFiveCe = reduceCe(topFiveCe);
let { quality } = getGoodById(teamStatus.blueprtId);
if (lv < COM_BTL_CONST.ENABLE_LV) {
return resResult(STATUS.COM_BATTLE_LV_NOT_ENOUGH);
} else if (comBtlLvInvalid(lv, teamStatus.lvRange)) {
return resResult(STATUS.COM_BATTLE_ASSIST_LV_NOT_ENOUGH);
} else if (topFiveCe < teamStatus.ceLimit) {
return resResult(STATUS.COM_BATTLE_CE_LIMIT);
}
if (!isFrd) {
isFrd = await getFrd(roleId, quality);
}
// 加入队伍
let roleStatus = new RoleStatus(roleId, roleName, false, isFrd, headHid, sHid, topFiveCe, lv);
const team = await ComBattleTeamModel.addRole(teamCode, roleStatus);
if (!team || team.roleIds.length > 3) {
return resResult(STATUS.COM_BATTLE_JOIN_ERR);
}
// 推送队伍信息给所有玩家
let channelService = this.app.get('channelService');
let channel = channelService.getChannel(teamCode, false);
teamStatus.roleIds.push(roleId);
teamStatus.roleStatus.push(roleStatus);
let users = channel.getMembers();
if (users.indexOf(roleId) === -1) {
channel.add(roleId, sid);
}
channel.pushMessage('onTeamJoin', resResult(STATUS.SUCCESS, {teamInfo: teamStatus}));
setDismissTimer(teamStatus, this.teamMap, roleId, this.teamDisTimer, this.app);
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 = this.teamMap.get(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, teamStatus.quality);
}
if (isFrd !== isFrdPre) {
return resResult(STATUS.COM_BATTLE_ASSIST_NOT_ENOUGH);
}
teamStatus.roleStatus.forEach(async st => {
st.isFrd = isFrd;
await ComBattleTeamModel.updateRoleStFrd(teamCode, roleId, isFrd);
});
return resResult(STATUS.SUCCESS, { teamCode, isFrd });
}
/**
* 获取队伍列表-deprecated
* @param msg
* @param session
*/
async getTeams(msg: { blueprtIds: Array<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});
}
/**
* @description 队伍准备-deprecate
* @param {{teamCode: string, heroes: Array<number>}} msg
* @param {BackendSession} session
* @returns
* @memberof ComBattleHandler
*/
async teammateReady(msg: {teamCode: string, heroes: Array<number>}, session: BackendSession) {
let roleId = session.get('roleId');
let { teamCode, heroes } = msg;
let teamStatus = this.teamMap.get(teamCode);
if (!teamStatus || !teamStatus.roleIds || teamStatus.roleIds.indexOf(roleId) === -1) return resResult(STATUS.COM_BATTLE_TEAM_INVALID);
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;
}
});
let channelService = this.app.get('channelService');
let channel = channelService.getChannel(teamCode, false);
channel.pushMessage('onTeammateReady', resResult(STATUS.SUCCESS, {teamCode, roleId, heroes}));
return resResult(STATUS.SUCCESS);
}
/**
* @description 出兵时记录玩家阵容
* @param {{teamCode: string, heroes: Array<number>}} msg 队伍编号;阵容
* @param {BackendSession} session
* @returns
* @memberof ComBattleHandler
*/
async setupHeroes(msg: {teamCode: string, heroes: Array<number>, battleCode: string}, session: BackendSession) {
let roleId = session.get('roleId');
let { teamCode, heroes, battleCode = 'default' } = msg;
if (!heroes || heroes.length === 0) return resResult(STATUS.COM_BATTLE_HEROES_ERR);
let teamStatus = this.teamMap.get(teamCode);
if (!teamStatus || !teamStatus.roleIds || teamStatus.roleIds.indexOf(roleId) === -1) return resResult(STATUS.COM_BATTLE_TEAM_INVALID);
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;
}
});
let channelService = this.app.get('channelService');
let channel = channelService.getChannel(teamCode, false);
channel.pushMessage('onTeammateReady', resResult(STATUS.SUCCESS, {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 = this.teamMap.get(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);
let roleIdx = teamStatus.roleIds.indexOf(roleIdToRm);
teamStatus.roleIds.splice(roleIdx, 1);
teamStatus.roleStatus.some((elem, idx) => {
if (elem.roleId === roleIdToRm) {
teamStatus.roleStatus.splice(idx, 1);
}
});
// 推送移除消息
let channelService = this.app.get('channelService');
let channel = channelService.getChannel(teamCode, false);
// 先推送离队消息,再将玩家从房间中移除
channel.pushMessage('onLeaveTeam', resResult(STATUS.SUCCESS, {teamCode, roleId: roleIdToRm}));
let users = channel.getMembers();
if (users.indexOf(roleIdToRm) !== -1) {
channel.removeMember(roleIdToRm);
}
clearComBtlTimer(teamCode, this.teamDisTimer); // 移除队员停止解散计时
let thiz = this;
setTimeout(async () => {
let team = thiz.teamMap.get(teamCode);
if (team && team.roleIds && team.status === COM_TEAM_STATUS.DEFAULT && team.roleIds.length < 3) {
let roleSt: RoleStatus;
for (let st of team.roleStatus) {
if (st.roleId === roleId) {
roleSt = st;
}
}
if (!roleSt) return;
let { topFiveCe, lv } = roleSt;
let { robotStArr, robotIdArr } = getRandComBtlRobots(topFiveCe, team.ceLimit, lv, 3 - team.roleIds.length);
team.roleIds = team.roleIds.concat(robotIdArr);
team.roleStatus = team.roleStatus.concat(robotStArr);
for (let st of robotStArr) {
await ComBattleTeamModel.addRole(teamCode, st);
}
channel.pushMessage('onTeamJoin', resResult(STATUS.SUCCESS, {teamInfo: team}));
}
await setDismissTimer(team, thiz.teamMap, roleId, thiz.teamDisTimer, thiz.app);
}, 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 = this.teamMap.get(teamCode);
let result = await dismissTeam(teamStatus, this.teamMap, roleId, this.teamDisTimer, this.app);
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 sid = session.get('sid');
let { teamCode } = msg;
let teamStatus = this.teamMap.get(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);
teamStatus.status = COM_TEAM_STATUS.FIGHTING;
let team = await ComBattleTeamModel.updateStatusByCode(teamCode, COM_TEAM_STATUS.FIGHTING);
if (!team) return resResult(STATUS.COM_BATTLE_START_ERR);
clearComBtlTimer(teamCode, this.teamDisTimer); // 战斗开始停止解散计时
let channelService = this.app.get('channelService');
let channel = channelService.getChannel(teamCode, false);
channel.pushMessage('onComBtlStart', resResult(STATUS.SUCCESS, {teamCode, roleStatus: teamStatus.roleStatus}));
// 每场倒计时结算
let thiz = this;
setTimeout(async () => {
let teamStatus = thiz.teamMap.get(teamCode);
if (teamStatus && teamStatus.status === COM_TEAM_STATUS.FIGHTING) {
clearRobotHurtTimer(teamStatus, this.robotHurtTimer);
let channelService = thiz.app.get('channelService');
let channel = channelService.getChannel(teamCode, false);
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;
channel.pushMessage('onTeamComplete', resResult(STATUS.SUCCESS, {teamCode, result: false, timeout: true}));
thiz.teamMap.delete(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, channel, this.robotHurtTimer, this.teamMap);
}
});
return resResult(STATUS.SUCCESS);
}
/**
* @description 战场中的状态同步,战斗结果判定
* @param {{teamCode: string, bossHurts: Array<{dataId: number, hurtHp: number}>, killed: Array<number>, curRnd: number}} msg 队伍编号;boss 承伤;武将阵亡;当前回合
* @param {BackendSession} session
* @returns
* @memberof ComBattleHandler
*/
async action(msg: {teamCode: string, bossHurts: Array<{dataId: number, hurtHp: number}>, killed: Array<number>, curRnd: number}, session: BackendSession) {
let roleId = session.get('roleId');
let sid = session.get('sid');
let { teamCode, killed, bossHurts, curRnd } = msg;
let teamStatus = this.teamMap.get(teamCode);
if (!teamStatus || !teamStatus.roleIds || teamStatus.roleIds.indexOf(roleId) === -1) return resResult(STATUS.COM_BATTLE_TEAM_INVALID);
if (teamStatus.status !== COM_TEAM_STATUS.FIGHTING) return resResult(STATUS.COM_BATTLE_NOT_START);
for (let st of teamStatus.roleStatus) {
if (st.roleId === roleId && (!st.heroes || st.heroes.length === 0)) {
return resResult(STATUS.COM_BATTLE_HEROES_ERR);
}
}
// 重置总血量,计算真实伤害
let actBossHurts = [];
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 += deltaHp;
boss.curHp -= deltaHp;
} else if (boss.curHp > 0) {
actBossHurts.push({dataId: boss.dataId, hurtHp: boss.curHp});
totalHurtHp += boss.curHp;
boss.curHp = 0;
}
}
}
});
// 更新玩家武将阵亡情况,计算玩家总伤害,计算机器人造成的伤害
teamStatus.roleStatus.forEach(roleSt => {
if (roleSt.roleId === roleId) {
if (killed && killed.length) {
killed.forEach(killedHid => {
if (roleSt.killed.indexOf(killedHid) === -1 && roleSt.heroes.indexOf(killedHid) !== -1) {
roleSt.killed.push(killedHid);
}
});
}
roleSt.totalDmg += totalHurtHp;
}
});
teamStatus.curRnd = curRnd;
teamStatus.bossCurHp -= totalHurtHp;
if (teamStatus.bossCurHp < 0) teamStatus.bossCurHp = 0;
// 推送本次行动情况
let channelService = this.app.get('channelService');
let channel = channelService.getChannel(teamCode, false);
const roleStatus = teamStatus.roleStatus.map(st => {return {roleId: st.roleId, totalDmg: st.totalDmg, killed: st.killed}});
channel.pushMessage('onTeammateAct', resResult(STATUS.SUCCESS, { teamCode, bossCurHp: teamStatus.bossCurHp, bossHpArr: teamStatus.bossHpArr, roleStatus, actRoleId: roleId, actBossHurts }));
const result = await handleComBtlProgress(teamStatus, this.robotHurtTimer, this.teamMap, channel);
if (result && result.code !== 0) return result;
return resResult(STATUS.SUCCESS);
}
/**
* 客户端给服务器的战斗结束通知-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);
let channelService = this.app.get('channelService');
let channel = channelService.getChannel(teamCode, false);
if (isSuccess && teamStatus.bossCurHp <= 0) {
let team = await ComBattleTeamModel.updateResult(teamCode, roleId, isSuccess);
if (!team) return resResult(STATUS.COM_BATTLE_RESULT_ERR);
channel.pushMessage('onTeamComplete', resResult(STATUS.SUCCESS, {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) {
channel.pushMessage('onTeamComplete', resResult(STATUS.SUCCESS, {teamCode, result: isSuccess}));
} else {
channel.pushMessage('onTeammateAct', resResult(STATUS.SUCCESS, {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 { teamCode } = msg;
let team = await ComBattleTeamModel.getTeamByCode(teamCode);
clearRobotHurtTimer(team, this.robotHurtTimer);
if (!team || team.status !== COM_TEAM_STATUS.WIN) return resResult(STATUS.COM_BATTLE_REWARD_ERR);
let { roleStatus, blueprtId, status, bossHpArr } = team;
let roleSt: RoleStatus = null;
roleStatus.forEach(st => {
if (st && st.roleId === roleId) {
roleSt = st;
}
});
if (roleSt.gotReward) return resResult(STATUS.COM_BATTLE_REWARDED);
let warInfo = getWarById(getWarIdByBlueprtId(blueprtId));
if (!warInfo) return resResult(STATUS.BATTLE_MISS_INFO);
if(!warInfo.hasOwnProperty('cost')) {
warInfo['cost'] = 0;
}
const now = Date.now(); // 当前时间戳
let apJson = await setAp(now, roleId, -1 * warInfo.cost); // 扣除体力
if(!apJson) {
return resResult(STATUS.BATTLE_ACTION_POINT_LACK);
}
const { dropResult } = await incEquipPrintDrop(roleSt);
if (dropResult) {
const dropEquipId = randEquipPrintId(warInfo);
if (dropEquipId) {
roleSt.fixReward = `${roleSt.fixReward}|${dropEquipId}&${1}`;
}
}
await ComBattleTeamModel.updateRewardSt(teamCode, roleId, true);
const goods = await handleFixedReward(roleId, roleName, sid, roleSt.fixReward, 1);
let actordata = await roleLevelup(roleId, warInfo.kingExp, session);// 主公升级经验
return resResult(STATUS.SUCCESS, { battleGoods: goods, ...actordata, teamInfo: {status, teamCode, roleStatus, bossHpArr} });
}
async getComBtlStatus(msg: {teamCode: string}, session: BackendSession) {
let { teamCode } = msg;
let roleStatus, status, bossHpArr, timeout;
let memTeam = this.teamMap.get(teamCode);
if (memTeam) {
({ roleStatus, status, bossHpArr, timeout } = memTeam);
} else {
let dbTeam = await ComBattleTeamModel.getTeamByCode(teamCode);
if (!dbTeam) return resResult(STATUS.COM_BATTLE_TEAM_INVALID);
({ roleStatus, status, bossHpArr, timeout } = dbTeam);
}
return resResult(STATUS.SUCCESS, { teamInfo: {status, teamCode, roleStatus, bossHpArr, timeout} });
}
/**
* @description 获取自己一段时间内的寻宝记录
* @param {{}} msg
* @param {BackendSession} session
* @returns
* @memberof ComBattleHandler
*/
async getTeamRec(msg: {}, session: BackendSession) {
let roleId = session.get('roleId');
let teams = await ComBattleTeamModel.getTeamByRoleAndTime(roleId, null, new Date(new Date().setHours(0, 0, 0, 0) - 2 * 24 * 60 * 60 * 1000));
if (!teams) return resResult(STATUS.COM_BATTLE_NO_RECENT_REC);
return resResult(STATUS.SUCCESS, {teamInfos: teams});
}
/**
* @description 获取藏宝图数量
* @param {{ids: Array<number>}} msg 藏宝图Id数组,可选参数
* @param {BackendSession} session
* @returns
* @memberof ComBattleHandler
*/
async getBlueprtCount(msg: {ids: Array<number>}, session: BackendSession) {
let roleId = session.get('roleId');
let { ids } = msg;
let blueprts;
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 cntMap = await getAssistTimesByQuality(roleId);
let cnt = [];
for (let i = 0; i < COM_BTL_QUALITY.length; ++i) {
cnt[i] = cntMap.get(i + 1) || 0;
}
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 cntMap = await getAssistTimesByQuality(roleId);
let cnt = [];
for (let i = 0; i < COM_BTL_QUALITY.length; ++i) {
cnt[i] = cntMap.get(i + 1) || 0;
}
const blueprts = await ItemModel.findByRoleAndType(roleId, CONSUME_TYPE.BLUEPRT);
return resResult(STATUS.SUCCESS, { blueprts, assistCnt: cnt });
}
/**
* @description 藏宝图合成
* @param {{original: Array<{id: number, count: number}>}} msg
* @param {BackendSession} session
* @returns
* @memberof ComBattleHandler
*/
async composeBlueprt(msg: {original: Array<{id: number, count: number}>}, session: BackendSession) {
const roleId = session.get('roleId');
const roleName = session.get('roleName');
const sid = session.get('sid');
const { original } = msg;
// 原材料检查
let originalQuality: number, originalSum: number = 0;
for(let {id, count} of original) {
const goodInfo = getGoodById(id);
if(!originalQuality) originalQuality = goodInfo.quality;
if(originalQuality != goodInfo.quality) {
return resResult(STATUS.COM_BLUEPRT_QUALITY_ERROR);
}
if(goodInfo.itid == IT_TYPE.BLUEPRT) {
originalSum += count;
}
}
const dicCompose = getBlueprtComposeByQuality(originalQuality);
if(!dicCompose) {
return resResult(STATUS.COM_BLUEPRT_QUALITY_CANNOT_COMPOSE);
}
if(originalSum != dicCompose.blueprtNum) {
return resResult(STATUS.COM_BLUEPRT_COUNT_ERROR);
}
// 添加寻宝币
original.push({
id: CURRENCY_BY_TYPE.get(CURRENCY_TYPE.TREASURE_POINT),
count: dicCompose.coinNum
});
// 消耗藏宝图和寻宝币
let costResult = await handleCost(roleId, sid, original);
if(!costResult) return resResult(STATUS.ROLE_MATERIAL_NOT_ENOUGH);
const targetList = getBluePrtByQuality(dicCompose.targetQuality);
const target = getRandomByLen(targetList);
const reward = [{id: target, count: 1}];
const goods = await addItems(roleId, roleName, sid, reward);
return resResult(STATUS.SUCCESS, { goods, costGold: 0 });
}
}