import { ITEM_CHANGE_REASON, LADDER_OPP_STATUS, LADDER_STATUS, MAIL_TYPE, PUSH_ROUTE, REDIS_KEY } from "../consts"; import { HeroType } from "../db/Hero"; import { LadderMatchModel, LadderMatchType, LadderUpdateInter } from "../db/LadderMatch"; import { LadderMatchRecModel, LadderMatchRecType } from "../db/LadderMatchRec"; import { RoleModel, RoleType } from "../db/Role"; import { LadderDataReturn, LadderDefense, LadderOppDetailReturn, LadderOppPlayerHeroInfo, LadderOppPlayerInDB, LadderOppPlayerInfo, LadderOppPlayerReturn } from "../domain/battleField/ladder"; import { RoleAndGuildRankInfo, RoleRankInfo } from "../domain/rank"; import { gameData, getDicLadderMatchByMyRank, getLadderRankReward } from "../pubUtils/data"; import { EXTERIOR, LADDER } from "../pubUtils/dicParam"; import { ItemInter, RewardInter } from "../pubUtils/interface"; import { getRandValueByMinMax, shouldRefresh } from "../pubUtils/util"; import { sendMailByContent } from "./mailService"; import { getHeroesAttributes } from "./playerCeService"; import { sendMessageToUserWithSuc } from "./pushService"; import { Rank } from "./rankService"; import { addItems } from "./role/rewardService"; import { combineItems } from "./role/util"; import { WarReward } from "./warRewardService"; export function refreshLadderDaily(ladderData: LadderMatchType) { let { refOppCnt = 0, buyCnt = 0, challengeCnt = 0, refDaily } = ladderData; let curTime = new Date(); let shouldRefOpp = shouldRefresh(refDaily, curTime); if (shouldRefOpp) { refOppCnt = 0; buyCnt = 0; challengeCnt = 0; refDaily = curTime; } return { shouldRefOpp, refOppCnt, refDaily, buyCnt, challengeCnt, } } export function getBuyCntCost(buyCnt: number, incCnt: number) { let items: ItemInter[] = []; for(let i = 1; i <= incCnt; i++) { let cost = getSingleBuyCost(buyCnt + i); if(!cost) return false; items.push(cost); } return combineItems(items); } function getSingleBuyCost(buyCnt: number) { for(let { id, count, times } of gameData.ladderConsume) { if(buyCnt <= times) { return { id, count } } } return false } export async function getLadderData(roleId: string, needOppPlayer: boolean) { let ladderData = await LadderMatchModel.findByRoleId(roleId); if(!ladderData) { let role = await RoleModel.findByRoleId(roleId); ladderData = await LadderMatchModel.createLadder({ serverId: role.serverId, roleId: role.roleId, role: role._id }); } // 刷新次数 let refOppObj = refreshLadderDaily(ladderData); if(refOppObj.shouldRefOpp) { ladderData = await LadderMatchModel.updateByRoleId(roleId, refOppObj); } let ladderRec = await LadderMatchRecModel.findNotCompleteRecByRoleId(roleId); let result = new LadderDataReturn(); result.setLadderData(ladderData, ladderRec); if(needOppPlayer) { let oppPlayersReturn = await getLadderEnemies(ladderData); result.setOppPlayers(oppPlayersReturn); } return result; } export async function getLadderEnemies(ladderData: LadderMatchType) { let oppPlayers = ladderData.oppPlayers||[]; if(!oppPlayers || oppPlayers.length) { return await getOppPlayerDetailByDb(ladderData.serverId, ladderData.oppPlayers); } else { return await refreshLadderEnemies(ladderData); } } async function getOppPlayerDetailByDb(serverId: number, oppPlayers: LadderOppPlayerInDB[]) { let oppPlayersReturn: LadderOppPlayerReturn[] = []; for(let oppPlayer of oppPlayers) { let player = await generateOppPlayer(serverId, oppPlayer.rank, oppPlayers); oppPlayersReturn.push(player); } return oppPlayersReturn; } // 获取redis当中的玩家排名数据 export async function getLadderMatchByRank(serverId: number, max: number|'+inf', min: number|'-inf') { let r = new Rank(REDIS_KEY.LADDER, { serverId }, false, 4000); let rawResults = await r.getRankByRangeRaw(max, min, false); return rawResults.map(cur => ({ rank: cur.scores[0], roleId: cur.field })) } export async function refreshLadderEnemies(ladderData: LadderMatchType, update: LadderUpdateInter = {}) { let rankNumbers = randOppRankNumbers(ladderData.rank); // 随机出想要的排名 let min = rankNumbers[0], max = rankNumbers[rankNumbers.length - 1]; let ranks = await getLadderMatchByRank(ladderData.serverId, max, min); let oppPlayers: LadderOppPlayerReturn[] = [], oppPlayersSave: LadderOppPlayerInDB[] = []; for(let rank of rankNumbers) { let player = await generateOppPlayer(ladderData.serverId, rank, ranks); oppPlayersSave.push({ rank, roleId: player.roleId, isRobot: player.isRobot }); oppPlayers.push(player); } await LadderMatchModel.updateByRoleId(ladderData.roleId, { ...update, oppPlayers: oppPlayersSave }); return oppPlayers } async function generateOppPlayer(serverId: number, rank: number, oppPlayers: { rank: number, roleId: string, isRobot?: boolean }[]) { let player = oppPlayers.find(cur => cur.rank == rank && !cur.isRobot); let result = new LadderOppPlayerReturn(rank, !player); if(player) { let r = new Rank(REDIS_KEY.LADDER, { serverId }, false); let param = await r.getParam(player.rank, player.roleId, [player.rank]); result.setByRedis(param, 0); } else { let dicLadderDifficultRatio = gameData.ladderDifficultRatio.get(rank); result.setByRobot(dicLadderDifficultRatio); } return result; } function randOppRankNumbers(myRank: number) { let dicLadderMatch = getDicLadderMatchByMyRank(myRank); if(!dicLadderMatch) return new Array(5).fill(3001); let beforeRanks = randomRank(true, myRank, dicLadderMatch.rangeBeforeMax, dicLadderMatch.rangeBeforeNum, dicLadderMatch.gap.min, dicLadderMatch.gap.max); let afterRanks = randomRank(false, myRank, dicLadderMatch.rangeAfterMax, dicLadderMatch.rangeAfterNum, dicLadderMatch.gap.min, dicLadderMatch.gap.max); let topTen = getNumberArr(1, 10); let ranks = [...topTen, ...beforeRanks, ...afterRanks]; return uniqueArr(ranks.sort((a, b) => a - b)); } function uniqueArr(arr: T[]) { return arr.filter( (item, index, arr) => { //当前元素,在原始数组中的第一个索引==当前索引值,否则返回当前元素 return arr.indexOf(item) === index; }); } function getNumberArr(from: number, to: number) { let arr: number[] = []; for(let i = from; i <= to; i++) arr.push(i); return arr; } function randomRank(isBefore: boolean, myRank: number, range: number, num: number, gapMin: number, gapMax: number) { let ratio = isBefore? -1: 1; let arr: number[] = [], result = myRank; while(arr.length < num) { let minMax = [ result + ratio * gapMin, result + ratio * gapMax ].sort((a, b) => a - b); let num = getRandValueByMinMax(minMax[0], minMax[1], 0); if(isOver(isBefore, myRank, range, num)) break; result = num; arr.push(result); } return arr; } // 超出匹配范围 function isOver(isBefore: boolean, myRank: number, range: number, result: number) { if(isBefore) { console.log('before', myRank - range < result) return result < myRank - range; } else { console.log('after', myRank + range > result) return result > myRank + range; } } export function checkRank(myRank: number, targetRank: number) { let dicLadderMatch = getDicLadderMatchByMyRank(myRank); if(!dicLadderMatch) { if(myRank == 0 && targetRank == 3001) return true; return false } if(myRank < targetRank) { // 向后打 if(myRank + dicLadderMatch.rangeAfterMax < targetRank) return false; } else { // 向前打 if(myRank - dicLadderMatch.rangeBeforeMax > targetRank) return false; } if(targetRank <= 10 && !dicLadderMatch.topChallenge) { return false } return true; } export async function getLadderOppStatus(ladderData: LadderMatchType, targetRoleId: string, myRank: number, rank: number) { if(ladderData.rank != myRank) return { status: LADDER_OPP_STATUS.MY_RANK_CHANGE }; let { oppPlayers } = ladderData; let curOppPlayer = oppPlayers.find(cur => cur.roleId == targetRoleId); if(!curOppPlayer|| curOppPlayer.rank != rank) return { status: LADDER_OPP_STATUS.OPP_RANK_CHANGE }; if(curOppPlayer.isRobot) { if(rank <= 3000) { let hisLadderData = await LadderMatchModel.checkByRank(rank); if(hisLadderData) return { status: LADDER_OPP_STATUS.OPP_RANK_CHANGE }; } return { status: LADDER_OPP_STATUS.BATTLE, isRobot: true }; } else { let hisLadderData = await LadderMatchModel.lock(targetRoleId, rank); if(!hisLadderData) return { status: LADDER_OPP_STATUS.OPP_IS_LOCKED }; return { status: LADDER_OPP_STATUS.BATTLE, isRobot: false, hisLadderData }; } } /** * * @param isRobot 是否是机器 * @param isDefense 是攻还是守方 * @param rank 排名 * @param ladderData 需要populate过的ladderMatch表 * @returns */ export function generateInitRecInfo(isRobot: boolean, isDefense: boolean, rank: number, ladderData: LadderMatchType) { if(isRobot) { let dicLadderDifficultRatio = gameData.ladderDifficultRatio.get(rank); let dicWarJson = gameData.warJson.get(dicLadderDifficultRatio.gkId)||[]; let heroes: LadderOppPlayerHeroInfo[] = []; for(let json of dicWarJson) { if(json.relation == 2) { let hero = new LadderOppPlayerHeroInfo(); hero.setByWarJson(json); heroes.push(hero); } } let info = new LadderOppPlayerInfo(); info.initByRobot(dicLadderDifficultRatio, heroes, isDefense); return info; } else { let defCe = calculateDefCeByDefense(ladderData.defense); let role = ladderData.role; let heroes: LadderOppPlayerHeroInfo[] = []; if(isDefense) { // 防守方可以将武将也存入,攻方要等出战 let defenseHeroes = ladderData.defense?.heroes||[]; for(let defenseHero of defenseHeroes) { let hero = new LadderOppPlayerHeroInfo(); hero.setByDefenseHero(defenseHero.hero); heroes.push(hero); } } let info = new LadderOppPlayerInfo(); info.initByPlayer(rank, role, heroes, defCe, isDefense); return info; } } function calculateDefCeByDefense(defense: LadderDefense) { if(!defense) return 0; let ce = 0; for(let hero of defense.heroes) { ce += hero.ce; } return ce; } export async function ladderTimeout(battleCode: string, status: LADDER_STATUS) { let rec = await LadderMatchRecModel.timeout(battleCode); if(status == LADDER_STATUS.CHECK) { await sendMessageToUserWithSuc(rec.roleId1, PUSH_ROUTE.LADDER_CHECK_STOP, { battleCode }); } else if (status == LADDER_STATUS.BATTLE) { await sendMessageToUserWithSuc(rec.roleId1, PUSH_ROUTE.LADDER_BATTLE_STOP, { battleCode }); } } export async function battleEndWhenChange(ladderMatch: LadderMatchType) { if(!ladderMatch) return; // 更新redis if(ladderMatch.rank > 0 && ladderMatch.rank < 3001) { let r = new Rank(REDIS_KEY.LADDER, { serverId: ladderMatch.serverId }); r.setRankWithRoleInfo(ladderMatch.roleId, ladderMatch.rank, 0); } // 刷新对手 let oppPlayers = await refreshLadderEnemies(ladderMatch); await sendMessageToUserWithSuc(ladderMatch.roleId, PUSH_ROUTE.LADDER_RANK_UPDATE, { oldRank: ladderMatch.oldRank, newRank: ladderMatch.rank, oppPlayers }); return oppPlayers } export async function ladderBattleEndReward(roleId: string, roleName: string, sid: string, rank: number, isSuccess: boolean, historyRank: number, num: number) { let dicLadderDifficultRatio = gameData.ladderDifficultRatio.get(rank); if(!dicLadderDifficultRatio) return null; let warReward = new WarReward(roleId, roleName, sid, dicLadderDifficultRatio.gkId, isSuccess); warReward.setLadderReward(dicLadderDifficultRatio.randomFailReward, true); warReward.setLadderReward(dicLadderDifficultRatio.randomWinReward, false); let battleGoods = await warReward.saveReward(num); let breakRewards: RewardInter[] = []; for(let i = historyRank - 1; i >= rank; i--) { let dicLadderDifficultRatio = gameData.ladderDifficultRatio.get(i); breakRewards.push(...(dicLadderDifficultRatio?.onceReward||[])); } let breakGoods = breakRewards.length > 0? await addItems(roleId, roleName, sid, dicLadderDifficultRatio?.onceReward||[], ITEM_CHANGE_REASON.LADDER_BATTLE_REWARD): []; return { battleGoods, breakGoods }; } export function completeLadderRanks(ranks: RoleAndGuildRankInfo[]) { let result: RoleAndGuildRankInfo[] = []; for(let i = 1; i <= 200; i++) { let dicLadderDifficultRatio = gameData.ladderDifficultRatio.get(i); let { head, ce, spine, level } = dicLadderDifficultRatio; let rank = ranks.find(cur => cur.rank == i); if(rank) { result.push(rank); } else { let rank = new RoleAndGuildRankInfo(i, 0); rank.setByField(`robot${i}`, LADDER.LADDER_ROBOT_NAME, head, EXTERIOR.EXTERIOR_FACECASE, spine, level, 1, '', ce ); result.push(rank); } } return result; } export async function sendLadderDailyReward(serverId: number) { let ranks = await getLadderMatchByRank(serverId, '+inf', '-inf'); for(let { rank, roleId } of ranks) { let dicRankReward = getLadderRankReward(rank); if(dicRankReward && dicRankReward.reward && dicRankReward.reward.length > 0) { await sendMailByContent(MAIL_TYPE.LADDER, roleId, { params: [`${rank}`], goods: dicRankReward.reward }); } } } export async function getLadderOppDetailData(rec: LadderMatchRecType) { let isRobot = rec.defenseInfo.isRobot; let dicLadderDifficultRatio = gameData.ladderDifficultRatio.get(rec.defenseInfo.oldRank); let result = new LadderOppDetailReturn(rec); if(isRobot) { result.setByRobot(dicLadderDifficultRatio); } else { let hisLadderData = await LadderMatchModel.findByRoleIdAndInclude(rec.roleId2); let dicWarJson = gameData.warJson.get(dicLadderDifficultRatio.gkId) result.setByPlayer(hisLadderData, dicWarJson); let attrByHid = await getHeroesAttributes(rec.roleId2); for(let [hid, attribute] of attrByHid) { result.setAttribute(hid, attribute.getAttributesToString()); } } return result; }