diff --git a/game-server/app/servers/guild/handler/gvgFighterHandler.ts b/game-server/app/servers/guild/handler/gvgFighterHandler.ts index f9f99e7ef..028d91f3e 100644 --- a/game-server/app/servers/guild/handler/gvgFighterHandler.ts +++ b/game-server/app/servers/guild/handler/gvgFighterHandler.ts @@ -1,12 +1,17 @@ import { Application, BackendSession, ChannelService, HandlerService } from "pinus"; -import { GVG_PERIOD, STATUS } from "../../../consts"; +import { GVG_ITEM, GVG_PERIOD, ITEM_CHANGE_REASON, STATUS, VESTIGE_OPP_STATUS } from "../../../consts"; +import { GVGLeagueModel } from "../../../db/GVGLeague"; +import { GVGUserItemModel } from "../../../db/GVGUserItem"; +import { GVGVestigeLockModel } from "../../../db/GVGVestigeLock"; import { GVGVestigeRankModel } from "../../../db/GVGVestigeRank"; +import { GVGVestigeRecModel } from "../../../db/GVGVestigeRec"; import { HeroModel } from "../../../db/Hero"; import { RoleModel } from "../../../db/Role"; import { GVGVestigeOppLineup } from "../../../domain/gvgField/returnData"; import { gameData } from "../../../pubUtils/data"; import { resResult } from "../../../pubUtils/util"; -import { checkVestige, getOppPlayerByRanks, refreshVestigeOppRanks } from "../../../services/gvg/gvgFightService"; +import { checkVestige, checkVestigeOppStatus, checkVestigeRank, generateAttackInfo, generateDefenseInfo, getOppDetailData, getOppPlayerByRanks, getVestigeUsedHeroes, isRobot, refreshVestigeOppRanks } from "../../../services/gvg/gvgFightService"; +import { addGVGReward, handleGVGCost } from "../../../services/gvg/gvgItemService"; import { getGroupIdOfServer, getGVGPeriodData } from "../../../services/gvg/gvgService"; import { getAllServerName } from "../../../services/redisService"; @@ -42,7 +47,7 @@ export class GVGProduceHandler { let dicRankMap = gameData.gvgVestige.get(vestigeId); if(!dicRankMap) return resResult(STATUS.DIC_DATA_NOT_FOUND); - let myVestigateRank = await GVGVestigeRankModel.findRankByRole(vestigeId, roleId); // 我在这个遗迹的排名 + let myVestigateRank = await GVGVestigeRankModel.findByRole(vestigeId, roleId); // 我在这个遗迹的排名 if(!myVestigateRank) { let role = await RoleModel.findByRoleId(roleId, 'roleId'); let oppRanks = refreshVestigeOppRanks(-1); @@ -53,6 +58,7 @@ export class GVGProduceHandler { let lineupCe = lineup.reduce((pre, cur) => pre + cur.ce, 0); let oppPlayers = await getOppPlayerByRanks(serverId, groupId, vestigeId, oppRanks); + let usedHeroes = await getVestigeUsedHeroes(roleId, vestigeId); return resResult(STATUS.SUCCESS, { vestigeId, @@ -60,7 +66,8 @@ export class GVGProduceHandler { score, lineup, lineupCe, oppPlayers, - refreshCnt + refreshCnt, + usedHeroes }) } @@ -75,7 +82,7 @@ export class GVGProduceHandler { const serverId: number = session.get('serverId'); let { vestigeId, roleId: targetRoleId, rank } = msg; - let myVestigateRank = await GVGVestigeRankModel.findRankByRole(vestigeId, roleId); // 我在这个遗迹的排名 + let myVestigateRank = await GVGVestigeRankModel.findByRole(vestigeId, roleId); // 我在这个遗迹的排名 const serverNames = await getAllServerName(); @@ -90,7 +97,7 @@ export class GVGProduceHandler { let dicWarJson = gameData.warJson.get(dicWar.dispatchJsonId); result.setRobot(dicGVGVestige, dicWarJson, serverNames[serverId]); } else { - let hisVestigateRank = await GVGVestigeRankModel.findRankByRole(vestigeId, roleId); // 我在这个遗迹的排名 + let hisVestigateRank = await GVGVestigeRankModel.findByRole(vestigeId, roleId); if(!hisVestigateRank) return resResult(STATUS.GVG_VESTIGE_TARGET_NOT_FOUND); let role = await RoleModel.findByRoleId(hisVestigateRank.roleId, 'roleId roleName serverId guildName title lv heads head spines spine frames frame') @@ -109,8 +116,57 @@ export class GVGProduceHandler { } // 选择对手 - async chooseOpp(msg: { vestigeId: number, rank: number, myRank: number }, session: BackendSession) { + async chooseOpp(msg: { vestigeId: number, roleId: string, rank: number, myRank: number }, session: BackendSession) { + let roleId = session.get('roleId'); + let roleName = session.get('roleName'); + let serverId = session.get('serverId'); + let guildCode = session.get('guildCode'); + let sid = session.get('sid'); + let { vestigeId, roleId: targetRoleId, rank, myRank } = msg; + + let { configId, period } = getGVGPeriodData(); + if(period != GVG_PERIOD.PREPARE) return resResult(STATUS.GVG_NOT_PREPARE_PERIOD); + + let dicRank = gameData.gvgVestige.get(vestigeId)?.get(rank); + if(!dicRank) return resResult(STATUS.DIC_DATA_NOT_FOUND); + + let myLeague = await GVGLeagueModel.findLeagueByGuild(guildCode); + if(!myLeague) return resResult(STATUS.GVG_LEAGUE_NOT_EXIST); + + // 检查对手排名/自己排名 + if(!checkVestigeRank(myRank, rank)) return resResult(STATUS.GVG_VESTIGE_RANK_ERR); + + const hasItem = await GVGUserItemModel.checkItemCnt(configId, myLeague.leagueCode, roleId, GVG_ITEM.FIGHT_COIN, 1); + if(!hasItem) return resResult(STATUS.GVG_ITEMS_NOT_ENOUGH); + let groupId = await getGroupIdOfServer(serverId); + + let status = await checkVestigeOppStatus(configId, groupId, vestigeId, roleId, myRank, targetRoleId, rank); // 锁 + if(status != VESTIGE_OPP_STATUS.BATTLE) { + let oppRanks = refreshVestigeOppRanks(myRank); + let oppPlayers = await getOppPlayerByRanks(serverId, groupId, vestigeId, oppRanks); + return resResult(STATUS.SUCCESS, { status, oppPlayers }); + } + + // 扣征战令 + const costResult = await handleGVGCost(roleId, myLeague.leagueCode, sid, [{ id: GVG_ITEM.FIGHT_COIN, count: 1 }], [], ITEM_CHANGE_REASON.GVG_VESTIGE_START); + if(!costResult) { + await GVGVestigeLockModel.releaseLock(groupId, vestigeId, rank); + return resResult(STATUS.GVG_ITEMS_NOT_ENOUGH); + } + // 获取征战令对应奖励 + const leagueGoods = await addGVGReward(roleId, roleName, myLeague.leagueCode, sid, dicRank.battleLeagueReward, dicRank.battleReward, ITEM_CHANGE_REASON.GVG_VESTIGE_START); + // 创建rec + const attackInfo = await generateAttackInfo(roleId, myRank); + const defenseInfo = await generateDefenseInfo(targetRoleId, vestigeId, rank); + const rec = await GVGVestigeRecModel.createRec(configId, vestigeId, groupId, attackInfo, defenseInfo, leagueGoods); + const oppDetail = await getOppDetailData(rec); + + return resResult(STATUS.SUCCESS, { + battleCode: rec.battleCode, + time: rec.checkTime, + ...oppDetail + }); } // 出战界面撤退 diff --git a/game-server/app/services/gvg/gvgFightService.ts b/game-server/app/services/gvg/gvgFightService.ts index 0800efdc1..8424bc63f 100644 --- a/game-server/app/services/gvg/gvgFightService.ts +++ b/game-server/app/services/gvg/gvgFightService.ts @@ -1,15 +1,20 @@ // 征战中原相关 -import { GVG_PERIOD } from "../../consts"; +import { GVG_PERIOD, STATUS, VESTIGE_OPP_STATUS } from "../../consts"; +import { ArtifactModel } from "../../db/Artifact"; import { GVGVestigeModel } from "../../db/GVGVestige"; -import { GVGVestigeRankModel } from "../../db/GVGVestigeRank"; +import { GVGVestigeLockModel } from "../../db/GVGVestigeLock"; +import { GVGVestigeRankModel, GVGVestigeRankType } from "../../db/GVGVestigeRank"; +import { GVGVestigeRecType } from "../../db/GVGVestigeRec"; import { HeroModel } from "../../db/Hero"; -import { RoleType } from "../../db/Role"; +import { RoleModel, RoleType } from "../../db/Role"; +import { OppDetailData, OppDetailHeroData, OppPlayerHeroInfo, OppPlayerInfo } from "../../domain/gvgField/gvgDb"; import { GVGVestigeOppPlayer } from "../../domain/gvgField/returnData"; import { gameData, getGVGVestigeRange } from "../../pubUtils/data"; import { getTimeFun } from "../../pubUtils/timeUtil"; -import { getRandEelm, getRandValueByMinMax } from "../../pubUtils/util"; +import { getRandEelm, getRandValueByMinMax, resResult } from "../../pubUtils/util"; import { getNumberArr, uniqueArr } from "../ladderService"; +import { getHeroesAttributes } from "../playerCeService"; import { calLineupScore } from "../pvpService"; import { getAllServerName } from "../redisService"; import { getGroupIdOfServer, getGVGServerType } from "./gvgService"; @@ -65,6 +70,7 @@ function randomRank(min: number, max: number, len: number) { return getRandEelm(arr, len); } +// 根据ranks获取oppPlayer显示数据 export async function getOppPlayerByRanks(serverId: number, groupId: number, vestigeId: number, ranks: number[]) { let serverNames = await getAllServerName(); let opps = await GVGVestigeRankModel.findByRanks(groupId, vestigeId, ranks); @@ -86,4 +92,123 @@ export async function getOppPlayerByRanks(serverId: number, groupId: number, ves } return result; -} \ No newline at end of file +} + +// 获取其他遗迹里使用过的武将 +export async function getVestigeUsedHeroes(roleId: string, curVestigeId: number) { + let myDatas = await GVGVestigeRankModel.findAllByRole(roleId); + let result: number[] = []; + for(let { vestigeId, lineup = [] } of myDatas) { + if(vestigeId == curVestigeId) continue; + result.push(...lineup.map(cur => cur.actorId)); + } + return result; +} + +// 检查我的排名是否可以挑战对方的排名 +export function checkVestigeRank(myRank: number, targetRank: number) { + let dicRange = getGVGVestigeRange(myRank); + if(!dicRange) return false; + if(myRank == targetRank || targetRank == 0) return false; + + // 是否可以挑战最前的几个排名 + if(dicRange.topChallengeFrom <= targetRank && dicRange.topChallengeTo >= targetRank) return true; + if(dicRange.rangeBeforeTo == -1) { + if(dicRange.rangeBeforeFrom < targetRank && myRank > targetRank) return true; + } else { + if(dicRange.rangeBeforeFrom < targetRank && dicRange.rangeBeforeTo >= targetRank) return true; + } + if(dicRange.rangeAfterTo == -1) { + if(dicRange.rangeAfterFrom < targetRank && myRank > targetRank) return true; + } else { + if(dicRange.rangeAfterFrom < targetRank && dicRange.rangeAfterTo >= targetRank) return true; + } + + return false; +} + +export function isRobot(roleId: string) { + if(!roleId) roleId = ''; + return roleId.startsWith('robot'); +} + +export async function checkVestigeOppStatus(configId: number, groupId: number, vestigeId: number, roleId: string, myRank: number, targetRoleId: string, rank: number) { + + let myData = await GVGVestigeRankModel.findByRole(vestigeId, roleId); + if(myData?.rank != myRank) return VESTIGE_OPP_STATUS.MY_RANK_CHANGE; + + if(isRobot(targetRoleId)) { + let targetData = await GVGVestigeRankModel.findByRank(vestigeId, rank); + if(targetData) return VESTIGE_OPP_STATUS.OPP_RANK_CHANGE; + } else { + let targetData = await GVGVestigeRankModel.findByRole(vestigeId, targetRoleId); + if(targetData?.rank != rank) return VESTIGE_OPP_STATUS.OPP_RANK_CHANGE; + } + let lockResult = await GVGVestigeLockModel.chooseOppLock(groupId, vestigeId, rank, configId, roleId, targetRoleId); + if(!lockResult) return VESTIGE_OPP_STATUS.OPP_IS_LOCKED; + + return VESTIGE_OPP_STATUS.BATTLE; +} + +// 生成防守数据 +export async function generateDefenseInfo(roleId: string, vestigeId: number, rank: number) { + if(isRobot(roleId)) { + let dicRank = gameData.gvgVestige.get(vestigeId)?.get(rank); // 调用之前已经验证过了 + let dicWar = gameData.war.get(dicRank?.warId); + let dicWarJson = gameData.warJson.get(dicWar?.dispatchJsonId); + if(!dicWarJson) return null; + + let heroes: OppPlayerHeroInfo[] = []; + for(let json of dicWarJson) { + if(json.relation == 2) { + let hero = new OppPlayerHeroInfo(); + hero.setByWarJson(json); + heroes.push(hero); + } + } + let info = new OppPlayerInfo(); + info.initByRobot(dicRank, heroes, true); + return info; + } else { + let playerVestigeData = await GVGVestigeRankModel.findByRole(vestigeId, roleId); + if(!playerVestigeData) return null; + let lineup = playerVestigeData.lineup||[]; + let hids = lineup.map(cur => cur.actorId); + let role = await RoleModel.findByRoleId(playerVestigeData.roleId, 'roleId roleName heads head frames frame spines spine title lv'); + let heroes = await HeroModel.findByHidRange(hids, playerVestigeData.roleId, 'hid skinId lv star colorStar quality'); + + let info = new OppPlayerInfo(); + info.initByPlayer(playerVestigeData.rank, role, heroes, lineup, true); + return info; + } +} + +export async function generateAttackInfo(roleId: string, rank: number) { + let role = await RoleModel.findByRoleId(roleId, 'roleId roleName heads head frames frame spines spine title lv'); + let info = new OppPlayerInfo(); + info.initByPlayer(rank, role, [], [], false); + return info; +} + +export async function getOppDetailData(rec: GVGVestigeRecType) { + let isRobot = rec.defenseInfo.isRobot; + let dicLadderDifficultRatio = gameData.ladderDifficultRatio.get(rec.defenseInfo.oldRank); + let result = new OppDetailData(); + if(!isRobot) { + const dicWar = gameData.war.get(dicLadderDifficultRatio.gkId); + const dicWarJson = gameData.warJson.get(dicWar.dispatchJsonId); + const defenseInfo = rec.defenseInfo.heroes||[]; + const hids = defenseInfo.map(cur => cur.hid); + const heroes = await HeroModel.findByHidRange(hids, rec.defenseRoleId, 'hid lv quality star colorStar'); + const artifactSeids = heroes.map(hero => hero.artifact); + const artifacts = await ArtifactModel.findbySeqIds(rec.defenseRoleId, artifactSeids); + result.setByPlayer(dicWarJson, defenseInfo, heroes, artifacts); + let attrByHid = await getHeroesAttributes(rec.defenseRoleId); + for(let [hid, attribute] of attrByHid) { + result.setAttribute(hid, attribute.getAttributesToString()); + } + } + + return result; + +} diff --git a/shared/consts/constModules/gvgConst.ts b/shared/consts/constModules/gvgConst.ts index ea72ce696..c48c869b1 100644 --- a/shared/consts/constModules/gvgConst.ts +++ b/shared/consts/constModules/gvgConst.ts @@ -73,6 +73,7 @@ export enum GVG_ITEM { CORN = 2, // 粟米 RICE = 3, // 水稻 PRODUCE_COIN = 10, // 内政令 + FIGHT_COIN = 11, // 征战令 BATTLE_FEAT = 12, // 战功 } @@ -114,4 +115,19 @@ export enum GVG_REC_ID { VESTIGE_RANK = 5, // 联军每日的征战排名 LEAGUE_JOIN_GUILD = 6, // 军团加入联军 LEAGUE_QUIT_GUILD = 7, // 军团退出联军 -} \ No newline at end of file +} + +// 遗迹选择对手状态 +export enum VESTIGE_OPP_STATUS { + BATTLE = 1, // 可以挑战 + OPP_IS_LOCKED = 2, // 对手被挑战 + OPP_RANK_CHANGE = 3, // 对手排名变化 + MY_RANK_CHANGE = 4, // 自己排名变化 + } + +export enum VESTIGE_STATUS { + NO = 0, // 无战场 + CHECK = 1, // 出兵中 + BATTLE = 2, // 战斗中 + COMPLETE = 3, // 战斗结束 + } \ No newline at end of file diff --git a/shared/consts/constModules/sysConst.ts b/shared/consts/constModules/sysConst.ts index da422c131..37db961e2 100644 --- a/shared/consts/constModules/sysConst.ts +++ b/shared/consts/constModules/sysConst.ts @@ -1092,6 +1092,7 @@ export enum ITEM_CHANGE_REASON { GVG_RECEIVE_BOX = 167, // 领取宝箱 GVG_RECEIVE_LV = 168, // 领取等级奖励 GVG_RECEIVE_TASK = 169, // 领取任务奖励 + GVG_VESTIGE_START = 170, // gvg征战中原挑战 } export enum TA_EVENT { diff --git a/shared/consts/statusCode.ts b/shared/consts/statusCode.ts index 972cfe48b..38010b4b0 100644 --- a/shared/consts/statusCode.ts +++ b/shared/consts/statusCode.ts @@ -336,8 +336,11 @@ export const STATUS = { GVG_TASK_HAS_RECEIVED: { code: 21330, simStr: '任务已领取过' }, GVG_BATTLE_CITY_FULL: { code: 21331, simStr: '城市已满员' }, GVG_BATTLE_TEAM_INVALID: { code: 21332, simStr: '无效的队伍' }, - GVG_VESTIGE_ERR: { code: 21333, simStr: '今日未开放该遗迹' }, - GVG_VESTIGE_TARGET_NOT_FOUND: { code: 21334, simStr: '对手未找到' }, + + // GVG征战中原 + GVG_VESTIGE_ERR: { code: 21350, simStr: '今日未开放该遗迹' }, + GVG_VESTIGE_TARGET_NOT_FOUND: { code: 21351, simStr: '对手未找到' }, + GVG_VESTIGE_RANK_ERR: { code: 21352, simStr: '该排名玩家不可挑战' }, // 通用 30000 - 30099 DIC_DATA_NOT_FOUND: { code: 30000, simStr: '数据表未找到' }, diff --git a/shared/db/GVGVestigeLock.ts b/shared/db/GVGVestigeLock.ts new file mode 100644 index 000000000..90394431e --- /dev/null +++ b/shared/db/GVGVestigeLock.ts @@ -0,0 +1,59 @@ +// 征战中原对手锁 +import BaseModel from './BaseModel'; +import { index, getModelForClass, prop, DocumentType } from '@typegoose/typegoose'; +import { getZeroPoint, nowSeconds } from '../pubUtils/timeUtil'; +import { GVG } from '../pubUtils/dicParam'; + +@index({ day: 1, groupId: 1, vestigeId: 1, rank: 1 }) +export default class GVGVestigeLock extends BaseModel { + + @prop({ required: true }) + configId: number; // 赛期 + + @prop({ required: true }) + day: number; // 每天5点 + + @prop({ required: true }) + groupId: number; // 战区id + + @prop({ required: true }) + vestigeId: number; // 遗迹id + + @prop({ required: true }) + rank: number; // 排名 + + @prop({ required: true }) + unlockTime: number; // 锁定到什么时候 + + @prop({ required: true }) + attackRoleId: string; // 锁定到什么时候 + + @prop({ required: true }) + defenseRoleId: string; // 锁定到什么时候 + + public static async chooseOppLock(groupId: number, vestigeId: number, rank: number, configId: number, attackRoleId: string, defenseRoleId: string ) { + return await this.lock(groupId, vestigeId, rank, configId, attackRoleId, defenseRoleId, nowSeconds() + GVG.GVG_VESTIGE_PREPARE_COUNTDOWN); + } + + private static async lock(groupId: number, vestigeId: number, rank: number, configId: number, attackRoleId: string, defenseRoleId: string, unlockTime: number) { + let day = getZeroPoint(); + await GVGVestigeLockModel.findOneAndUpdate({ day, groupId, vestigeId, rank }, { $setOnInsert: { configId, unlockTime: 0 } }, { upsert: true }); + let result: GVGVestigeLockType = await GVGVestigeLockModel.findOneAndUpdate({ + day, groupId, vestigeId, rank, unlockTime: { $lt: nowSeconds() } + }, { $set: { unlockTime, attackRoleId, defenseRoleId } }, { new: true }).lean(); + return result; + } + + public static async releaseLock(groupId: number, vestigeId: number, rank: number) { + let day = getZeroPoint(); + let result: GVGVestigeLockType = await GVGVestigeLockModel.findOneAndUpdate({ day, groupId, vestigeId, rank }, { $set: { unlockTime: 0 } }, { new: true }).lean(); + return result; + } +} + +export const GVGVestigeLockModel = getModelForClass(GVGVestigeLock); + +export interface GVGVestigeLockType extends Pick, keyof GVGVestigeLock> { + id: number; +}; +export type GVGVestigeLockUpdate = Partial; // 将所有字段变成可选项 diff --git a/shared/db/GVGVestigeRank.ts b/shared/db/GVGVestigeRank.ts index 6beb18b72..c33925516 100644 --- a/shared/db/GVGVestigeRank.ts +++ b/shared/db/GVGVestigeRank.ts @@ -3,7 +3,7 @@ import { index, getModelForClass, prop, DocumentType, mongoose, Ref } from '@typ import Role, { RoleType } from './Role'; import { getZeroPoint } from '../pubUtils/timeUtil'; -class Lineup { +export class Lineup { @prop({ required: true }) actorId: number; // 武将 @prop({ required: true }) @@ -60,12 +60,24 @@ export default class GVGVestigeRank extends BaseModel { @prop({ required: true, default: 0 }) refreshCnt: number; // 刷新次数 - public static async findRankByRole(vestigeId: number, roleId: string) { + public static async findAllByRole(roleId: string) { + let today = getZeroPoint(); + let result: GVGVestigeRankType[] = await GVGVestigeRankModel.find({ day: today, roleId }).lean(); + return result; + } + + public static async findByRole(vestigeId: number, roleId: string) { let today = getZeroPoint(); let result: GVGVestigeRankType = await GVGVestigeRankModel.findOne({ day: today, vestigeId, roleId }).lean(); return result; } + public static async findByRank(vestigeId: number, rank: number) { + let today = getZeroPoint(); + let result: GVGVestigeRankType = await GVGVestigeRankModel.findOne({ day: today, vestigeId, rank }).lean(); + return result; + } + public static async initRank(configId: number, vestigeId: number, groupId: number, role: RoleType, oppRanks: number[]) { let today = getZeroPoint(); let doc = new GVGVestigeRankModel(); diff --git a/shared/db/GVGVestigeRec.ts b/shared/db/GVGVestigeRec.ts new file mode 100644 index 000000000..448613283 --- /dev/null +++ b/shared/db/GVGVestigeRec.ts @@ -0,0 +1,76 @@ +// 征战中原对手锁 +import BaseModel from './BaseModel'; +import { index, getModelForClass, prop, DocumentType } from '@typegoose/typegoose'; +import { OppPlayerInfo } from '../domain/gvgField/gvgDb'; +import { genCode } from '../pubUtils/util'; +import { VESTIGE_STATUS } from '../consts'; + +class LeagueGood { + @prop({ required: true }) + itemType: number; + + @prop({ required: true }) + id: number; + + @prop({ required: true }) + count: number +} + +@index({ day: 1, groupId: 1, vestigeId: 1, rank: 1 }) +export default class GVGVestigeRec extends BaseModel { + + @prop({ required: true }) + battleCode: string; // 唯一code + + @prop({ required: true }) + attackRoleId: string; // 攻方玩家id + + @prop({ required: true }) + defenseRoleId: string; // 守方玩家id + + @prop({ required: true }) + groupId: number; // 战区id + + @prop({ required: true }) + vestigeId: number; // 遗迹id + + @prop({ required: true }) + status: number; // 1-出兵中 2-挑战中 3-挑战完毕 4-取消 + + @prop({ required: true }) + checkTime: number; // 开始出兵的时间,13位时间戳 + + @prop({ required: true }) + battleTime: number; // 开始挑战的时间,13位时间 + + @prop({ required: true }) + endTime: number; // 结束时间,13位时间戳 + + @prop({ required: true, type: () => OppPlayerInfo, default: {}, _id: false }) + attackInfo: OppPlayerInfo; // 攻方信息 + + @prop({ required: true, type: () => OppPlayerInfo, default: {}, _id: false }) + defenseInfo: OppPlayerInfo; // 守方信息 + + @prop({ required: true, type: () => LeagueGood, default: [], _id: false }) + leagueGoods: LeagueGood[]; // 奖励 + + @prop({ required: true, default: false }) + hasRpl: boolean; // 是否存在对应录像 + + public static async createRec(configId: number, vestigeId: number, groupId: number, attackInfo: OppPlayerInfo, defenseInfo: OppPlayerInfo, leagueGoods: LeagueGood[]) { + const battleCode = genCode(10); + const result: GVGVestigeRecType = await GVGVestigeRecModel.findOneAndUpdate({ battleCode }, { + $set: { configId, vestigeId, groupId, attackRoleId: attackInfo.roleId, defenseRoleId: defenseInfo.roleId, status: VESTIGE_STATUS.CHECK, checkTime: Date.now(), attackInfo, defenseInfo, leagueGoods } + }, { new: true, upsert: true }).lean(); + return result; + } + +} + +export const GVGVestigeRecModel = getModelForClass(GVGVestigeRec); + +export interface GVGVestigeRecType extends Pick, keyof GVGVestigeRec> { + id: number; +}; +export type GVGVestigeRecUpdate = Partial; // 将所有字段变成可选项 diff --git a/shared/domain/gvgField/gvgDb.ts b/shared/domain/gvgField/gvgDb.ts new file mode 100644 index 000000000..ed26e451d --- /dev/null +++ b/shared/domain/gvgField/gvgDb.ts @@ -0,0 +1,219 @@ +import { prop } from "@typegoose/typegoose"; +import { ArtifactModelType } from "../../db/Artifact"; +import { Lineup } from "../../db/GVGVestigeRank"; +import { HeroType, Talent } from "../../db/Hero"; +import { RoleType } from "../../db/Role"; +import { gameData } from "../../pubUtils/data"; +import { EXTERIOR, GVG } from "../../pubUtils/dicParam"; +import { DicGVGVestige } from "../../pubUtils/dictionary/DicGVGVestige"; +import { dicWarJson, DicWarJson } from "../../pubUtils/dictionary/DicWarJson"; + +export class OppPlayerHeroInfo { + @prop({ required: true }) + hid: number; // 武将id + @prop({ required: true }) + skinId: number; // 皮肤id + @prop({ required: true }) + quality: number; // 品质 + @prop({ required: true }) + star: number; // 星级 + @prop({ required: true }) + colorStar: number; // 彩星 + @prop({ required: true }) + lv: number; // 等级 + @prop({ required: true }) + dataId: number; // 出兵表上所占位置 + @prop({ required: true }) + order: number; // 行动顺序 + + setByWarJson(warJson: DicWarJson) { + this.hid = warJson.actorId; + this.skinId = warJson.actorId; + this.lv = warJson.lv; + this.dataId = warJson.dataId; + this.order = warJson.outIndex; + let dicHero = gameData.hero.get(warJson.actorId); + + if(dicHero) { + this.quality = dicHero.quality; + if(this.quality == 4) { + this.star = 6; + this.colorStar = warJson.star; + } else { + this.star = warJson.star; + this.colorStar = 0; + } + } + } + + setByDefenseHero(hero: HeroType, lineup: Lineup[]) { + this.hid = hero.hid; + this.skinId = hero.skinId; + this.quality = hero.quality; + this.star = hero.star; + this.colorStar = hero.colorStar; + this.lv = hero.lv; + let curLineup = lineup.find(cur => cur.actorId == hero.hid); + if(curLineup) { + this.dataId = curLineup.dataId; + this.order = curLineup.order; + } + } +} + +// ladderMatchRec +export class OppPlayerInfo { + @prop({ required: true }) + oldRank: number = 0; // 原排名 + @prop({ required: true }) + newRank: number = 0; // 交换后的排名 + @prop({ required: true }) + roleId: string = ''; // 角色 id + @prop({ required: true }) + roleName: string = GVG.GVG_ROBOT_NAME; // 角色 名 + @prop({ required: true, default: 0 }) + lv: number = 1; // 等级 + @prop({ required: true, default: EXTERIOR.EXTERIOR_FACE }) + head: number = EXTERIOR.EXTERIOR_FACE; // 头像 + @prop({ required: true, default: EXTERIOR.EXTERIOR_FACECASE }) + frame: number = EXTERIOR.EXTERIOR_FACECASE; // 相框 + @prop({ required: true, default: EXTERIOR.EXTERIOR_APPEARANCE }) + spine: number = EXTERIOR.EXTERIOR_APPEARANCE; // 形象 + @prop({ required: true, default: 0 }) + title: number = 1; // 爵位 + @prop({ required: true, default: 0 }) + ce: number = 0; // 防守战力 + @prop({ required: true, default: false }) + isSuccess: boolean = false; // 是否胜利 + @prop({ required: true, default: [], type: OppPlayerHeroInfo, _id: false }) + heroes: OppPlayerHeroInfo[] = []; // 武将 + @prop({ required: true }) + isRobot: boolean = false; // 原排名 + + initByPlayer(rank: number, role: RoleType, heroes: HeroType[], lineup: Lineup[], isSuccess: boolean ) { + this.oldRank = rank; + this.newRank = rank; + this.roleId = role.roleId; + this.roleName = role.roleName; + this.head = role.head; + this.frame = role.frame; + this.spine = role.spine; + this.title = role.title; + this.lv = role.lv; + this.isSuccess = isSuccess; + for(let hero of heroes) { + let obj = new OppPlayerHeroInfo(); + obj.setByDefenseHero(hero, lineup); + this.ce += hero.ce; + this.heroes.push(obj); + } + this.isRobot = false; + } + + initByRobot(dic: DicGVGVestige, heroes: OppPlayerHeroInfo[], isSuccess: boolean) { + this.oldRank = dic.rank; + this.newRank = dic.rank; + this.roleId = `robot${this.oldRank}`; + this.head = dic.head; + this.spine = dic.spine; + this.ce = dic.ce; + this.lv = dic.level; + this.isSuccess = isSuccess; + this.heroes = heroes; + this.isRobot = true; + } +} + +class HeroArtifact { + @prop({ required: true }) + artifactId: number; + + @prop({ required: true }) + lv: number; +} + +export class OppDetailHeroData { + actorId: number = 0; // 武将id + skinId: number = 0; // 皮肤id,fashions表的heroId + actorName: string = ''; // 武将名 + dataId: number = 0; // 出兵表唯一id + relation: number = 0; // 地方还是我方 + dirction: number = 0; // 方向 + outIndex: number = 0; // 玩家设置的出场顺序,即order字段 + x: number = 0; // 战场x坐标 + y: number = 0; // 战场y坐标 + var: number = 0; // 变量 + lv: number = 0; // 等级 + hide: number = 0; // 是否隐藏 + initial_ai: number = 0; // ai类型 + attribute: string = ''; // 武将属性,格式 id&val|id&val|...,这里的次级属性都是没有除1000的值 + star: number = 0; // 星级 + colorStar: number = 0; // 彩星 + quality: number = 0; // 品质 + job: number = 0; // 职业 + skill: string = ''; // 技能 + seid: string = ''; // 技能 + spine: string = ''; // 动画 + talent: Talent[] = []; + subHid: number = 0; // 副将 + artifact: HeroArtifact[] = []; + + constructor(warJson: DicWarJson, defensHero: OppPlayerHeroInfo, hero: HeroType, artifacts: ArtifactModelType[]) { + this.dataId = warJson.dataId; + this.relation = warJson.relation; + this.dirction = warJson.dirction; + this.initial_ai = warJson.initial_ai; + this.x = warJson.x; + this.y = warJson.y; + this.var = warJson.var; + this.hide = warJson.hide; + this.skill = warJson.skill; + this.seid = warJson.seid; + this.spine = warJson.spine; + let dicHero = gameData.hero.get(warJson.actorId); + if(dicHero) this.job = dicHero.jobid; + + if(defensHero) { + this.outIndex = defensHero.order; + if(hero) { + this.actorId = hero.hid; + this.skinId = hero.skinId; + this.actorName = hero.hName; + this.lv = hero.lv; + this.star = hero.star; + this.colorStar = hero.colorStar; + let skin = hero.skins.find(cur => cur.enable); + if(skin) this.talent = skin.talent; + this.job = hero.job; + this.subHid = hero.subHid; + let artifact = artifacts.find(cur => cur.seqId == hero.artifact); + if(artifact) this.artifact.push({ artifactId: artifact.artifactId, lv: artifact.lv }); + } + } + } + + setAttribute(attribute: string) { + this.attribute = attribute; + } +} + +export class OppDetailData{ + heroes: OppDetailHeroData[] = []; + lineupCe: number = 0; + + + setByPlayer(dicWarJson: DicWarJson[], defenseHeroes: OppPlayerHeroInfo[], heroes: HeroType[], artifacts: ArtifactModelType[]) { + for(let warJson of dicWarJson) { + let defenseHero = defenseHeroes.find(cur => cur.dataId == warJson.dataId); + let curHero = heroes.find(cur => cur.hid == defenseHero.hid); + let hero = new OppDetailHeroData(warJson, defenseHero, curHero, artifacts); + this.heroes.push(hero); + this.lineupCe += curHero.ce; + } + } + + setAttribute(hid: number, attribute: string) { + let hero = this.heroes.find(cur => cur.actorId == hid); + if(hero) hero.setAttribute(attribute); + } +} \ No newline at end of file diff --git a/shared/pubUtils/data.ts b/shared/pubUtils/data.ts index a31e43987..22d4969a2 100644 --- a/shared/pubUtils/data.ts +++ b/shared/pubUtils/data.ts @@ -1172,15 +1172,15 @@ function parseGVGVestigeCnt() { } export function getGVGVestigeRange(myRank: number) { - let latestRank: DicGVGVestigeRange; + let lastRank: DicGVGVestigeRange; for(let dic of gameData.gvgVestigeRange) { - latestRank = dic; + lastRank = dic; let { rankMin, rankMax } = dic; if((rankMin < myRank && (rankMax >= myRank || rankMax == -1))) { return dic; } } - return latestRank + return lastRank } // 初始加载 diff --git a/shared/pubUtils/dicParam.ts b/shared/pubUtils/dicParam.ts index f34fa30bf..79cfd0518 100644 --- a/shared/pubUtils/dicParam.ts +++ b/shared/pubUtils/dicParam.ts @@ -394,5 +394,7 @@ export const GVG = { GVG_FIELD_TYPE_RATIO: '1&5%|2&10%|3&10%', // 全联盟的特殊格子上限百分比(a%的格子为小麦这个)1&a|2&b|3&c GVG_SP_FIELD_RATIO: '10%&30%', // 分配给玩家的时候有多少特殊格子 min&max 填最大最小百分比即可 GVG_REFRESH_TIME: 5, // 0 - GVG_ROBOT_NAME: '遗迹守护者', // 0 + GVG_ROBOT_NAME: '遗迹守护者', // 0, + GVG_VESTIGE_PREPARE_COUNTDOWN: 120, // + GVG_VESTIGE_BATTLE_COUNTDOWN: 300, // }; diff --git a/shared/resource/jsons/dic_zyz_GVGItems.json b/shared/resource/jsons/dic_zyz_GVGItems.json index 36a28ca5f..0c758dbde 100644 --- a/shared/resource/jsons/dic_zyz_GVGItems.json +++ b/shared/resource/jsons/dic_zyz_GVGItems.json @@ -11,7 +11,9 @@ "value": 100, "reward": "31002&5000", "leagueReward": "12&100", - "ripeTime": 43200 + "ripeTime": 43200, + "getWays": "&", + "info_1": "&" }, { "id": 2, @@ -25,7 +27,9 @@ "value": 150, "reward": "31002&5001", "leagueReward": "12&150", - "ripeTime": 36000 + "ripeTime": 36000, + "getWays": "&", + "info_1": "&" }, { "id": 3, @@ -39,7 +43,9 @@ "value": 200, "reward": "31002&5002", "leagueReward": "12&150", - "ripeTime": 43200 + "ripeTime": 43200, + "getWays": "&", + "info_1": "&" }, { "id": 4, @@ -53,7 +59,9 @@ "value": 15, "reward": "31002&5003", "leagueReward": "12&200", - "ripeTime": 0 + "ripeTime": 0, + "getWays": "&", + "info_1": "&" }, { "id": 5, @@ -67,7 +75,9 @@ "value": 20, "reward": "31002&5004", "leagueReward": "12&300", - "ripeTime": 0 + "ripeTime": 0, + "getWays": "&", + "info_1": "&" }, { "id": 6, @@ -81,7 +91,9 @@ "value": 30, "reward": "31002&5005", "leagueReward": "12&300", - "ripeTime": 0 + "ripeTime": 0, + "getWays": "&", + "info_1": "&" }, { "id": 7, @@ -95,7 +107,9 @@ "value": 25, "reward": "31002&5006", "leagueReward": "12&200", - "ripeTime": 0 + "ripeTime": 0, + "getWays": "&", + "info_1": "&" }, { "id": 8, @@ -109,7 +123,9 @@ "value": 30, "reward": "31002&5007", "leagueReward": "12&300", - "ripeTime": 0 + "ripeTime": 0, + "getWays": "&", + "info_1": "&" }, { "id": 9, @@ -123,7 +139,9 @@ "value": 40, "reward": "31002&5008", "leagueReward": "12&300", - "ripeTime": 0 + "ripeTime": 0, + "getWays": "&", + "info_1": "&" }, { "id": 10, @@ -137,7 +155,9 @@ "value": 0, "reward": "&", "leagueReward": "&", - "ripeTime": 0 + "ripeTime": 0, + "getWays": "&", + "info_1": "&" }, { "id": 11, @@ -151,7 +171,9 @@ "value": 0, "reward": "&", "leagueReward": "&", - "ripeTime": 0 + "ripeTime": 0, + "getWays": "&", + "info_1": "&" }, { "id": 12, @@ -165,7 +187,9 @@ "value": 0, "reward": "&", "leagueReward": "&", - "ripeTime": 0 + "ripeTime": 0, + "getWays": "62&63", + "info_1": "&" }, { "id": 13, @@ -179,7 +203,9 @@ "value": 0, "reward": "&", "leagueReward": "&", - "ripeTime": 0 + "ripeTime": 0, + "getWays": "&", + "info_1": "&" }, { "id": 14, @@ -193,7 +219,9 @@ "value": 0, "reward": "&", "leagueReward": "&", - "ripeTime": 0 + "ripeTime": 0, + "getWays": "&", + "info_1": "&" }, { "id": 15, @@ -207,6 +235,8 @@ "value": 0, "reward": "&", "leagueReward": "&", - "ripeTime": 0 + "ripeTime": 0, + "getWays": "&", + "info_1": "&" } ] \ No newline at end of file