import { PvpDefenseModel, Heroes, OppPlayers, PvpDefenseType, HeroScores, pvpUpdateInter } from '../db/PvpDefense'; import { RoleType } from '../db/Role'; import { PVP_HERO_POS, REDIS_KEY, PVP_CONST, COUNTER } from '../consts'; import { setPvpDefResult } from '../services/timeTaskService'; import { dicPvpOpponent, DicPvpOpponent } from "../pubUtils/dictionary/DicPvpOpponent"; import { getRandSingleIndex, genCode, shouldRefresh, getChineseName } from '../pubUtils/util'; import { oppPlayersInter, pvpEndParamInter } from '../pubUtils/interface'; import { gameData, getPLvByScore } from "../pubUtils/data"; import { PVP } from '../pubUtils/dicParam'; import { PVPConfigModel } from '../db/SystemConfig' import { nowSeconds, getTimeFun } from '../pubUtils/timeUtil'; import { HeroesRecord } from '../db/PvpRecord'; import { HeroModel } from '../db/Hero'; import { CeAttrData, CeAttrDataRole, AttributeCal } from '../domain/roleField/attribute'; import { PvpEnemies, PvpHeroInfo, PvpOtherHeroes } from '../domain/dbGeneral'; import { DicWarJson } from '../pubUtils/dictionary/DicWarJson'; import { findWhere, findIndex } from 'underscore'; import { pinus } from 'pinus'; import { PvpHistoryOppModel, PvpHistoryOppType } from '../db/PvpHistoryOpp'; import { Rank } from './rankService'; import { CounterModel } from '../db/Counter'; export async function getPvpData() { } export async function initPvpInfo(role: RoleType) { let heroes: Array = []; //初始化最强5人阵容 let defCe = 0; for (let i = PVP_HERO_POS.START; i <= PVP_HERO_POS.END; i++) { let index = i - PVP_HERO_POS.START; let item = role.topLineup[index]; if (item) defCe += item.ce; heroes.push({ actorId: item?.hid || 0, hero: item?.hero || null, ce: item?.ce || 0, dataId: i, order: index + 1, }); } //初始化对手人阵容 let oppPlayers: Array = await refreshEnemies(role, 0, 1); let { seasonNum } = await PVPConfigModel.findCurPVPConfig(); let challengeCnt = PVP.PVP_CHALLENGE_COUNTS; let result = await PvpDefenseModel.createPvpDefense({ roleId: role.roleId, roleName: role.roleName, role: role._id, heroes, oppPlayers, defCe, seasonNum, challengeCnt }); //加入排行榜 let r = new Rank(REDIS_KEY.PVP_RANK, {}); await r.setRankWithRoleInfo(REDIS_KEY.PVP_RANK, result.score, result.updatedAt.getTime(), role, false); return result; } export async function checkPvp(role: RoleType) { if (role.hasInit) { let result = await PvpDefenseModel.findByRoleId(role.roleId, true); if (!!result) return result; result = await initPvpInfo(role); return result; } } /** * 返回对手三人信息 * * @param oppPlayers pvpDefense表中的oppPlayers字段,需要populate过的 * @param pLv 玩家本人的队伍等级 */ export async function getEnemies(oppPlayers: OppPlayers[], winStreakNum: number) { let result = new Array(); for (let oppPlayer of oppPlayers) { let dicOpponent = dicPvpOpponent.get(oppPlayer.pos); let oppDef = oppPlayer.oppDef; // select 'oppRoleId pos roleName head frame spine rankLv pLv defCe' if (oppDef) delete oppDef.heroes; result.push({ ...oppDef, roleId: oppDef.oppRoleId, defCe: oppDef.defCe, addScore: dicOpponent.score, rankLv: oppDef.rankLv, plusScore: getPlusScore(winStreakNum) }); } return result } /** * @description 刷新对手三个人 * @param role 数据库里我的role * @param score 我的军功 * @param pLv 我的排名 */ export async function refreshEnemies(role: RoleType, score: number, pLv: number) { let system = await PVPConfigModel.findCurPVPConfig(); let mapWarJson = gameData.warJson.get(system.warId); let { roleId } = role; let oppPlayers = new Array(); let opp = dicPvpOpponent.values() for (let dicOpp of opp) { let flag = false; // 是否筛选成功 if (score >= PVP_CONST.SCORE_LINE) { // 将这个放到const flag = await matchPlayer(system.seasonNum, oppPlayers, mapWarJson, roleId, pLv, dicOpp); // 按照等级匹配对手 if (!flag) flag = await matchPlayerByRank(system.seasonNum, oppPlayers, mapWarJson, roleId, dicOpp.id); // 当前后分数段没有时,返回前一名的玩家 if (!flag) flag = await matchRobot(oppPlayers, mapWarJson, role, pLv, dicOpp); } else { flag = await matchRobot(oppPlayers, mapWarJson, role, pLv, dicOpp); } if (!flag) continue; } return oppPlayers; } export async function matchPlayerByRank(seasonNum: number, oppPlayers: OppPlayers[], mapWarJson: DicWarJson[], roleId: string, pos: number) { // console.log('matchPlayerByRank', JSON.stringify(oppPlayers)) let r = new Rank(REDIS_KEY.PVP_RANK, {}); let ridRanks = new Array(); // 已经被使用了的排名 for (let { roleId: curRoleId } of oppPlayers) { let rankLv = await r.getMyRank({ roleId: robotIdComBack(curRoleId) }); ridRanks.push(rankLv); } let myRank = await r.getMyRank({ roleId }); ridRanks.push(myRank); let oppRoleId = ''; let oppRank = 0; if (myRank == 0) { return false } else if (myRank == 1) { // 第一名 if (pos == 1) { oppRank = 2; while (ridRanks.includes(oppRank)) { oppRank--; } } else if (pos == 2) { oppRank = 3; while (ridRanks.includes(oppRank)) { oppRank--; } } else { oppRank = 4; while (ridRanks.includes(oppRank)) { oppRank++; } } } else if (myRank == 0) { return false } else { if (pos == 1 || pos == 2) { // 刷新我前一名 oppRank = myRank - 1; while (ridRanks.includes(oppRank)) { oppRank--; } } else { // 刷新我后一名 oppRank = myRank + 1; while (ridRanks.includes(oppRank)) { oppRank++; } } } let result = await r.getUserByRank(oppRank); if (result.length <= 0) return false; oppRoleId = result[0]; let pvpdefense = await PvpDefenseModel.findByRoleIdIncludeAll(oppRoleId); if (!pvpdefense || pvpdefense.seasonNum != seasonNum) return false; let pvpHistoryOpp = await generPlayerOppHis(pvpdefense, mapWarJson, roleId, pos); if (!pvpHistoryOpp) return false; oppPlayers.push({ roleId: pvpHistoryOpp.oppRoleId, oppDef: pvpHistoryOpp._id, pos, isRobot: false, }); return true } async function matchPlayer(seasonNum: number, oppPlayers: OppPlayers[], mapWarJson: DicWarJson[], roleId: string, pLv: number, dicOpp: DicPvpOpponent) { // console.log('matchPlayer', JSON.stringify(oppPlayers)) let { id: pos, minLv, maxLv } = dicOpp let range = await PvpDefenseModel.findByTeamLv(seasonNum, pLv + minLv, pLv + maxLv); range = range.filter(cur => { return oppPlayers.findIndex(ccur => ccur.roleId == makeRobotId(cur.roleId)) == -1; }); if (range.length <= 0) return false; let index = getRandSingleIndex(range.length); let result = range[index]; // 本次匹配结果 pvpdefense if (!result) return false; if (result.roleId == roleId) { range.splice(index, 1); if (range.length <= 0) return false; index = getRandSingleIndex(range.length); result = range[index]; } let pvpHistoryOpp = await generPlayerOppHis(result, mapWarJson, roleId, pos); if (!pvpHistoryOpp) return false; oppPlayers.push({ roleId: pvpHistoryOpp.oppRoleId, oppDef: pvpHistoryOpp._id, pos, isRobot: false, }); return true; } /** * @description 对手是玩家时,生成并返回pvpHistoryOpp * @param result 随机出的对手pvpDefense * @param mapWarJson 本周地图出兵表 * @param roleId 自己的玩家id * @param pos 刷新这个对手的位置 */ async function generPlayerOppHis(pvpdefense: PvpDefenseType, mapWarJson: DicWarJson[], roleId: string, pos: number) { let { heroScores, heroes: defenseHeroes } = pvpdefense; let role = pvpdefense.role; let r = new Rank(REDIS_KEY.PVP_RANK, {}); let rankLv = await r.getMyRank({ roleId: role.roleId }); let dbHeroes = await HeroModel.findByRole(role.roleId, [{ field: 'ce', sortBy: -1 }]); let heroes = new Array(); let otherHeroes = new Array(); // 阵容外的所有武将信息 for (let dbHero of dbHeroes) { let h = defenseHeroes.find(cur => cur.actorId == dbHero.hid); // 阵容里是否有这个武将 let hs = heroScores.find(cur => cur.hid == dbHero.hid); // 这个武将是否有这个得分 if (!!h) { let warJson = mapWarJson.find(cur => cur.dataId == h.dataId); if (warJson && warJson.relation == 2) { let heroInfo = new PvpHeroInfo(); heroInfo.setHeroInfo(dbHero); heroInfo.setOutIndex(h.order); let { attribute, ce } = getPlayerAttribute(dbHero.attr, role.attr); heroInfo.setAttribute(attribute); let enemy = new PvpEnemies(warJson, heroInfo, hs ? hs.score : 0, ce); heroes.push(enemy); } } else { let heroInfo = new PvpOtherHeroes(hs ? hs.score : 0); heroInfo.setHeroInfo(dbHero); otherHeroes.push(heroInfo); } } heroes.sort((a, b) => b.score - a.score); otherHeroes.sort((a, b) => b.score - a.score); let pvpHistoryOpp = await PvpHistoryOppModel.createPvpOpp({ ...pvpdefense, ...role, pos, rankLv, heroes, otherHeroes, roleId, oppRoleId: makeRobotId(pvpdefense.roleId) }); return pvpHistoryOpp; } async function matchRobot(oppPlayers: OppPlayers[], mapWarJson: DicWarJson[], role: RoleType, pLv: number, dicOpp: DicPvpOpponent) { // console.log('matchRobot', JSON.stringify(oppPlayers)) let { lv: myLv, topLineupCe: myCe, roleId } = role; let { id: pos, minLv, maxLv, ratio } = dicOpp; let range = gameData.pvpWar; if (range.length <= 0) return false; let index = getRandSingleIndex(range.length); let result = range[index]; if (!result) return false; let robotWarjson = gameData.warJson.get(result.war_id); if (!robotWarjson) { /* TODO 判空处理 */ } let heroes = new Array(); let defCe = 0; for (let warJson of mapWarJson) { if (warJson.relation == 1) continue; let h = robotWarjson.find(cur => cur.dataId == warJson.dataId); if (h) { let dicHero = gameData.hero.get(h.actorId); if (!dicHero) continue; let heroInfo = new PvpHeroInfo(); heroInfo.setRobotInfo(h, myLv, dicHero.initialStars, dicHero.quality); let { attribute, ce } = getRobotAttribute(h.attribute, myCe, PVP_CONST.ENEMY_CE, ratio); defCe += ce; heroInfo.setAttribute(attribute); let enemy = new PvpEnemies(warJson, heroInfo, 0, ce); heroes.push(enemy); } } let oppRoleId = generateRobotRoleId(); let roleName = getChineseName(); let hisPLv = Math.floor(pLv + (minLv + maxLv) / 2); if (hisPLv < 1) hisPLv = 1 let pvpHistoryOpp = await PvpHistoryOppModel.createPvpOpp({ roleId, oppRoleId, roleName, pos, defCe, pLv: hisPLv, lv: myLv, heroes, rankLv: 0 }); oppPlayers.push({ roleId: oppRoleId, oppDef: pvpHistoryOpp._id, pos, isRobot: true }); return true } // 生成机器人roleId function generateRobotRoleId() { return `${genCode(10)}_r`; } // 根据roleId判断是不是机器人 export function checkRoleIsRobot(roleId: string) { return !!roleId.match(/_r/); } // 将一般的roleId转为带_r的 export function makeRobotId(roleId: string) { return `${roleId}_r`; } // 将一般的roleId去掉_r export function robotIdComBack(robotRoleId: string) { return robotRoleId.replace(/_r/, '') } // 根据连胜次数,获得加成的积分 export function getPlusScore(win: number) { let result = win - 1; if (result < 0) result = 0; if (result > PVP.PVP_WINREWARD_UPLIMIT) result = PVP.PVP_WINREWARD_UPLIMIT; return result; } export function getLvByScore(heroScores: HeroScores[]) { heroScores.sort((a, b) => b.score - a.score); let score = 0; for (let i = 0; i < 5; i++) { if (!heroScores[i]) break; score += heroScores[i].score; } return getPLvByScore(score); } export async function defaultHeroes(role: RoleType, challengeCnt?: number, challengeRefTime?: number, isUpdate?: boolean) { let pvpDefense = await PvpDefenseModel.findByRoleId(role.roleId); if (!pvpDefense) return; let { heroes, isDefaultHero } = pvpDefense; if (!isUpdate && !isDefaultHero) { return; } let orders = [1, 2, 3, 4, 5, 6]; role.topLineup.sort(function (a, b) { return b.ce - a.ce; }); heroes.sort(function (a, b) { return b.ce - b.dataId - a.ce + a.dataId; }); for (let hero of heroes) { if (!!hero.order && !!hero.hero && findIndex(role.topLineup, { hid: hero.actorId }) != -1) { let index = orders.indexOf(hero.order); orders.splice(index, 1); } } let defCe = 0; let num = 0; for (let i = 0; i < role.topLineup.length; i++) { let item = role.topLineup[i]; let index = findIndex(heroes, { actorId: item.hid }); if (index == -1) { for (let j = num; j < heroes.length; j++) { let hero = heroes[j]; if (num >= 6) { break; } if (!!findWhere(role.topLineup, { hid: hero.actorId })) { continue; } if (!orders[0]) { break; } hero.actorId = item.hid; hero.hero = item.hero; hero.ce = item.ce; hero.order = orders[0]; num = j; orders.splice(0, 1); break; } } else { heroes[index].ce = item.ce; heroes[index].hero = item.hero; if (!heroes[index].order) { heroes[index].order = orders[0]; orders.splice(0, 1); } } defCe += item.ce; } if (!!challengeCnt && !!challengeRefTime) { let { heroes: resHeroes } = await PvpDefenseModel.updateInfo(role.roleId, { defCe, heroes, challengeCnt, challengeRefTime, isDefaultHero: true }); return { resHeroes }; } else { let { heroes: resHeroes } = await PvpDefenseModel.updateInfo(role.roleId, { defCe, heroes, isDefaultHero: true }); return { resHeroes }; } } export function refresh(challengeCnt: number, challengeRefTime: number, seasonEndTime: number) { if (challengeCnt >= PVP.PVP_CHALLENGE_COUNTS) { return { challengeCnt, challengeRefTime }; } let period = PVP.PVP_CHALLENGE_NORMALTIMES * 60; if (getTimeFun(seasonEndTime).checkDay()) { period = PVP.PVP_CHALLENGE_FINALTIMES * 60; } let time = nowSeconds(); let num = Math.floor((time - challengeRefTime) / period); if (num > 0) { challengeCnt += num; challengeRefTime = challengeRefTime + period * num; } challengeCnt = challengeCnt > PVP.PVP_CHALLENGE_COUNTS ? PVP.PVP_CHALLENGE_COUNTS : challengeCnt; return { challengeCnt, challengeRefTime }; } export function comsumeChallengeCnt(challengeCnt: number, challengeRefTime: number, seasonEndTime: number) { challengeCnt--; if (challengeCnt >= PVP.PVP_CHALLENGE_COUNTS) { return { challengeCnt, challengeRefTime }; } if (challengeCnt == PVP.PVP_CHALLENGE_COUNTS - 1) { challengeRefTime = nowSeconds(); return { challengeCnt, challengeRefTime }; } return refresh(challengeCnt, challengeRefTime, seasonEndTime); } export async function findPvpDefByRoleId(roleId: string) { let pvpDefense = await PvpDefenseModel.findByRoleIdIncludeAll(roleId); let { warId, seasonNum, seasonEndTime } = await PVPConfigModel.findCurPVPConfig(); let oldSeasonNum = await CounterModel.getCounter(COUNTER.PVP_SEASON_NUM); let { seasonEndTime: oldSeasonEndTime } = await PVPConfigModel.findPVPConfig(oldSeasonNum); if (pvpDefense.seasonNum !== seasonNum && oldSeasonEndTime < nowSeconds()) { let newPvpDefense = await setPvpDefResult(pvpDefense, seasonNum, oldSeasonEndTime); return { pvpDefense: newPvpDefense, warId }; } let { challengeCnt, challengeRefTime } = refresh(pvpDefense.challengeCnt, pvpDefense.challengeRefTime, seasonEndTime); let { refOppCnt, refOppTime, shouldRefOpp } = getRefOppCnt(pvpDefense.refOppCnt, pvpDefense.refOppTime); // 刷新次数 if (challengeCnt != pvpDefense.challengeCnt || refOppCnt != pvpDefense.refOppCnt) { await PvpDefenseModel.updateInfo(roleId, { challengeCnt, challengeRefTime, refOppCnt }); pvpDefense.challengeCnt = challengeCnt; pvpDefense.challengeRefTime = challengeRefTime; pvpDefense.refOppCnt = refOppCnt; pvpDefense.refOppTime = refOppTime; } return { pvpDefense, warId, shouldRefOpp }; } // 获取刷新对手次数及消耗 export function getRefOppCnt(refOppCnt: number, refOppTime: Date) { let curTime = new Date(); let shouldRefOpp = shouldRefresh(refOppTime, curTime); if (shouldRefOpp) { refOppCnt = 0; refOppTime = curTime; } return { shouldRefOpp, refOppCnt, refOppTime, consume: gameData.pvpRefreshConsume.get(refOppCnt + 1) } } /** * 获得玩家pvp信息,并检查是否在pvp赛季结算中结算过,未结算的进行结算 * @param roleId */ export async function findPvpDefAllByRoleId(roleId: string) { let pvpDefense = await PvpDefenseModel.findByRoleIdIncludeAll(roleId); let { warId, seasonNum, seasonEndTime } = await PVPConfigModel.findCurPVPConfig(); let oldSeasonNum = await CounterModel.getCounter(COUNTER.PVP_SEASON_NUM); let { seasonEndTime: oldSeasonEndTime } = await PVPConfigModel.findPVPConfig(oldSeasonNum); if (pvpDefense.seasonNum != seasonNum && oldSeasonEndTime < nowSeconds()) {//检查玩家是否在pvp赛季结算中结算过 let { score, pLv, winStreakNum, heroScores, challengeCnt, challengeRefTime } = await pinus.app.rpc.systimer.systimerRemote.setPvpDefResult.toServer('systimer-server-1', pvpDefense, seasonNum, oldSeasonEndTime); pvpDefense.score = score; pvpDefense.pLv = pLv; pvpDefense.winStreakNum = winStreakNum; pvpDefense.heroScores = heroScores; pvpDefense.challengeCnt = challengeCnt; pvpDefense.challengeRefTime = challengeRefTime; } pvpDefense.seasonNum = seasonNum; let { challengeCnt, challengeRefTime } = refresh(pvpDefense.challengeCnt, pvpDefense.challengeRefTime, seasonEndTime); let { refOppCnt, refOppTime, shouldRefOpp } = getRefOppCnt(pvpDefense.refOppCnt, pvpDefense.refOppTime); // 刷新次数 if (challengeCnt != pvpDefense.challengeCnt || refOppCnt != pvpDefense.refOppCnt || shouldRefOpp) { let update: pvpUpdateInter = { challengeCnt, challengeRefTime, refOppCnt, refOppTime }; if (shouldRefOpp) { let role = pvpDefense.role; let oppPlayers = await refreshEnemies(role, pvpDefense.score, pvpDefense.pLv); update.oppPlayers = oppPlayers; } let updateResult = await PvpDefenseModel.updateInfoAndInclude(roleId, update); pvpDefense.oppPlayers = updateResult.oppPlayers; pvpDefense.challengeCnt = challengeCnt; pvpDefense.challengeRefTime = challengeRefTime; pvpDefense.refOppCnt = refOppCnt; pvpDefense.refOppTime = refOppTime; } return { pvpDefense, warId, shouldRefOpp, seasonEndTime, oldSeasonEndTime }; } /** * 根据比例计算机器人属性 * @param attribute 出兵表中的属性字段 * @param ce 我的战力 * @param enemyCe 出兵表对手战力 * @param ratio 系数 */ export function getRobotAttribute(attribute: { id: number, val: number }[], ce: number, enemyCe: number, ratio: number) { let newAttribute = new AttributeCal(); newAttribute.setByWarJson(attribute, Math.sqrt(ce / enemyCe * ratio)); let attrArr = newAttribute.getReduceAttributesToString(); let newCe = newAttribute.calCelAndReduce(); return { attribute: attrArr, ce: newCe }; } /** * @description 根据玩家数据获取到他的属性 * @param ceAttr hero表的ceAttr * @param globalCeAttr role表中的globalCeAttr */ export function getPlayerAttribute(heroAttrs: CeAttrData[] = [], roleAttrs: CeAttrDataRole[] = []) { let newAttribute = new AttributeCal(); newAttribute.setByDbData(roleAttrs, heroAttrs); let attribute = newAttribute.getReduceAttributesToString(); let ce = newAttribute.calCelAndReduce(); return { attribute, ce }; } /** * @description 根据玩家数据获取到他的主属性 * @param ceAttr * @param globalCeAttr */ export function getPlayerMainAttribute(heroAttrs: CeAttrData[], roleAttrs: CeAttrDataRole[]) { let newAttribute = new AttributeCal(); newAttribute.setByDbData(roleAttrs, heroAttrs); let mainAttributes = newAttribute.getReduceMainAttributes(); return mainAttributes; } // 获取我方战报记录 export async function generMyRecInfo(heroScores: HeroScores[], winStreakNum: number, role: RoleType, isSuccess: boolean, pos: number, myHeroes: pvpEndParamInter[]) { let { roleId, roleName } = role; const dicOpp = gameData.pvpOpponent.get(pos); const plusScore = getPlusScore(winStreakNum); let pLv = getLvByScore(heroScores); let myHeroRecords = new Array(); let showHeroScores = new Array<{ hid: number, addScore: number, plusScore: number, score: number }>(); let addSumScore = 0; for (let { hid, damage, heal, underDamage } of myHeroes) { let curHeroScore = heroScores.find(cur => cur.hid == hid); if (isSuccess) { if (!curHeroScore) { curHeroScore = { hid, score: dicOpp.score + plusScore }; heroScores.push(curHeroScore); } else { curHeroScore.score += dicOpp.score + plusScore; } addSumScore += dicOpp.score + plusScore; showHeroScores.push({ hid, addScore: dicOpp.score, plusScore, score: curHeroScore.score }); } else { showHeroScores.push({ hid, addScore: 0, plusScore: 0, score: curHeroScore ? curHeroScore.score : 0 }); } const myHero = await HeroModel.findByHidAndRole(hid, roleId, 'quality star colorStar lv'); let { quality, star, colorStar, lv } = myHero; myHeroRecords.push({ hid, quality, star, colorStar, lv, damage, heal, underDamage }); } let attackInfo = { roleId, roleName, lv: pLv, head: role.head, frame: role.frame, spine: role.spine, title: role.title, ce: role.topLineupCe, heroes: myHeroRecords, isSuccess, score: isSuccess ? addSumScore : 0 } return { attackInfo, showHeroScores, addSumScore } } // 获取对手战报记录 export async function generPVPOppRecInfo(isSuccess: boolean, curOpp: OppPlayers, oppHeroes: pvpEndParamInter[], myLv: number) { let oppHeroRecords = new Array(); let oppRole = curOpp.oppDef; if (!oppRole) { console.error('opp role not found') } for (let { hid, damage, heal, underDamage } of oppHeroes) { let historyHero = oppRole.heroes.find(cur => cur.actorId == hid); if (historyHero) { let hs = new HeroesRecord(historyHero, damage, heal, underDamage); oppHeroRecords.push(hs); } } return { roleId: robotIdComBack(oppRole.oppRoleId), roleName: oppRole.roleName, lv: oppRole.pLv, head: oppRole.head, frame: oppRole.frame, spine: oppRole.spine, title: oppRole.title, ce: oppRole.defCe, heroes: oppHeroRecords, isSuccess: !isSuccess, score: 0 } }