/** * 体力系统 */ import { PvpDefenseModel, Heroes, OppPlayers, Robot, PvpDefenseType, HeroScores } from '../db/PvpDefense'; import { RoleType, RoleModel } from '../db/Role'; import { PVP_HERO_POS, ROBOT_NAME, REDIS_KEY, PVP_CONST } from '../consts'; import { setPvpDefResult } from '../services/timeTaskService'; import { dicPvpOpponent, DicPvpOpponent } from "../pubUtils/dictionary/DicPvpOpponent"; import { getRandomIndexByLen, genCode, getRandomByLen, shouldRefresh, reduceCe } from '../pubUtils/util'; import { oppPlayersInter, RankParam, pvpEndParamInter, oppHeroesDefenseInter, Attributes } from '../pubUtils/interface'; import { gameData, getPLvByScore } from "../pubUtils/data"; import { PVP } from '../pubUtils/dicParam'; import { SystemConfigModel } from '../db/SystemConfig' import { setRank, getMyRank, getFieldByRank } from './redisService'; import { nowSeconds, checkTodayTime } from '../pubUtils/timeUtil'; import { HeroesRecord } from '../db/PvpRecord'; import { HeroModel, HeroType } from '../db/Hero'; import { CeAttrNumber, CeAttr, CeAttrRole } from '../db/generalField'; import { DicWarJson } from '../pubUtils/dictionary/DicWarJson'; import { findWhere, findIndex } from 'underscore'; import { pinus } from 'pinus'; export async function initPvpInfo(role: RoleType) { let heroes: Array = []; //初始化最强5人阵容 for (let i = PVP_HERO_POS.START; i <= PVP_HERO_POS.END; i++) { let index = i - PVP_HERO_POS.START; let item = role.topFive[index]; 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, seasonEndTime} = await SystemConfigModel.findSystemConfig(); let challengeCnt = PVP.PVP_CHALLENGE_COUNTS; let result = await PvpDefenseModel.createPvpDefense({ roleId: role.roleId, roleName: role.roleName, role: role._id, heroes, oppPlayers, defCe: role.topFiveCe, seasonNum, seasonEndTime, challengeCnt }); //加入排行榜 let { roleId, roleName, lv, vLv, headHid, sHid, title } = role; let params = new RankParam(roleName, lv, vLv, headHid, sHid, title); await setRank(REDIS_KEY.PVP_RANK, 0, roleId, result.score, result.updatedAt.getTime(), params); return result; } export async function checkPvp(role: RoleType) { let result = await PvpDefenseModel.findByRoleId(role.roleId); 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 {pos, isRobot, oppDef, robot} of oppPlayers) { let dicOpponent = dicPvpOpponent.get(pos); if(isRobot) { let { roleId, roleName, headHid, sHid, pLv, defCe } = robot; result.push({ pos, roleId, roleName, headHid, sHid, pLv, defCe: reduceCe(defCe), addScore: dicOpponent.score, rankLv: 0, plusScore: getPlusScore(winStreakNum) }); } else { let opp = oppDef; let role = opp.role; let { roleId, roleName, headHid, sHid, lv } = role; let { pLv, defCe } = opp; let rankLv = await getMyRank(REDIS_KEY.PVP_RANK, 0, roleId); result.push({ pos, roleId, roleName, headHid, sHid, pLv, defCe: reduceCe(defCe), addScore: dicOpponent.score, rankLv, // 读取排名 plusScore: getPlusScore(winStreakNum) }); } } return result } // 刷新对手 export async function refreshEnemies(role: RoleType, score: number, pLv: number) { let { roleId, topFiveCe } = role; let oppPlayers = new Array(); let opp = dicPvpOpponent.values() for(let dicOpp of opp) { let flag = false; // 是否筛选成功 if(score >= PVP_CONST.SCORE_LINE) { // TODO 将这个放到const flag = await matchPlayer(oppPlayers, roleId, pLv, dicOpp); if(!flag) flag = await matchPlayerByRank(oppPlayers, roleId, dicOpp.id); // 当前后分数段没有时,返回前一名的玩家 if(!flag) flag = matchRobot(oppPlayers, topFiveCe, pLv, dicOpp); } else { flag = matchRobot(oppPlayers, topFiveCe, pLv, dicOpp); } if(!flag) continue; } return oppPlayers; } export async function matchPlayerByRank(oppPlayers: OppPlayers[], roleId: string, pos: number) { let ridRanks = new Array(); // 已经被使用了的排名 for(let { roleId: curRoleId } of oppPlayers) { let rankLv = await getMyRank(REDIS_KEY.PVP_RANK, 0, curRoleId); ridRanks.push(rankLv); } let myRank = await getMyRank(REDIS_KEY.PVP_RANK, 0, roleId); ridRanks.push(myRank); let oppRoleId = ''; let oppRank = 0; if(myRank == 1) { // 第一名 if(pos == 1) { oppRank = 2; } else if(pos == 2) { oppRank = 3; } else { oppRank = 4; } } 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 getFieldByRank(REDIS_KEY.PVP_RANK, 0, oppRank); if(result.length <= 0) return false; oppRoleId = result[0]; let pvpdefense = await PvpDefenseModel.findByRoleId(oppRoleId); oppPlayers.push({ roleId: pvpdefense.roleId, oppDef: pvpdefense._id, pos, isRobot: false, robot: null }); return true } async function matchPlayer(oppPlayers: OppPlayers[], roleId: string, pLv: number, dicOpp: DicPvpOpponent ) { let { id: pos, minLv, maxLv } = dicOpp let range = await PvpDefenseModel.findByTeamLv(pLv + minLv, pLv + maxLv); range = range.filter(cur => { return oppPlayers.findIndex(ccur => ccur.roleId == cur.roleId) != -1; }); if(range.length <= 0) return false; let index = getRandomIndexByLen(range.length); let result = range[index]; if(!result) return false; if(result.roleId == roleId) { range.splice(index, 1); if(range.length <= 0) return false; index = getRandomIndexByLen(range.length); result = range[index]; } oppPlayers.push({ roleId: result.roleId, oppDef: result._id, pos, isRobot: false, robot: null }); return true; } function matchRobot(oppPlayers: OppPlayers[], myCe: number, pLv: number, dicOpp: DicPvpOpponent) { let { id: pos, minLv, maxLv, ratio } = dicOpp; let range = gameData.pvpWar; if(range.length <= 0) return false; let index = getRandomIndexByLen(range.length); let result = range[index]; if(!result) return false; let roleId = generateRobotRoleId(); let roleName = getRandomByLen(ROBOT_NAME); let hisPLv = Math.floor(pLv + (minLv + maxLv)/2); if(hisPLv < 1) hisPLv = 1 let robot = new Robot(roleId, roleName, Math.floor(myCe * ratio), hisPLv, result.war_id) oppPlayers.push({ roleId, oppDef: null, pos, isRobot: true, robot }); return true } // 生成机器人roleId function generateRobotRoleId() { return `${genCode(10)}_r`; } // 根据roleId判断是不是机器人 export function checkRoleIsRobot(roleId: string) { return !!roleId.match(/_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 { heroes, isDefaultHero } = await PvpDefenseModel.findByRoleId(role.roleId); if (!isUpdate && !isDefaultHero) { return; } let orders = [1, 2, 3, 4, 5]; role.topFive.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.topFive, {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.topFive.length; i++) { let item = role.topFive[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 >= 5) { break; } if (!!findWhere(role.topFive, {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 (checkTodayTime(seasonEndTime)) { 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.findByRoleId(roleId); let {warId, seasonNum, seasonEndTime} = await SystemConfigModel.findSystemConfig(); if (pvpDefense.seasonNum !== seasonNum) { let newPvpDefense = await setPvpDefResult(pvpDefense, seasonNum, seasonEndTime); return {pvpDefense: newPvpDefense, warId}; } let {challengeCnt, challengeRefTime} = refresh(pvpDefense.challengeCnt, pvpDefense.challengeRefTime, pvpDefense.seasonEndTime); let { refOppCnt, refOppTime } = 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}; } // 获取刷新对手次数及消耗 export function getRefOppCnt(refOppCnt: number, refOppTime: Date) { let curTime = new Date(); if(shouldRefresh(refOppTime, curTime, PVP_CONST.REFRESH_TIME)) { refOppCnt = 0; refOppTime = curTime; } return { refOppCnt, refOppTime, consume: gameData.pvpRefreshConsume.get(refOppCnt + 1) } } export async function findPvpDefAllByRoleId(roleId: string) { let pvpDefense = await PvpDefenseModel.findByRoleIdIncludeAll(roleId); let {warId, seasonNum, seasonEndTime} = await SystemConfigModel.findSystemConfig(); if (pvpDefense.seasonNum !== seasonNum) { let { score, pLv, winStreakNum, heroScores, challengeCnt, challengeRefTime } = await pinus.app.rpc.systimer.systimerRemote.setPvpDefResult.toServer('systimer-server-1', pvpDefense, seasonNum, seasonEndTime); pvpDefense.score = score; pvpDefense.pLv = pLv; pvpDefense.winStreakNum = winStreakNum; pvpDefense.heroScores = heroScores; pvpDefense.challengeCnt = challengeCnt; pvpDefense.challengeRefTime = challengeRefTime; } let {challengeCnt, challengeRefTime} = refresh(pvpDefense.challengeCnt, pvpDefense.challengeRefTime, pvpDefense.seasonEndTime); let { refOppCnt, refOppTime } = getRefOppCnt(pvpDefense.refOppCnt, pvpDefense.refOppTime); // 刷新次数 if (challengeCnt != pvpDefense.challengeCnt || refOppCnt != pvpDefense.refOppCnt) { await PvpDefenseModel.updateInfo( roleId, { challengeCnt, challengeRefTime, refOppCnt, refOppTime}); pvpDefense.challengeCnt = challengeCnt; pvpDefense.challengeRefTime = challengeRefTime; pvpDefense.refOppCnt = refOppCnt; pvpDefense.refOppTime = refOppTime; } return {pvpDefense, warId}; } // 获取机器人阵容 export async function getRobotLineup(mapWarJson: DicWarJson[], curOpp: OppPlayers, topFiveCe: number, lv: number) { let heroes = new Array(); let { pos, robot } = curOpp; let { warId } = robot; let dicWarJson = gameData.warJson.get(warId); let dicOpp = gameData.pvpOpponent.get(pos); for(let json of dicWarJson) { let curDicMapJson = mapWarJson.find(cur => cur.dataId == json.dataId); const { actorId, actorName, attribute, skill, seid, star, spine, relation } = json; if(relation == 2 && actorId > 0) { // 默认格子 let newAttribute = getRobotAttribute(attribute, topFiveCe, PVP_CONST.ENEMY_CE, dicOpp.ratio); let heroInfo = { actorId, actorName, skill, seid, star, spine, attribute: newAttribute, lv }; heroes.push({ ...curDicMapJson, ...heroInfo }); } } return heroes } // 获取阵容阵容 export async function getPlayerLineup(mapWarJson: DicWarJson[], curOpp: OppPlayers, ) { let heroes = new Array(); let oppDef = curOpp.oppDef; let role = oppDef.role; let { globalCeAttr } = role; for(let { actorId, hero, dataId, order } of oppDef.heroes) { if(actorId > 0) { let curDicMapJson = mapWarJson.find(cur => cur.dataId == dataId); let dicHero = gameData.hero.get(actorId); let h = hero; let { ceAttr } = h; let newAttribute = getPlayerAttribute(ceAttr, globalCeAttr); let heroInfo = { outIndex: order, actorId, actorName: dicHero.name, skill:0, seid:'&', star: h.star, spine: 0, attribute: newAttribute, lv: h.lv }; heroes.push({ ...curDicMapJson, ...heroInfo }); } } return heroes } // 按比例计算出兵表中的属性 export function getRobotAttribute(attribute: Attributes, ce: number, enemyCe: number, ratio: number) { let newAttribute = new CeAttrNumber(); for(let attrName in newAttribute) { newAttribute[attrName] = Math.floor(attribute[attrName] * reduceCe(ce) / enemyCe * ratio); } newAttribute['speed'] = 0; newAttribute['ap'] = 0; return newAttribute; } // 计算玩家的属性 export function getPlayerAttribute(ceAttr: CeAttr, globalCeAttr: CeAttrRole) { let newAttribute = new CeAttrNumber(); for(let attrName in newAttribute) { let { base, ratioUp, fixUp, equipUp } = ceAttr[attrName]; let { ratioUp: ratioUp2, fixUp: fixUp2 } = globalCeAttr[attrName]; let result = base * ( 1 + ratioUp + ratioUp2) + fixUp + fixUp2 + equipUp; newAttribute[attrName] += reduceCe(result); } newAttribute['speed'] = 0; newAttribute['ap'] = 0; return newAttribute; } // 获取我方战报记录 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 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: role.lv, sHid: role.sHid, headHid: role.headHid, title: role.title, ce: role.topFiveCe, heroes: myHeroRecords, isSuccess, score: isSuccess ? addSumScore : 0 } return { attackInfo, showHeroScores, addSumScore } } // 获取对手战报记录 export async function generPVPOppRecInfo(isSuccess: boolean, curOpp: OppPlayers, oppRoleId: string, oppHeroes: pvpEndParamInter[], myLv: number) { let oppHeroRecords = new Array(); for (let { hid, damage, heal, underDamage } of oppHeroes) { if (curOpp.isRobot) { let dicHero = gameData.hero.get(hid); let { quality, initialStars: star } = dicHero; oppHeroRecords.push({ hid, quality, star, colorStar: 0, lv: 0, damage, heal, underDamage }); } else { const myHero = await HeroModel.findByHidAndRole(hid, oppRoleId, 'quality star colorStar lv'); let { quality, star, colorStar, lv } = myHero; oppHeroRecords.push({ hid, quality, star, colorStar, lv, damage, heal, underDamage }); } } let oppRole; if (curOpp.isRobot) { oppRole = { ...curOpp.robot, title: 1, topFiveCe: curOpp.robot.defCe, lv: myLv }; } else { oppRole = await RoleModel.findByRoleId(oppRoleId); } return { roleId: oppRole.roleId, roleName: oppRole.roleName, lv: oppRole.lv, sHid: oppRole.sHid, headHid: oppRole.headHid, title: oppRole.title, ce: oppRole.topFiveCe, heroes: oppHeroRecords, isSuccess: !isSuccess, score: 0 } }