Files
ZYZ/game-server/app/servers/battle/handler/ladderHandler.ts
2023-09-01 18:09:52 +08:00

455 lines
21 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 { Application, BackendSession, pinus, HandlerService, } from 'pinus';
import { isArray, pick } from 'underscore';
import { gameData } from '../../../pubUtils/data';
import { STATUS } from '../../../consts/statusCode';
import { arrToMap, resResult } from '../../../pubUtils/util';
import { getTimeFunM } from '../../../pubUtils/timeUtil';
import { LadderMatchModel, LadderUpdateInter } from '../../../db/LadderMatch';
import { battleEndWhenChange, checkRank, generateInitRecInfo, generateOppPlayers, getBuyCntCost, getLadderData, getLadderEnemies, getLadderOppDetailData, getLadderOppStatus, getNumberArr, ladderBattleEndReward, pushLadderIconShow, refreshLadderDaily, refreshLadderEnemies, sendLadderDailyReward, uniqueArr } from '../../../services/ladderService';
import { LadderDataReturn, LadderDefense, LadderDefenseHero, LadderOppDetailReturn, LadderOppLineupReturn, LadderOppPlayerHeroInfo, LadderOppPlayerReturn } from '../../../domain/battleField/ladder';
import { LadderMatchRecModel } from '../../../db/LadderMatchRec';
import { HeroModel } from '../../../db/Hero';
import { LADDER } from '../../../pubUtils/dicParam';
import { handleCost } from '../../../services/role/rewardService';
import { DEBUG_MAGIC_WORD, ITEM_CHANGE_REASON, LADDER_OPP_STATUS, LADDER_STATUS, REDIS_KEY, WAR_TYPE } from '../../../consts';
import { checkBattleHeroesByHid, getBattleRecordParam } from '../../../services/normalBattleService';
import { ServerlistModel } from '../../../db/Serverlist';
import { saveLadderDefCeByData } from '../../../services/redisService';
import { pushLadderTopChangeMsg } from '../../../services/sysChatService';
import { checkTaskInLadderEnd, checkTaskInLadderStart, checkTaskInLadderSweep } from '../../../services/task/taskService';
import { BattleRecordModel } from '../../../db/BattleRecord';
import { isHeroHidden } from '../../../services/dataService';
import { getRemoteRplFilePath, getRemoteRplPrefix } from '../../../pubUtils/battleUtils';
import { pvpEndParamInter } from '../../../pubUtils/interface';
import { Combo } from '../../../domain/battleField/pvp';
export default function (app: Application) {
new HandlerService(app, {});
return new LadderHandler(app);
}
export class LadderHandler {
constructor(private app: Application) {
}
// 1. 获取主界面
async getData(msg: {}, session: BackendSession) {
let roleId = session.get('roleId');
let result = await getLadderData(roleId, true)
return resResult(STATUS.SUCCESS, result);
}
// 2. 获取对手阵容信息
async getOppLineup(msg: { roleId: string, rank: number }, session: BackendSession) {
let roleId: string = session.get('roleId');
let { roleId: targetRoleId, rank } = msg;
let ladderData = await LadderMatchModel.findByRoleId(roleId);
if(!ladderData || !ladderData.defense) return resResult(STATUS.LADDER_NOT_OPEN);
let isRobot = targetRoleId.startsWith('robot');
let result = new LadderOppLineupReturn();
result.setMyRank(ladderData.rank);
if(isRobot) {
let dicLadderDifficultRatio = gameData.ladderDifficultRatio.get(rank);
let dicWar = gameData.war.get(dicLadderDifficultRatio.gkId);
let dicWarJson = gameData.warJson.get(dicWar.dispatchJsonId);
result.setRobot(dicLadderDifficultRatio, dicWarJson);
} else {
let hisLadderData = await LadderMatchModel.findOne({ roleId: targetRoleId })
.populate('role', 'roleId roleName head frame spine heads frames spines title lv guildName updatedAt')
.populate('defense.heroes.hero', 'hid skinId quality star colorStar lv skins job artifact subHid subActorId')
.lean();
result.setPlayer(hisLadderData);
}
return resResult(STATUS.SUCCESS, result);
}
// 3. 点击挑战
async chooseOpp(msg: { roleId: string, rank: number, myRank: number }, session: BackendSession) {
let roleId = session.get('roleId');
let serverId = session.get('serverId');
let sid = session.get('sid');
let { roleId: targetRoleId, rank, myRank } = msg;
// 检查双方排名等,不行的刷新对手回去
let ladderData = await LadderMatchModel.findByRoleId(roleId);
if(!ladderData || !ladderData.defense) return resResult(STATUS.LADDER_NOT_OPEN);
if(!checkRank(myRank, rank)) return resResult(STATUS.LADDER_RANK_ERROR);
let { status, isRobot, hisLadderData } = await getLadderOppStatus(ladderData, targetRoleId, myRank, rank);
if(status != LADDER_OPP_STATUS.BATTLE) {
// await LadderMatchModel.unlock(serverId, targetRoleId);
let oppPlayers = await refreshLadderEnemies(ladderData);
return resResult(STATUS.SUCCESS, {
status, oppPlayers
});
}
// 刷新次数
let refOppObj = refreshLadderDaily(ladderData);
let update: LadderUpdateInter = {};
if(refOppObj.shouldRefOpp) {
update = { ...refOppObj };
}
if(refOppObj.challengeCnt + 1 > LADDER.LADDER_CHALLENGE_FREE_TIMES + refOppObj.buyCnt) {
await LadderMatchModel.unlock(serverId, targetRoleId);
return resResult(STATUS.LADDER_CHALLENGE_CNT_MAX);
}
update.challengeCnt = refOppObj.challengeCnt + 1;
ladderData = await LadderMatchModel.updateByRoleIdAndInclude(roleId, update);
// 创建ladderMatchRec发行battleCode
let attackInfo = await generateInitRecInfo(false, false, ladderData.rank, ladderData);
let defenseInfo = await generateInitRecInfo(isRobot, true, rank, hisLadderData);
let rec = await LadderMatchRecModel.createRec(serverId, roleId, targetRoleId, hisLadderData?.defense, attackInfo, defenseInfo);
// 倒计时倒计时结束没有check设为失败并发失败通知
pinus.app.rpc.systimer.systimerRemote.setLadderCountDown.broadcast(rec.battleCode, rec.checkTime, LADDER_STATUS.CHECK);
let result = await getLadderOppDetailData(rec);
await checkTaskInLadderStart(serverId, roleId, sid);
return resResult(STATUS.SUCCESS, {
status,
challengeCnt: ladderData.challengeCnt,
...result
});
}
// 4. 出战撤退
async giveupCheck(msg: { battleCode: string }, session: BackendSession) {
let roleId = session.get('roleId');
let { battleCode } = msg;
// 更新ladderMatchRec
let rec = await LadderMatchRecModel.giveup(roleId, battleCode);
if(rec) {
pinus.app.rpc.systimer.systimerRemote.cancelLadderCountDown.broadcast(rec.battleCode);
if(rec.defenseInfo && !rec.defenseInfo.isRobot) await LadderMatchModel.unlock(rec.serverId, rec.defenseInfo.roleId);
}
return resResult(STATUS.SUCCESS, { status: 0, time: 0 });
}
// 5. 获取对手具体战场数据
async getOppData(msg: { battleCode: string }, session: BackendSession) {
let roleId = session.get('roleId');
let { battleCode } = msg;
let rec = await LadderMatchRecModel.findByBattleCode(battleCode);
if(!rec || rec.roleId1 != roleId) return resResult(STATUS.LADDER_REC_NOT_FOUND);
if(rec.status == LADDER_STATUS.COMPLETE || rec.status == LADDER_STATUS.NO) return resResult(STATUS.LADDER_REC_STATUS_IS_COMPLETE);
let result = await getLadderOppDetailData(rec);
return resResult(STATUS.SUCCESS, result);
}
// 6. 布完阵开始挑战
async checkBattle(msg: { battleCode: string, battleId: number, heroes: { actorId: number, order: number }[] }, session: BackendSession) {
let roleId = session.get('roleId');
let roleName = session.get('roleId');
let { battleId, battleCode, heroes } = msg;
let rec = await LadderMatchRecModel.findByBattleCode(battleCode);
if(!rec || rec.roleId1 != roleId) return resResult(STATUS.LADDER_REC_NOT_FOUND);
if(rec.status != LADDER_STATUS.CHECK) return resResult(STATUS.LADDER_REC_STATUS_ERR);
for(let { actorId } of heroes) {
if(isHeroHidden(actorId)) return resResult(STATUS.HERO_IS_HIDDEN);
}
let { isOK, heroes: dbHeroes } = await checkBattleHeroesByHid(roleId, heroes.map(cur => cur.actorId));
if(!isOK) return resResult(STATUS.BATTLE_HERO_NOT_FOUND);
let attackHeroes = dbHeroes.map(hero => {
let heroInfo = new LadderOppPlayerHeroInfo();
heroInfo.setByDefenseHero(hero);
return heroInfo;
});
let warInfo = gameData.war.get(battleId);
await BattleRecordModel.updateBattleRecordByCode(battleCode, {
$set: {
roleId, roleName, battleId,
status: 0,
warName: warInfo?.gk_name,
warType: warInfo?.warType,
}
});
rec = await LadderMatchRecModel.startBattle(battleCode, attackHeroes);
pinus.app.rpc.systimer.systimerRemote.setLadderCountDown.broadcast(rec.battleCode, rec.battleTime, LADDER_STATUS.BATTLE);
return resResult(STATUS.SUCCESS, {
time: Math.floor(rec.battleTime/1000) + LADDER.LADDER_BATTLE_COUNTDOWN,
battleCode: rec.battleCode
})
}
// 7. 挑战结算
async battleEnd(msg: { battleCode: string, isSuccess: boolean, damageRecords: pvpEndParamInter[], round: number }, session: BackendSession) {
let roleId = session.get('roleId');
let roleName = session.get('roleName');
let serverId = session.get('serverId');
let sid = session.get('sid');
let { battleCode, isSuccess, damageRecords, round } = msg;
let rec = await LadderMatchRecModel.findByBattleCode(battleCode);
if(!rec || rec.roleId1 != roleId) return resResult(STATUS.LADDER_REC_NOT_FOUND);
if(rec.status != LADDER_STATUS.BATTLE) return resResult(STATUS.LADDER_REC_STATUS_ERR);
let rank = rec.defenseInfo.oldRank;
let ladderData = await LadderMatchModel.findByRoleId(roleId);
if(!ladderData || !ladderData.defense) return resResult(STATUS.LADDER_NOT_OPEN);
// 取消 checkBattle 的倒计时
pinus.app.rpc.systimer.systimerRemote.cancelLadderCountDown.broadcast(battleCode);
let result = new LadderDataReturn();
// 交换双方排名, transaction
let { isChange, atkLadderMatch, defLadderMatch, endTime } = await LadderMatchModel.changeRank(isSuccess, rec.attackInfo, rec.defenseInfo);
if(isChange) {
let oppPlayers = await battleEndWhenChange(atkLadderMatch, defLadderMatch, true);
result.setOppPlayers(oppPlayers);
rec = await LadderMatchRecModel.battleEnd(battleCode, isSuccess, endTime, atkLadderMatch.rank, defLadderMatch? defLadderMatch.rank: rec.attackInfo.oldRank);
if(atkLadderMatch.rank == 1) {
pushLadderTopChangeMsg(roleId, roleName, serverId);
}
} else {
rec = await LadderMatchRecModel.battleEnd(battleCode, isSuccess, endTime);
let oppPlayers = await getLadderEnemies(atkLadderMatch);
result.setOppPlayers(oppPlayers);
}
await BattleRecordModel.updateBattleRecordByCode(battleCode, { $set: { status: isSuccess? 1: 2, ...getBattleRecordParam(damageRecords, round) } });
if(defLadderMatch) {
await LadderMatchModel.unlock(defLadderMatch.serverId, defLadderMatch.roleId);
}
let historyRank = ladderData.historyRank;
ladderData = await LadderMatchModel.updateByRoleId(roleId, { historyRank: (historyRank > atkLadderMatch.rank || historyRank == 0)? atkLadderMatch.rank: historyRank });
result.setLadderData(ladderData, rec);
// 获取奖励
let rewardResult = await ladderBattleEndReward(roleId, roleName, sid, ladderData.historyRank, isSuccess, historyRank, 1);
let { battleGoods = [], breakGoods = [] } = rewardResult||{};
await checkTaskInLadderEnd(serverId, roleId, sid, isSuccess, historyRank, atkLadderMatch.rank);
if (isSuccess && (!rec.defenseInfo?.isRobot)) await pushLadderIconShow(rec.roleId2, true);
return resResult(STATUS.SUCCESS, {...pick(result, ['rank', 'historyRank', 'challengeCnt','status', 'time', 'oppPlayers']), battleGoods, breakGoods});
}
// 8. 战5次扫荡
async battleSweep(msg: { roleId: string, rank: number, count: number }, session: BackendSession) {
let serverId = session.get('serverId');
let roleId = session.get('roleId');
let roleName = session.get('roleName');
let sid = session.get('sid');
let { roleId: targetRoleId, rank, count } = msg;
if(count > LADDER.LADDER_INITIAL_CHALLENGE_TIMES) {
return resResult(STATUS.LADDER_SWEEP_TIMES_OVER);
}
let ladderData = await LadderMatchModel.findByRoleId(roleId);
if(!ladderData || !ladderData.defense) return resResult(STATUS.LADDER_NOT_OPEN);
if(ladderData.rank >= rank) return resResult(STATUS.LADDER_RANK_ERR);
// 刷新次数
let refOppObj = refreshLadderDaily(ladderData);
let update: LadderUpdateInter = {};
if(refOppObj.shouldRefOpp) {
update = { ...refOppObj };
}
if(refOppObj.challengeCnt + count > LADDER.LADDER_CHALLENGE_FREE_TIMES + refOppObj.buyCnt) {
return resResult(STATUS.LADDER_CHALLENGE_CNT_MAX);
}
update.challengeCnt = refOppObj.challengeCnt + count;
ladderData = await LadderMatchModel.updateByRoleIdAndInclude(roleId, update);
// 获取奖励
let { battleGoods } = await ladderBattleEndReward(roleId, roleName, sid, rank, true, rank, count);
await checkTaskInLadderSweep(serverId, roleId, sid, count)
return resResult(STATUS.SUCCESS, {
challengeCnt: ladderData.challengeCnt,
battleGoods
});
}
// 9. 设置防守阵容
async saveDefense(msg: { warId: number, heroes: { actorId: number, ai: number, dataId: number, order: number, subHid?: number }[], combo: Combo[] }, session: BackendSession) {
let { warId, heroes, combo } = msg;
let roleId = session.get('roleId');
let ladderData = await LadderMatchModel.findByRoleId(roleId);
if(!ladderData) return resResult(STATUS.LADDER_NOT_OPEN);
let hids: number[] = heroes.map(cur => cur.actorId);
if(isHeroHidden(...hids)) return resResult(STATUS.HERO_IS_HIDDEN);
for(let { actorId, subHid } of heroes) {
let dicHero = gameData.hero.get(actorId);
if(subHid && (!dicHero || dicHero.urType != 1)) return resResult(STATUS.HERO_CAN_NOT_SET_SUB);
if(subHid && hids.indexOf(subHid) != -1) return resResult(STATUS.HERO_SUB_DUPLICATE);
}
let dbHeroes = await HeroModel.findByHidRange(hids, roleId, '_id hid ce', true);
let defenseHeroes = heroes.map(cur => {
let dbHero = dbHeroes.find(ccur => ccur.hid == cur.actorId);
return dbHero && new LadderDefenseHero(cur, dbHero._id, dbHero.ce);
}).filter(cur => cur);
let defense = new LadderDefense(defenseHeroes, warId, combo);
ladderData = await LadderMatchModel.updateByRoleId(roleId, { defense, hasDefense: true });
// 返回
let result = new LadderDataReturn();
result.setLadderData(ladderData);
await saveLadderDefCeByData(roleId, ladderData);
return resResult(STATUS.SUCCESS, pick(result, 'defense'));
}
// 10. 刷新对手
async refreshOppPlayers(msg: {}, session: BackendSession) {
let roleId = session.get('roleId');
let sid = session.get('sid');
let ladderData = await LadderMatchModel.findByRoleId(roleId);
if(!ladderData || !ladderData.defense) return resResult(STATUS.LADDER_NOT_OPEN);
let update: LadderUpdateInter = {};
// 刷新次数
let refOppObj = refreshLadderDaily(ladderData);
if(refOppObj.shouldRefOpp) {
update = { ...refOppObj };
}
if(refOppObj.refOppCnt + 1 > LADDER.LADDER_REFRESH_TIMES) {
return resResult(STATUS.LADDER_REFRESH_CNT_MAX);
}
update.refOppCnt = refOppObj.refOppCnt + 1;
let oppPlayers = await refreshLadderEnemies(ladderData, update);
// 返回
return resResult(STATUS.SUCCESS, { refOppCnt: update.refOppCnt, oppPlayers });
}
// 11. 购买次数
async buyCnt(msg: { count: number }, session: BackendSession) {
let { count } = msg;
let roleId = session.get('roleId');
let sid = session.get('sid');
let ladderData = await LadderMatchModel.findByRoleId(roleId);
if(!ladderData) return resResult(STATUS.LADDER_NOT_OPEN);
let update: LadderUpdateInter = {};
// 刷新次数
let refOppObj = refreshLadderDaily(ladderData);
if(refOppObj.shouldRefOpp) {
update = { ...refOppObj };
}
if(refOppObj.buyCnt + count > LADDER.LADDER_CHALLENGE_COST_TIMES) {
return resResult(STATUS.LADDER_BUY_MAX);
}
let consumes = getBuyCntCost(refOppObj.buyCnt, count);
if(!consumes) return resResult(STATUS.LADDER_BUY_MAX);
let consumeResult = await handleCost(roleId, sid, consumes||[], ITEM_CHANGE_REASON.LADDER_BUY_CNT);
if(!consumeResult) return resResult(STATUS.ROLE_MATERIAL_NOT_ENOUGH);
update.buyCnt = refOppObj.buyCnt + count;
ladderData = await LadderMatchModel.updateByRoleId(roleId, update);
// 返回
let result = new LadderDataReturn();
result.setLadderData(ladderData);
return resResult(STATUS.SUCCESS, pick(result, ['buyCnt', 'challengeCnt']));
}
// 12. 获取战报信息2天内
async getRec(msg: {}, session: BackendSession) {
let roleId = session.get('roleId');
let list = await LadderMatchRecModel.findRecentRec(roleId, <number>getTimeFunM().getBeforeDay(2));
return resResult(STATUS.SUCCESS, {
list: list.map(rec => {
const { roleId1, battleCode, hasRpl, remoteUrl } = rec;
const rplFileUrl = battleCode && hasRpl ? remoteUrl : '';
return { ...rec, rplFileUrl };
}),
rplPrefixUrl: getRemoteRplPrefix(pinus.app.get('env'))
});
}
// debug接口
// 重置挑战次数 & 购买次数
async resetCnt(msg: { magicWord: string }, session: BackendSession) {
const { magicWord } = msg;
if (magicWord !== DEBUG_MAGIC_WORD) {
return resResult(STATUS.TOKEN_ERR);
}
let roleId = session.get('roleId');
let ladderData = await LadderMatchModel.findByRoleId(roleId);
if(!ladderData) return resResult(STATUS.LADDER_NOT_OPEN);
await LadderMatchModel.updateByRoleId(roleId, { refDaily: new Date(Date.now() - 86400000) });
return resResult(STATUS.SUCCESS);
}
async setMyRank(msg: { magicWord: string, rank: number }, session: BackendSession) {
const { magicWord, rank } = msg;
if (magicWord !== DEBUG_MAGIC_WORD) {
return resResult(STATUS.TOKEN_ERR);
}
let roleId = session.get('roleId');
let serverId = session.get('serverId');
let atkLadderMatch = await LadderMatchModel.findByRoleId(roleId);
if(!atkLadderMatch || !atkLadderMatch.defense) return resResult(STATUS.LADDER_NOT_OPEN);
let myRank = atkLadderMatch.rank;
let defLadderMatch = await LadderMatchModel.findByRank(serverId, rank);
if(defLadderMatch) {
atkLadderMatch = await LadderMatchModel.findOneAndUpdate({ roleId }, { $set: { rank, oldRank: myRank, historyRank: rank } }, { new: true }).lean();
defLadderMatch = await LadderMatchModel.findOneAndUpdate({ roleId: defLadderMatch.roleId }, { $set: { rank: myRank, oldRank: rank } }, { new: true }).lean();
} else {
atkLadderMatch = await LadderMatchModel.findOneAndUpdate({ roleId }, { $set: { rank, oldRank: atkLadderMatch.rank, historyRank: rank } }, { new: true }).lean();
}
await battleEndWhenChange(atkLadderMatch, defLadderMatch, true);
return resResult(STATUS.SUCCESS);
}
async setMyOpp(msg: { magicWord: string, oppRanks: number[] }, session: BackendSession) {
const { magicWord, oppRanks } = msg;
if (magicWord !== DEBUG_MAGIC_WORD) {
return resResult(STATUS.WRONG_PARMS);
}
if(!isArray(oppRanks) || oppRanks.length <= 0) return resResult(STATUS.WRONG_PARMS);
let roleId = session.get('roleId');
let serverId = session.get('serverId');
let ladderData = await LadderMatchModel.findByRoleId(roleId);
if(!ladderData) return resResult(STATUS.LADDER_NOT_OPEN);
let topTen = getNumberArr(1, 10);
let ranks = [...topTen, ...oppRanks];
let newRanks = uniqueArr(ranks.sort((a, b) => a - b));
await generateOppPlayers(newRanks, serverId, roleId);
return resResult(STATUS.SUCCESS);
}
async sendDailyReward(msg: { magicWord: string }, session: BackendSession) {
const { magicWord } = msg;
if (magicWord !== DEBUG_MAGIC_WORD) {
return resResult(STATUS.TOKEN_ERR);
}
let servers = await ServerlistModel.findByEnv(pinus.app.get('env'));
for(let { id } of servers) {
await sendLadderDailyReward(id);
}
return resResult(STATUS.SUCCESS)
}
}