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

932 lines
43 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 { WAR_TYPE } from './../../../consts/constModules/battleConst';
import { Application, BackendSession, pinus, HandlerService, } from 'pinus';
import { findIndex } from 'underscore';
import { gameData, getPvpBoxBySeasonNumAndIndex } from '../../../pubUtils/data';
import { refreshEnemies, getEnemies, refChallengeCnt, generPVPOppRecInfo, generMyRecInfo, sendLastSeasonRewardIfNotSent, refreshRefOppCnt, generPvpLineupCe, calLineupScore, checkPvpSeasonIsStart, checkPvpSeasonIsSummit, getPvpSeasonStatus, checkPvpSeasonIsWaiting, refreshBuyChallengeCnt } from '../../../services/pvpService';
import { RoleModel, RoleType } from '../../../db/Role';
import { STATUS } from '../../../consts/statusCode';
import { resResult, genCode, checkRoleIsRobot, robotIdComBack, parseGoodStr } from '../../../pubUtils/util';
import { PvpDefenseModel, pvpUpdateInter } from '../../../db/PvpDefense';
import { PvpSeasonResultModel } from '../../../db/PvpSeasonResult';
import { Rank } from '../../../services/rankService';
import { checkTask, checkTaskInPvpEnd } from '../../../services/task/taskService';
import { Attack, Combo, Defense, DefenseHero, PvpDataReturn, pvpSaveDataReturn } from '../../../domain/battleField/pvp';
import { DEBUG_MAGIC_WORD, FIGURE_UNLOCK_CONDITION, ITEM_CHANGE_REASON, LINEUP_NUM, REDIS_KEY, TASK_TYPE } from '../../../consts';
import { PVP } from '../../../pubUtils/dicParam';
import { addItems, getGoldObject, handleCost, unlockFigure } from '../../../services/role/rewardService';
import { pick } from "underscore";
import { HeroModel } from '../../../db/Hero';
import PvpHistoryOpp, { PvpHistoryOppModel, PvpHistoryOppType } from '../../../db/PvpHistoryOpp';
import { BattleRecordModel } from '../../../db/BattleRecord';
import { PvpRecordModel, PvpRecordParam, PvpRecordType } from '../../../db/PvpRecord';
import { pvpEndParamInter } from '../../../pubUtils/interface';
import { getSeconds, nowSeconds } from '../../../pubUtils/timeUtil';
import { getRemoteRplPrefix, getRemoteRplFilePath } from '../../../pubUtils/battleUtils';
import { PlayerDetail, PlayerDetailHero } from '../../../domain/battleField/guild';
import { PvpSaveDataModel } from '../../../db/PvpSaveData';
import { PVPConfigModel } from '../../../db/PvpConfig';
import { getAllServerName, getServerName } from '../../../services/redisService';
import { RoleRankInfo } from '../../../domain/rank';
import { isHeroHidden } from '../../../services/dataService';
import { getPVPGroupIdOfServer } from '../../../services/serverService';
import { getBattleRecordParam } from '../../../services/normalBattleService';
export default function (app: Application) {
new HandlerService(app, {});
return new PvpHandler(app);
}
export class PvpHandler {
constructor(private app: Application) {
}
//1获取主界面
async getData(msg: {}, session: BackendSession) {
let roleId = session.get('roleId');
let serverId = session.get('serverId');
let seasonNum: number = this.app.get('pvpSeasonNum');
let seasonStartTime: number = this.app.get('pvpSeasonStartTime');
let seasonEndTime: number = this.app.get('pvpSeasonEndTime');
let seasonRewardTime: number = this.app.get('pvpSeasonRewardTime');
let pvpDefense = await PvpDefenseModel.findByRoleIdIncludeAll(roleId);
if(!pvpDefense) {
let role = await RoleModel.findByRoleId(roleId);
pvpDefense = await PvpDefenseModel.createPvpDefense(seasonNum, { serverId: role.serverId, roleId: role.roleId, roleName: role.roleName, role: role._id });
} else {
// 如果没有发过,将上赛季的奖励发下
pvpDefense = await sendLastSeasonRewardIfNotSent(pvpDefense);
}
let update: pvpUpdateInter = { };
let result = new PvpDataReturn(); // 返回对象
// 刷新次数
let refChallengeObj = await refChallengeCnt(pvpDefense.challengeCnt, pvpDefense.challengeRefTime, seasonEndTime, roleId, session.get('vipStartTime'));
if(refChallengeObj.hasChanged) {
update = { ...update, ...refChallengeObj }
}
let refOppObj = refreshRefOppCnt(pvpDefense);
// 每天首次打开要刷掉对手
if(refOppObj.shouldRefOpp) {
if(pvpDefense.attack) {
let role = <RoleType>pvpDefense.role;
let oppPlayers = await refreshEnemies(role, seasonNum, pvpDefense.score, pvpDefense.attack.score, pvpDefense.attack.pLv||1);
update.oppPlayers = oppPlayers;
}
update = { ...update, ...refOppObj }
}
if (refreshBuyChallengeCnt(pvpDefense.buyChallengeTime)) {
update = { ...update, buyChallengeCnt: 0, buyChallengeTime: Date.now() };
}
if(Object.keys(update).length > 0) {
pvpDefense = await PvpDefenseModel.updateInfoAndInclude(roleId, update);
}
result.setPvpConfig(seasonNum, seasonStartTime, seasonEndTime, seasonRewardTime);
result.setPvpDefense(pvpDefense);
result.calHasSaveDefense();
let groupId = await getPVPGroupIdOfServer(serverId);
let r = new Rank(REDIS_KEY.PVP_RANK, { seasonNum, groupId });
let oppPlayersReturn = await getEnemies(r, pvpDefense.oppPlayers||[], pvpDefense.winStreakNum);
result.setOppPlayers(oppPlayersReturn);
// 赛季结算
let pvpSeasonResult = await PvpSeasonResultModel.getPvpSeasonResult(roleId);
if (!!pvpSeasonResult && !!pvpSeasonResult.show) {
result.setPvpSeasonResult(pvpSeasonResult);
await PvpSeasonResultModel.setShow(roleId);
}
// 排行榜
let myRank = await r.getMyRank({ roleId });//去redis中获取排名
result.setMyRank(myRank);
return resResult(STATUS.SUCCESS, result);
}
// 刷新对手
async refreshOppPlayer(msg: {}, session: BackendSession) {
let roleId = session.get('roleId');
let sid = session.get('sid');
let serverId = session.get('serverId');
let pvpDefense = await PvpDefenseModel.findByRoleIdIncludeAll(roleId);
if(!pvpDefense) return resResult(STATUS.PVP_NOT_OPEN);
if(!pvpDefense.attack) return resResult(STATUS.PVP_NOT_SET_ATTACK);
// 刷新次数及消耗
let refOppObj = refreshRefOppCnt(pvpDefense);
if (refOppObj.refOppCnt >= gameData.maxPvpRefreshCnt.max) {
return resResult(STATUS.PVP_REFRESH_CNT_REACH_MAX);
}
let costResult = await handleCost(roleId, sid, refOppObj.consume, ITEM_CHANGE_REASON.PVP_REFRESH_OPP);
if (!costResult) return resResult(STATUS.ROLE_MATERIAL_NOT_ENOUGH);
refOppObj.refOppCnt ++;
// 刷新对手
let role = <RoleType>pvpDefense.role;
let seasonNum: number = this.app.get('pvpSeasonNum');
let oppPlayers = await refreshEnemies(role, seasonNum, pvpDefense.score, pvpDefense.attack.score, pvpDefense.attack.pLv||1);
pvpDefense = await PvpDefenseModel.updateInfoAndInclude(roleId, { oppPlayers, ...refOppObj });
let result = new PvpDataReturn(); // 返回对象
result.setPvpDefense(pvpDefense);
let groupId = await getPVPGroupIdOfServer(serverId);
let r = new Rank(REDIS_KEY.PVP_RANK, { seasonNum, groupId });
let oppPlayersReturn = await getEnemies(r, pvpDefense.oppPlayers, pvpDefense.winStreakNum);
result.setOppPlayers(oppPlayersReturn);
return resResult(STATUS.SUCCESS, pick(result, ['oppPlayers', 'refOppCnt', 'buyAttackCnt', 'setAttackCnt']));
}
// 获取对手阵容具体信息
async getOppPlayer(msg: { roleId: string }, session: BackendSession) {
let roleId = session.get('roleId');
if(!checkPvpSeasonIsStart()) {
return resResult(STATUS.PVP_SEASON_NOT_OPEN);
}
let oppoRoleId = msg.roleId;
let pvpDefense = await PvpDefenseModel.findByRoleIdIncludeAll(roleId);
if(!pvpDefense) return resResult(STATUS.PVP_NOT_OPEN);
let { oppPlayers } = pvpDefense;
let curOpp = oppPlayers.find(cur => cur.roleId == oppoRoleId);
if (!curOpp) return resResult(STATUS.PVP_ROLE_NOT_FOUND);
let pvpHistoryOpp = <PvpHistoryOpp>curOpp.oppDef;
return resResult(STATUS.SUCCESS, { roleId: pvpHistoryOpp.oppRoleId, ...pick(pvpHistoryOpp, ['pos', 'title', 'warId', 'buff', 'heroes', 'combo']) });
}
// 开战
async pvpStart(msg: { warId: number, roleId: string }, session: BackendSession) {
const { warId, roleId: oppRoleId } = msg;
let roleId = session.get('roleId');
let roleName = session.get('roleName');
let serverId = session.get('serverId');
let sid = session.get('sid');
let warInfo = gameData.war.get(warId);
if (!warInfo) {
return resResult(STATUS.BATTLE_MISS_INFO);
}
const role = await RoleModel.findByRoleId(roleId, null, true);
if(!role || role.lv < 50) return resResult(STATUS.PVP_NOT_OPEN);
let pvpDefense = await PvpDefenseModel.findByRoleId(roleId);
if (!pvpDefense) return resResult(STATUS.PVP_NOT_OPEN);
// 检查挑战次数
let seasonNum: number = this.app.get('pvpSeasonNum');
let seasonEndTime: number = this.app.get('pvpSeasonEndTime');
let { challengeCnt, challengeRefTime } = await refChallengeCnt(pvpDefense.challengeCnt, pvpDefense.challengeRefTime, seasonEndTime, roleId, session.get('vipStartTime'));
if (challengeCnt == 0) {
return resResult(STATUS.PVP_CHALLENGE_TIMES_NOT_ENOUGH);
}
let curOpp = pvpDefense.oppPlayers.find(cur => cur.roleId == oppRoleId);
if (!curOpp) return resResult(STATUS.PVP_ROLE_NOT_FOUND)
const battleCode = genCode(8); // 关卡唯一值
let heroes = pvpDefense.attack.heroes.map(cur => cur.actorId);
await BattleRecordModel.updateBattleRecordByCode(battleCode, {
$set: {
roleId, roleName, battleId: warId,
status: 0,
warName: warInfo.gk_name,
warType: warInfo.warType,
record: { heroes, pos: curOpp.pos, oppRoleId, pvpSeasonNum: seasonNum }
}
}, true);
await checkTask(serverId, roleId, sid, TASK_TYPE.PVP);
// 战报记录
await PvpRecordModel.createRec(roleId, robotIdComBack(oppRoleId), battleCode);
// 对手记录更新
await PvpHistoryOppModel.setStatus(curOpp.oppDef.toString(), 1);
let update: pvpUpdateInter = {
oppBeforePlayers: pvpDefense.oppPlayers
};
// 刷新对手
let oppPlayers = await refreshEnemies(role, seasonNum, pvpDefense.score ,pvpDefense.attack.score, pvpDefense.attack.pLv||1);
update.oppPlayers = oppPlayers;
// 减少挑战次数
let refChallengeObj = await refChallengeCnt(pvpDefense.challengeCnt, pvpDefense.challengeRefTime, seasonEndTime, roleId, session.get('vipStartTime'));
refChallengeObj.challengeCnt--;
update = {...update, ...refChallengeObj};
pvpDefense = await PvpDefenseModel.updateInfoAndInclude(roleId, update);
let result = new PvpDataReturn(); // 返回对象
result.setPvpDefense(pvpDefense);
let groupId = await getPVPGroupIdOfServer(serverId);
let r = new Rank(REDIS_KEY.PVP_RANK, { seasonNum, groupId });
let oppPlayersReturn = await getEnemies(r, pvpDefense.oppPlayers, pvpDefense.winStreakNum);
result.setOppPlayers(oppPlayersReturn);
return resResult(STATUS.SUCCESS, {
battleCode,
...pick(result, ['oppPlayers', 'challengeCnt', 'challengeRefTime'])
});
}
// 结算
async pvpEnd(msg: { battleCode: string, isSuccess: boolean, myHeroes: pvpEndParamInter[], oppHeroes: pvpEndParamInter[], round: number }, session: BackendSession) {
let roleId = session.get('roleId');
let roleName = session.get('roleName');
let sid = session.get('sid');
const serverId = session.get('serverId');
const { battleCode, isSuccess, myHeroes, oppHeroes, round } = msg;
const BattleRecord = await BattleRecordModel.getBattleRecordByCode(battleCode, true);
if (!BattleRecord || BattleRecord.status != 0) {
return resResult(STATUS.BATTLE_STATUS_WRONG);
}
let { record: { heroes: dbHeroes, oppRoleId, pos, pvpSeasonNum }, createdAt } = BattleRecord;
let flag = 1; // 对比hero信息
for (let { hid } of myHeroes) {
if (dbHeroes.indexOf(hid) == -1) flag = 0;
}
if (!flag) return resResult(STATUS.BATTLE_INFO_VALIDATE_ERR);
// 更新军功
let pvpDefense = await PvpDefenseModel.findByRoleIdIncludeAll(roleId);
if (!pvpDefense) return resResult(STATUS.PVP_NOT_OPEN);
let { hisWinStreakNum = 0 } = pvpDefense;
let update: pvpUpdateInter = {};
const role = await RoleModel.findByRoleId(roleId, null, true);
const myRecInfo = await generMyRecInfo(pvpDefense, role, isSuccess, pos, myHeroes);
let { attackInfo, showHeroScores, updateParam } = myRecInfo
update = { ...update, ...updateParam };
let curOpp = pvpDefense.oppBeforePlayers.find(cur => cur.roleId == oppRoleId && cur.pos == pos);
if (!curOpp) return resResult(STATUS.PVP_ROLE_NOT_FOUND);
const defenseInfo = await generPVPOppRecInfo(isSuccess, curOpp, oppHeroes, serverId);
let seasonNum: number = this.app.get('pvpSeasonNum');
// 战报记录
await PvpRecordModel.updateByBattleCode(battleCode, { warId: BattleRecord.battleId, attackInfo, defenseInfo, createTime: nowSeconds(), timeout: seasonNum != pvpSeasonNum });
// 更新battleRecord
await BattleRecordModel.updateBattleRecordByCode(battleCode, {
$set: { status: isSuccess ? 1 : 2, ...getBattleRecordParam(myHeroes, round) }
}, true);
if(seasonNum == pvpSeasonNum) {
pvpDefense = await PvpDefenseModel.updateInfoAndInclude(roleId, update);
}
let result = new PvpDataReturn(); // 返回对象
result.setPvpDefense(pvpDefense);
if(seasonNum == pvpSeasonNum) {
// 加入排行榜
let groupId = await getPVPGroupIdOfServer(serverId);
let r = new Rank(REDIS_KEY.PVP_RANK, { seasonNum: pvpSeasonNum, groupId });
await r.setRankWithRoleInfo(roleId, pvpDefense.score, pvpDefense.updatedAt.getTime(), role);
let myRank = await r.getMyRank({ roleId });
result.setMyRank(myRank);
if(defenseInfo && defenseInfo.score > 0) await r.setRankWithRoleInfo(defenseInfo.roleId, defenseInfo.score, pvpDefense.updatedAt.getTime(), null, true);
}
let groupId = await getPVPGroupIdOfServer(serverId);
let r = new Rank(REDIS_KEY.PVP_RANK, { seasonNum, groupId });
let oppPlayersReturn = await getEnemies(r, pvpDefense.oppPlayers, pvpDefense.winStreakNum);
result.setOppPlayers(oppPlayersReturn);
await checkTaskInPvpEnd(serverId, roleId, sid, isSuccess, pvpDefense.heroScores, pvpDefense.seasonWinNum);
if(hisWinStreakNum < pvpDefense.hisWinStreakNum) {
await unlockFigure(sid, roleId, [{ type: FIGURE_UNLOCK_CONDITION.PVP_WIN_SERIES, paramWinStreakNum: pvpDefense.hisWinStreakNum }]);
}
return resResult(STATUS.SUCCESS, {
battleCode, isSuccess,
heroScores: showHeroScores,
createdAt: getSeconds(createdAt),
...pick(result, ['score', 'myRank', 'winStreakNum', 'attack', 'oppPlayers', 'challengeCnt', 'challengeRefTime', 'seasonWinNum'])
});
}
async buyAttackCnt(msg: { count: number }, session: BackendSession) {
let { count } = msg;
let roleId = session.get('roleId');
let sid = session.get('sid');
let serverId = session.get('serverId');
let seasonNum: number = this.app.get('pvpSeasonNum');
let pvpDefense = await PvpDefenseModel.findByRoleId(roleId);
if(!pvpDefense) return resResult(STATUS.PVP_NOT_OPEN);
// 次数
let refOppObj = refreshRefOppCnt(pvpDefense);
if(refOppObj.buyAttackCnt + count > PVP.PVP_BUY_SET_ATTACK_CNT) {
return resResult(STATUS.PVP_BUY_ATTACK_CNT_NOT_ENOUGH);
}
refOppObj.buyAttackCnt += count;
let gold = getGoldObject(PVP.PVP_SET_ATTACK_CNT_GOLD * count);
let costResult = await handleCost(roleId, sid, [gold], ITEM_CHANGE_REASON.PVP_BUY_ATTACK_CNT);
if(!costResult) return resResult(STATUS.TOWER_GOLD_NOT_ENOUGH);
let update: pvpUpdateInter = { ...refOppObj };
// 更新挑战阵容
if(refOppObj.shouldRefOpp) {
if(pvpDefense.attack) {
let role = await RoleModel.findByRoleId(roleId);
let seasonNum: number = this.app.get('pvpSeasonNum');
let oppPlayers = await refreshEnemies(role, seasonNum, pvpDefense.score, pvpDefense.attack.score, pvpDefense.attack.pLv||1);
update.oppPlayers = oppPlayers;
}
}
// 保存
pvpDefense = await PvpDefenseModel.updateInfoAndInclude(roleId, update);
// 返回
let result = new PvpDataReturn();
result.setPvpDefense(pvpDefense);
let pickParam = ['buyAttackCnt', 'setAttackCnt', 'refOppCnt'];
if(refOppObj.shouldRefOpp) {
let groupId = await getPVPGroupIdOfServer(serverId);
let r = new Rank(REDIS_KEY.PVP_RANK, { seasonNum, groupId });
let oppPlayersReturn = await getEnemies(r, pvpDefense.oppPlayers||[], pvpDefense.winStreakNum);
result.setOppPlayers(oppPlayersReturn);
pickParam.push('oppPlayers');
}
return resResult(STATUS.SUCCESS, pick(result, pickParam));
}
async saveAttack(msg: { heroes: { actorId: number, order: number, subHid: number }[] }, session: BackendSession) {
let { heroes } = msg;
let roleId = session.get('roleId');
let serverId = session.get('serverId');
let pvpDefense = await PvpDefenseModel.findByRoleIdIncludeAll(roleId);
if(!pvpDefense) return resResult(STATUS.PVP_NOT_OPEN);
// 次数
let refOppObj = refreshRefOppCnt(pvpDefense);
if(refOppObj.setAttackCnt >= PVP.PVP_SET_ATTACK_FREE_CNT + refOppObj.buyAttackCnt) {
return resResult(STATUS.PVP_SET_ATTACK_CNT_NOT_ENOUGH);
}
if(!!pvpDefense.attack) {
refOppObj.setAttackCnt ++;
}
let seasonNum: number = this.app.get('pvpSeasonNum');
let seasonEndTime: number = this.app.get('pvpSeasonEndTime');
let refChallengeObj = await refChallengeCnt(pvpDefense.challengeCnt, pvpDefense.challengeRefTime, seasonEndTime, roleId, session.get('vipStartTime'));
// 更新挑战阵容
let scores: number[] = [];
for(let { actorId, subHid } of heroes) {
if(isHeroHidden(actorId)) return resResult(STATUS.HERO_IS_HIDDEN);
let heroScore = pvpDefense.heroScores.find(cur => cur.hid == actorId);
if(heroScore) {
scores.push(heroScore.score);
}
let dicHero = gameData.hero.get(actorId);
if(subHid && (!dicHero || dicHero.urType != 1)) return resResult(STATUS.HERO_CAN_NOT_SET_SUB);
if(subHid && heroes.findIndex(hero => hero.actorId == subHid) != -1) return resResult(STATUS.HERO_SUB_DUPLICATE);
}
let attack = new Attack(heroes, scores);
let lineupCe = await generPvpLineupCe(roleId, pvpDefense.lineupCe, attack.heroes, pvpDefense.defense?.heroes??[], []);
// 刷对手
let role = <RoleType>pvpDefense.role;
let oppPlayers = await refreshEnemies(role, seasonNum, pvpDefense.score, attack?.score||0, attack?.pLv||1);
// 保存
pvpDefense = await PvpDefenseModel.updateInfoAndInclude(roleId, { ...refOppObj, ...refChallengeObj, attack, oppPlayers, lineupCe });
// 返回
let result = new PvpDataReturn();
result.setPvpDefense(pvpDefense);
let groupId = await getPVPGroupIdOfServer(serverId);
let r = new Rank(REDIS_KEY.PVP_RANK, { seasonNum, groupId });
let oppPlayersReturn = await getEnemies(r, pvpDefense.oppPlayers||[], pvpDefense.winStreakNum);
result.setOppPlayers(oppPlayersReturn);
return resResult(STATUS.SUCCESS, pick(result, ['attack', 'oppPlayers', 'buyAttackCnt', 'setAttackCnt', 'challengeCnt', 'challengeRefTime']));
}
// 只保存副将
async saveAttackSub(msg: { heroes: { actorId: number, order: number, subHid: number }[] }, session: BackendSession) {
let { heroes } = msg;
let roleId = session.get('roleId');
let serverId = session.get('serverId');
let pvpDefense = await PvpDefenseModel.findByRoleIdIncludeAll(roleId);
if(!pvpDefense) return resResult(STATUS.PVP_NOT_OPEN);
// 更新挑战阵容
let oldAttack = pvpDefense.attack?.heroes||[];
let scores: number[] = [];
for(let { actorId, order, subHid } of heroes) {
let oldData = oldAttack.find(cur => cur.actorId == actorId);
if(!oldData || oldData.order != order) return resResult(STATUS.PVP_CAN_NOT_CHANGE_HERO);
if(isHeroHidden(actorId)) return resResult(STATUS.HERO_IS_HIDDEN);
let heroScore = pvpDefense.heroScores.find(cur => cur.hid == actorId);
if(heroScore) {
scores.push(heroScore.score);
}
let dicHero = gameData.hero.get(actorId);
if(subHid && (!dicHero || dicHero.urType != 1)) return resResult(STATUS.HERO_CAN_NOT_SET_SUB);
if(subHid && heroes.findIndex(hero => hero.actorId == subHid) != -1) return resResult(STATUS.HERO_SUB_DUPLICATE);
}
let attack = new Attack(heroes, scores);
let lineupCe = await generPvpLineupCe(roleId, pvpDefense.lineupCe, attack.heroes, pvpDefense.defense?.heroes??[], []);
// 保存
pvpDefense = await PvpDefenseModel.updateInfoAndInclude(roleId, { attack, lineupCe });
// 返回
let result = new PvpDataReturn();
result.setPvpDefense(pvpDefense);
return resResult(STATUS.SUCCESS, pick(result, ['attack']));
}
// 获取存档列表
async getSaveData(msg: {}, session: BackendSession) {
let roleId = session.get('roleId');
let pvpDefense = await PvpDefenseModel.findByRoleId(roleId);
if(!pvpDefense) return resResult(STATUS.PVP_NOT_OPEN);
let pvpConfig = await PVPConfigModel.findCurPVPConfig();
if(!pvpConfig) return resResult(STATUS.PVP_SEASON_NOT_OPEN);
let saveDatas = await PvpSaveDataModel.findByRoleId(roleId);
let list: pvpSaveDataReturn[] = [];
for(let warId of (pvpConfig.warIds||[])) {
let dicWar = gameData.war.get(warId);
if(dicWar && dicWar.selectView == 1) {
let data = new pvpSaveDataReturn(warId);
let curSaveData = saveDatas.find(cur => cur.warId == warId);
data.setUserSaveData(curSaveData);
if(pvpDefense.defense?.warId == warId) {
data.setAsUsing();
}
list.push(data);
}
}
return resResult(STATUS.SUCCESS, { list });
}
//3. 保存防守阵容
async saveDefense(msg: { heroes: { actorId: number, dataId: number, order: number, ai: number, subHid?: number }[], warId: number, buff: number, combo: Combo[] }, session: BackendSession) {
let { heroes, warId, buff, combo } = msg;
let roleId = session.get('roleId');
if (heroes.length > LINEUP_NUM || heroes.length <= 0) {
return resResult(STATUS.WRONG_PARMS);
}
if(checkPvpSeasonIsSummit()) {
return resResult(STATUS.PVP_CAN_NOT_SAVE_DEFENSE);
}
let pvpDefense = await PvpDefenseModel.findByRoleId(roleId);
if(!pvpDefense) return resResult(STATUS.PVP_NOT_OPEN);
// 刷新次数
let seasonNum: number = this.app.get('pvpSeasonNum');
let seasonEndTime: number = this.app.get('pvpSeasonEndTime');
let refChallengeObj = await refChallengeCnt(pvpDefense.challengeCnt, pvpDefense.challengeRefTime, seasonEndTime, roleId, session.get('vipStartTime'));
// 更新防守阵容
let scores: number[] = [], heroIdMap = new Map<number, string>(), hids: number[] = [];
for(let { actorId, subHid } of heroes) {
if(isHeroHidden(actorId)) return resResult(STATUS.HERO_IS_HIDDEN);
let dicHero = gameData.hero.get(actorId);
if(subHid && (!dicHero || dicHero.urType != 1)) return resResult(STATUS.HERO_CAN_NOT_SET_SUB);
if(subHid && heroes.findIndex(hero => hero.actorId == subHid) != -1) return resResult(STATUS.HERO_SUB_DUPLICATE);
let heroScore = pvpDefense.heroScores.find(cur => cur.hid == actorId);
if(heroScore) {
scores.push(heroScore.score);
}
// 记录hero的objectId用于存defense.heroes.hero
if(pvpDefense.defense && pvpDefense.defense.heroes) {
let heroInDefense = pvpDefense.defense.heroes.find(cur => cur.actorId == actorId);
if(heroInDefense) {
heroIdMap.set(actorId, <string>heroInDefense.hero);
} else {
hids.push(actorId);
}
} else {
hids.push(actorId);
}
}
let dbHeroes = await HeroModel.findByHidRange(hids, roleId, '_id hid ce', true);
for(let hero of dbHeroes) {
heroIdMap.set(hero.hid, hero._id);
}
let defenseHeroes = heroes.map(cur => {
return new DefenseHero(cur, heroIdMap.get(cur.actorId));
});
let defense = new Defense(defenseHeroes, scores, warId, buff, combo);
let lineupCe = await generPvpLineupCe(roleId, pvpDefense.lineupCe, pvpDefense.attack?.heroes??[], defense.heroes, dbHeroes);
await PvpSaveDataModel.createSaveData(roleId, warId, buff, defenseHeroes);
pvpDefense = await PvpDefenseModel.updateInfoAndInclude(roleId, { ...refChallengeObj, defense, lineupCe, hasDefense: true, seasonNum });
// 返回
let result = new PvpDataReturn();
result.setPvpDefense(pvpDefense);
return resResult(STATUS.SUCCESS, {...pick(result, ['defense', 'challengeCnt', 'challengeRefTime']), hasSaveDefense: true});
}
//8. 排行榜
async getRank(msg: {}, session: BackendSession) {
let roleId = session.get('roleId');
let serverId = session.get('serverId');
let seasonNum: number = this.app.get('pvpSeasonNum');
if(checkPvpSeasonIsWaiting()) { // 休赛期
seasonNum ++;
}
let serverNames = await getAllServerName();
let groupId = await getPVPGroupIdOfServer(serverId);
let r = new Rank(REDIS_KEY.PVP_RANK, { seasonNum, groupId });
r.setGenerFieldsFun(function (param: RoleRankInfo) {
let { serverId } = param;
return { ...param, serverName: serverNames[serverId]||'无' };
});
let { ranks, myRank } = await r.getRankListWithMyRank({ roleId });
if (!myRank) {
let pvpDefense = await PvpDefenseModel.findByRoleId(roleId);
myRank = await r.generMyRankWithRole(roleId, pvpDefense.score, getSeconds(pvpDefense.refDaily));
}
return resResult(STATUS.SUCCESS, { ranks, myRank });
}
//2.5. 获取对手详细信息(排行榜,战报,阵容查看)
async getPlayerDetail(msg: { roleId: string }, session: BackendSession) {
let roleId = session.get('roleId');
let serverId = session.get('serverId');
let { roleId: oppoRoleId } = msg;
let isRobot = checkRoleIsRobot(oppoRoleId);
let result: PlayerDetail;
if (isRobot) { // 如果是机器人从自己的pvpDefense中寻找
let pvpHistoryOpp = await PvpHistoryOppModel.findByRoleIdAndOppId(roleId, oppoRoleId);
if (!pvpHistoryOpp) {
return resResult(STATUS.PVP_ROLE_NOT_FOUND);
}
let heroes = new Array<PlayerDetailHero>();
for (let hero of pvpHistoryOpp.heroes) {
let newHero = new PlayerDetailHero();
newHero.setPvpHeroInfo(hero);
newHero.setIsDefense(true);
heroes.push(newHero);
}
for (let hero of pvpHistoryOpp.otherHeroes) {
let newHero = new PlayerDetailHero();
newHero.setPvpHeroInfo(hero);
newHero.setIsDefense(false);
heroes.push(newHero);
}
result = new PlayerDetail(pvpHistoryOpp.oppRoleId, heroes, pvpHistoryOpp.rankLv);
result.setRobot(pvpHistoryOpp);
if(pvpHistoryOpp.isRobot) {
result.setServerName(await getServerName(serverId));
} else {
let role = await RoleModel.findByRoleId(robotIdComBack(oppoRoleId), 'serverId');
result.setServerName(await getServerName(role.serverId));
}
} else { // 查询对方pvpDefense
let pvpDefense = await PvpDefenseModel.findByRoleId(oppoRoleId);
let dbHeroes = await HeroModel.findByRole(oppoRoleId);
let role = await RoleModel.findByRoleId(oppoRoleId);
let heroes = new Array<PlayerDetailHero>();
for (let dbHero of dbHeroes) {
let hero = new PlayerDetailHero();
hero.setByDbHero(dbHero);
hero.setPvpDefense(pvpDefense);
heroes.push(hero);
}
heroes.sort((a, b) => b.score - a.score);
let seasonNum: number = this.app.get('pvpSeasonNum');
let groupId = await getPVPGroupIdOfServer(serverId);
let r = new Rank(REDIS_KEY.PVP_RANK, { seasonNum, groupId });
let rank = await r.getMyRank({ roleId: oppoRoleId });//去redis中获取排名
result = new PlayerDetail(oppoRoleId, heroes, rank);
result.setPlayer(role);
result.setPvpDefense(pvpDefense);
result.setServerName(await getServerName(role.serverId));
}
return resResult(STATUS.SUCCESS, result);
}
//7. 领取巅峰之路奖励
async receiveBox(msg: { id: number }, session: BackendSession) {
let { id } = msg;
let roleId = session.get('roleId');
let sid: string = session.get('sid');
let serverId = session.get('serverId');
let roleName = session.get('roleName');
let seasonNum: number = this.app.get('pvpSeasonNum');
let seasonEndTime: number = this.app.get('pvpSeasonEndTime');
let { seasonWinNum, receivedBox, challengeCnt: lastChallengeCnt, challengeRefTime: lastChallengeRefTime } = await PvpDefenseModel.findByRoleIdIncludeAll(roleId);
let dicPvpBox = getPvpBoxBySeasonNumAndIndex(seasonNum, id);
if(!dicPvpBox) return resResult(STATUS.DIC_DATA_NOT_FOUND);
if (seasonWinNum < dicPvpBox.winTimes) {
return resResult(STATUS.PVP_NOT_REACH_BOX_SCORE);
}
if (receivedBox.indexOf(id) != -1) {
return resResult(STATUS.PVP_BOX_IS_GOT);
}
receivedBox.push(id);
let { challengeCnt, challengeRefTime } = await refChallengeCnt(lastChallengeCnt, lastChallengeRefTime, seasonEndTime, roleId, session.get('vipStartTime'));
await PvpDefenseModel.updateInfo(roleId, { receivedBox, challengeCnt, challengeRefTime });
let result = await addItems(roleId, roleName, sid, dicPvpBox.reward, ITEM_CHANGE_REASON.PVP_BOX_REWARD);
// 任务
await checkTask(serverId, roleId, sid, TASK_TYPE.PVP_RECEIVE_BOX);
return resResult(STATUS.SUCCESS, { goods: result, receivedBox, challengeCnt, challengeRefTime, seasonWinNum });
}
//9. 战报
async getRec(msg: {}, session: BackendSession) {
let roleId = session.get('roleId');
let pvpRecords = await PvpRecordModel.getRecByRoleId(roleId);
let serverNames = await getAllServerName();
for(let record of pvpRecords) {
// console.log('#####', record.defenseInfo.serverId, serverNames, serverNames[record.defenseInfo.serverId])
if(record.defenseInfo) record.defenseInfo['serverName'] = serverNames[record.defenseInfo.serverId];
if(record.attackInfo) record.attackInfo['serverName'] = serverNames[record.attackInfo.serverId];
}
return resResult(STATUS.SUCCESS, {
list: pvpRecords.map(rec => {
const { roleId1, battleCode, hasRpl, remoteUrl } = rec;
const rplFileUrl = battleCode && hasRpl ? remoteUrl : '';
return { ...rec, rplFileUrl };
}),
rplPrefixUrl: getRemoteRplPrefix(pinus.app.get('env'))
});
}
async buyChallengeCnt(msg: { count: number }, session: BackendSession) {
let { count } = msg;
let roleId = session.get('roleId');
let sid: string = session.get('sid');
let seasonEndTime: number = this.app.get('pvpSeasonEndTime');
let pvpDefense = await PvpDefenseModel.findByRoleId(roleId);
if (!pvpDefense) return resResult(STATUS.PVP_NOT_OPEN);
let isRefreshBuyChallengeCnt = refreshBuyChallengeCnt(pvpDefense.buyChallengeTime);
if (isRefreshBuyChallengeCnt) pvpDefense.buyChallengeCnt = 0;
if (count + pvpDefense.buyChallengeCnt > PVP.PVP_CHALLENGE_COST_TIMES) {
return resResult(STATUS.PVP_CAN_NOT_BUY_CHALLENGE_CAT);
}
let cost = parseGoodStr(PVP.PVP_CHALLENGE_BUY_TIMES_COST);
for (let cur of cost) {
cur.count = cur.count * count;
}
let costResult = await handleCost(roleId, sid, cost, ITEM_CHANGE_REASON.PVP_BUY_CHALLENGE_CNT);
if (!costResult) return resResult(STATUS.ROLE_MATERIAL_NOT_ENOUGH);
// 刷新次数
let refChallengeObj = await refChallengeCnt(pvpDefense.challengeCnt, pvpDefense.challengeRefTime, seasonEndTime, roleId, session.get('vipStartTime'));
let challengeCnt = refChallengeObj.challengeCnt + count;
pvpDefense = await PvpDefenseModel.updateInfoAndInclude(roleId, { challengeCnt, buyChallengeCnt: pvpDefense.buyChallengeCnt + count, buyChallengeTime: Date.now() });
if (!pvpDefense) return resResult(STATUS.PVP_BUY_CHALLENGE_FAIL)
return resResult(STATUS.SUCCESS, { challengeCnt: pvpDefense.challengeCnt, buyChallengeCnt: pvpDefense.buyChallengeCnt })
}
async sweep(msg: { warId: number, oppRoleId: string }, session: BackendSession) {
let roleId = session.get('roleId');
let roleName = session.get('roleName');
let sid = session.get('sid');
const serverId = session.get('serverId');
const { warId, oppRoleId } = msg;
let warInfo = gameData.war.get(warId);
if (!warInfo) {
return resResult(STATUS.BATTLE_MISS_INFO);
}
const role = await RoleModel.findByRoleId(roleId, null, true);
if (!role || role.lv < 50) return resResult(STATUS.PVP_NOT_OPEN);
let pvpDefense = await PvpDefenseModel.findByRoleId(roleId);
if (!pvpDefense) return resResult(STATUS.PVP_NOT_OPEN);
let { hisWinStreakNum = 0 } = pvpDefense;
// 检查挑战次数
let seasonNum: number = this.app.get('pvpSeasonNum');
let seasonEndTime: number = this.app.get('pvpSeasonEndTime');
let { challengeCnt, challengeRefTime } = await refChallengeCnt(pvpDefense.challengeCnt, pvpDefense.challengeRefTime, seasonEndTime, roleId, session.get('vipStartTime'));
if (challengeCnt == 0) {
return resResult(STATUS.PVP_CHALLENGE_TIMES_NOT_ENOUGH);
}
let { attackCe } = PvpDataReturn.getAttackAndDefenseCe(pvpDefense);
let curOpp = pvpDefense.oppPlayers.find(cur => cur.roleId == oppRoleId);
if (!curOpp) return resResult(STATUS.PVP_ROLE_NOT_FOUND);
let oppDef = <PvpHistoryOppType>curOpp.oppDef;
// 战力检测
console.log('xxxxxxxxxxxxxxxxxxxxxxxxx attackCe',attackCe)
console.log('xxxxxxxxxxxxxxxxxxxxxxxxx oppDef',oppDef)
if (attackCe <= oppDef.defCe) return resResult(STATUS.PVP_SWEEP_NOT_CE);
// 对手记录更新
await PvpHistoryOppModel.setStatus(curOpp.oppDef.toString(), 1);
// 战斗记录
const battleCode = genCode(8) + '_sweep'; // 关卡唯一值
let heroes = pvpDefense.attack.heroes.map(cur => cur.actorId);
await BattleRecordModel.updateBattleRecordByCode(battleCode, {
$set: {
roleId, roleName, battleId: warId,
status: 0,
warName: warInfo.gk_name,
warType: warInfo.warType,
record: { heroes, pos: curOpp.pos, oppRoleId, pvpSeasonNum: seasonNum }
}
}, true);
let update: pvpUpdateInter = {};
const myHeroes: pvpEndParamInter[] = heroes.map(hid => { return { hid, damage: 0, heal: 0, underDamage: 0 } })
const myRecInfo = await generMyRecInfo(pvpDefense, role, true, curOpp.pos, myHeroes);
let { attackInfo, showHeroScores, updateParam } = myRecInfo
update = { ...update, ...updateParam };
const oppHeroes: pvpEndParamInter[] = oppDef.heroes.map(cur => { return { hid: cur.actorId, damage: 0, heal: 0, underDamage: 0 } })
const defenseInfo = await generPVPOppRecInfo(true, curOpp, oppHeroes, serverId);
update = { ...update, oppBeforePlayers: pvpDefense.oppPlayers };
// 刷新对手
let oppPlayers = await refreshEnemies(role, seasonNum, pvpDefense.score, pvpDefense.attack.score, pvpDefense.attack.pLv || 1);
update.oppPlayers = oppPlayers;
// 减少挑战次数
let refChallengeObj = await refChallengeCnt(pvpDefense.challengeCnt, pvpDefense.challengeRefTime, seasonEndTime, roleId, session.get('vipStartTime'));
refChallengeObj.challengeCnt--;
update = { ...update, ...refChallengeObj };
pvpDefense = await PvpDefenseModel.updateInfoAndInclude(roleId, update);
let result = new PvpDataReturn(); // 返回对象
result.setPvpDefense(pvpDefense);
// 加入排行榜
let groupId = await getPVPGroupIdOfServer(serverId);
let r = new Rank(REDIS_KEY.PVP_RANK, { seasonNum, groupId });
await r.setRankWithRoleInfo(roleId, pvpDefense.score, pvpDefense.updatedAt.getTime(), role);
if (defenseInfo && defenseInfo.score > 0) await r.setRankWithRoleInfo(defenseInfo.roleId, defenseInfo.score, pvpDefense.updatedAt.getTime(), null, true);
let myRank = await r.getMyRank({ roleId });
result.setMyRank(myRank);
let oppPlayersReturn = await getEnemies(r, pvpDefense.oppPlayers, pvpDefense.winStreakNum);
result.setOppPlayers(oppPlayersReturn);
await checkTaskInPvpEnd(serverId, roleId, sid, true, pvpDefense.heroScores, pvpDefense.seasonWinNum);
if (hisWinStreakNum < pvpDefense.hisWinStreakNum) {
await unlockFigure(sid, roleId, [{ type: FIGURE_UNLOCK_CONDITION.PVP_WIN_SERIES, paramWinStreakNum: pvpDefense.hisWinStreakNum }]);
}
return resResult(STATUS.SUCCESS, {
battleCode, isSuccess: true,
heroScores: showHeroScores,
createdAt: getSeconds(new Date()),
...pick(result, ['score', 'myRank', 'winStreakNum', 'attack', 'oppPlayers', 'challengeCnt', 'challengeRefTime', 'seasonWinNum'])
});
}
// debug接口
async addRoleScore(msg: { heroScores: Array<{ hid: number, score: number }> }, session: BackendSession) {
let { heroScores: addHeroScores } = msg;
let roleId = session.get('roleId');
let sid = session.get('sid');
let serverId = session.get('serverId');
let { heroScores, hisScore, attack, defense } = await PvpDefenseModel.findByRoleId(roleId);
let score = 0;
for (let { hid, score: heroScore } of addHeroScores) {
if (heroScore < 0) continue;
let index = findIndex(heroScores, { hid });
if (index != -1) {
heroScores[index].score = heroScore;
} else {
heroScores.push({ hid, score: heroScore });
}
}
for (let heroScore of heroScores) {
score += heroScore.score;
}
if (score < 0) {
score = 0;
}
if (hisScore < score) {
hisScore = score;
}
let newAttack = <Attack>calLineupScore(attack, heroScores);
let newDefense = <Defense>calLineupScore(defense, heroScores);
let pvpDefense = await PvpDefenseModel.updateInfoAndInclude(roleId, { score, hisScore, heroScores, attack: newAttack, defense: newDefense });
const role = await RoleModel.findByRoleId(roleId);
// 加入排行榜
let seasonNum: number = this.app.get('pvpSeasonNum');
let groupId = await getPVPGroupIdOfServer(serverId);
let r = new Rank(REDIS_KEY.PVP_RANK, { seasonNum, groupId });
await r.setRankWithRoleInfo(roleId, pvpDefense.score, pvpDefense.updatedAt.getTime(), role);
// 任务
await checkTask(serverId, roleId, sid, TASK_TYPE.PVP_HERO_SCORE, { heroScores });
return resResult(STATUS.SUCCESS, { score, hisScore, heroScores });
}
async debugPvpSeasonResetTime(msg: {}, session: BackendSession) {
return resResult(STATUS.DEBUG_FUNCTION_ERR);
}
async debugAddChallengeCnt(msg: { challengeCnt: number }, session: BackendSession) {
let { challengeCnt } = msg;
let roleId = session.get('roleId');
if (challengeCnt > PVP.PVP_CHALLENGE_COUNTS) {
challengeCnt = PVP.PVP_CHALLENGE_COUNTS;
}
let { challengeRefTime } = await PvpDefenseModel.updateInfoAndInclude(roleId, { challengeCnt, challengeRefTime: nowSeconds(), refOppCnt: 0 });
return resResult(STATUS.SUCCESS, { challengeCnt, challengeRefTime });
}
async debugResetRefOppCnt(msg: { magicWord: string }, session: BackendSession) {
const { magicWord } = msg;
if (magicWord !== DEBUG_MAGIC_WORD) {
return resResult(STATUS.TOKEN_ERR);
}
let roleId = session.get('roleId');
await PvpDefenseModel.updateInfo(roleId, { refOppCnt: 0 });
return resResult(STATUS.SUCCESS);
}
async debugResetAttackCnt(msg: { magicWord: string }, session: BackendSession) {
const { magicWord } = msg;
if (magicWord !== DEBUG_MAGIC_WORD) {
return resResult(STATUS.TOKEN_ERR);
}
let roleId = session.get('roleId');
await PvpDefenseModel.updateInfo(roleId, { setAttackCnt: 0, buyAttackCnt: 0 });
return resResult(STATUS.SUCCESS);
}
async debugResetBox(msg: { magicWord: string }, session: BackendSession) {
const { magicWord } = msg;
if (magicWord !== DEBUG_MAGIC_WORD) {
return resResult(STATUS.TOKEN_ERR);
}
let roleId = session.get('roleId');
await PvpDefenseModel.updateInfo(roleId, { hisScore: 5000, receivedBox: [] });
return resResult(STATUS.SUCCESS);
}
}