Files
ZYZ/game-server/app/services/pvpService.ts
2021-01-14 18:37:13 +08:00

573 lines
22 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 { PvpDefenseModel, Heroes, OppPlayers, Robot, PvpDefenseType, HeroScores, pvpUpdateInter } 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, getChineseName } 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, PvpEnemies, PvpHeroInfo } from '../db/generalField';
import { DicWarJson } from '../pubUtils/dictionary/DicWarJson';
import { findWhere, findIndex } from 'underscore';
import { pinus } from 'pinus';
import { PvpHistoryOppModel, PvpHistoryOppType } from '../db/PvpHistoryOpp';
export async function initPvpInfo(role: RoleType) {
let heroes: Array<Heroes> = [];
//初始化最强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<OppPlayers> = 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<oppPlayersInter>();
for(let oppPlayer of oppPlayers) {
let dicOpponent = dicPvpOpponent.get(oppPlayer.pos);
let oppDef = <PvpHistoryOppType>oppPlayer.oppDef; // select 'oppRoleId pos roleName headHid sHid rankLv pLv defCe'
delete oppDef.heroes;
result.push({
...oppDef,
roleId: oppDef.oppRoleId,
defCe: reduceCe(oppDef.defCe),
addScore: dicOpponent.score,
rankLv: 0,
plusScore: getPlusScore(winStreakNum)
});
}
return result
}
// 刷新对手
export async function refreshEnemies(role: RoleType, score: number, pLv: number) {
let system = await SystemConfigModel.findSystemConfig();
let mapWarJson = gameData.warJson.get(system.warId);
let { roleId } = role;
let oppPlayers = new Array<OppPlayers>();
let opp = dicPvpOpponent.values()
for(let dicOpp of opp) {
let flag = false; // 是否筛选成功
if(score >= PVP_CONST.SCORE_LINE) { // 将这个放到const
flag = await matchPlayer(oppPlayers, mapWarJson, roleId, pLv, dicOpp); // 按照等级匹配对手
if(!flag) flag = await matchPlayerByRank(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(oppPlayers: OppPlayers[], mapWarJson: DicWarJson[], roleId: string, pos: number) {
let ridRanks = new Array<number>(); // 已经被使用了的排名
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.findByRoleIdIncludeAll(oppRoleId);
let pvpHistoryOpp = await generPlayerOppHis(pvpdefense, mapWarJson, roleId, pos);
oppPlayers.push({
roleId: pvpdefense.roleId,
oppDef: pvpHistoryOpp._id,
pos,
isRobot: false,
});
return true
}
async function matchPlayer(oppPlayers: OppPlayers[], mapWarJson: DicWarJson[], 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]; // 本次匹配结果 pvpdefense
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];
}
let pvpHistoryOpp = await generPlayerOppHis(result, mapWarJson, roleId, pos);
oppPlayers.push({
roleId: result.roleId,
oppDef: pvpHistoryOpp._id,
pos,
isRobot: false,
});
return true;
}
/**
* @description 对手是玩家时生成并返回pvpHistoryOpp
* @param result 随机出的对手pvpDefense
* @param mapWarJson 本周地图出兵表
* @param roleId 自己的玩家id
* @param pos 刷新这个对手的位置
*/
async function generPlayerOppHis(result: PvpDefenseType, mapWarJson: DicWarJson[], roleId: string, pos: number) {
let heroScores = result.heroScores;
let role = <RoleType>result.role;
let rankLv = await getMyRank(REDIS_KEY.PVP_RANK, 0, role.roleId);
let heroes = new Array<PvpEnemies>();
for(let warJson of mapWarJson) {
if(warJson.relation == 1) continue;
let h = result.heroes.find(cur => cur.dataId == warJson.dataId);
if(h && h.hero) {
let hs = heroScores.find(cur => cur.hid == h.actorId); // 这个武将的军功
let hero = <HeroType>h.hero;
let heroInfo = new PvpHeroInfo();
heroInfo.setHeroInfo(hero);
heroInfo.setOutIndex(h.order);
let attribute = getPlayerAttribute(hero.ceAttr, role.globalCeAttr);
heroInfo.setAttribute(attribute);
let enemy = new PvpEnemies(warJson, heroInfo, hs?hs.score: 0);
heroes.push(enemy);
} else {
let enemy = new PvpEnemies(warJson, new PvpHeroInfo(), 0);
heroes.push(enemy);
}
}
let pvpHistoryOpp = await PvpHistoryOppModel.createPvpOpp({
...result, ...role, pos, rankLv, heroes, roleId, oppRoleId: result.roleId
});
return pvpHistoryOpp;
}
async function matchRobot(oppPlayers: OppPlayers[], mapWarJson: DicWarJson[], role: RoleType, pLv: number, dicOpp: DicPvpOpponent) {
let { lv: myLv, topFiveCe: myCe, roleId } = role;
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 robotWarjson = gameData.warJson.get(result.war_id);
if(!robotWarjson) { /* TODO 判空处理 */ }
let heroes = new Array<PvpEnemies>();
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 = getRobotAttribute(h.attribute, myCe, PVP_CONST.ENEMY_CE, ratio);
heroInfo.setAttribute(attribute);
let enemy = new PvpEnemies(warJson, heroInfo, 0);
heroes.push(enemy);
} else {
let enemy = new PvpEnemies(warJson, new PvpHeroInfo(), 0);
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: Math.floor(myCe * ratio), pLv: hisPLv, lv: myLv, heroes
});
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/);
}
// 根据连胜次数,获得加成的积分
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.findByRoleIdIncludeAll(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, 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, PVP_CONST.REFRESH_TIME);
if(shouldRefOpp) {
refOppCnt = 0; refOppTime = curTime;
}
return {
shouldRefOpp,
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;
pvpDefense.seasonNum = seasonNum;
pvpDefense.seasonEndTime = seasonEndTime;
}
let {challengeCnt, challengeRefTime} = refresh(pvpDefense.challengeCnt, pvpDefense.challengeRefTime, pvpDefense.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 = <RoleType>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};
}
/**
* 根据比例计算机器人属性
* @param attribute 出兵表中的属性字段
* @param ce 我的战力
* @param enemyCe 出兵表对手战力
* @param ratio 系数
*/
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;
}
/**
* @description 根据玩家数据获取到他的属性
* @param ceAttr hero表的ceAttr
* @param globalCeAttr role表中的globalCeAttr
*/
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<HeroesRecord>();
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, oppHeroes: pvpEndParamInter[], myLv: number) {
let oppHeroRecords = new Array<HeroesRecord>();
let oppRole = <PvpHistoryOppType>curOpp.oppDef;
if(!oppRole) { /* TODO 判空处理 */ }
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: oppRole.roleId,
roleName: oppRole.roleName,
lv: oppRole.lv,
sHid: oppRole.sHid,
headHid: oppRole.headHid,
title: oppRole.title,
ce: oppRole.defCe,
heroes: oppHeroRecords,
isSuccess: !isSuccess,
score: 0
}
}