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

761 lines
36 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 { 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_TECH_TYPE, MAIL_TYPE, PUSH_ROUTE, REDIS_KEY, STATUS } from "../../consts";
import { getTimeFun, nowSeconds } from "../../pubUtils/timeUtil";
import { DicGVGAreaPoint } from "../../pubUtils/dictionary/DicGVGAreaPoint";
import { getGVGBattleData, getGVGBattleMap } from "./gvgBattleMemory";
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 } from "./gvgService";
import { GVGLeaguePrepareModel } from "../../db/GVGLeaguePrepare";
import { pinus } from "pinus";
import { dispatch } from "../../util/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";
/**
* 获取本联军上周占领的城池
* @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 }[]) {
let attrByHid = await getHeroesAttributes(roleId);
let newLineup: GVGHeroInfo[] = [], newLineupCe = 0;
for(let { actorId, dataId, order } 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 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 servers = pinus.app.getServersByType('guild');
let { configId } = getGVGConfig();
let guardCities = await GVGCityModel.findAllGuardCities(configId);
for(let { cityId, groupKey, guardLeague, guardLeagueName } of guardCities) {
let sid = dispatch(cityId.toString(), servers)?.id;
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, cityId });
await r.setExpire(nextWeek);
await r.setRankWithRoleInfo(roleId, incScore, Date.now(), null, true);
}
export async function redisAddSettleScore(gvgTeam: GVGTeamType, incScore: number) {
let { configId, groupKey, cityId, leagueCode, isRobot } = 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);
}
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 }
}
// 每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);
await sendMessageToGVGCityWithSuc(groupKey, cityId, PUSH_ROUTE.GVG_CITY_RANK_UPDATE, { cityId, ranks });
}
}
}
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);
}
}
}
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);
}
}
for(let city of cities) { // 没有成功防守到联军的城池,进行下一轮处理
if(cityResult.indexOf(city.cityId) != -1) 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;
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);
}
}
return { ranks, myRank }
}