✨ feat(gvg): 开始挑战和结束挑战
This commit is contained in:
@@ -1093,6 +1093,7 @@ export enum ITEM_CHANGE_REASON {
|
||||
GVG_RECEIVE_LV = 168, // 领取等级奖励
|
||||
GVG_RECEIVE_TASK = 169, // 领取任务奖励
|
||||
GVG_VESTIGE_START = 170, // gvg征战中原挑战
|
||||
GVG_VESTIGE_END = 171, // gvg征战中原挑战
|
||||
}
|
||||
|
||||
export enum TA_EVENT {
|
||||
|
||||
@@ -341,6 +341,11 @@ export const STATUS = {
|
||||
GVG_VESTIGE_ERR: { code: 21350, simStr: '今日未开放该遗迹' },
|
||||
GVG_VESTIGE_TARGET_NOT_FOUND: { code: 21351, simStr: '对手未找到' },
|
||||
GVG_VESTIGE_RANK_ERR: { code: 21352, simStr: '该排名玩家不可挑战' },
|
||||
GVG_VESTIGE_REC_NOT_FOUND: { code: 21353, simStr: '未找到该条记录' },
|
||||
GVG_VESTIGE_CHECK_BATTLE_STATUS_ERR: { code: 21363, simStr: '备战时间超时' },
|
||||
GVG_VESTIGE_HERO_USED: { code: 21364, simStr: '该武将在其他遗迹已使用' },
|
||||
GVG_VESTIGE_BATTLE_END_STATUS_ERR: { code: 21365, simStr: '挑战时间超时' },
|
||||
GVG_VESTIGE_RANK_NOT_INIT: { code: 21366, simStr: '玩家数据未初始' },
|
||||
|
||||
// 通用 30000 - 30099
|
||||
DIC_DATA_NOT_FOUND: { code: 30000, simStr: '数据表未找到' },
|
||||
|
||||
@@ -25,13 +25,13 @@ export default class GVGVestige extends BaseModel {
|
||||
@prop({ required: true })
|
||||
day: number; // 每天5点
|
||||
|
||||
public static async getVestigate(groupId: number) {
|
||||
public static async getVestige(groupId: number) {
|
||||
let day = getZeroPoint();
|
||||
let result: GVGVestigeType = await GVGVestigeModel.findOne({ groupId, day }).lean();
|
||||
return result;
|
||||
}
|
||||
|
||||
public static async initTodayVestigate(groupId: number, type: number, vestiges: Vestige[]) {
|
||||
public static async initTodayVestige(groupId: number, type: number, vestiges: Vestige[]) {
|
||||
let day = getZeroPoint();
|
||||
let result: GVGVestigeType = await GVGVestigeModel.findOneAndUpdate({ groupId, day }, { $setOnInsert: { type, vestiges } }, { new: true, upsert: true }).lean();
|
||||
return result;
|
||||
|
||||
@@ -23,24 +23,28 @@ export default class GVGVestigeLock extends BaseModel {
|
||||
rank: number; // 排名
|
||||
|
||||
@prop({ required: true })
|
||||
unlockTime: number; // 锁定到什么时候
|
||||
unlockTime: number; // 锁定到什么时候,13位时间戳
|
||||
|
||||
@prop({ required: true })
|
||||
attackRoleId: string; // 锁定到什么时候
|
||||
attackRoleId: string; // 挑战玩家
|
||||
|
||||
@prop({ required: true })
|
||||
defenseRoleId: string; // 锁定到什么时候
|
||||
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();
|
||||
day, groupId, vestigeId, rank, unlockTime: { $lt: Date.now() }
|
||||
}, { $set: { unlockTime: Date.now() + GVG.GVG_VESTIGE_PREPARE_COUNTDOWN * 1000, attackRoleId, defenseRoleId } }, { new: true }).lean();
|
||||
return result;
|
||||
}
|
||||
|
||||
public static async checkBattleLock(groupId: number, vestigeId: number, rank: number, attackRoleId: string) {
|
||||
let day = getZeroPoint();
|
||||
let result: GVGVestigeLockType = await GVGVestigeLockModel.findOneAndUpdate({
|
||||
day, groupId, vestigeId, rank, attackRoleId, unlockTime: { $gt: Date.now() }
|
||||
}, { $set: { unlockTime: Date.now() + GVG.GVG_VESTIGE_BATTLE_COUNTDOWN * 1000 } }, { new: true }).lean();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -45,16 +45,13 @@ export default class GVGVestigeRank extends BaseModel {
|
||||
@prop({ required: true, default: 0 })
|
||||
historyRank: number; // 历史最高排名
|
||||
|
||||
@prop({ required: true, default: 0 })
|
||||
locked: number; // 是否被挑战
|
||||
|
||||
@prop({ required: true, default: false })
|
||||
hasAdjustLineup: boolean; // 是否调整过阵容
|
||||
hasDefense: boolean; // 是否调整过阵容,首次挑战自动保存阵容
|
||||
|
||||
@prop({ required: true, type: Lineup, default: [] })
|
||||
@prop({ required: true, type: Lineup, default: [], _id: false })
|
||||
lineup: Lineup[]; // 阵容
|
||||
|
||||
@prop({ required: true, type: Number, default: [] })
|
||||
@prop({ required: true, type: Number, default: [], _id: false })
|
||||
oppRanks: number[]; // 对手的排名
|
||||
|
||||
@prop({ required: true, default: 0 })
|
||||
@@ -86,6 +83,12 @@ export default class GVGVestigeRank extends BaseModel {
|
||||
return result;
|
||||
}
|
||||
|
||||
public static async updateByRoleId(vestigeId: number, roleId: string, update: GVGVestigeRankUpdate) {
|
||||
let today = getZeroPoint();
|
||||
let result: GVGVestigeRankType = await GVGVestigeRankModel.findOneAndUpdate({ day: today, vestigeId, roleId }, { $set: update }, { new: true, upsert: true }).lean();
|
||||
return result;
|
||||
}
|
||||
|
||||
public static async findByRanks(groupId: number, vestigeId: number, ranks: number[]) {
|
||||
let today = getZeroPoint();
|
||||
let result: GVGVestigeRankType[] = await GVGVestigeRankModel.find({ day: today, vestigeId, groupId, rank: { $in: ranks } })
|
||||
@@ -96,13 +99,45 @@ export default class GVGVestigeRank extends BaseModel {
|
||||
|
||||
public static async updateCe(roleId: string, hid: number, ce: number) {
|
||||
let today = getZeroPoint();
|
||||
await GVGVestigeRankModel.updateMany({ day: today, roleId, 'lineup.hid': hid }, { $set: { 'lineupCe.$.ce': ce } });
|
||||
await GVGVestigeRankModel.updateMany({ day: today, hasDefense: true, roleId, 'lineup.hid': hid }, { $set: { 'lineupCe.$.ce': ce } });
|
||||
}
|
||||
|
||||
public static async changeRank(isSuccess: boolean, attackInfo: { roleId: string }, defenseInfo: { isRobot: boolean, roleId: string, oldRank: number }): Promise<{ isChange: boolean, atkData?: GVGVestigeRankType, defData?: GVGVestigeRankType, endTime: number }> {
|
||||
const session = await mongoose.connection.startSession();
|
||||
session.startTransaction()
|
||||
try {
|
||||
let newAtkData: GVGVestigeRankType, newDefData: GVGVestigeRankType, isChange = false, endTime = Date.now();
|
||||
if(defenseInfo.isRobot) {
|
||||
let atkData = await GVGVestigeRankModel.findOne({ roleId: attackInfo.roleId }).lean();
|
||||
if(isSuccess && atkData && (atkData.rank == 0 || atkData.rank > defenseInfo.oldRank)) {
|
||||
newAtkData = await GVGVestigeRankModel.findOneAndUpdate({ roleId: attackInfo.roleId }, { $set: { rank: defenseInfo.oldRank, oldRank: atkData.rank } }, { new: true, session }).lean();
|
||||
isChange = true;
|
||||
endTime = Date.now();
|
||||
} else {
|
||||
newAtkData = atkData;
|
||||
}
|
||||
} else {
|
||||
let atkData = await GVGVestigeRankModel.findOne({ roleId: attackInfo.roleId }).lean();
|
||||
let defData = await GVGVestigeRankModel.findOne({ roleId: defenseInfo.roleId }).lean();
|
||||
if(isSuccess && atkData && defData && (atkData.rank == 0 || atkData.rank > defData.rank)) {
|
||||
newAtkData = await GVGVestigeRankModel.findOneAndUpdate({ roleId: attackInfo.roleId }, { $set: { rank: defData.rank, oldRank: atkData.rank } }, { new: true, session }).lean();
|
||||
newDefData = await GVGVestigeRankModel.findOneAndUpdate({ roleId: defenseInfo.roleId }, { $set: { rank: atkData.rank, oldRank: defData.rank } }, { new: true, session }).lean();
|
||||
isChange = true;
|
||||
endTime = Date.now();
|
||||
} else {
|
||||
newAtkData = atkData;
|
||||
newDefData = defData;
|
||||
}
|
||||
}
|
||||
await session.commitTransaction()
|
||||
return { isChange, atkData: newAtkData, defData: newDefData, endTime }
|
||||
} finally {
|
||||
session.endSession()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const GVGVestigeRankModel = getModelForClass(GVGVestigeRank);
|
||||
|
||||
export interface GVGVestigeRankType extends Pick<DocumentType<GVGVestigeRank>, keyof GVGVestigeRank> {
|
||||
id: number;
|
||||
};
|
||||
export interface GVGVestigeRankType extends Pick<DocumentType<GVGVestigeRank>, keyof GVGVestigeRank> {};
|
||||
export type GVGVestigeRankUpdate = Partial<GVGVestigeRankType>; // 将所有字段变成可选项
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
// 征战中原对手锁
|
||||
import BaseModel from './BaseModel';
|
||||
import { index, getModelForClass, prop, DocumentType } from '@typegoose/typegoose';
|
||||
import { OppPlayerInfo } from '../domain/gvgField/gvgDb';
|
||||
import { OppPlayerInfo, OppPlayerHeroInfo } from '../domain/gvgField/gvgDb';
|
||||
import { genCode } from '../pubUtils/util';
|
||||
import { VESTIGE_STATUS } from '../consts';
|
||||
|
||||
class LeagueGood {
|
||||
@prop({ required: true })
|
||||
@@ -16,12 +15,16 @@ class LeagueGood {
|
||||
count: number
|
||||
}
|
||||
|
||||
@index({ battleCode: 1 })
|
||||
@index({ day: 1, groupId: 1, vestigeId: 1, rank: 1 })
|
||||
export default class GVGVestigeRec extends BaseModel {
|
||||
|
||||
@prop({ required: true })
|
||||
battleCode: string; // 唯一code
|
||||
|
||||
@prop({ required: true })
|
||||
warId: number; // 关卡id
|
||||
|
||||
@prop({ required: true })
|
||||
attackRoleId: string; // 攻方玩家id
|
||||
|
||||
@@ -35,7 +38,7 @@ export default class GVGVestigeRec extends BaseModel {
|
||||
vestigeId: number; // 遗迹id
|
||||
|
||||
@prop({ required: true })
|
||||
status: number; // 1-出兵中 2-挑战中 3-挑战完毕 4-取消
|
||||
cancel: boolean; // 是否取消了
|
||||
|
||||
@prop({ required: true })
|
||||
checkTime: number; // 开始出兵的时间,13位时间戳
|
||||
@@ -58,19 +61,50 @@ export default class GVGVestigeRec extends BaseModel {
|
||||
@prop({ required: true, default: false })
|
||||
hasRpl: boolean; // 是否存在对应录像
|
||||
|
||||
public static async createRec(configId: number, vestigeId: number, groupId: number, attackInfo: OppPlayerInfo, defenseInfo: OppPlayerInfo, leagueGoods: LeagueGood[]) {
|
||||
public static async createRec(configId: number, vestigeId: number, groupId: number, warId: 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 }
|
||||
$set: { configId, vestigeId, groupId, attackRoleId: attackInfo.roleId, defenseRoleId: defenseInfo.roleId, warId, checkTime: Date.now(), attackInfo, defenseInfo, leagueGoods }
|
||||
}, { new: true, upsert: true }).lean();
|
||||
return result;
|
||||
}
|
||||
|
||||
public static async findByBattleCode(battleCode: string, select = '') {
|
||||
const result: GVGVestigeRecType = await GVGVestigeRecModel.findOne({ battleCode }).select(select).lean();
|
||||
return result;
|
||||
}
|
||||
|
||||
public static async startBattle(battleCode: string, attackHeroes: OppPlayerHeroInfo[]) {
|
||||
const result: GVGVestigeRecType = await GVGVestigeRecModel.findOneAndUpdate({ battleCode }, {
|
||||
$set: { battleTime: Date.now(), 'attackInfo.heroes': attackHeroes }
|
||||
}, { new: true }).lean();
|
||||
return result;
|
||||
}
|
||||
|
||||
public static async battleEnd(battleCode: string, isSuccess: boolean, endTime: number, atkNewRank?: number, defNewRank?: number) {
|
||||
let update: GVGVestigeRecUpdate = { endTime };
|
||||
if(isSuccess) {
|
||||
update['attackInfo.isSuccess'] = true;
|
||||
update['defenseInfo.isSuccess'] = false;
|
||||
}
|
||||
if(atkNewRank != undefined && defNewRank != undefined) {
|
||||
update['attackInfo.newRank'] = atkNewRank;
|
||||
update['attackInfo.oldRank'] = defNewRank;
|
||||
update['defenseInfo.newRank'] = defNewRank;
|
||||
update['defenseInfo.oldRank'] = atkNewRank;
|
||||
}
|
||||
|
||||
const result: GVGVestigeRecType = await GVGVestigeRecModel.findOneAndUpdate({ battleCode }, { $set: update }, { new: true }).lean();
|
||||
return result;
|
||||
}
|
||||
|
||||
public static async giveup(roleId: string, battleCode: string) {
|
||||
const result: GVGVestigeRecType = await GVGVestigeRecModel.findOneAndUpdate({ attackRoleId: roleId, battleCode }, { $set: { cancel: true }}, { new: true }).lean();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
export const GVGVestigeRecModel = getModelForClass(GVGVestigeRec);
|
||||
|
||||
export interface GVGVestigeRecType extends Pick<DocumentType<GVGVestigeRec>, keyof GVGVestigeRec> {
|
||||
id: number;
|
||||
};
|
||||
export interface GVGVestigeRecType extends Pick<DocumentType<GVGVestigeRec>, keyof GVGVestigeRec> {};
|
||||
export type GVGVestigeRecUpdate = Partial<GVGVestigeRecType>; // 将所有字段变成可选项
|
||||
|
||||
@@ -1,30 +1,31 @@
|
||||
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";
|
||||
import { DicWarJson } from "../../pubUtils/dictionary/DicWarJson";
|
||||
|
||||
export class OppPlayerHeroInfo {
|
||||
@prop({ required: true })
|
||||
hid: number; // 武将id
|
||||
hid: number; // 武将id
|
||||
@prop({ required: true })
|
||||
skinId: number; // 皮肤id
|
||||
skinId: number; // 皮肤id
|
||||
@prop({ required: true })
|
||||
quality: number; // 品质
|
||||
quality: number; // 品质
|
||||
@prop({ required: true })
|
||||
star: number; // 星级
|
||||
star: number; // 星级
|
||||
@prop({ required: true })
|
||||
colorStar: number; // 彩星
|
||||
@prop({ required: true })
|
||||
lv: number; // 等级
|
||||
lv: number; // 等级
|
||||
@prop({ required: true })
|
||||
dataId: number; // 出兵表上所占位置
|
||||
dataId: number; // 出兵表上所占位置
|
||||
@prop({ required: true })
|
||||
order: number; // 行动顺序
|
||||
order: number; // 行动顺序
|
||||
@prop({ required: true })
|
||||
ce: number; // 战力
|
||||
|
||||
setByWarJson(warJson: DicWarJson) {
|
||||
this.hid = warJson.actorId;
|
||||
@@ -46,13 +47,14 @@ export class OppPlayerHeroInfo {
|
||||
}
|
||||
}
|
||||
|
||||
setByDefenseHero(hero: HeroType, lineup: Lineup[]) {
|
||||
setByDefenseHero(hero: HeroType, lineup: {actorId: number, dataId: number, order: number}[]) {
|
||||
this.hid = hero.hid;
|
||||
this.skinId = hero.skinId;
|
||||
this.quality = hero.quality;
|
||||
this.star = hero.star;
|
||||
this.colorStar = hero.colorStar;
|
||||
this.lv = hero.lv;
|
||||
this.ce = hero.ce;
|
||||
let curLineup = lineup.find(cur => cur.actorId == hero.hid);
|
||||
if(curLineup) {
|
||||
this.dataId = curLineup.dataId;
|
||||
@@ -90,7 +92,7 @@ export class OppPlayerInfo {
|
||||
@prop({ required: true })
|
||||
isRobot: boolean = false; // 原排名
|
||||
|
||||
initByPlayer(rank: number, role: RoleType, heroes: HeroType[], lineup: Lineup[], isSuccess: boolean ) {
|
||||
initByPlayer(rank: number, role: RoleType, isSuccess: boolean ) {
|
||||
this.oldRank = rank;
|
||||
this.newRank = rank;
|
||||
this.roleId = role.roleId;
|
||||
@@ -101,13 +103,16 @@ export class OppPlayerInfo {
|
||||
this.title = role.title;
|
||||
this.lv = role.lv;
|
||||
this.isSuccess = isSuccess;
|
||||
this.isRobot = false;
|
||||
}
|
||||
|
||||
setPlayerHeroes(heroes: HeroType[], lineup: {actorId: number, dataId: number, order: number}[]) {
|
||||
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) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[
|
||||
{
|
||||
"id": 1,
|
||||
"cityType": 1,
|
||||
"cityType": 3,
|
||||
"foodAdd": 10,
|
||||
"mineralAdd": 10,
|
||||
"woodAdd": 10
|
||||
@@ -15,7 +15,7 @@
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"cityType": 3,
|
||||
"cityType": 1,
|
||||
"foodAdd": 50,
|
||||
"mineralAdd": 50,
|
||||
"woodAdd": 50
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -20,104 +20,5 @@
|
||||
"additionValue": "6&1000",
|
||||
"content": "策士",
|
||||
"position": "&"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"vestigeId": 3,
|
||||
"name": "燕都遗址",
|
||||
"mapType": "1&2",
|
||||
"imageName": "&",
|
||||
"additionType": 1,
|
||||
"additionValue": "5&1000",
|
||||
"content": "游侠",
|
||||
"position": "&"
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"vestigeId": 4,
|
||||
"name": "韩都遗址",
|
||||
"mapType": "1&2",
|
||||
"imageName": "&",
|
||||
"additionType": 1,
|
||||
"additionValue": "4&1000",
|
||||
"content": "弓兵",
|
||||
"position": "&"
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"vestigeId": 5,
|
||||
"name": "赵都遗址",
|
||||
"mapType": "1&2",
|
||||
"imageName": "&",
|
||||
"additionType": 1,
|
||||
"additionValue": "3&1000",
|
||||
"content": "骑兵",
|
||||
"position": "&"
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"vestigeId": 6,
|
||||
"name": "魏都遗址",
|
||||
"mapType": "1&2",
|
||||
"imageName": "&",
|
||||
"additionType": 1,
|
||||
"additionValue": "1&1000",
|
||||
"content": "步兵",
|
||||
"position": "&"
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"vestigeId": 7,
|
||||
"name": "山巅帝陵",
|
||||
"mapType": "1&2",
|
||||
"imageName": "&",
|
||||
"additionType": 2,
|
||||
"additionValue": "1&1000",
|
||||
"content": "魏国",
|
||||
"position": "&"
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"vestigeId": 8,
|
||||
"name": "崖底河泽",
|
||||
"mapType": "1&2",
|
||||
"imageName": "&",
|
||||
"additionType": 2,
|
||||
"additionValue": "2&1000",
|
||||
"content": "吴国",
|
||||
"position": "&"
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"vestigeId": 9,
|
||||
"name": "深山迷林",
|
||||
"mapType": "1&2",
|
||||
"imageName": "&",
|
||||
"additionType": 2,
|
||||
"additionValue": "3&1000",
|
||||
"content": "蜀国",
|
||||
"position": "&"
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
"vestigeId": 10,
|
||||
"name": "桃源古村",
|
||||
"mapType": "1&2",
|
||||
"imageName": "&",
|
||||
"additionType": 2,
|
||||
"additionValue": "4&1000",
|
||||
"content": "群雄",
|
||||
"position": "&"
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"vestigeId": 11,
|
||||
"name": "鹿鸣幽谷",
|
||||
"mapType": "1&2",
|
||||
"imageName": "&",
|
||||
"additionType": 3,
|
||||
"additionValue": "2&1000",
|
||||
"content": "女性",
|
||||
"position": "&"
|
||||
}
|
||||
]
|
||||
Reference in New Issue
Block a user