import BaseModel from './BaseModel'; import { index, getModelForClass, prop, Ref, mongoose, DocumentType } from '@typegoose/typegoose'; // import Equip, { } from './Equip'; import { DEFAULT_HERO_LV } from '../consts'; import Skin, { SkinUpdate } from './Skin'; import { SearchHeroParam } from '../domain/backEndField/search'; import { Reward } from '../domain/battleField/pvp'; import { gameData, getHeroExpByLv, getHeroInitTalent } from '../pubUtils/data'; /** * 英雄表 */ export class Connect { @prop({ required: true }) shipId: number; @prop({ required: true }) level: number; @prop({ required: true }) exp: number; } export class Talent { @prop({ required: true }) id: number; // 天赋表id @prop({ required: true }) level: number; // 激活等级 constructor(id: number, level = 1) { this.id = id; this.level = level; } } export class HeroSkin { @prop({ required: true }) id: number; @prop({ required: true }) skinId: number; // fashions表的heroId字段 @prop({ ref: 'Skin', type: mongoose.Schema.Types.ObjectId }) skin: Ref; @prop({ required: true }) enable: boolean; @prop({ required: true, type: Talent, _id: false }) talent: Talent[]; // 天赋树 @prop({ required: true }) usedTalentPoint: number; // 已使用的天赋点数 constructor(skin: SkinUpdate, enable = true) { this.id = skin.id; this.skinId = skin.skinId; this.skin = skin._id; this.enable = enable; this.talent = getHeroInitTalent(skin.skinId); this.usedTalentPoint = 0; } } export class Stone { @prop({ required: true }) id: number; @prop({ required: true }) stone: number; } export class EPlace { @prop({ required: true }) id: number; @prop({ required: true }) equipId: number; @prop({ required: true }) lv: number = 0; @prop({ required: true }) quality: number = 1; @prop({ required: true }) qualityStage: number = 0; @prop({ required: true }) star: number = 0; @prop({ required: true }) starStage: number = 0; @prop({ required: true, type: Stone, _id: false }) stones: Stone[]; @prop({ required: true }) jewel: number = 0; getInitialStone?() { let result: Stone[] = []; for (let id = 1; id <= 3; id++) { result.push({ id, stone: 0 }); } return result; } constructor(id: number, equipId: number) { this.id = id; this.equipId = equipId; this.stones = this.getInitialStone(); } } @index({ seqId: 1 }) @index({ roleId: 1, hid: 1 }) @index({ roleId: 1, ce: -1 }) export default class Hero extends BaseModel { @prop({ required: true }) roleId: string; // 角色 id @prop({ required: true }) roleName: string; // 角色名称 @prop({ required: true }) serverId: number; // 区服 id @prop({ required: true }) hid: number; // 武将 id @prop({ required: true }) hName: string; // 武将名 @prop({ required: true }) seqId: number; // 武将表自增 id @prop({ required: true, default: 0 }) exp: number; // 经验值 @prop({ required: true, default: 1 }) lv: number; // 武将等级 @prop({ required: true, default: 0 }) ce: number; // 武将战力 @prop({ required: true, default: 1 }) star: number; // 星级 @prop({ required: true, default: 0 }) starStage: number; // 星级六维阶段 @prop({ required: true, default: 0 }) colorStar: number; // 觉醒, 彩星 @prop({ required: true, default: 0 }) colorStarStage: number; // 觉醒六维阶段 @prop({ required: true, default: 0 }) quality: number; // 品质 @prop({ required: true, default: false }) scrollActive: boolean; // 是否在名将谱中激活 @prop({ required: true, default: 0 }) scrollId: number; // 名将谱id @prop({ required: true, default: 0 }) scrollStar: number; // 名将谱中保存的星级 @prop({ required: true, default: 0 }) scrollColorStar: number; // 名将谱中保存的觉醒等级 @prop({ required: true, default: 0 }) scrollQuality: number; // 名将谱中保存的品质 @prop({ required: true, default: 0 }) job: number; // 职业 @prop({ required: true, default: 0 }) jobStage: number; // 职阶 @prop({ required: true, default: 0 }) skinId: number; // 当前皮肤id,fashions表的heroId字段 @prop({ required: true, type: Connect, default: [], _id: false }) connections: Connect[]; // 羁绊 @prop({ required: true, type: HeroSkin, default: [], _id: false }) skins: HeroSkin[]; // 皮肤 @prop({ required: true, type: EPlace, default: [], _id: false }) ePlace: EPlace[]; // 武将装备引用数组 @prop({ required: true, type: Reward, default: [], _id: false }) consumes: Reward[]; // 消耗 @prop({ required: true, default: 0 }) artifact: number | string; // 宝物 @prop({ required: true, default: 0 }) subHid: number; // 副将 @prop({ required: true, default: 0 }) subActorId: number; // 副将 public static async findByRole(roleId: string, sort: { field: string, sortBy: number }[] = [], select?: string, getters = false) { let sortParam = {}; for (let { field, sortBy } of sort) { sortParam[field] = sortBy; } const heros: HeroType[] = await HeroModel.find({ roleId }).sort(sortParam).select(select).lean({ getters }); return heros; } public static async findAndSortByCe(roleId: string) { const heroes: HeroType[] = await HeroModel.find({ roleId }).sort({ ce: -1 }).lean(); return heroes } public static async findBySeqIdRange(seqIds: Array, roleId: string, lean = true) { const hero: HeroType[] = await HeroModel.find({ seqId: { $in: seqIds }, roleId }).lean(lean); return hero; } public static async findByHidRange(hids: Array, roleId: string, select?: string, getters = false) { const hero: HeroType[] = await HeroModel.find({ hid: { $in: hids }, roleId }).select(select).lean({ getters }); return hero; } public static async checkEquipByQuality(roleId: string, quality: number) { const result = await HeroModel.exists({ roleId, 'ePlace.quality': { $gte: quality } }); return result; } public static async findMapByHidRange(hids: Array, roleId: string, select?: string, getters = false) { const hero = await HeroModel.findByHidRange(hids, roleId, select, getters); let map = new Map(); for (let h of hero) { map.set(h.hid, h); } return map; } public static async findBySeqIdAndRole(seqId: number, roleId: string, lean = true) { const hero: HeroType = await HeroModel.findOne({ seqId, roleId }).lean(lean); return hero; } public static async findByHidAndRole(hid: number, roleId: string, select?: string, getters = false) { const hero: HeroType = await HeroModel.findOne({ hid, roleId }).select(select).lean({ getters }); return hero; } public static async findByHidsAndRole(roleId: string, hids: Array, select?: string, getters = false) { const heros: HeroType[] = await HeroModel.find({ roleId, hid: { $in: hids } }).select(select).lean({ getters }); return heros; } public static async findSubHero(roleId: string, subHid: number) { const hero: HeroType = await HeroModel.findOne({ roleId, subHid }).lean(); return hero; } // 设置副将 public static async setSubHero(roleId: string, hid: number, subSkinId: number, subActorId: number) { const preHero = subActorId > 0 ? await HeroModel.findOneAndUpdate({ roleId, subActorId }, { $set: { subActorId: 0, subHid: 0 } }, { new: true }).lean() : null; const curHero: HeroType = await HeroModel.findOneAndUpdate({ roleId, hid }, { $set: { subHid: subSkinId, subActorId } }, { new: true }).lean(); return { preHid: preHero?.hid || 0, curHero }; } public static async addEquip(roleId: string, hid: number, ePlaceId: number, equipId: string) { const hero: HeroType = await HeroModel.findOneAndUpdate( { roleId, hid, 'ePlace.id': ePlaceId }, { $set: { 'ePlace.$.equip': equipId } }, { new: true }).populate('ePlace.equip').lean(); return hero; } public static async removeEquip(roleId: string, hid: number, ePlaceId: number, lean = true) { const hero: HeroType = await HeroModel.findOneAndUpdate( { roleId, hid, 'ePlace.id': ePlaceId }, { $set: { 'ePlace.$.equip': null } }, { new: true }).populate('ePlace.equip').lean(lean); return hero; } public static getInitInfo(hid: number, heroInfo: HeroUpdate = {}): HeroUpdate { let dicHero = gameData.hero.get(hid) let { quality, initialStar: star, initialColorStar: colorStar, jobid: job, name: hName } = dicHero; const doc = new HeroModel(); const update = { ...doc.toJSON(), hid, skinId: hid, hName, star, colorStar, quality, job, lv: DEFAULT_HERO_LV, exp: getHeroExpByLv(DEFAULT_HERO_LV - 1) || 0, ...heroInfo }; delete update._id; return update } // public static async createHero(heroInfo: HeroUpdate, lean = true) { // const seqId = await CounterModel.getNewCounter(COUNTER.HID) || -1; // const update = this.getInitInfo(heroInfo.hid, { ...heroInfo, seqId }); // const hero: HeroType = await HeroModel.findOneAndUpdate({ roleId: heroInfo.roleId, hid: heroInfo.hid }, update, { upsert: true, new: true }).lean(lean); // return hero; // } // public static async insertHeroes(roleId: string, roleName: string, serverId: number, heroInfos: HeroUpdate[]) { // let insertInfos: HeroUpdate[] = []; // for(let hero of heroInfos) { // const seqId = await CounterModel.getNewCounter(COUNTER.HID) || -1; // insertInfos.push({ ...hero, seqId, roleId, roleName, serverId }) // } // await HeroModel.insertMany(insertInfos); // return insertInfos; // } public static async sumHeroCe(roleId: string) { let ce: Array<{ ce: number }> = await HeroModel.aggregate([ { $match: { roleId } }, { $group: { _id: null, ce: { $sum: '$ce' } } } ]); return ce.length > 0 ? ce[0].ce : 0; } public static async getTopHero(roleId: string, num: number, lean = true) { const heroes: HeroType[] = await HeroModel.find({ roleId }).limit(num).sort({ ce: -1 }).lean(lean); return heroes; } public static async deleteAccount(roleId: string) { let result = await HeroModel.deleteMany({ roleId }); return result; } public static async deleteHero(roleId: string, hid: number) { let result = await HeroModel.deleteMany({ roleId, hid }); return result; } public static async updateHeroInfo(roleId: string, hid: number, heroUpdate: HeroUpdate, select?: string, getters = false) { delete heroUpdate._id; let result: HeroType = await HeroModel.findOneAndUpdate({ roleId, hid }, { $set: heroUpdate }, { new: true, upsert: true }).select(select).lean({ getters }); return result; } public static async unloadHeroAndEquip(roleId: string, hid: number, id: number, lean = true) { const hero: HeroType = await HeroModel.findOneAndUpdate( { roleId, hid, 'ePlace.id': id }, { $set: { 'ePlace.$.equip': null } }, { new: true }).lean(lean); return hero; } public static async getAllRank(serverId: number, select?: string, limit = 200) { let result: HeroType[] = await HeroModel.find({ serverId }, { _id: false }).select(select).limit(limit).sort({ ce: -1, updatedAt: 1 }).lean({ getters: true }); return result; } public static async getMyTopHero(roleId: string, select?: string) { let result: HeroType = await HeroModel.findOne({ roleId }, { _id: false }).select(select).sort({ ce: -1, updatedAt: 1 }).lean({ getters: true }); return result; } public static async getRank(hid: number, serverId: number, select?: string, limit = 200) { let result: HeroType[] = await HeroModel.find({ serverId, hid }, { _id: false }).select(select).limit(limit).lean({ getters: true }); return result; } private static getSearchObj(form: SearchHeroParam) { let searchObj = {}; if (form.roleId) searchObj['roleId'] = form.roleId; if (form.roleName) searchObj['roleName'] = { $regex: new RegExp(form.roleName.toString(), 'i') }; if (form.hid) searchObj['hid'] = form.hid; return searchObj } public static async findByCondition(page: number, pageSize: number, sortField: string = 'updatedAt', sortOrder: string = 'descend', form: SearchHeroParam = {}) { let searchObj = this.getSearchObj(form); let sort = {}; if (sortField && sortOrder) { if (sortOrder == 'ascend') { sort[sortField] = 1; } else if (sortOrder == 'descend') { sort[sortField] = -1; } } const result: HeroType[] = await HeroModel.find(searchObj).limit(pageSize).skip((page - 1) * pageSize).sort(sort).lean({ getters: true, virtuals: true }); return result; } public static async countByCondition(form: SearchHeroParam = {}) { let searchObj = this.getSearchObj(form); const result = await HeroModel.count(searchObj); return result; } } export const HeroModel = getModelForClass(Hero); export interface HeroType extends Pick, keyof Hero> { }; export type HeroUpdate = Partial; // 将所有字段变成可选项