Files
ZYZ/game-server/app/services/pvpService.ts
2021-01-11 16:31:53 +08:00

546 lines
20 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 } 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 { OutliningSpanKind } from 'typescript';
const _ = require('underscore');
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 {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 = <PvpDefenseType>oppDef;
let role = <RoleType>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<OppPlayers>();
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<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.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,) {
let { heroes } = await PvpDefenseModel.findByRoleId(role.roleId);
var orders = [1, 2, 3, 4, 5];
heroes.sort(function(a, b) {
if (!!a.order) {
let index = orders.indexOf(a.order);
orders.splice(index, 1);
}
return b.ce - b.dataId - a.ce + a.dataId;
});
role.topFive.sort(function(a, b) {
return b.ce - a.ce;
});
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;
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 setPvpDefResult(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<oppHeroesDefenseInter>();
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<oppHeroesDefenseInter>();
let oppDef = <PvpDefenseType>curOpp.oppDef;
let role = <RoleType>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 = <HeroType>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<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.score
});
}
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<HeroesRecord>();
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
}
}