Files
ZYZ/game-server/app/services/gvg/gvgBattleService.ts
2023-08-09 09:35:04 +08:00

909 lines
42 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 { GVGTeamMem } from "../../domain/battleField/gvgBattle";
import { GVGLeagueModel, GVGLeagueType } from "../../db/GVGLeague";
import GVGTeam, { GVGTeamModel, GVGTeamType, GVGTeamUpdate } from "../../db/GVGTeam";
import { GVGCityModel, GVGCityType } from "../../db/GVGCity";
import { gameData, getGVGBattleRankReward } from "../../pubUtils/data";
import { COUNTER, GVG_AREA_TYPE, GVG_ATTACK_TYPE, GVG_BATTLE_RANK_TYPE, GVG_PERIOD, GVG_POINT_TYPE, GVG_SERVER_TYPE, GVG_TECH_TYPE, MAIL_TYPE, PUSH_ROUTE, REDIS_KEY, SERVER_GROUP_FUN_TYPE, STATUS } from "../../consts";
import { getTimeFun, nowSeconds } from "../../pubUtils/timeUtil";
import { DicGVGAreaPoint } from "../../pubUtils/dictionary/DicGVGAreaPoint";
import { getGVGBattleData, getGVGBattleMap } from "../memoryCache/gvgBattleData";
import { GVGAttackSpine, GVGCityMapInfo, GVGTeamInList, GVGTeamInListOnPoint, GVGTeamSpineInMap } from "../../domain/gvgField/returnData";
import { GVG } from "../../pubUtils/dicParam";
import { GVGHeroInfo, PvpEnemies, PvpHeroInfo } from "../../domain/dbGeneral";
import { getGroupKey, getGVGConfig, getGVGConfigFromRemote, getGVGPeriodData, getGVGServerType } from "./gvgService";
import { GVGLeaguePrepareModel } from "../../db/GVGLeaguePrepare";
import { pinus } from "pinus";
import { dispatch } from "../../pubUtils/dispatcher";
import { Rank } from "../rankService";
import { KeyNameParam, LeagueRankInfo, myIdInter, RoleRankInfo } from "../../domain/rank";
import { findKeys, getAllServerName, redisClient } from "../redisService";
import { sendMessageToGVGAreaByTeamWithSuc, sendMessageToGVGAreaWithSuc, sendMessageToGVGCityWithSuc, sendMessageToUserWithSuc } from "../pushService";
import { sendMailByContent, sendMailToLeagueByContent } from "../mailService";
import { GVGCityAreaPointModel } from "../../db/GVGCityAreaPoint";
import { addCityGuardMessage } from "./gvgRecService";
import { GVGUserDataModel } from "../../db/GVGUserData";
import { RoleModel } from "../../db/Role";
import { getFightTimeByPeriod } from "./gvgFightService";
import { getRandSingleEelm } from "../../pubUtils/util";
import { HeroModel, HeroType } from "../../db/Hero";
import { ArtifactModel } from "../../db/Artifact";
import { getHeroesAttributes } from "../playerCeService";
import { CounterModel } from "../../db/Counter";
import { getGroupIdOfServer } from "../serverService";
import { LineupHero } from "../../domain/roleField/hero";
/**
* 获取本联军上周占领的城池
* @param league
* @returns number[] 城池id
*/
export async function getGVGCities(league: GVGLeagueType) {
let { configId } = getGVGConfig();
let cities = await GVGCityModel.findGuardCityByLeague(configId, league.leagueCode, 'cityId');
return cities.map(city => city.cityId);
}
/**
* 获取当前城池状态
* @returns [{cityId: number, guardLeagueCode: string, guardLeagueName: string, teamCnt: number }]
*/
export async function getGVGCitiesInfo(configId: number, groupKey: string, league?: GVGLeagueType) {
let cities = await GVGCityModel.findGuardCity(configId, groupKey);
let result: GVGCityMapInfo[] = [];
for(let city of cities) {
let obj = new GVGCityMapInfo(city.cityId);
obj.setCity(city);
if(league) {
let players = (city.players||[]).filter(cur => cur.leagueCode == league.leagueCode);
obj.setTeamCnt(players.length);
let r = new Rank(REDIS_KEY.GVG_BATTLE_LEAGUE_RANK_BY_CITY, { configId, groupKey, cityId: city.cityId });
let score = await r.getMyScore({ leagueCode: league.leagueCode });
obj.setScore(score||0);
}
result.push(obj);
}
return result;
}
export async function getAllGVGCitiesInfo(configId: number, groupKey: string, serverType: number) {
let cities = await GVGCityModel.findGuardCity(configId, groupKey);
let result: GVGCityMapInfo[] = [];
for(let [cityId, { mapType }] of gameData.gvgCity) {
if(mapType != serverType) continue;
let obj = new GVGCityMapInfo(cityId);
let city = cities.find(cur => cur.cityId == cityId);
obj.setCity(city);
result.push(obj);
}
return result
}
/**
* 获取内存中队伍的数据结构
*/
export function getGVGTeamMemInfo(team: GVGTeamType): GVGTeamMem {
const teamMem = new GVGTeamMem(team);
teamMem.isMoving = false;
teamMem.startMoveTime = 0;
teamMem.stopMoveTime = 0;
return teamMem;
}
/**
* 获取 重新进入城池/复活 所在的区域
* @param city GVGCity
* @param leagueCode 联军id
* @returns
*/
export function getBirthAreaOfCity(city: GVGCityType, leagueCode: string) {
let isGuard = city.guardLeague == leagueCode;
let dicGVGCity = gameData.gvgCity.get(city.cityId);
return isGuard? dicGVGCity.defenseBirth: dicGVGCity.attackBirth;
}
/**
* 玩家是否可以进入城池
* @param city
* @param leagueCode
* @returns
*/
export function checkEnterCityTime(city: GVGCityType, leagueCode: string) {
let isGuard = city?.guardLeague == leagueCode;
let { startFightTime, endFightTime } = getFightTimeByPeriod(GVG_PERIOD.BATTLE);
if(isGuard && startFightTime - GVG.GVG_GUARD_START_TIME > nowSeconds()) return false;
if(!isGuard && startFightTime > nowSeconds()) return false;
if(endFightTime < nowSeconds()) return false;
return true
}
// guild.gvgBattleHandler.startMove 检测
export function checkMoveStatus(team: GVGTeamType, cityId: number, areaId: number) {
if(!team) return STATUS.GVG_BATTLE_TEAM_NOT_FOUND;
if(team.cityId != cityId) return STATUS.GVG_BATTLE_IS_NOT_IN_CITY;
if(team.pointId > 0) return STATUS.GVG_BATTLE_TEAM_IS_SELLTED;
if(team.stopMoveTime > nowSeconds()) return STATUS.GVG_BATTLE_IS_MOVING;
if(team.moveCdTime > nowSeconds()) return STATUS.GVG_BATTLE_IS_MOVING_CD;
if(team.lockTime > nowSeconds()) return STATUS.GVG_TEAM_DEFENSEING;
if(team.restartTime > nowSeconds()) return STATUS.GVG_BATTLE_TEAM_REVIVE;
let dicArea = gameData.gvgArea.get(areaId);
if(!dicArea) return STATUS.DIC_DATA_NOT_FOUND;
if(dicArea.relateArea.indexOf(team.areaId) == -1) return STATUS.GVG_BATTLE_AREA_NOT_RELATE;
return STATUS.SUCCESS;
}
export function checkSettleStatus(team: GVGTeamType) {
if(!team) return STATUS.GVG_BATTLE_TEAM_NOT_FOUND;
if(team.stopMoveTime > nowSeconds()) return STATUS.GVG_BATTLE_IS_MOVING;
// if(team.moveCdTime > nowSeconds()) return STATUS.GVG_BATTLE_IS_MOVING_CD;
if(team.lockTime > nowSeconds()) return STATUS.GVG_TEAM_DEFENSEING;
if(team.restartTime > nowSeconds()) return STATUS.GVG_BATTLE_TEAM_REVIVE;
return STATUS.SUCCESS;
}
export async function initRobots(configId: number, groupKey: string, city: GVGCityType | { cityId: number, guardLeague: string }) {
let { cityId, guardLeague = '' } = city;
if(guardLeague) return [];
let robotTeams = await GVGTeamModel.findRobotTeams(configId, groupKey, cityId);
let updateDicPoints: DicGVGAreaPoint[] = [];
let { battleAreaIds = []} = gameData.gvgCity.get(cityId);
for(let areaId of battleAreaIds) {
let pointIds = gameData.gvgPointByAreaId.get(areaId)||[];
for(let pointId of pointIds) {
let dicPoint = gameData.gvgAreaPoint.get(pointId);
if(!dicPoint || dicPoint.type != GVG_POINT_TYPE.ROBOT) continue;
let robotTeam = robotTeams.find(team => team.pointId == dicPoint.pointId);
if(!robotTeam || (!robotTeam.isBroken && robotTeam.configId != configId) ) {
updateDicPoints.push(dicPoint);
}
}
}
if(updateDicPoints.length > 0) {
let lv = gameData.war.get(GVG.GVG_ROBOT_WARJSON)?.level||50;
robotTeams = await GVGTeamModel.initRobots(configId, groupKey, cityId, updateDicPoints, lv);
// 存入内存
let teamObj = getGVGBattleData(groupKey);
teamObj.enterCity(...robotTeams);
}
return robotTeams;
}
export function checkAreaIsInCity(cityId: number, areaIds: number[]) {
let dicCity = gameData.gvgCity.get(cityId);
if(!dicCity) return false;
for(let areaId of areaIds) {
if(dicCity.areaIds.indexOf(areaId) == -1) return false;
}
return true;
}
export function calBattleScoreByCe(isSuccess: boolean, lineupCe: number) {
let winScore = Math.floor(lineupCe/GVG.GVG_BATTLE_SCORE);
if(winScore <= 0) winScore = 1;
return isSuccess? winScore: 0;
}
export async function refreshTeams(configId: number, groupKey: string, roleId: string, myLeague: GVGLeagueType, hasConfirm: boolean) {
let oldTeams = await GVGTeamModel.findByRole(roleId, '-_id');
let teams: GVGTeamType[] = [];
for(let team of oldTeams) {
if(team.configId != configId || (hasConfirm && team.confirmConfigId != configId)) {
let { teamCode, maxDurability, lineup } = team;
let { lv, title, roleName, guildCode } = await RoleModel.findByRoleId(roleId, 'lv title roleName guildCode');
let heroes = await HeroModel.findByHidRange(lineup.map(hero => hero.actorId), roleId);
let { newLineup, newLineupCe } = await generNewLineup(roleId, heroes, lineup.map(({ actorId, dataId, outIndex }) => ({ actorId, dataId, order: outIndex })));
let addUpdate = hasConfirm? { confirmConfigId: configId }: {};
let newTeam = await GVGTeamModel.refreshByConfig(teamCode, { configId, lv, title, durability: maxDurability, cityId: 0, areaId: 0, pointId: 0, roleName, guildCode, leagueCode: myLeague.leagueCode, leagueName: myLeague.name, groupKey, lineup: newLineup, lineupCe: newLineupCe, ...addUpdate });
teams.push(newTeam);
} else {
teams.push(team);
}
}
return teams
}
export async function generNewLineup(roleId: string, heroes: HeroType[], lineup: { actorId: number, dataId: number, order: number, subHid?: number }[]) {
let attrByHid = await getHeroesAttributes(roleId);
let newLineup: GVGHeroInfo[] = [], newLineupCe = 0;
for(let { actorId, dataId, order, subHid } of lineup) {
let hero = heroes.find(cur => cur.hid == actorId);
if(hero) {
let artifact = hero.artifact? await ArtifactModel.findbySeqId(roleId, hero.artifact): null;
let heroInfo = new GVGHeroInfo();
heroInfo.setHeroInfo(hero, artifact, []);
heroInfo.setDataId(dataId, order);
let subHero = heroes.find(cur => cur.hid == subHid);
if(subHero) heroInfo.setSubHero(subHero);
let attr = attrByHid.get(actorId);
if(!attr) continue;
let attribute = attr.getAttributesToString();
heroInfo.setAttribute(attribute);
newLineup.push(heroInfo);
newLineupCe += hero.ce;
}
}
return { newLineup, newLineupCe };
}
/**
* 离开城池
* 当玩家占领据点的时候,可以保留据点;不占领的时候,不保留;退出游戏的时候,全不保留
* @param isForce 是否是玩家关闭游戏的那种离开
* @param roleId
* @param serverId
* @param guildCode
* @param myLeague
* @returns
*/
export async function leaveCity(isForce: boolean, roleId: string, serverId: number, guildCode: string, myLeague?: GVGLeagueType) {
if(!myLeague) myLeague = await GVGLeagueModel.findLeagueByGuild(guildCode);
let groupKey = await getGroupKey(serverId);
let { configId } = await getGVGConfigFromRemote();
let teams = await GVGTeamModel.findByRole(roleId);
let hasPoint = !!teams.find(team => team.pointId > 0);
// console.log('######### leaveCity', configId, groupKey)
if(isForce || !hasPoint) {
await GVGTeamModel.leaveCity(roleId);
await GVGCityModel.decreasePlayer(configId, groupKey, roleId);
if(myLeague) await GVGUserDataModel.changeCity(configId, myLeague.leagueCode, roleId, 0);
await GVGCityAreaPointModel.playerLeave(configId, groupKey, roleId);
// 处理内存数据
await pinus.app.rpc.guild.guildRemote.leaveCityMem.broadcast(groupKey, roleId);
for(let team of teams) {
if(team.cityId > 0 && team.areaId > 0) {
await sendMessageToGVGAreaByTeamWithSuc(groupKey, team.areaId, PUSH_ROUTE.GVG_PLAYER_LEAVE_AREA, {
cityId: team.cityId, areaId: team.areaId, teamCode: team.teamCode
});
}
}
}
}
export async function leaveCityMem(groupKey: string, roleId: string) {
let teamObj = getGVGBattleData(groupKey);
teamObj.leaveCity(roleId);
}
// 复活cd
export async function getTechReviveMinus(groupKey: string, configId: number, leagueCode: string) {
let teamObj = getGVGBattleData(groupKey);
let activeTech = teamObj.getLeagueTech(leagueCode);
if(!activeTech) {
let leaguePrepare = await GVGLeaguePrepareModel.findByLeague(configId, leagueCode);
activeTech = leaguePrepare?.activeTech||[];
teamObj.setLeagueTech(leagueCode, activeTech);
}
let minusCd = 0;
for(let techId of activeTech) {
let dicTech = gameData.gvgTech.get(techId);
if(dicTech && dicTech.type == GVG_TECH_TYPE.BATTLE_REVIVE_GAP) {
minusCd += dicTech.param[0];
}
}
let cd = GVG.GVG_DEFAULT_REVIVE_CD - minusCd;
if(cd < 0) cd = 0;
return cd;
}
// 诸葛连弩的伤害
export async function getTechKnifeHurt(configId: number, leagueCode: string) {
let leaguePrepare = await GVGLeaguePrepareModel.findByLeague(configId, leagueCode);
let hurt = 0;
let activeTech = leaguePrepare?.activeTech||[];
for(let techId of activeTech) {
let dicTech = gameData.gvgTech.get(techId);
if(dicTech && dicTech.type == GVG_TECH_TYPE.BATTLE_ITEM_KNIFE) {
if(dicTech.param[2] > hurt) hurt = dicTech.param[2];
}
}
return hurt
}
export function getGVGWarId(defenseTeam: GVGTeamType) {
if(!defenseTeam.isRobot) return GVG.GVG_CITY_BGMAP_GKID; // 玩家防守地图
if(!defenseTeam.isCatapult) return GVG.GVG_CATAPULT_WARJSON; // 投石车使用
return GVG.GVG_ROBOT_WARJSON; // 据点守卫使用
}
// guild.gvgBattleHandler.battleStart 里的heroes返回
export function getOppHeroes(warId: number, isRobot: boolean, lineup: GVGHeroInfo[]) {
let heroes: PvpEnemies[] = [];
const dicWar = gameData.war.get(warId);
if(!dicWar) { console.error(`warId ${warId} not found`); return [] }
const dicWarJson = gameData.warJson.get(dicWar.dispatchJsonId)||[];
for(let warJson of dicWarJson) {
if(!isRobot) {
let heroInfo = lineup.find(cur => cur.dataId == warJson.dataId);
if(!heroInfo) continue;
let hero = new PvpEnemies(warJson, heroInfo);
heroes.push(hero);
} else {
if(warJson.relation == 2) {
let dicHero = gameData.hero.get(warJson.actorId);
if(!dicHero) continue;
let heroInfo = new PvpHeroInfo();
heroInfo.setRobotInfo(dicHero, warJson.lv);
let hero = new PvpEnemies(warJson, heroInfo);
heroes.push(hero);
}
}
}
return heroes
}
// guild.gvgBattleHandler.battleStart 检测
export function checkGVGBattleStart(roleId: string, attackTeam: GVGTeamType, defenseTeam: GVGTeamType) {
if(!attackTeam || !defenseTeam) return STATUS.GVG_BATTLE_TEAM_INVALID;
if(attackTeam.roleId != roleId) return STATUS.GVG_TEAM_IS_NOT_MINE;
if(attackTeam.leagueCode == defenseTeam.leagueCode) return STATUS.GVG_SAME_LEAGUE_CANNOT_ATTACK;
if(attackTeam.areaId != defenseTeam.areaId) return STATUS.GVG_TEAM_NOT_SAME_AREA;
if(attackTeam.attackTime > nowSeconds()) return STATUS.GVG_TEAM_ATTACKING;
if(defenseTeam.defenseTime > nowSeconds() || defenseTeam.lockTime > nowSeconds()) {
return defenseTeam.isRobot? STATUS.GVG_ROBOT_DEFENSEING: STATUS.GVG_TEAM_DEFENSEING;
}
if(attackTeam.lockTime > nowSeconds()) return STATUS.GVG_BATTLE_TEAM_LOCK;
if(attackTeam.durability <= 0) return STATUS.GVG_ATTACK_TEAM_BROKEN;
if(defenseTeam.durability <= 0) return STATUS.GVG_DEFENSE_TEAM_BROKEN;
return STATUS.SUCCESS;
}
// —————————— 定时器相关 —————————— //
// gvg激战期开始定时器
export async function gvgBattleStart() {
let { configId } = getGVGConfig();
let guardCities = await GVGCityModel.findAllGuardCities(configId);
for(let { cityId, groupKey, guardLeague, guardLeagueName } of guardCities) {
let sid = await dispatchTeam(groupKey, cityId);
await pinus.app.rpc.guild.guildRemote.initCatapult.toServer(sid, cityId, groupKey, guardLeague, guardLeagueName);
}
}
// 每次活动开始初始化投石车
export async function initCatapult(cityId: number, groupKey: string, leagueCode: string, leagueName: string) {
let { configId } = getGVGConfig();
let leaguePrepare = await GVGLeaguePrepareModel.findByLeague(configId, leagueCode);
let activeTech = leaguePrepare?.activeTech||[];
let hasCatapult = false, atk = 0, durability = 0;
for(let techId of activeTech) {
let dicTech = gameData.gvgTech.get(techId);
if(dicTech && dicTech.type == GVG_TECH_TYPE.BATTLE_ITEM_CATAPULT) {
hasCatapult = true, atk = dicTech.param[0]; durability = dicTech.param[1];
}
}
if(hasCatapult) {
let updateDicPoints: DicGVGAreaPoint[] = [];
let { catapultAreaIds = []} = gameData.gvgCity.get(cityId);
for(let areaId of catapultAreaIds) {
let pointIds = gameData.gvgPointByAreaId.get(areaId)||[];
for(let pointId of pointIds) {
let dicPoint = gameData.gvgAreaPoint.get(pointId);
if(!dicPoint || dicPoint.type != GVG_POINT_TYPE.CATAPULT) continue;
updateDicPoints.push(dicPoint);
}
}
let lv = gameData.war.get(GVG.GVG_CATAPULT_WARJSON)?.level||50;
let teams = await GVGTeamModel.initCatapult(configId, groupKey, cityId, updateDicPoints, leagueCode, leagueName, atk, durability, lv);
// 处理内存
let teamObj = getGVGBattleData(groupKey);
teamObj.enterCity(...teams);
}
}
// 投石车投伤害
export async function catapultHurt() {
let { configId, period } = getGVGPeriodData();
let { startFightTime, endFightTime } = getFightTimeByPeriod(period);
if(nowSeconds() < startFightTime || nowSeconds() > endFightTime) return;
for(let [_key, teamObj] of getGVGBattleMap()) {
let teams = teamObj.findCatapult();
for(let catapult of teams) {
let dicGVGCity = gameData.gvgCity.get(catapult?.cityId);
let battleAreaIds = dicGVGCity?.battleAreaIds||[]
if(!catapult || catapult.isBroken || !dicGVGCity || battleAreaIds.length <= 0) continue;
let areaId = getRandSingleEelm(battleAreaIds);
// let areaId = catapult.cityId * 100 + 2;
let teamMems = teamObj.findCatapultAttackTeam(areaId, catapult.leagueCode);
let teams: GVGTeamType[] = [];
for(let { teamCode, leagueCode } of teamMems) {
let team = await GVGTeamModel.attackByCatapult(teamCode, catapult.captapultAtk, dicGVGCity.attackBirth, await getTechReviveMinus(teamObj.groupKey, configId, leagueCode));
teams.push(team);
}
teamObj.battleEnd(teams);
if(teams.length > 0) {
let roleToTeams = new Map<string, GVGTeamInList[]>();
let areaTeams: GVGTeamInList[] = [];
for(let team of teams) {
await pushTeamBeHurtMessage(team);
if(team.curTeamBreak && team.originPointId > 0) {
await GVGCityAreaPointModel.leavePoint(configId, teamObj.groupKey, team.teamCode);
}
if(!roleToTeams.has(team.roleId)) roleToTeams.set(team.roleId, []);
roleToTeams.get(team.roleId).push(new GVGTeamInList(team));
areaTeams.push(new GVGTeamInList(team));
}
await sendMessageToGVGAreaByTeamWithSuc(teamObj.groupKey, areaId, PUSH_ROUTE.GVG_TEAM_ATTACKED, {
cityId: catapult.cityId, areaId, attackType: GVG_ATTACK_TYPE.CATAPULT, teams: areaTeams
});
for(let [roleId, myTeams] of roleToTeams) {
await sendMessageToUserWithSuc(roleId, PUSH_ROUTE.GVG_MY_TEAM_ATTACKED, {
cityId: catapult.cityId, areaId, attackType: GVG_ATTACK_TYPE.CATAPULT, teams: myTeams
});
}
}
await sendMessageToGVGCityWithSuc(teamObj.groupKey, catapult.cityId, PUSH_ROUTE.GVG_SPINE_ATTACKED, {
cityId: catapult.cityId, areaId, teamCode: catapult.teamCode, attackType: GVG_ATTACK_TYPE.CATAPULT, spines: teams.map(team => new GVGAttackSpine(team, catapult.captapultAtk))
});
}
}
}
// 战斗积分更新
export async function redisAddBattleScore(gvgTeam: GVGTeamType, incScore: number) {
let { configId, groupKey, cityId, roleId, isRobot } = gvgTeam;
if(isRobot) return;
let nextWeek = <number>getTimeFun().getAfterDayWithHour(7);
let r = new Rank(REDIS_KEY.GVG_BATTLE_RANK, { configId, groupKey });
await r.setExpire(nextWeek);
await r.setRankWithRoleInfo(roleId, incScore, Date.now(), null, true);
// let r2 = new Rank(REDIS_KEY.GVG_BATTLE_USER_RANK_BY_CITY, { configId, groupKey, cityId });
// await r2.setExpire(nextWeek);
// await r2.setRankWithRoleInfo(roleId, incScore, Date.now(), null, true);
}
export async function redisAddSettleScore(gvgTeam: GVGTeamType, incScore: number) {
let { configId, groupKey, cityId, leagueCode, isRobot, roleId } = gvgTeam;
if(isRobot) return;
let nextWeek = <number>getTimeFun().getAfterDayWithHour(7);
let r1 = new Rank(REDIS_KEY.GVG_BATTLE_LEAGUE_RANK, { configId, groupKey });
await r1.setExpire(nextWeek);
await r1.setRankWithLeagueInfo(leagueCode, incScore, Date.now(), null, true);
let r2 = new Rank(REDIS_KEY.GVG_BATTLE_LEAGUE_RANK_BY_CITY, { configId, groupKey, cityId });
await r2.setExpire(nextWeek);
await r2.setRankWithLeagueInfo(leagueCode, incScore, Date.now(), null, true);
let r3 = new Rank(REDIS_KEY.GVG_BATTLE_USR_SETTLE_RANK, { configId, groupKey });
await r3.setExpire(nextWeek);
await r3.setRankWithRoleInfo(roleId, incScore, Date.now(), null, true)
// let r4 = new Rank(REDIS_KEY.GVG_BATTLE_USR_SETTLE_RANK_BY_CITY, { configId, groupKey, cityId });
// await r4.setExpire(nextWeek);
// await r4.setRankWithRoleInfo(roleId, incScore, Date.now(), null, true)
}
export async function getSpineCnt() {
let cnt = await redisClient().getAsync(REDIS_KEY.GVG_SPINE_CNT);
return cnt? parseInt(cnt): 20;
}
// 获取联军分城池排行榜
export async function getBattleRanksByCity(configId: number, groupKey: string, cityId: number, myLeague?: GVGLeagueType) {
let teamObj = getGVGBattleData(groupKey);
let pointByLeague = teamObj.findSettledPointMapByLeague(cityId);
let r = new Rank(REDIS_KEY.GVG_BATTLE_LEAGUE_RANK_BY_CITY, { configId, groupKey, cityId });
r.setGenerFieldsFun((obj => {
if(obj instanceof LeagueRankInfo) {
let pointIds = pointByLeague.get(obj.code)||[];
let incScore = 0;
for(let pointId of pointIds) incScore += gameData.gvgAreaPoint.get(pointId)?.score||0;
return { rank: obj.rank, leagueCode: obj.code, leagueName: obj.name, score: obj.num, incScore }
}
return obj
}));
let { ranks, myRank } = await r.getRankListWithMyRank({ leagueCode: myLeague?.leagueCode });
if (myLeague && !myRank) {
myRank = await r.generMyRankWithLeague(myLeague.leagueCode, 0, 0, myLeague);
}
return { ranks, myRank }
}
// 玩家进驻的积分排行榜
export async function getPlayerSettleRanksByCity(configId: number, groupKey: string, cityId: number, roleId?: string) {
let teamObj = getGVGBattleData(groupKey);
let r = new Rank(REDIS_KEY.GVG_BATTLE_USR_SETTLE_RANK, { configId, groupKey, cityId });
r.setGenerFieldsFun((obj => {
if(obj instanceof RoleRankInfo) {
let pointIds = teamObj.rolePoints.get(obj.roleId)||[];
let incScore = 0;
for(let [pointId] of pointIds) incScore += gameData.gvgAreaPoint.get(pointId)?.score||0;
return { rank: obj.rank, roleId: obj.roleId, name: obj.roleName, score: obj.num, incScore }
}
return obj
}));
let { ranks, myRank } = await r.getRankListWithMyRank({ roleId });
if (roleId && !myRank) {
myRank = await r.generMyRankWithRole(roleId, 0, 0);
}
return { ranks, myRank }
}
// 玩家挑战的积分排行榜,按城池
export async function getPlayerRanksByCity(configId: number, groupKey: string, cityId: number, roleId?: string) {
let r = new Rank(REDIS_KEY.GVG_BATTLE_RANK, { configId, groupKey, cityId });
r.setGenerFieldsFun((obj => {
if(obj instanceof RoleRankInfo) {
return { rank: obj.rank, roleId: obj.roleId, name: obj.roleName, score: obj.num }
}
return obj
}));
let { ranks, myRank } = await r.getRankListWithMyRank({ roleId });
if (roleId && !myRank) {
myRank = await r.generMyRankWithRole(roleId, 0, 0);
}
return { ranks, myRank }
}
// 每5秒一次结算
export async function gvgBattleSeconds() {
const { configId, period } = getGVGPeriodData();
let { startFightTime, endFightTime } = getFightTimeByPeriod(period);
const serverNames = await getAllServerName();
let spineCnt = await getSpineCnt();
let keys: { groupKey: string, cityId: number }[] = []
for(let [_key, teamObj] of getGVGBattleMap()) {
// console.log('#### gvgBattleSeconds groupKey: ', _key, 'areas', teamObj.findAreas(), 'appid', pinus.app.getServerId());
if(startFightTime <= nowSeconds() && endFightTime >= nowSeconds()) {
// 每5秒给据点上的人加积分
let teams = teamObj.findSettledPoint();
for(let teamMem of teams) {
if(teamMem.isBroken || teamMem.durability <= 0) continue;
let addScore = gameData.gvgAreaPoint.get(teamMem.pointId)?.score||0;
let team = await GVGTeamModel.addScore(teamMem.teamCode, 0, addScore);
await redisAddSettleScore(team, addScore);
}
}
// 向下推送区域数据
let spinesByCity = new Map<number, GVGTeamSpineInMap[]>();
for(let areaId of teamObj.findAreas()) {
let dicArea = gameData.gvgArea.get(areaId);
let teams = teamObj.findTeamsByArea(areaId, spineCnt);
let spines = teams.map(team => new GVGTeamSpineInMap(team, serverNames));
if(!spinesByCity.has(dicArea.cityId)) spinesByCity.set(dicArea.cityId, []);
spinesByCity.get(dicArea.cityId).push(...spines);
let index = keys.findIndex(cur => cur.cityId == dicArea.cityId && cur.groupKey == teamObj.groupKey);
if(index == -1) keys.push({ groupKey: teamObj.groupKey, cityId: dicArea.cityId });
}
for(let [cityId, spines] of spinesByCity) {
if(spines.length > 0) await sendMessageToGVGCityWithSuc(teamObj.groupKey, cityId, PUSH_ROUTE.GVG_AREA_SPINE_CHANGE, { cityId, spines });
}
}
if(startFightTime <= nowSeconds() && endFightTime >= nowSeconds()) {
// console.log('#### cityRank keys', JSON.stringify(keys), 'appid', pinus.app.getServerId())
for(let { groupKey, cityId } of keys) {
let { ranks } = await getBattleRanksByCity(configId, groupKey, cityId);
let { ranks: memberRanks } = await getPlayerRanksByCity(configId, groupKey, cityId);
let { ranks: memberSettleRanks } = await getPlayerSettleRanksByCity(configId, groupKey, cityId);
await sendMessageToGVGCityWithSuc(groupKey, cityId, PUSH_ROUTE.GVG_CITY_RANK_UPDATE, { cityId, ranks, memberRanks, memberSettleRanks });
}
}
}
export async function gvgBattleEnd() {
console.log('######### gvgBattleEnd #######')
let { configId } = getGVGConfig();
let dontSendReward = await redisClient().getAsync(REDIS_KEY.GVG_SEND_REWARD) == 'true';
// 城池占领情况
await calCityGuard(configId, dontSendReward);
// 联军排行榜发放奖励
let leagueKeys = await findKeys(`${REDIS_KEY.GVG_BATTLE_LEAGUE_RANK}:${configId}:`);
for(let key of leagueKeys) {
let [,, groupKey] = key.split(':');
let r = new Rank(REDIS_KEY.GVG_BATTLE_LEAGUE_RANK, { configId, groupKey });
let ranks = await r.getRankByRange();
// 联军排行榜发放奖励
for(let _obj of ranks) {
let obj = <LeagueRankInfo>_obj;
let dicRank = getGVGBattleRankReward(GVG_BATTLE_RANK_TYPE.LEAGUE, obj.rank);
await sendMailToLeagueByContent(MAIL_TYPE.GVG_BATTLE_LEAGUE_RANK_REWARD, obj.code, { params: [`${obj.rank}`], goods: dicRank.reward }, null, dontSendReward);
}
}
// 个人排行榜发放奖励
let playerKeys = await findKeys(`${REDIS_KEY.GVG_BATTLE_RANK}:${configId}:`);
for(let key of playerKeys) {
let [,, groupKey] = key.split(':');
let r = new Rank(REDIS_KEY.GVG_BATTLE_RANK, { configId, groupKey });
let ranks = await r.getRankByRange();
for(let _obj of ranks) {
let obj = <RoleRankInfo>_obj;
let dicRank = getGVGBattleRankReward(GVG_BATTLE_RANK_TYPE.PLAYER, obj.rank);
await sendMailByContent(MAIL_TYPE.GVG_BATTLE_PLAYER_RANK_REWARD, obj.roleId, { params: [`${obj.rank}`], goods: dicRank.reward }, dontSendReward);
}
}
// 个人积分排行榜发放奖励
let playerSettleKeys = await findKeys(`${REDIS_KEY.GVG_BATTLE_USR_SETTLE_RANK}:${configId}:`);
for(let key of playerSettleKeys) {
let [,, groupKey] = key.split(':');
let r = new Rank(REDIS_KEY.GVG_BATTLE_USR_SETTLE_RANK, { configId, groupKey });
let ranks = await r.getRankByRange();
for(let _obj of ranks) {
let obj = <RoleRankInfo>_obj;
let dicRank = getGVGBattleRankReward(GVG_BATTLE_RANK_TYPE.PLAYER_SETTLE, obj.rank);
await sendMailByContent(MAIL_TYPE.GVG_BATTLE_PLAYER_SETTLE_RANK_REWARD, obj.roleId, { params: [`${obj.rank}`], goods: dicRank.reward }, dontSendReward);
}
}
}
interface SortCities { cityType: number, cityId: number, index: number, league: string, score: number };
interface RankAndLastLeague { ranks: LeagueRankInfo[], lastLeague: string };
// 赛期末结算守城
export async function calCityGuard(configId: number, canSendReward: boolean) {
let sortCities = new Map<string, SortCities[]>(); // groupKey => cities
let rankByCity = new Map<string, Map<number, RankAndLastLeague>>(); // groupKey => city => {ranks, lastLeague}
await generateData(configId, sortCities, rankByCity);
for(let [groupKey, cities] of sortCities) {
let cityResult: number[] = [];
let index = 0; // 防死循环万一出什么事cityResult.length一直不等于cities.length
let guardLeagueCnt = new Map<string, number>();
while(cityResult.length != cities.length && (++index < 1000)) {
let sorted = cities
.filter(city => cityResult.indexOf(city.cityId) == -1)
.sort((a, b) => {
if(a.cityType != b.cityType) return a.cityType - b.cityType;
if(a.score != b.score) return b.score - a.score;
return b.cityId - a.cityId;
});
for(let { league: leagueCode, cityId } of sorted) {
if(!leagueCode) continue;
let cnt = guardLeagueCnt.get(leagueCode)||0;
if(cnt < GVG.GVG_CITY_OCCUPIED_NUMBER) {
await addGuardCity(configId, groupKey, cityId, leagueCode, canSendReward);
guardLeagueCnt.set(leagueCode, cnt + 1);
cityResult.push(cityId);
break;
} else {
let city = cities.find(cur => cur.cityId == cityId);
if(!city) continue;
if(city.index == -1) {
cityResult.push(city.cityId); continue;
}
let nextIndex = city.index + 1;
let { ranks, lastLeague } = rankByCity.get(groupKey)?.get(city.cityId)||{ ranks: [], lastLeague: '' };
if(ranks[nextIndex]) {
city.index = nextIndex;
city.league = ranks[nextIndex].code;
city.score = ranks[nextIndex].num;
} else {
if(lastLeague) {
city.index = -1;
city.league = lastLeague;
city.score = 0;
} else {
cityResult.push(city.cityId); continue;
}
}
}
}
}
}
}
// 结算守城处理数据
async function generateData(configId: number, sortCities: Map<string, SortCities[]>, rankByCity: Map<string, Map<number, RankAndLastLeague>>) {
let keys = await findKeys(`${REDIS_KEY.GVG_BATTLE_LEAGUE_RANK_BY_CITY}:${configId}:`);
let lastCities = await GVGCityModel.findByConfig(configId);
for(let key of keys) {
let [,, groupKey, _cityId] = key.split(':');
let cityId = parseInt(_cityId);
let dicCity = gameData.gvgCity.get(cityId);
if(!dicCity) continue;
let r = new Rank(REDIS_KEY.GVG_BATTLE_LEAGUE_RANK_BY_CITY, { configId, groupKey, cityId });
let ranks = <LeagueRankInfo[]>await r.getRankByRange();
if(ranks.length <= 0) continue;
if(!sortCities.has(groupKey)) sortCities.set(groupKey, []);
sortCities.get(groupKey).push({ cityType: dicCity.cityType, cityId, index: 0, league: ranks[0].code, score: ranks[0].num });
if(!rankByCity.has(groupKey)) rankByCity.set(groupKey, new Map());
let guardCity = lastCities.find(city => city.cityId == cityId && city.groupKey == groupKey);
if(!rankByCity.get(groupKey).has(cityId)) rankByCity.get(groupKey).set(cityId, { ranks, lastLeague: guardCity?.guardLeague??'' })
}
for(let { groupKey, cityId, guardLeague } of lastCities) {
if(rankByCity.has(groupKey) && rankByCity.get(groupKey).has(cityId)) continue;
let dicCity = gameData.gvgCity.get(cityId);
if(!dicCity) continue;
if(!sortCities.has(groupKey)) sortCities.set(groupKey, []);
sortCities.get(groupKey).push({ cityType: dicCity.cityType, cityId, index: -1, league: guardLeague, score: 0 });
if(!rankByCity.has(groupKey)) rankByCity.set(groupKey, new Map());
if(!rankByCity.get(groupKey).has(cityId)) rankByCity.get(groupKey).set(cityId, { ranks: [], lastLeague: guardLeague||'' })
}
}
async function addGuardCity(configId: number, groupKey: string, cityId: number, leagueCode: string, canSendReward: boolean) {
let dicCity = gameData.gvgCity.get(cityId);
let dicCityAdd = gameData.gvgCityAdd.get(dicCity.cityType);
let league = await GVGLeagueModel.findByCodeWithoutPopulate(leagueCode);
if(!league) return;
let leagueGroupKey = await getGroupKey(league.serverId);
if(groupKey != leagueGroupKey) {
console.log('addGuardCity groupKey err', cityId, groupKey, leagueCode, league.serverId);
return;
}
await GVGCityModel.guardCity(configId, groupKey, cityId, league);
await sendMailToLeagueByContent(MAIL_TYPE.GVG_GUARD_CITY_REWARD, leagueCode, { params: [dicCity.cityName], goods: dicCityAdd.occupyReward }, league, canSendReward);
await addCityGuardMessage(league, cityId);
}
// —————————— 定时器相关 end —————————— //
// —————————— 推送相关 —————————— //
// 推送
export async function battleEndSendMessage(groupKey: string, cityId: number, defenseTeam: GVGTeamType, attackTeam: GVGTeamType, attackType: GVG_ATTACK_TYPE) {
let areaId = defenseTeam.curTeamBreak? defenseTeam.fromAreaId: defenseTeam.areaId;
// 推送伤害
await sendMessageToGVGAreaByTeamWithSuc(groupKey, areaId, PUSH_ROUTE.GVG_TEAM_ATTACKED, {
cityId, areaId, attackType, teams: [new GVGTeamInList(defenseTeam), new GVGTeamInList(attackTeam)]
});
if(!defenseTeam.isRobot) {
await sendMessageToUserWithSuc(defenseTeam.roleId, PUSH_ROUTE.GVG_MY_TEAM_ATTACKED, {
cityId, areaId, attackType, teams: [new GVGTeamInList(defenseTeam)]
});
}
await pushTeamBeHurtMessage(defenseTeam, attackTeam);
await pushTeamBeHurtMessage(attackTeam);
}
// 队伍移动
export async function pushTeamBeHurtMessage(team: GVGTeamType, replaceTeam?: GVGTeamType) {
if(team.curTeamBreak && team.originPointId > 0) {
await sendMessageToGVGAreaByTeamWithSuc(team.groupKey, team.fromAreaId, PUSH_ROUTE.GVG_AREA_POINT_CHANGE, {
cityId: team.cityId, areaId: team.fromAreaId, targetPointId: team.originPointId, originPointId: replaceTeam?.originPointId??0, point: new GVGTeamInListOnPoint(replaceTeam?.pointId||team.pointId, true, replaceTeam||team)
});
if(replaceTeam && !replaceTeam.curTeamBreak && replaceTeam.originPointId > 0) {
await sendMessageToGVGAreaByTeamWithSuc(replaceTeam.groupKey, replaceTeam.fromAreaId, PUSH_ROUTE.GVG_AREA_POINT_CHANGE, {
cityId: replaceTeam.cityId, areaId: replaceTeam.fromAreaId, targetPointId: replaceTeam.originPointId, originPointId: 0, point: new GVGTeamInListOnPoint(replaceTeam.pointId, true, replaceTeam)
});
}
}
if(team.curTeamBreak) {
await pushTeamMoveMessage(team);
}
if(team.isRobot && team.isBroken) {
await sendMessageToGVGAreaByTeamWithSuc(team.groupKey, team.fromAreaId, PUSH_ROUTE.GVG_PLAYER_LEAVE_AREA, {
cityId: team.cityId, areaId: team.fromAreaId, teamCode: team.teamCode
});
}
}
export async function pushTeamMoveMessage(team: GVGTeamType) {
if(team.fromAreaId != team.areaId) {
if(team.fromAreaId > 0) {
await sendMessageToGVGAreaByTeamWithSuc(team.groupKey, team.fromAreaId, PUSH_ROUTE.GVG_PLAYER_LEAVE_AREA, {
cityId: team.cityId, areaId: team.fromAreaId, teamCode: team.teamCode
});
}
if(team.areaId > 0) {
await sendMessageToGVGAreaByTeamWithSuc(team.groupKey, team.areaId, PUSH_ROUTE.GVG_PLAYER_AREA_ADD, {
cityId: team.cityId, areaId: team.areaId, fromAreaId: team.fromAreaId, player: new GVGTeamInList(team)
});
}
}
}
// —————————— 推送相关 end —————————— //
// 外面页面上的排行榜
export async function getBattleRank(redisKey: REDIS_KEY, keyParam: KeyNameParam, myId: myIdInter) {
let r = new Rank(redisKey, keyParam);
r.setGenerFieldsFun((obj => {
if(obj instanceof LeagueRankInfo) return { rank: obj.rank, name: obj.name, score: obj.num }
if(obj instanceof RoleRankInfo) return { rank: obj.rank, name: obj.roleName, score: obj.num }
}));
let { ranks, myRank } = await r.getRankListWithMyRank(myId);
if (!myRank) {
if(redisKey == REDIS_KEY.GVG_BATTLE_RANK) {
myRank = await r.generMyRankWithRole(myId.roleId, 0, 0);
} else if (redisKey == REDIS_KEY.GVG_BATTLE_LEAGUE_RANK) {
myRank = await r.generMyRankWithLeague(myId.leagueCode, 0, 0);
} else if (redisKey == REDIS_KEY.GVG_BATTLE_USR_SETTLE_RANK) {
myRank = await r.generMyRankWithRole(myId.roleId, 0, 0);
}
}
return { ranks, myRank }
}
export async function initTeamToMem() {
let sid = pinus.app.getServerId();
let servers = pinus.app.getServersByType('guild');
let { configId, period } = getGVGPeriodData();
if(period != GVG_PERIOD.BATTLE) return;
let teams = await GVGTeamModel.findByConfigId(configId);
for(let team of teams) {
if(team.isBroken) continue;
let dispacthSid = await dispatchTeam(team.groupKey, team.cityId);
if(dispacthSid == sid) {
let teamObj = getGVGBattleData(team.groupKey);
teamObj.enterCity(team);
}
}
}
async function dispatchTeam(groupKey: string, cityId: number) {
let groupId = '';
if(groupKey.startsWith('s')) {
let serverId = parseInt(groupKey.slice(1));
groupId = (await getGroupIdOfServer(serverId, SERVER_GROUP_FUN_TYPE.GVG)).toString();
} else {
groupId = groupKey.slice(1);
}
let servers = pinus.app.getServersByType('guild');
let sid = (await dispatch(redisClient(), `${groupId}_${cityId}`, servers, 'guild'))?.id;
return sid;
}
/**
* 查询队伍中的武将是否可以使用
* @param roleId
* @param index
* @param lineup
* @returns
*/
export async function checkGVGLineupWhenSave(roleId: string, index: number, lineup: LineupHero[]) {
const teams = await GVGTeamModel.findByRole(roleId, '-_id index lineup');
for(let { actorId, subHid } of lineup) {
let dicHero = gameData.hero.get(actorId);
if(subHid && (!dicHero || dicHero.urType != 1)) return STATUS.HERO_CAN_NOT_SET_SUB;
for(let team of teams) {
// 查actorId没有在其他队伍中使用
if(team.index != index) {
let hasActor = team.lineup.find(hero => hero.actorId == actorId);
if(hasActor) return STATUS.GVG_TEAM_HERO_DUPLICATE;
// 查actorId没有在其他队伍作为副将
let hasActorIsSubHero = team.lineup.find(hero => hero.subHid == actorId);
if(hasActorIsSubHero) return STATUS.GVG_TEAM_HERO_USED_AS_SUB_HERO;
// 查副将
if(subHid) {
let hasSubHero = team.lineup.find(hero => hero.actorId == subHid);
if(hasSubHero) return STATUS.GVG_TEAM_SUB_HERO_DUPLICATE;
}
} else {
// 查actorId没有在其他队伍作为副将
let hasActorIsSubHero = lineup.find(hero => hero.subHid == actorId);
if(hasActorIsSubHero) return STATUS.GVG_TEAM_HERO_USED_AS_SUB_HERO;
// 查副将
if(subHid) {
let hasSubHero = lineup.find(hero => hero.actorId == subHid);
if(hasSubHero) return STATUS.GVG_TEAM_SUB_HERO_DUPLICATE;
}
}
}
}
return STATUS.SUCCESS
}