Files
ZYZ/game-server/app/services/pvpService.ts

800 lines
32 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, 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';
import { JewelModel } from '../db/Jewel';
/**
* 返回对手三人信息
*
* @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);
let hids = defenseHeroes.map(cur => cur.actorId);
console.log('#### hids', hids);
let artifacts = await ArtifactModel.findbyHids(role.roleId, hids);
let jewels = await JewelModel.findbyRoleAndHids(role.roleId, hids);
console.log('#### jewels', jewels.length);
for (let dbHero of dbHeroes) {
let artifact = dbHero.artifact? artifacts.find(cur => cur.seqId == dbHero.artifact): null;
let curJewels = jewels.filter(jewel => jewel.hid == dbHero.hid);
console.log('#### curJewels', dbHero.hid, curJewels.length)
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, curJewels);
// 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, curJewels);
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 == 0 || 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, pvpDefense.score);
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;
}