Files
ZYZ/shared/db/Hero.ts
2022-10-24 18:46:24 +08:00

338 lines
13 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 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<Skin>;
@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, roleId: 1, hid: 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; // 当前皮肤idfashions表的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[]; // 武将装备引用数组
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 findBySeqIdRange(seqIds: Array<number>, roleId: string, lean = true) {
const hero: HeroType[] = await HeroModel.find({ seqId: { $in: seqIds }, roleId }).lean(lean);
return hero;
}
public static async findByHidRange(hids: Array<number>, 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<number>, roleId: string, select?: string, getters = false) {
const hero = await HeroModel.findByHidRange(hids, roleId, select, getters);
let map = new Map<number, HeroType>();
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 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 <HeroType[]>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<DocumentType<Hero>, keyof Hero> { };
export type HeroUpdate = Partial<HeroType>; // 将所有字段变成可选项