import { ITEM_CHANGE_REASON, LADDER_OPP_STATUS, LADDER_SERVER_GAP_TIME, 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 { getLadderDefCe, saveLadderDefCeByData } from "./redisService"; 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); // 随机出想要的排名 return await generateOppPlayers(rankNumbers, ladderData.serverId, ladderData.roleId, update); } export async function generateOppPlayers(rankNumbers: number[], serverId: number, roleId: string, update: LadderUpdateInter = {}) { let min = rankNumbers[0], max = rankNumbers[rankNumbers.length - 1]; let ranks = await getLadderMatchByRank(serverId, max, min); let oppPlayers: LadderOppPlayerReturn[] = [], oppPlayersSave: LadderOppPlayerInDB[] = []; for(let rank of rankNumbers) { let player = await generateOppPlayer(serverId, rank, ranks); oppPlayersSave.push({ rank, roleId: player.roleId, isRobot: player.isRobot }); oppPlayers.push(player); } await LadderMatchModel.updateByRoleId(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 || rank == 3001); if(player && rank != 3001) { let r = new Rank(REDIS_KEY.LADDER, { serverId }, false); let param = await r.getParam(player.rank, player.roleId, [player.rank]); let defCe = await getLadderDefCe(player.roleId); result.setByRedis(param, parseInt(`${defCe}`)); } 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); let afterRanks = randomRank(false, myRank, dicLadderMatch.rangeAfterMax, dicLadderMatch.rangeAfterNum); let topTen = getNumberArr(1, 10); let ranks = [...topTen, ...beforeRanks, ...afterRanks]; return uniqueArr(ranks.sort((a, b) => a - b)); } export function uniqueArr(arr: T[]) { return arr.filter( (item, index, arr) => { //当前元素,在原始数组中的第一个索引==当前索引值,否则返回当前元素 return arr.indexOf(item) === index; }); } export 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, len: number) { if(range == 0 || len == 0) return []; let arr: number[] = []; let n = Math.floor(range / len); for(let i = 0; i < len; i++) { let rand = getRandValueByMinMax(n * i + 1, n * (i + 1) + 1, 0); // 因为这个的范围是[min, max),而想要的是 (n * i, n * (i - 1)] if(rand) { let ratio = isBefore? -1: 1; arr.push(myRank + ratio * rand); } } return arr; } 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) return false; if(targetRank <= 10) { return dicLadderMatch.topChallenge == 1; } else { if(myRank < targetRank) { // 向后打 if(myRank + dicLadderMatch.rangeAfterMax < targetRank) return false; } else { // 向前打 if(myRank - dicLadderMatch.rangeBeforeMax > targetRank) 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 isRobot = targetRoleId.startsWith('robot'); if(isRobot) { if(rank <= 3000) { let hisLadderData = await LadderMatchRecModel.checkByRank(ladderData.serverId, `robot${rank}`); if(hisLadderData) return { status: LADDER_OPP_STATUS.OPP_IS_LOCKED }; let ladderMatch = await LadderMatchModel.isRankExist(rank); if(ladderMatch) { return { status: LADDER_OPP_STATUS.OPP_RANK_CHANGE }; } } return { status: LADDER_OPP_STATUS.BATTLE, isRobot: true }; } else { let ladderMatch = await LadderMatchModel.findByRoleId(targetRoleId); if(!ladderMatch || ladderMatch.rank != rank) { return { status: LADDER_OPP_STATUS.OPP_RANK_CHANGE }; } let hisLadderData = await LadderMatchModel.lock(ladderData.serverId, 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 dicWar = gameData.war.get(dicLadderDifficultRatio.gkId); let dicWarJson = gameData.warJson.get(dicWar.dispatchJsonId)||[]; 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 ladderTimeWillout(battleCode: string, status: LADDER_STATUS) { console.log('######### ladderTimeWillout') if (status == LADDER_STATUS.BATTLE) { let rec = await LadderMatchRecModel.findByBattleCode(battleCode); await sendMessageToUserWithSuc(rec.roleId1, PUSH_ROUTE.LADDER_BATTLE_STOP, { battleCode }); } } export async function ladderTimeout(battleCode: string, status: LADDER_STATUS) { console.log('######### ladderTimeout') let rec = await LadderMatchRecModel.timeout(battleCode); // console.log('##### rec.defenseInfo', rec.defenseInfo, rec.defenseInfo.isRobot, rec.defenseInfo.roleId) if(rec.defenseInfo && !rec.defenseInfo.isRobot) await LadderMatchModel.unlock(rec.serverId, rec.defenseInfo.roleId); if(status == LADDER_STATUS.CHECK) { await sendMessageToUserWithSuc(rec.roleId1, PUSH_ROUTE.LADDER_CHECK_STOP, { battleCode }); } } /** * 结算时候设置排行、发送推送、刷新对手、发送跑马灯 * @param ladderMatch * @param isAtk * @returns */ export async function battleEndWhenChange(atkLadderMatch: LadderMatchType, defLadderMatch: LadderMatchType, needRefresh: boolean) { await updateLadderRedis(atkLadderMatch); await updateLadderRedis(defLadderMatch); await refreshOpp(defLadderMatch, needRefresh); return await refreshOpp(atkLadderMatch, needRefresh); } async function updateLadderRedis(ladderMatch: LadderMatchType) { if(!ladderMatch) return; // 更新redis if(ladderMatch.rank > 0 && ladderMatch.rank < 3001) { let r = new Rank(REDIS_KEY.LADDER, { serverId: ladderMatch.serverId }); await r.setRankWithRoleInfo(ladderMatch.roleId, ladderMatch.rank, 0); await saveLadderDefCeByData(ladderMatch.roleId, ladderMatch); } } async function refreshOpp(ladderMatch: LadderMatchType, needRefresh: boolean) { if(!ladderMatch) return [] // 刷新对手 if(needRefresh) { let oppPlayers = await refreshLadderEnemies(ladderMatch); await sendMessageToUserWithSuc(ladderMatch.roleId, PUSH_ROUTE.LADDER_RANK_UPDATE, { oldRank: ladderMatch.oldRank, newRank: ladderMatch.rank, oppPlayers }); return oppPlayers } else { return [] } } 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, combineItems(breakRewards), 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, 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 dicWar = gameData.war.get(dicLadderDifficultRatio.gkId); let dicWarJson = gameData.warJson.get(dicWar.dispatchJsonId); result.setByPlayer(hisLadderData, dicWarJson); let attrByHid = await getHeroesAttributes(rec.roleId2); for(let [hid, attribute] of attrByHid) { result.setAttribute(hid, attribute.getAttributesToString()); } } return result; }