diff --git a/game-server/app/servers/guild/handler/gvgBattleHandler.ts b/game-server/app/servers/guild/handler/gvgBattleHandler.ts index 5c6374536..bd5f2e97d 100644 --- a/game-server/app/servers/guild/handler/gvgBattleHandler.ts +++ b/game-server/app/servers/guild/handler/gvgBattleHandler.ts @@ -1,13 +1,16 @@ +import { GVGCityType } from './../../../db/GVGCity'; +import { LeagueCityPoint, GVGTeamMem } from './../../../../../shared/domain/battleField/gvgBattle'; import { GVGRecModel } from '../../../db/GVGRec'; import { LeagueGood } from '../../../domain/gvgField/returnData'; import { GVGTeamModel } from '../../../db/GVGTeam'; import { GVGUserDataModel } from '../../../db/GVGUserData'; import { GVGCityModel } from '../../../db/GVGCity'; import { Application, BackendSession, ChannelService, HandlerService } from "pinus"; -import { DEBUG_MAGIC_WORD, STATUS } from "../../../consts"; +import { DEBUG_MAGIC_WORD, GVG_PERIOD, STATUS } from "../../../consts"; import { LineupHero } from "../../../domain/roleField/hero"; import { resResult, genCode } from "../../../pubUtils/util"; -import { GVGConfigModel } from '../../../db/GVGConfig'; +import { GVGLeagueModel } from '../../../db/GVGLeague'; +import { checkGVGPeriod, getGVGPeriodData } from '../../../services/gvg/gvgService'; export default function (app: Application) { new HandlerService(app, {}); @@ -16,6 +19,12 @@ export default function (app: Application) { export class GVGBattleHandler { channelService: ChannelService; + + // 积分点占领情况,cityId -> LeagueCode -> LeagueCityPoint + private pointOccupy: Map> = new Map(); + // 城池队伍状态,cityId -> areaId -> GVGTeamMem + private cityTeamStatus: Map> = new Map(); + constructor(private app: Application) { this.channelService = app.get('channelService'); } @@ -27,41 +36,116 @@ export class GVGBattleHandler { // spine: 形象 // lineup: 阵容 async saveTeam(msg: { index: number, head: number, frame: number, spine: number, lineup: [ LineupHero ] }, session: BackendSession) { + if(checkGVGPeriod(GVG_PERIOD.BATTLE)) return resResult(STATUS.GVG_NOT_BATTLE_PERIOD); + + const roleId = session.get('roleId'); + const guildCode = session.get('guildCode'); + + let myLeague = await GVGLeagueModel.findLeagueByGuild(guildCode); + if(!myLeague) return resResult(STATUS.GVG_LEAGUE_NOT_EXIST); const { index, head, frame, spine, lineup } = msg; - return resResult(STATUS.SUCCESS, { teams: [ 'teamCode' ] }); + const team = await GVGTeamModel.saveTeam(roleId, myLeague.leagueCode, index, head, frame, spine, lineup); + if (!team) { + return resResult(STATUS.GVG_SAVE_TEAM_FAILED); + } + return resResult(STATUS.SUCCESS, { teams: [ team.teamCode ] }); } // 获取城池信息 async getCity(msg: { cityId: number }, session: BackendSession) { - const { cityId } = msg; + if(checkGVGPeriod(GVG_PERIOD.BATTLE)) return resResult(STATUS.GVG_NOT_BATTLE_PERIOD); - const city = await GVGCityModel.findOne({ cityId }).lean(); + const { cityId } = msg; + let { configId } = getGVGPeriodData(); + + const city = await GVGCityModel.findOne({ configId, cityId }).lean(); return resResult(STATUS.SUCCESS, { city }); } // 进入城池 async enterCity(msg: { cityId: number }, session: BackendSession) { + if(checkGVGPeriod(GVG_PERIOD.BATTLE)) return resResult(STATUS.GVG_NOT_BATTLE_PERIOD); + + const roleId = session.get('roleId') + const guildCode = session.get('guildCode') + const { cityId } = msg; + let { configId } = getGVGPeriodData(); - - - let city = await GVGCityModel.findOne({ cityId }).lean(); + let city: GVGCityType = await GVGCityModel.findOne({ cityId, configId }).lean(); if (!city) { return resResult(STATUS.GVG_CITY_NOT_FOUND); } - const { teamCnt, userCnt } = city; + // 检测是否已经在城池中,如果在城池中,直接返回城池信息 + let myLeague = await GVGLeagueModel.findLeagueByGuild(guildCode); + if(!myLeague) return resResult(STATUS.GVG_LEAGUE_NOT_EXIST); + let gvgUserData = await GVGUserDataModel.findByRole(configId, myLeague.leagueCode, roleId); + if (!gvgUserData) { + return resResult(STATUS.GVG_USER_NOT_FOUND); + } + if (gvgUserData.cityId === cityId) { + return resResult(STATUS.SUCCESS, { city }); + } + + // 不在城池则检测是否满员 + const { userCnt } = city; // 检测是否满员 if (userCnt >= 200) { return resResult(STATUS.GVG_BATTLE_CITY_FULL); } - // ! 更新城池人数和队伍数,更新用户城池信息 + // 检测玩家是否已经在其他城池中,由 checkMyTeam 接口检测 + // if (gvgUserData.cityId) { + // } + + const roleTeamCnt = await GVGTeamModel.getTeamCnt(roleId); + city = await GVGCityModel.updateCityUser(configId, cityId, 1, roleTeamCnt); + + gvgUserData = await GVGUserDataModel.changeCity(configId, myLeague.leagueCode, roleId, cityId); + // ! 队伍默认进入的据点暂时设为 0;更新内存队伍信息 + const res = await GVGTeamModel.resetTeamsLoc(roleId, cityId, 0); + if (!res) { + return resResult(STATUS.GVG_RESET_TEAM_LOC_FAILED); + } + return resResult(STATUS.SUCCESS, { city }); } // 离开城池 async leaveCity(msg: { cityId: number }, session: BackendSession) { + if(checkGVGPeriod(GVG_PERIOD.BATTLE)) return resResult(STATUS.GVG_NOT_BATTLE_PERIOD); + const roleId = session.get('roleId') + const guildCode = session.get('guildCode') + const { cityId } = msg; + let { configId } = getGVGPeriodData(); + + // 检测是否已经在城池中 + let myLeague = await GVGLeagueModel.findLeagueByGuild(guildCode); + if(!myLeague) return resResult(STATUS.GVG_LEAGUE_NOT_EXIST); + let gvgUserData = await GVGUserDataModel.findByRole(configId, myLeague.leagueCode, roleId); + if (!gvgUserData) { + return resResult(STATUS.GVG_USER_NOT_FOUND); + } + if (gvgUserData.cityId != cityId) { + return resResult(STATUS.GVG_USER_NOT_IN_CITY); + } + + const roleTeamCnt = await GVGTeamModel.getTeamCnt(roleId); + const city = await GVGCityModel.updateCityUser(configId, cityId, -1, -roleTeamCnt); + if (!city) { + return resResult(STATUS.GVG_CITY_NOT_FOUND); + } + + // 更新玩家城池和队伍城池 + gvgUserData = await GVGUserDataModel.changeCity(configId, myLeague.leagueCode, roleId, 0); + const res = await GVGTeamModel.resetTeamsLoc(roleId, 0, 0); + if (!res) { + return resResult(STATUS.GVG_RESET_TEAM_LOC_FAILED); + } + + // ! 还需处理内存数据 + return resResult(STATUS.SUCCESS); } @@ -192,20 +276,15 @@ export class GVGBattleHandler { if (magicWord !== DEBUG_MAGIC_WORD) { return resResult(STATUS.INTERNAL_ERR); } + let { configId } = getGVGPeriodData(); // 检查城市是否存在 - let city = await GVGCityModel.getCityByCityId(cityId); + let city = await GVGCityModel.getCityByCityId(configId, cityId); if (city) { return resResult(STATUS.SUCCESS, { city }); } - // 创建城市 - const config = await GVGConfigModel.findConfig(); - if (!config || !config.configId) { - return resResult(STATUS.INTERNAL_ERR); - } - const { configId } = config; - city = await GVGCityModel.createCity(cityId, configId); + city = await GVGCityModel.createCity(configId, cityId); return resResult(STATUS.SUCCESS, { city }); } } diff --git a/game-server/app/services/gvg/gvgBattleService.ts b/game-server/app/services/gvg/gvgBattleService.ts index e4c3cf511..d84280aac 100644 --- a/game-server/app/services/gvg/gvgBattleService.ts +++ b/game-server/app/services/gvg/gvgBattleService.ts @@ -1,4 +1,6 @@ +import { GVGTeamMem } from "../../domain/battleField/gvgBattle"; import { GVGLeagueType } from "../../db/GVGLeague"; +import { GVGTeamType } from "../../db/GVGTeam"; /** @@ -25,4 +27,15 @@ export async function getGVGCities(league: GVGLeagueType) { */ export async function getGVGCitiesInfo(league: GVGLeagueType): Promise<{cityId: number, guardLeagueName: string, teamCnt: number }[]> { return []; -} \ No newline at end of file +} + +/** + * 获取内存中队伍的数据结构 + */ +export function getGVGTeamMemInfo(team: GVGTeamType): GVGTeamMem { + const teamMem = team as GVGTeamMem; + teamMem.isMoving = false; + teamMem.startMoveTime = 0; + teamMem.stopMoveTime = 0; + return teamMem; +} diff --git a/game-server/app/services/gvg/gvgService.ts b/game-server/app/services/gvg/gvgService.ts index 0eee25feb..f67f7f30a 100644 --- a/game-server/app/services/gvg/gvgService.ts +++ b/game-server/app/services/gvg/gvgService.ts @@ -200,3 +200,9 @@ export async function calLeagueCe(league: GVGLeagueType) { const guilds = await GuildModel.findByCodes(guildCodes); return guilds.reduce((pre, guild) => pre + guild.guildCe, 0); } + +// 检查当前赛季阶段 +export async function checkGVGPeriod(p : GVG_PERIOD) { + let { period } = getGVGPeriodData(); + return period == p; +} \ No newline at end of file diff --git a/shared/consts/statusCode.ts b/shared/consts/statusCode.ts index 753fb7048..dfd41f310 100644 --- a/shared/consts/statusCode.ts +++ b/shared/consts/statusCode.ts @@ -338,6 +338,11 @@ export const STATUS = { GVG_BATTLE_TEAM_INVALID: { code: 21332, simStr: '无效的队伍' }, GVG_CITY_NOT_FOUND: { code: 21333, simStr: '城池不存在' }, GVG_GUILD_HAS_LEAGUE: { code: 21334, simStr: '需要先退出联军才能解散军团' }, + GVG_SAVE_TEAM_FAILED: { code: 21335, simStr: '保存队伍失败' }, + GVG_USER_NOT_FOUND: { code: 21336, simStr: '您的激战期数据不存在' }, + GVG_RESET_TEAM_LOC_FAILED: { code: 21337, simStr: '切换队伍城池失败' }, + GVG_USER_NOT_IN_CITY: { code: 21338, simStr: '您不在该城池中' }, + GVG_NOT_BATTLE_PERIOD: { code: 21339, simStr: '您只能在激战期进行此操作' }, // GVG征战中原 GVG_VESTIGE_ERR: { code: 21350, simStr: '今日未开放该遗迹' }, diff --git a/shared/db/GVGCity.ts b/shared/db/GVGCity.ts index 0f93dbc81..0d319800d 100644 --- a/shared/db/GVGCity.ts +++ b/shared/db/GVGCity.ts @@ -34,8 +34,14 @@ export default class GVGCity extends BaseModel { } // 通过 cityId 获取城市 - public static async getCityByCityId(cityId: number) { - const city: GVGCityType | null = await GVGCityModel.findOne({ cityId }).lean(); + public static async getCityByCityId(configId: number, cityId: number) { + const city: GVGCityType | null = await GVGCityModel.findOne({ configId, cityId }).lean(); + return city; + } + + // 更新城市字段 + public static async updateCityUser(configId: number, cityId: number, userInc: number, teamInc: number) { + const city: GVGCityType | null = await GVGCityModel.findOneAndUpdate({ configId, cityId}, { $inc: {userCnt: userInc, teamCnt: teamInc} }, { new: true }).lean(); return city; } } @@ -45,4 +51,7 @@ export const GVGCityModel = getModelForClass(GVGCity); export interface GVGCityType extends Pick, keyof GVGCity> { id: number; -}; \ No newline at end of file +}; + +export type GVGCityUpdate = Partial; // 将所有字段变成可选项 + diff --git a/shared/db/GVGTeam.ts b/shared/db/GVGTeam.ts index eaabf7778..1cf29365d 100644 --- a/shared/db/GVGTeam.ts +++ b/shared/db/GVGTeam.ts @@ -2,6 +2,7 @@ import BaseModel from "./BaseModel"; import { index, getModelForClass, prop, DocumentType } from '@typegoose/typegoose'; import { genCode } from "../pubUtils/util"; +import { LineupHero } from './../domain/roleField/hero'; @index({ roleId: 1, teamId: 1 }) @index({ teamCode: 1 }) @@ -52,20 +53,44 @@ export default class GVGTeam extends BaseModel { defenseTime: number; // 防守保护时间 @prop({ required: true }) - lineup: [{ - actorId: number; // 武将id - dataId: number; // 位置 - order: number; // 进攻顺序 - }] + lineup: [LineupHero] // 创建队伍 - public static async createTeam(roleId: string, leagueCode: string, teamId: number, head: number, spine: number, frame: number, lineup: any) { + public static async createTeam(roleId: string, leagueCode: string, teamId: number, head: number, spine: number, frame: number, lineup: [LineupHero]) { const doc = new GVGTeamModel(); const teamCode = genCode(8); - let update = Object.assign(doc.toJSON(), { roleId, leagueCode, teamId, teamCode, head, spine, frame, lineup }); + const update = Object.assign(doc.toJSON(), { roleId, leagueCode, teamId, teamCode, head, spine, frame, lineup }); const team: GVGTeamType | null = await GVGTeamModel.findOneAndUpdate({ teamCode }, { $setOnInsert: update }, { upsert: true, new: true }).lean(); return team; } + + // 保存队伍 + public static async saveTeam(roleId: string, leagueCode: string, teamId: number, head: number, spine: number, frame: number, lineup: [LineupHero]) { + let team: GVGTeamType = await GVGTeamModel.findOne({ roleId, teamId }).lean(); + if (!team) { + team = await this.createTeam(roleId, leagueCode, teamId, head, spine, frame, lineup); + return team; + } + + const update = { head, spine, frame }; + if (lineup && lineup.length > 0) { + update['lineup'] = lineup; + } + team = await GVGTeamModel.findOneAndUpdate({ roleId, teamId }, { update }, { new: true }).lean(); + return team; + } + + // 查找角色队伍数 + public static async getTeamCnt(roleId: string) { + const teams: GVGTeamType[] = await GVGTeamModel.find({ roleId }).select('teamCode').lean(); + return teams.length; + } + + // 玩家切换城池更新队伍信息 + public static async resetTeamsLoc(roleId: string, cityId: number, areaId: number) { + const res = await GVGTeamModel.updateMany({ roleId }, { cityId, areaId, pointId: 0 }).lean(); + return !!res['ok']; + } } export const GVGTeamModel = getModelForClass(GVGTeam); diff --git a/shared/db/GVGUserData.ts b/shared/db/GVGUserData.ts index 1a6f7f6bf..c29a3984b 100644 --- a/shared/db/GVGUserData.ts +++ b/shared/db/GVGUserData.ts @@ -76,6 +76,8 @@ export default class GVGUserData extends BaseModel { @prop({ required: true, default: 0 }) protectTime: number; // 保护期 + @prop({ required: false }) + cityId: number; // 城池id public static async findByRole(configId: number, leagueCode: string, roleId: string) { const result: GVGUserDataType = await GVGUserDataModel.findOneAndUpdate({ configId, leagueCode, roleId }, {}, { new: true, upsert: true}).lean(); @@ -121,6 +123,12 @@ export default class GVGUserData extends BaseModel { const result: GVGUserDataType = await GVGUserDataModel.findOneAndUpdate({ configId, leagueCode, roleId }, { $set: { lv }}, { new: true }).lean(); return result; } + + // 更换城池 + public static async changeCity(configId: number, leagueCode: string, roleId: string, cityId: number) { + const result: GVGUserDataType = await GVGUserDataModel.findOneAndUpdate({ configId, leagueCode, roleId }, { $set: { cityId }}, { new: true }).lean(); + return result; + } } export const GVGUserDataModel = getModelForClass(GVGUserData); diff --git a/shared/domain/battleField/gvgBattle.ts b/shared/domain/battleField/gvgBattle.ts new file mode 100644 index 000000000..25de0a355 --- /dev/null +++ b/shared/domain/battleField/gvgBattle.ts @@ -0,0 +1,23 @@ +import { GVGTeamType } from "../../db/GVGTeam"; + +// 积分点分类统计,1-3级积分点代表从小到大 +export interface LeagueCityPoint { + // 计分点数 + pointCnt1: number; + pointCnt2: number; + pointCnt3: number; + // 击杀守卫数 + guardCnt1: number; + // 击杀投石车数 + catapultCnt1: number; +} + +// 队伍状态 +export interface GVGTeamMem extends GVGTeamType { + // 开始移动时间戳 + startMoveTime: number; + // 停止移动时间戳 + stopMoveTime: number; + // 是否正在移动 + isMoving: boolean; +}