789 lines
32 KiB
TypeScript
789 lines
32 KiB
TypeScript
|
||
import { PvpDefenseModel, PvpDefenseType, pvpUpdateInter } from '../db/PvpDefense';
|
||
import { Defense, Attack, LineupCe, OppPlayer, HeroScore, HeroReward, OppPlayerReturn, AttackHero, DefenseHero } from '../domain/battleField/pvp';
|
||
import { RoleType } from '../db/Role';
|
||
import { REDIS_KEY, TASK_TYPE, MAIL_TYPE, TA_EVENT, ITID, getHeadItid, getFrameItid, getSpineItid, PVP_SEASON_STATUS } from '../consts';
|
||
import { dicPvpOpponent, DicPvpOpponent } from "../pubUtils/dictionary/DicPvpOpponent";
|
||
import { getRandSingleIndex, genCode, shouldRefresh, getChineseName, makeRobotId, robotIdComBack, getRandSingleEelm } from '../pubUtils/util';
|
||
import { pvpEndParamInter, RewardInter } from '../pubUtils/interface';
|
||
import { gameData, getPLvByScore, getPvpHeroRewardsByScore, getPvpRankRewardsByRank, getPvpDifficultByScore, getPlvAndScore, getPvpBoxsBySeasonNum, getPvpRankMaxRewardsBySeasonNum, randomGoodsByItid } from "../pubUtils/data";
|
||
import { EXTERIOR, PVP } from '../pubUtils/dicParam';
|
||
import { PVPConfigModel } from '../db/PvpConfig'
|
||
import { nowSeconds, getTimeFun } from '../pubUtils/timeUtil';
|
||
import { HeroesRecord, PvpRecordPlayerInfo } from '../db/PvpRecord';
|
||
import { HeroModel, HeroType } from '../db/Hero';
|
||
import { AttributeCal } from '../domain/roleField/attribute';
|
||
import { PvpEnemies, PvpHeroInfo, PvpOtherHeroes } from '../domain/dbGeneral';
|
||
import { pinus } from 'pinus';
|
||
import { PvpHistoryOppModel, PvpHistoryOppType, PvpOppCreateParam } from '../db/PvpHistoryOpp';
|
||
import { Rank } from './rankService';
|
||
import { DicRankRewads } from '../pubUtils/dictionary/DicPvpRankReward';
|
||
import { PvpSeasonResultModel, PvpSeasonResultType } from '../db/PvpSeasonResult';
|
||
import { checkTask } from './task/taskService';
|
||
import { sendMailByContent } from './mailService';
|
||
import { RoleRankInfo } from '../domain/rank';
|
||
import { reportTAEvent } from './sdkService';
|
||
import { getVipPvpChallengeMaxCnt } from './activity/monthlyTicketService';
|
||
import { getHeroesAttributes } from './playerCeService';
|
||
import { setPvpSettleSeasonNumToRemote } from './timeTaskService';
|
||
import { ArtifactModel } from '../db/Artifact';
|
||
import { getPVPGroupIdOfServer, getPvpServersByGroupId } from './serverService';
|
||
import { findKeys } from './redisService';
|
||
|
||
/**
|
||
* 返回对手三人信息
|
||
*
|
||
* @param oppPlayers pvpDefense表中的oppPlayers字段,需要populate过的
|
||
* @param pLv 玩家本人的队伍等级
|
||
*/
|
||
export async function getEnemies(r: Rank, oppPlayers: OppPlayer[], winStreakNum: number) {
|
||
|
||
let result = new Array<OppPlayerReturn>();
|
||
for (let oppPlayer of oppPlayers) {
|
||
let dicOpponent = dicPvpOpponent.get(oppPlayer.pos);
|
||
if(!dicOpponent) continue;
|
||
let oppDef = <PvpHistoryOppType>oppPlayer.oppDef; // select 'oppRoleId pos roleName head frame spine rankLv pLv defCe'
|
||
|
||
if (oppDef) delete oppDef.heroes;
|
||
let myRank = await r.getMyRank({ roleId: robotIdComBack(oppDef.oppRoleId) });//去redis中获取排名
|
||
result.push({
|
||
...oppDef,
|
||
roleId: oppDef.oppRoleId,
|
||
defCe: oppDef.defCe,
|
||
addScore: dicOpponent.score,
|
||
rankLv: myRank||0,
|
||
plusScore: getPlusScore(winStreakNum)
|
||
});
|
||
}
|
||
return result
|
||
}
|
||
|
||
/**
|
||
* @description 刷新对手三个人
|
||
* @param role 数据库里我的role
|
||
* @param score 我的军功
|
||
* @param pLv 我的排名
|
||
*/
|
||
export async function refreshEnemies(role: RoleType, seasonNum: number, sumScore: number, score: number, pLv: number) {
|
||
let { roleId, serverId } = role;
|
||
let groupId = await getPVPGroupIdOfServer(serverId);
|
||
let chosenOpps: string[] = [];
|
||
let pvpHistoryOppParam: PvpOppCreateParam[] = [];
|
||
for(let pos = 1; pos <= 3; pos++) {
|
||
let dicOpp = gameData.pvpOpponent.get(pos);
|
||
if(!dicOpp) continue;
|
||
let pvpHistoryOpp: PvpOppCreateParam; // 是否筛选成功
|
||
if (sumScore >= PVP.PVP_MATCH_ROBOT) {
|
||
pvpHistoryOpp = await matchPlayer(groupId, seasonNum, chosenOpps, roleId, pLv, dicOpp); // 按照等级匹配对手
|
||
if (!pvpHistoryOpp) pvpHistoryOpp = await matchPlayerByRank(groupId, seasonNum, chosenOpps, roleId, dicOpp.id); // 当前后分数段没有时,返回前一名的玩家
|
||
if (!pvpHistoryOpp) pvpHistoryOpp = await matchRobot(chosenOpps, role, score, dicOpp);
|
||
} else {
|
||
pvpHistoryOpp = await matchRobot(chosenOpps, role, score, dicOpp);
|
||
}
|
||
if (!pvpHistoryOpp) continue;
|
||
pvpHistoryOppParam.push(pvpHistoryOpp);
|
||
}
|
||
|
||
let oppPlayers: OppPlayer[] = [];
|
||
pvpHistoryOppParam.sort((a, b) => {
|
||
if(a.pLv != b.pLv) return b.pLv - a.pLv;
|
||
return b.defCe - a.defCe;
|
||
});
|
||
for(let i = 0; i < pvpHistoryOppParam.length; i++) {
|
||
let param = pvpHistoryOppParam[i];
|
||
param.pos = i + 1;
|
||
let pvpHistoryOpp = await PvpHistoryOppModel.createPvpOpp(param);
|
||
oppPlayers.push({
|
||
roleId: pvpHistoryOpp.oppRoleId,
|
||
oppDef: pvpHistoryOpp._id,
|
||
isRobot: pvpHistoryOpp.isRobot,
|
||
pos: pvpHistoryOpp.pos,
|
||
});
|
||
}
|
||
if(dicPvpOpponent.size > 3 && !pinus.app.get('dicPvpOpponent')) {
|
||
pinus.app.set('dicPvpOpponent', dicPvpOpponent);
|
||
}
|
||
return oppPlayers;
|
||
}
|
||
|
||
export async function matchPlayerByRank(groupId: number, seasonNum: number, chosenOpps: string[], roleId: string, pos: number) {
|
||
// console.log('matchPlayerByRank', JSON.stringify(oppPlayers))
|
||
let r = new Rank(REDIS_KEY.PVP_RANK, { seasonNum, groupId });
|
||
let ridRanks = new Array<number>(); // 已经被使用了的排名
|
||
for (let curRoleId of chosenOpps) {
|
||
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 null
|
||
} 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 == 2) { // 第二名
|
||
if (pos == 1) {
|
||
oppRank = 1;
|
||
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 (pos == 1 || pos == 2) { // 刷新我前一名
|
||
oppRank = myRank - 1;
|
||
while (ridRanks.includes(oppRank)) {
|
||
oppRank--;
|
||
}
|
||
} else { // 刷新我后一名
|
||
oppRank = myRank + 1;
|
||
while (ridRanks.includes(oppRank)) {
|
||
oppRank++;
|
||
}
|
||
}
|
||
}
|
||
if(oppRank <= 0) return null;
|
||
let result = await r.getUserByRank(oppRank);
|
||
if (result.length <= 0) return null;
|
||
oppRoleId = result[0];
|
||
|
||
let pvpdefense = await PvpDefenseModel.findByRoleIdIncludeAll(oppRoleId);
|
||
if (!pvpdefense || pvpdefense.seasonNum != seasonNum || !pvpdefense.hasDefense) return null;
|
||
let pvpHistoryOpp = await generPlayerOppHis(pvpdefense, roleId, pos, groupId);
|
||
if (!pvpHistoryOpp) return null;
|
||
|
||
chosenOpps.push(pvpHistoryOpp.oppRoleId);
|
||
return pvpHistoryOpp;
|
||
}
|
||
|
||
async function matchPlayer(groupId: number, seasonNum: number, chosenOpps: string[], roleId: string, pLv: number, dicOpp: DicPvpOpponent) {
|
||
// console.log('matchPlayer', JSON.stringify(oppPlayers))
|
||
|
||
let serverIds = await getPvpServersByGroupId(groupId);
|
||
let { id: pos, minLv, maxLv } = dicOpp;
|
||
let min = pLv + minLv > 1? pLv + minLv: 1;
|
||
let max = pLv + maxLv > 1? pLv + maxLv: 1;
|
||
let range = await PvpDefenseModel.findByTeamLv(serverIds, seasonNum, min, max);
|
||
range = range.filter(cur => {
|
||
return chosenOpps.indexOf(makeRobotId(cur.roleId)) == -1;
|
||
});
|
||
if (range.length <= 0) return null;
|
||
|
||
let index = getRandSingleIndex(range.length);
|
||
let result = range[index]; // 本次匹配结果 pvpdefense
|
||
if (!result) return null;
|
||
if (result.roleId == roleId) {
|
||
range.splice(index, 1);
|
||
if (range.length <= 0) return null;
|
||
index = getRandSingleIndex(range.length);
|
||
result = range[index];
|
||
}
|
||
let pvpHistoryOpp = await generPlayerOppHis(result, roleId, pos, groupId);
|
||
if (!pvpHistoryOpp) return null;
|
||
|
||
chosenOpps.push(pvpHistoryOpp.oppRoleId);
|
||
return pvpHistoryOpp;
|
||
}
|
||
|
||
/**
|
||
* @description 对手是玩家时,生成并返回pvpHistoryOpp
|
||
* @param result 随机出的对手pvpDefense
|
||
* @param roleId 自己的玩家id
|
||
* @param pos 刷新这个对手的位置
|
||
*/
|
||
async function generPlayerOppHis(pvpdefense: PvpDefenseType, roleId: string, pos: number, groupId: number) {
|
||
let { heroScores, defense } = pvpdefense;
|
||
if(!defense) return false;
|
||
let { warId, heroes: defenseHeroes } = defense;
|
||
let role = <RoleType>pvpdefense.role;
|
||
|
||
let seasonNum: number = pinus.app.get('pvpSeasonNum');
|
||
let r = new Rank(REDIS_KEY.PVP_RANK, { seasonNum, groupId });
|
||
let rankLv = await r.getMyRank({ roleId: role.roleId });
|
||
let dbHeroes = await HeroModel.findByRole(role.roleId, [{ field: 'ce', sortBy: -1 }]);
|
||
let heroes = new Array<PvpEnemies>();
|
||
let otherHeroes = new Array<PvpOtherHeroes>(); // 阵容外的所有武将信息
|
||
let defCe = 0;
|
||
let attrByHid = await getHeroesAttributes(role.roleId);
|
||
for (let dbHero of dbHeroes) {
|
||
let artifact = dbHero.artifact? await ArtifactModel.findbySeqId(role.roleId, dbHero.artifact): null;
|
||
let h = defenseHeroes.find(cur => cur.actorId == dbHero.hid); // 阵容里是否有这个武将
|
||
let hs = heroScores.find(cur => cur.hid == dbHero.hid); // 这个武将是否有这个得分
|
||
if (!!h) {
|
||
let dicWar = gameData.war.get(warId);
|
||
let mapWarJson = gameData.warJson.get(dicWar.dispatchJsonId);
|
||
let warJson = mapWarJson.find(cur => cur.dataId == h.dataId);
|
||
if (warJson && warJson.relation == 2) {
|
||
let heroInfo = new PvpHeroInfo();
|
||
heroInfo.setHeroInfo(dbHero, artifact);
|
||
// heroInfo.setOutIndex(h.order);
|
||
let attr = attrByHid.get(h.actorId);
|
||
if(!attr) continue;
|
||
let attribute = attr.getAttributesToString();
|
||
let ce = attr.calCe();
|
||
if(isNaN(ce)) ce = 0;
|
||
heroInfo.setAttribute(attribute);
|
||
let enemy = new PvpEnemies(warJson, heroInfo, hs ? hs.score : 0, ce);
|
||
enemy.setOutIndex(h.order);
|
||
enemy.initial_ai = h.ai;
|
||
heroes.push(enemy);
|
||
defCe += ce;
|
||
}
|
||
} else {
|
||
let heroInfo = new PvpOtherHeroes(hs ? hs.score : 0);
|
||
heroInfo.setHeroInfo(dbHero, artifact);
|
||
otherHeroes.push(heroInfo);
|
||
}
|
||
}
|
||
heroes.sort((a, b) => b.score - a.score);
|
||
otherHeroes.sort((a, b) => b.score - a.score);
|
||
|
||
return {
|
||
isRobot: false, ...defense, defCe, ...role, pos, rankLv, heroes, otherHeroes, roleId, oppRoleId: makeRobotId(pvpdefense.roleId)
|
||
};
|
||
}
|
||
|
||
async function matchRobot(chosenOpps: string[], role: RoleType, score: number, dicOpp: DicPvpOpponent) {
|
||
// console.log('matchRobot', JSON.stringify(oppPlayers))
|
||
|
||
let { lv: myLv, roleId } = role;
|
||
let { id: pos, minLv, maxLv, ratio } = dicOpp;
|
||
|
||
let pvpConfig = await PVPConfigModel.findCurPVPConfig();
|
||
let range = pvpConfig?.warIds||[];
|
||
let warId = getRandSingleEelm(range);
|
||
if (!warId) return null;
|
||
let result = gameData.war.get(warId);
|
||
if (!result) return null;
|
||
|
||
let robotWarjson = gameData.warJson.get(result.dispatchJsonId);
|
||
if (!robotWarjson) return null
|
||
let heroes: PvpEnemies[] = [];
|
||
let defCe = 0;
|
||
for (let h of robotWarjson) {
|
||
if (h.relation == 1) continue;
|
||
let actorId = h.randomEnemy.length > 0? getRandSingleEelm(h.randomEnemy): 0;
|
||
let dicHero = gameData.hero.get(actorId);
|
||
if (!dicHero) continue;
|
||
let heroInfo = new PvpHeroInfo();
|
||
let robotInfo = getRobotAttribute(actorId, ratio, score);
|
||
if(!robotInfo) continue;
|
||
let { attribute, ce, lv } = robotInfo;
|
||
heroInfo.setRobotInfo(dicHero, lv);
|
||
defCe += ce;
|
||
heroInfo.setAttribute(attribute);
|
||
let enemy = new PvpEnemies(h, heroInfo, 0, ce);
|
||
enemy.setOutIndex(h.outIndex);
|
||
heroes.push(enemy);
|
||
}
|
||
|
||
let oppRoleId = generateRobotRoleId();
|
||
chosenOpps.push(oppRoleId);
|
||
let roleName = getChineseName();
|
||
let pLv = getPLvByScore(score);
|
||
let hisPLv = Math.floor(pLv + (minLv + maxLv) / 2);
|
||
if (hisPLv < 1) hisPLv = 1
|
||
return {
|
||
isRobot: true, roleId, oppRoleId, roleName, pos, defCe, pLv: hisPLv, lv: myLv, heroes, rankLv: 0, warId: result.war_id, buff: getRandSingleEelm(result.mapseid)||0,
|
||
head: randomHead(), frame: randomFrame(), spine: randomSpine()
|
||
};
|
||
}
|
||
|
||
function randomHead() {
|
||
return randomGoodsByItid(getHeadItid())||EXTERIOR.EXTERIOR_FACE;
|
||
}
|
||
|
||
function randomFrame() {
|
||
return randomGoodsByItid(getFrameItid())||EXTERIOR.EXTERIOR_FACECASE;
|
||
}
|
||
|
||
function randomSpine() {
|
||
return randomGoodsByItid(getSpineItid())||EXTERIOR.EXTERIOR_APPEARANCE;
|
||
}
|
||
|
||
// 生成机器人roleId
|
||
function generateRobotRoleId() {
|
||
return `${genCode(10)}_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: HeroScore[]) {
|
||
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 refChallengeCnt(challengeCnt: number, challengeRefTime: number, seasonEndTime: number, roleId: string, vipStartTime?: number) {
|
||
let hasChanged = false;
|
||
let initCount = await getVipPvpChallengeMaxCnt(roleId, vipStartTime)
|
||
if (challengeCnt >= initCount) {
|
||
return { hasChanged, challengeCnt, challengeRefTime: nowSeconds() };
|
||
}
|
||
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;
|
||
hasChanged = true;
|
||
}
|
||
if(challengeCnt > initCount) challengeCnt = initCount;
|
||
return { hasChanged, challengeCnt, challengeRefTime };
|
||
}
|
||
|
||
export async function comsumeChallengeCnt(challengeCnt: number, challengeRefTime: number, seasonEndTime: number, roleId: string) {
|
||
let initCount = await getVipPvpChallengeMaxCnt(roleId)
|
||
|
||
challengeCnt--;
|
||
if (challengeCnt >= initCount) {
|
||
return { challengeCnt, challengeRefTime };
|
||
}
|
||
if (challengeCnt == initCount - 1) {
|
||
challengeRefTime = nowSeconds();
|
||
return { challengeCnt, challengeRefTime };
|
||
}
|
||
return refChallengeCnt(challengeCnt, challengeRefTime, seasonEndTime, roleId);
|
||
}
|
||
|
||
export async function sendLastSeasonRewardIfNotSent(pvpDefense: PvpDefenseType) {
|
||
let seasonSettleNum: number = pinus.app.get('pvpSettleSeasonNum');
|
||
console.log('##### sendLastSeasonRewardIfNotSent seasonSettleNum', pvpDefense.roleId, pvpDefense.rankSeasonNum, seasonSettleNum)
|
||
if(seasonSettleNum && pvpDefense.rankSeasonNum <= seasonSettleNum) {
|
||
let oldPvpCongig = await PVPConfigModel.findPVPConfig(seasonSettleNum);
|
||
let result = await sendPVPRewardToUser(pvpDefense, seasonSettleNum, oldPvpCongig.seasonEndTime);
|
||
pvpDefense = result.pvpDefense;
|
||
}
|
||
return pvpDefense;
|
||
}
|
||
|
||
// async function checkHasSettled(seasonSettleNum: number, roleId: string) {
|
||
// let hasData = await PvpSeasonResultModel.checkResultBySeasonNum(roleId, seasonSettleNum);
|
||
// return hasData;
|
||
// }
|
||
|
||
// 获取刷新对手次数及消耗
|
||
export function refreshRefOppCnt(pvpDefense: PvpDefenseType) {
|
||
let { refOppCnt = 0, setAttackCnt = 0, buyAttackCnt = 0, refDaily } = pvpDefense;
|
||
let curTime = new Date();
|
||
let shouldRefOpp = shouldRefresh(refDaily, curTime);
|
||
if (shouldRefOpp) {
|
||
refOppCnt = 0; setAttackCnt = 0; buyAttackCnt =0; refDaily = curTime;
|
||
}
|
||
return {
|
||
shouldRefOpp,
|
||
refOppCnt, refDaily, setAttackCnt, buyAttackCnt,
|
||
consume: gameData.pvpRefreshConsume.get(refOppCnt + 1)
|
||
}
|
||
}
|
||
|
||
|
||
/**
|
||
* 根据比例计算机器人属性
|
||
* @param attribute 出兵表中的属性字段
|
||
* @param ce 我的战力
|
||
* @param enemyCe 出兵表对手战力
|
||
* @param ratio 系数
|
||
*/
|
||
export function getRobotAttribute(hid: number, posRatio: number, score: number) {
|
||
|
||
let difficultRatio = getPvpDifficultByScore(score);
|
||
if(!difficultRatio) return null
|
||
let dicHero = gameData.hero.get(hid);
|
||
|
||
let newAttribute = new AttributeCal();
|
||
newAttribute.setLv(difficultRatio.enemyLv);
|
||
newAttribute.setByMap(hid, dicHero.baseAbilityArr, difficultRatio.value / 10000 * posRatio);
|
||
let subAttr = gameData.towerPvpSubAttr.get(difficultRatio.secondAttrLevel);
|
||
if(subAttr) newAttribute.setByWarJson(hid, subAttr.secondAtr);
|
||
let attrArr = newAttribute.getAttributesToString();
|
||
let newCe = newAttribute.calCe();
|
||
return { attribute: attrArr, ce: newCe, lv: difficultRatio.enemyLv };
|
||
}
|
||
|
||
// 获取我方战报记录
|
||
export async function generMyRecInfo(pvpDefense: PvpDefenseType, role: RoleType, isSuccess: boolean, pos: number, myHeroes: pvpEndParamInter[]) {
|
||
let { attack, defense, heroScores, winStreakNum, hisWinStreakNum = 0, score, hisScore, seasonWinNum = 0 } = pvpDefense;
|
||
let { roleId } = role;
|
||
if (isSuccess) {
|
||
winStreakNum ++;
|
||
seasonWinNum ++;
|
||
} else {
|
||
winStreakNum = 0;
|
||
}
|
||
if(winStreakNum > hisWinStreakNum) {
|
||
hisWinStreakNum = winStreakNum;
|
||
}
|
||
|
||
const dicOpp = gameData.pvpOpponent.get(pos);
|
||
const plusScore = getPlusScore(winStreakNum);
|
||
|
||
let myHeroRecords: HeroesRecord[] = []; // 存入rec里面的数据
|
||
let showHeroScores = new Array<{ hid: number, addScore: number, plusScore: number, score: number }>();
|
||
let addSumScore = 0;
|
||
for (let { actorId: hid } of attack.heroes) {
|
||
if(hid == 0) continue;
|
||
let params = myHeroes.find(cur => cur.hid == hid);
|
||
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 skinId');
|
||
|
||
let record = new HeroesRecord();
|
||
record.setByHero(myHero, params);
|
||
myHeroRecords.push(record);
|
||
}
|
||
|
||
let attackInfo = new PvpRecordPlayerInfo();
|
||
attackInfo.setByRole(role, myHeroRecords, isSuccess, isSuccess ? addSumScore : 0, role.lv);
|
||
|
||
let newAttack = <Attack>calLineupScore(attack, heroScores);
|
||
let newDefense = <Defense>calLineupScore(defense, heroScores);
|
||
|
||
let updateParam = {
|
||
winStreakNum, hisWinStreakNum, attack: newAttack, defense: newDefense, heroScores, score: score + addSumScore, hisScore: hisScore > score + addSumScore? hisScore: score + addSumScore, seasonWinNum
|
||
}
|
||
|
||
return { attackInfo, showHeroScores, updateParam }
|
||
}
|
||
|
||
export function calLineupScore(lineup: Attack|Defense, heroScores: HeroScore[]) {
|
||
if(!lineup) return lineup;
|
||
let scores: number[] = [];
|
||
for(let { actorId } of lineup.heroes) {
|
||
let hs = heroScores.find(cur => cur.hid == actorId);
|
||
if(hs) {
|
||
scores.push(hs.score);
|
||
}
|
||
}
|
||
let { pLv, score } = getPlvAndScore(scores);
|
||
return {...lineup, score, pLv}
|
||
}
|
||
|
||
// 获取对手战报记录
|
||
export async function generPVPOppRecInfo(isSuccess: boolean, curOpp: OppPlayer, oppHeroes: pvpEndParamInter[], serverId: number) {
|
||
let oppHeroRecords = new Array<HeroesRecord>();
|
||
let oppRole = <PvpHistoryOppType>curOpp.oppDef;
|
||
if (!oppRole) { console.error('opp role not found') }
|
||
|
||
for (let params of oppHeroes) {
|
||
let historyHero = oppRole.heroes.find(cur => cur.actorId == params.hid);
|
||
if (historyHero) {
|
||
let hs = new HeroesRecord();
|
||
hs.setByPvpHeroInfo(historyHero, params);
|
||
oppHeroRecords.push(hs);
|
||
}
|
||
}
|
||
let addSumScore = 0;
|
||
let pvpDefense = curOpp.isRobot? null: await PvpDefenseModel.findByRoleId(robotIdComBack(curOpp.roleId));
|
||
if(pvpDefense && !isSuccess) {
|
||
if(pvpDefense && pvpDefense.defense) {
|
||
let { attack, defense, heroScores, score, defenseScoreCnt, refDefenseScore } = pvpDefense;
|
||
if(shouldRefresh(refDefenseScore, new Date())) {
|
||
defenseScoreCnt = 0; refDefenseScore = new Date();
|
||
}
|
||
if(defenseScoreCnt < PVP.PVP_DEFENSE_SUCCESS_REWARD_MAX_CNT) {
|
||
for(let { actorId } of defense.heroes) {
|
||
let hs = heroScores.find(cur => cur.hid == actorId);
|
||
const dicOpp = gameData.pvpOpponent.get(curOpp.pos);
|
||
let addScore = PVP.PVP_DEFENSE_SUCCESS_REWARD - dicOpp.score;
|
||
if(hs) {
|
||
hs.score += addScore;
|
||
} else {
|
||
heroScores.push({ hid: actorId, score: addScore })
|
||
}
|
||
addSumScore += addScore;
|
||
}
|
||
|
||
let newAttack = <Attack>calLineupScore(attack, heroScores);
|
||
let newDefense = <Defense>calLineupScore(defense, heroScores);
|
||
|
||
|
||
await PvpDefenseModel.updateInfo(pvpDefense.roleId, { attack: newAttack, defense: newDefense, heroScores, score: score + addSumScore, defenseScoreCnt: defenseScoreCnt + 1, refDefenseScore });
|
||
|
||
} else {
|
||
await PvpDefenseModel.updateInfo(pvpDefense.roleId, { defenseScoreCnt: defenseScoreCnt + 1, refDefenseScore });
|
||
}
|
||
}
|
||
}
|
||
|
||
let defenseInfo = new PvpRecordPlayerInfo();
|
||
defenseInfo.setByHistoryOpp(oppRole, oppHeroRecords, !isSuccess, isSuccess ? 0 : addSumScore, pvpDefense?.serverId||serverId)
|
||
return defenseInfo
|
||
}
|
||
|
||
/**
|
||
* pvp定时任务赛季结算
|
||
* @param obj
|
||
*/
|
||
export async function pvpSeasonEnd(seasonNum: number) {
|
||
let pvpConfig = await PVPConfigModel.findPVPConfig(seasonNum);
|
||
if(pvpConfig.hasSettleReward) return;
|
||
|
||
console.log('exce pvpSeasonEnd ' + pvpConfig.seasonNum);
|
||
let resultMaxRank = getPvpRankMaxRewardsBySeasonNum(pvpConfig.seasonNum);//根据排行榜的奖励表,获得最大排名挡位的最小值,其余不在结算中结算的玩家按照最大排名挡位在登录或进入pvp时结算
|
||
if(!resultMaxRank) return;
|
||
|
||
let pvpKeys = await findKeys(`${REDIS_KEY.PVP_RANK}:${pvpConfig.seasonNum}:`);
|
||
for(let key of pvpKeys) {
|
||
let [,, groupId] = key.split(':');
|
||
let r = new Rank(REDIS_KEY.PVP_RANK, { seasonNum: pvpConfig.seasonNum, groupId: parseInt(groupId) }, false, resultMaxRank.min - 1);
|
||
let allRank = <RoleRankInfo[]>(await r.getRankByRange());
|
||
console.log('******** allRank', 0, resultMaxRank.min - 2, allRank)
|
||
for(let { rank, roleId } of allRank) {
|
||
console.log('******** pvpSeasonEnd: ', rank, roleId);
|
||
let pvpDefense = await PvpDefenseModel.findByRoleId(roleId);
|
||
let { pvpSeasonResult } = await sendPVPRewardToUser(pvpDefense, pvpConfig.seasonNum, pvpConfig.seasonEndTime);
|
||
reportTAEvent(roleId, TA_EVENT.PVP_SEASON_END, { top_rank: rank, hero_score: pvpSeasonResult.heroScores })
|
||
}
|
||
}
|
||
await PvpDefenseModel.resetDefense();
|
||
let settledPvpConfig = await PVPConfigModel.setReward(pvpConfig.seasonNum);
|
||
await setPvpSettleSeasonNumToRemote(settledPvpConfig);
|
||
}
|
||
|
||
/**
|
||
* pvp定时任务结算获得添加邮件信息
|
||
* @param pvpDefense
|
||
* @param seasonNum
|
||
* @param oldSeasonEndTime
|
||
*/
|
||
export async function sendPVPRewardToUser(pvpDefense: PvpDefenseType, seasonNum: number, seasonEndTime: number) {
|
||
//检查并返回排名结算以及武将功勋结算
|
||
let pvpSeasonResult = await savePvpSeasonResult(pvpDefense, seasonNum, seasonEndTime);
|
||
let { rankGoods, heroGoods, rankLv, seasonWinNum, receivedBox } = pvpSeasonResult;
|
||
//下发邮件
|
||
if (rankGoods.length > 0) //排名奖励
|
||
await sendMailByContent(MAIL_TYPE.PVP_RANK_REWARD, pvpDefense.roleId, {
|
||
params: [JSON.stringify(seasonNum), (rankLv > 1000 ? '999+' : JSON.stringify(rankLv))],
|
||
goods: rankGoods
|
||
});
|
||
if (heroGoods.length > 0) //武将功勋奖励
|
||
await sendMailByContent(MAIL_TYPE.PVP_RESULT, pvpDefense.roleId, {
|
||
params: [JSON.stringify(seasonNum)],
|
||
goods: heroGoods
|
||
});
|
||
|
||
if(receivedBox.length > 0)
|
||
await sendUnreceivedPvpBox(pvpDefense.roleId, seasonNum, seasonWinNum, receivedBox);
|
||
|
||
return {
|
||
pvpSeasonResult,
|
||
pvpDefense: await resetPvpScores(pvpDefense, pvpSeasonResult)
|
||
}
|
||
}
|
||
|
||
async function sendUnreceivedPvpBox(roleId: string, seasonNum: number, seasonWinNum: number, receivedBox: number[]) {
|
||
let rewards: RewardInter[] = [];
|
||
let pvpBoxes = getPvpBoxsBySeasonNum(seasonNum);
|
||
for(let { index, winTimes, reward } of pvpBoxes) {
|
||
if(seasonWinNum >= winTimes && receivedBox.indexOf(index) == -1) {
|
||
rewards.push(...reward);
|
||
}
|
||
}
|
||
if(rewards.length > 0) {
|
||
await sendMailByContent(MAIL_TYPE.PVP_BOX, roleId, {
|
||
goods: rewards
|
||
});
|
||
}
|
||
}
|
||
|
||
async function resetPvpScores(pvpDefense: PvpDefenseType, pvpSeasonResult: PvpSeasonResultType) {
|
||
let { roleId, attack } = pvpDefense;
|
||
let { newHeroScores, newScore, seasonNum } = pvpSeasonResult;
|
||
let initCount = await getVipPvpChallengeMaxCnt(roleId)
|
||
|
||
let newAttack = <Attack>calLineupScore(attack, newHeroScores);
|
||
|
||
pvpDefense = await PvpDefenseModel.updateInfoAndInclude(roleId, {
|
||
heroScores: newHeroScores, score: newScore, attack: newAttack, defense: null, hasDefense: false, rankSeasonNum: seasonNum + 1,
|
||
challengeCnt: initCount, challengeRefTime: 0, winStreakNum: 0,
|
||
seasonWinNum: 0, receivedBox: []
|
||
});
|
||
return pvpDefense;
|
||
}
|
||
|
||
/**
|
||
* 检查并返回排名结算以及武将功勋结算
|
||
* @param pvpDefense
|
||
* @param seasonNum
|
||
* @param oldSeasonEndTime
|
||
* @param rankLv
|
||
*/
|
||
export async function savePvpSeasonResult(pvpDefense: PvpDefenseType, seasonNum: number, seasonEndTime: number, rankLv?: number) {
|
||
let groupId = await getPVPGroupIdOfServer(pvpDefense.serverId);
|
||
if (!rankLv) {
|
||
let r = new Rank(REDIS_KEY.PVP_RANK, { seasonNum, groupId });
|
||
rankLv = await r.getMyRank({ roleId: pvpDefense.roleId });// 获得排行榜排名
|
||
}
|
||
let pvpRankReward: DicRankRewads = getPvpRankRewardsByRank(seasonNum, rankLv);
|
||
let rankGoods: RewardInter[] = [];
|
||
if (pvpRankReward) {
|
||
rankGoods = pvpRankReward.reward;//排名奖励
|
||
}
|
||
let heroGoods: HeroReward[] = [];
|
||
let newHeroScores: HeroScore[] = [], newScore = 0;
|
||
for (let heroScore of pvpDefense.heroScores) {
|
||
let pvpHeroReward = getPvpHeroRewardsByScore(heroScore.score); //获得武将功勋奖励
|
||
if (pvpHeroReward) {
|
||
newHeroScores.push({...heroScore, score: pvpHeroReward.heroscore });
|
||
for(let { id, count } of pvpHeroReward.reward) {
|
||
heroGoods.push({
|
||
hid: heroScore.hid, id, count
|
||
});
|
||
}
|
||
newScore += pvpHeroReward.heroscore;
|
||
} else {
|
||
newHeroScores.push(heroScore);
|
||
newScore += heroScore.score;
|
||
}
|
||
}
|
||
|
||
//pvp锁定的信息存入赛季结算表中
|
||
let {receivedBox, score, seasonWinNum, heroScores } = pvpDefense;
|
||
let pvpSeasonResult = await PvpSeasonResultModel.updatePvpSeasonResult(pvpDefense.roleId, seasonNum, {
|
||
receivedBox, score, seasonWinNum, heroScores, rankLv, heroGoods, rankGoods, show: true, newScore, newHeroScores, seasonEndTime
|
||
});//结算修改玩家pvp信息
|
||
if(newScore > 0) {
|
||
let r = new Rank(REDIS_KEY.PVP_RANK, { seasonNum: seasonNum + 1, groupId });
|
||
await r.setRankWithRoleInfo(pvpDefense.roleId, newScore, Date.now());
|
||
}
|
||
|
||
// 更新任务
|
||
await checkTask(pvpDefense.serverId, pvpDefense.roleId, null, TASK_TYPE.PVP_HERO_SCORE, { heroScores: pvpDefense.heroScores });
|
||
await checkTask(pvpDefense.serverId, pvpDefense.roleId, null, TASK_TYPE.PVP_RANK, { pvpRank: rankLv });
|
||
return pvpSeasonResult;
|
||
}
|
||
|
||
export async function generPvpLineupCe(roleId: string, lineupCe: LineupCe[], attackHero: AttackHero[], defenseHero: DefenseHero[], dbHeroes: HeroType[]) {
|
||
let newHids: number[] = []; // 本次新增武将
|
||
let newLineupCe: { hid: number, ce: number }[] = []; // 新生成的lineupCe字段
|
||
for(let { actorId } of [...attackHero, ...defenseHero]) {
|
||
let n = newLineupCe.find(cur => cur.hid == actorId);
|
||
if(!n) {
|
||
let lineup = lineupCe.find(cur => cur.hid == actorId);
|
||
if(lineup) {
|
||
newLineupCe.push(lineup);
|
||
} else {
|
||
let dbHero = dbHeroes.find(cur => cur.hid == actorId);
|
||
if(dbHero) {
|
||
newLineupCe.push({ hid: actorId, ce: dbHero.ce });
|
||
} else {
|
||
newHids.push(actorId);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
let heroes = await HeroModel.findByHidRange(newHids, roleId, 'hid ce', true);
|
||
for(let { hid, ce } of heroes) {
|
||
newLineupCe.push({ hid, ce });
|
||
}
|
||
return newLineupCe;
|
||
}
|
||
|
||
|
||
export function getPvpSeasonStatus() {
|
||
let { seasonEndTime, seasonStartTime, seasonRewardTime } = getPvpTime();
|
||
let now = nowSeconds();
|
||
if(now >= seasonStartTime && now < seasonEndTime) {
|
||
return PVP_SEASON_STATUS.START;
|
||
} else if (now >= seasonEndTime && now < seasonRewardTime) {
|
||
return PVP_SEASON_STATUS.SUMMIT;
|
||
} else {
|
||
return PVP_SEASON_STATUS.WAITING;
|
||
}
|
||
}
|
||
|
||
interface PvpTime {
|
||
seasonNum: number, seasonEndTime: number, seasonStartTime: number, seasonRewardTime: number
|
||
}
|
||
export function getPvpTime(): PvpTime {
|
||
return {
|
||
seasonNum: pinus.app.get('pvpSeasonNum'),
|
||
seasonEndTime: pinus.app.get('pvpSeasonEndTime'),
|
||
seasonStartTime: pinus.app.get('pvpSeasonStartTime'),
|
||
seasonRewardTime: pinus.app.get('pvpSeasonRewardTime')
|
||
}
|
||
}
|
||
|
||
export async function getPvpTimeFromRemote(): Promise<PvpTime> {
|
||
let serverType = pinus.app.getServerType();
|
||
if(serverType == 'battle') {
|
||
return getPvpTime();
|
||
} else {
|
||
let battleServers = pinus.app.getServersByType('battle');
|
||
let server = getRandSingleEelm(battleServers);
|
||
return await pinus.app.rpc.battle.battleRemote.getPvpTime.toServer(server.id);
|
||
}
|
||
}
|
||
|
||
export function checkPvpSeasonIsStart() {
|
||
let status = getPvpSeasonStatus();
|
||
return status == PVP_SEASON_STATUS.START;
|
||
}
|
||
|
||
export function checkPvpSeasonIsSummit() {
|
||
let status = getPvpSeasonStatus();
|
||
return status == PVP_SEASON_STATUS.SUMMIT;
|
||
}
|
||
|
||
export function checkPvpSeasonIsWaiting() {
|
||
let status = getPvpSeasonStatus();
|
||
return status == PVP_SEASON_STATUS.WAITING;
|
||
} |